Skip to content

Commit 5019ec0

Browse files
committed
Adds Conditions to MachineDeployment. In particular, it adds
`AvailableCondition` and `ReadyCondition`.
1 parent 6c8b2dc commit 5019ec0

10 files changed

+248
-13
lines changed

api/v1alpha3/conversion.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,9 @@ func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error {
137137
dst.Spec.Strategy.RollingUpdate = &v1alpha4.MachineRollingUpdateDeployment{}
138138
}
139139
dst.Spec.Strategy.RollingUpdate.DeletePolicy = restored.Spec.Strategy.RollingUpdate.DeletePolicy
140-
141140
}
142141

142+
dst.Status.Conditions = restored.Status.Conditions
143143
return nil
144144
}
145145

@@ -158,6 +158,10 @@ func (dst *MachineDeployment) ConvertFrom(srcRaw conversion.Hub) error {
158158
return nil
159159
}
160160

161+
func Convert_v1alpha4_MachineDeploymentStatus_To_v1alpha3_MachineDeploymentStatus(in *v1alpha4.MachineDeploymentStatus, out *MachineDeploymentStatus, s apiconversion.Scope) error {
162+
return autoConvert_v1alpha4_MachineDeploymentStatus_To_v1alpha3_MachineDeploymentStatus(in, out, s)
163+
}
164+
161165
func (src *MachineDeploymentList) ConvertTo(dstRaw conversion.Hub) error {
162166
dst := dstRaw.(*v1alpha4.MachineDeploymentList)
163167

api/v1alpha3/zz_generated.conversion.go

Lines changed: 6 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v1alpha4/condition_consts.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,14 @@ const (
201201
// from making any further remediations.
202202
TooManyUnhealthyReason = "TooManyUnhealthy"
203203
)
204+
205+
// Conditions and condition Reasons for MachineDeployments
206+
207+
const (
208+
// MachineDeploymentAvailableCondition means the MachineDeployment is available, that is, at least the minimum available
209+
// machines required (i.e. Spec.Replicas-MaxUnavailable) are up and running for at least minReadySeconds.
210+
MachineDeploymentAvailableCondition ConditionType = "Available"
211+
212+
// MinimumMachinesAvailableReason reflects the minimum availability of machines for a machinedeployment.
213+
MinimumMachinesAvailableReason = "MinimumMachinesAvailable"
214+
)

api/v1alpha4/machinedeployment_types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ type MachineDeploymentStatus struct {
207207
// Phase represents the current phase of a MachineDeployment (ScalingUp, ScalingDown, Running, Failed, or Unknown).
208208
// +optional
209209
Phase string `json:"phase,omitempty"`
210+
211+
// Conditions defines current service state of the MachineDeployment.
212+
// +optional
213+
Conditions Conditions `json:"conditions,omitempty"`
210214
}
211215

212216
// ANCHOR_END: MachineDeploymentStatus
@@ -283,3 +287,11 @@ type MachineDeploymentList struct {
283287
func init() {
284288
SchemeBuilder.Register(&MachineDeployment{}, &MachineDeploymentList{})
285289
}
290+
291+
func (in *MachineDeployment) GetConditions() Conditions {
292+
return in.Status.Conditions
293+
}
294+
295+
func (in *MachineDeployment) SetConditions(conditions Conditions) {
296+
in.Status.Conditions = conditions
297+
}

api/v1alpha4/zz_generated.deepcopy.go

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/cluster.x-k8s.io_machinedeployments.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,50 @@ spec:
864864
minReadySeconds) targeted by this deployment.
865865
format: int32
866866
type: integer
867+
conditions:
868+
description: Conditions defines current service state of the MachineDeployment.
869+
items:
870+
description: Condition defines an observation of a Cluster API resource
871+
operational state.
872+
properties:
873+
lastTransitionTime:
874+
description: Last time the condition transitioned from one status
875+
to another. This should be when the underlying condition changed.
876+
If that is not known, then using the time when the API field
877+
changed is acceptable.
878+
format: date-time
879+
type: string
880+
message:
881+
description: A human readable message indicating details about
882+
the transition. This field may be empty.
883+
type: string
884+
reason:
885+
description: The reason for the condition's last transition
886+
in CamelCase. The specific API may choose whether or not this
887+
field is considered a guaranteed API. This field may not be
888+
empty.
889+
type: string
890+
severity:
891+
description: Severity provides an explicit classification of
892+
Reason code, so the users or machines can immediately understand
893+
the current situation and act accordingly. The Severity field
894+
MUST be set only when Status=False.
895+
type: string
896+
status:
897+
description: Status of the condition, one of True, False, Unknown.
898+
type: string
899+
type:
900+
description: Type of condition in CamelCase or in foo.example.com/CamelCase.
901+
Many .condition.type values are consistent across resources
902+
like Available, but because arbitrary conditions can be useful
903+
(see .node.status.conditions), the ability to deconflict is
904+
important.
905+
type: string
906+
required:
907+
- status
908+
- type
909+
type: object
910+
type: array
867911
observedGeneration:
868912
description: The generation observed by the deployment controller.
869913
format: int64

controllers/machinedeployment_controller.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
3232
"sigs.k8s.io/cluster-api/util"
3333
"sigs.k8s.io/cluster-api/util/annotations"
34+
"sigs.k8s.io/cluster-api/util/conditions"
3435
"sigs.k8s.io/cluster-api/util/patch"
3536
"sigs.k8s.io/cluster-api/util/predicates"
3637
ctrl "sigs.k8s.io/controller-runtime"
@@ -129,7 +130,12 @@ func (r *MachineDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Re
129130

130131
defer func() {
131132
// Always attempt to patch the object and status after each reconciliation.
132-
if err := patchHelper.Patch(ctx, deployment); err != nil {
133+
// Patch ObservedGeneration only if the reconciliation completed successfully
134+
patchOpts := []patch.Option{}
135+
if reterr == nil {
136+
patchOpts = append(patchOpts, patch.WithStatusObservedGeneration{})
137+
}
138+
if err := patchMachineDeployment(ctx, patchHelper, deployment, patchOpts...); err != nil {
133139
reterr = kerrors.NewAggregate([]error{reterr, err})
134140
}
135141
}()
@@ -148,6 +154,24 @@ func (r *MachineDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Re
148154
return result, err
149155
}
150156

157+
func patchMachineDeployment(ctx context.Context, patchHelper *patch.Helper, d *clusterv1.MachineDeployment, options ...patch.Option) error {
158+
// Always update the readyCondition by summarizing the state of other conditions.
159+
conditions.SetSummary(d,
160+
conditions.WithConditions(
161+
clusterv1.MachineDeploymentAvailableCondition,
162+
),
163+
)
164+
165+
// Patch the object, ignoring conflicts on the conditions owned by this controller.
166+
options = append(options,
167+
patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
168+
clusterv1.ReadyCondition,
169+
clusterv1.MachineDeploymentAvailableCondition,
170+
}},
171+
)
172+
return patchHelper.Patch(ctx, d, options...)
173+
}
174+
151175
func (r *MachineDeploymentReconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster, d *clusterv1.MachineDeployment) (ctrl.Result, error) {
152176
log := ctrl.LoggerFrom(ctx)
153177
log.V(4).Info("Reconcile MachineDeployment")

controllers/machinedeployment_controller_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,21 @@ var _ = Describe("MachineDeployment Reconciler", func() {
389389
return len(machineSets.Items)
390390
}, timeout*5).Should(BeEquivalentTo(0))
391391

392+
By("Verifying MachineDeployment has correct Conditions")
393+
Eventually(func() bool {
394+
key := client.ObjectKey{Name: deployment.Name, Namespace: deployment.Namespace}
395+
if err := testEnv.Get(ctx, key, deployment); err != nil {
396+
return false
397+
}
398+
var availableCond bool
399+
for _, c := range deployment.Status.Conditions {
400+
if c.Type == clusterv1.MachineDeploymentAvailableCondition && c.Status == corev1.ConditionTrue {
401+
availableCond = true
402+
}
403+
}
404+
return availableCond
405+
}, timeout).Should(BeTrue())
406+
392407
// Validate that the controller set the cluster name label in selector.
393408
Expect(deployment.Status.Selector).To(ContainSubstring(testCluster.Name))
394409
})

controllers/machinedeployment_sync.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
3333
"sigs.k8s.io/cluster-api/controllers/mdutil"
3434
"sigs.k8s.io/cluster-api/util"
35+
"sigs.k8s.io/cluster-api/util/conditions"
3536
"sigs.k8s.io/cluster-api/util/patch"
3637
ctrl "sigs.k8s.io/controller-runtime"
3738
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -349,6 +350,14 @@ func (r *MachineDeploymentReconciler) scale(ctx context.Context, deployment *clu
349350
// syncDeploymentStatus checks if the status is up-to-date and sync it if necessary.
350351
func (r *MachineDeploymentReconciler) syncDeploymentStatus(allMSs []*clusterv1.MachineSet, newMS *clusterv1.MachineSet, d *clusterv1.MachineDeployment) error {
351352
d.Status = calculateStatus(allMSs, newMS, d)
353+
354+
minReplicasNeeded := *(d.Spec.Replicas) - mdutil.MaxUnavailable(*d)
355+
if d.Status.AvailableReplicas >= minReplicasNeeded {
356+
// NOTE: The structure of calculateStatus() does not allow us to update the machinedeployment directly, we can only update the status obj it returns. Ideally, we should change calculateStatus() --> updateStatus() to be consistent with the rest of the code base, until then, we update conditions here.
357+
conditions.MarkTrue(d, clusterv1.MachineDeploymentAvailableCondition)
358+
} else {
359+
conditions.MarkFalse(d, clusterv1.MachineDeploymentAvailableCondition, clusterv1.MinimumMachinesAvailableReason, clusterv1.ConditionSeverityWarning, "Minimum availability requires %d replicas, current %d available", minReplicasNeeded, d.Status.AvailableReplicas)
360+
}
352361
return nil
353362
}
354363

@@ -376,6 +385,7 @@ func calculateStatus(allMSs []*clusterv1.MachineSet, newMS *clusterv1.MachineSet
376385
ReadyReplicas: mdutil.GetReadyReplicaCountForMachineSets(allMSs),
377386
AvailableReplicas: availableReplicas,
378387
UnavailableReplicas: unavailableReplicas,
388+
Conditions: deployment.Status.Conditions,
379389
}
380390

381391
if *deployment.Spec.Replicas == status.ReadyReplicas {

0 commit comments

Comments
 (0)