Skip to content

Commit a3e4b37

Browse files
authored
Merge pull request kubernetes-sigs#5056 from Nordix/ms-avail-condition
✨ Add Conditions to MachineSet for Machine Create and Ready
2 parents 6a81b84 + 5883e99 commit a3e4b37

8 files changed

+340
-21
lines changed

api/v1alpha3/conversion.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,35 @@ func (dst *MachineList) ConvertFrom(srcRaw conversion.Hub) error {
130130
func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error {
131131
dst := dstRaw.(*v1alpha4.MachineSet)
132132

133-
return Convert_v1alpha3_MachineSet_To_v1alpha4_MachineSet(src, dst, nil)
133+
if err := Convert_v1alpha3_MachineSet_To_v1alpha4_MachineSet(src, dst, nil); err != nil {
134+
return err
135+
}
136+
// Manually restore data.
137+
restored := &v1alpha4.MachineSet{}
138+
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
139+
return err
140+
}
141+
dst.Status.Conditions = restored.Status.Conditions
142+
return nil
134143
}
135144

136145
func (dst *MachineSet) ConvertFrom(srcRaw conversion.Hub) error {
137146
src := srcRaw.(*v1alpha4.MachineSet)
138147

139-
return Convert_v1alpha4_MachineSet_To_v1alpha3_MachineSet(src, dst, nil)
148+
if err := Convert_v1alpha4_MachineSet_To_v1alpha3_MachineSet(src, dst, nil); err != nil {
149+
return err
150+
}
151+
152+
// Preserve Hub data on down-conversion except for metadata
153+
if err := utilconversion.MarshalData(src, dst); err != nil {
154+
return err
155+
}
156+
return nil
157+
}
158+
159+
// Status.Conditions was introduced in v1alpha4, thus requiring a custom conversion function; the values is going to be preserved in an annotation thus allowing roundtrip without loosing informations
160+
func Convert_v1alpha4_MachineSetStatus_To_v1alpha3_MachineSetStatus(in *v1alpha4.MachineSetStatus, out *MachineSetStatus, s apiconversion.Scope) error {
161+
return autoConvert_v1alpha4_MachineSetStatus_To_v1alpha3_MachineSetStatus(in, out, s)
140162
}
141163

142164
func (src *MachineSetList) ConvertTo(dstRaw conversion.Hub) error {

api/v1alpha3/zz_generated.conversion.go

+6-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v1alpha4/condition_consts.go

+33
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,36 @@ const (
218218
// WaitingForAvailableMachinesReason (Severity=Warning) reflects the fact that the required minimum number of machines for a machinedeployment are not available.
219219
WaitingForAvailableMachinesReason = "WaitingForAvailableMachines"
220220
)
221+
222+
// Conditions and condition Reasons for MachineSets
223+
224+
const (
225+
// MachinesCreatedCondition documents that the machines controlled by the MachineSet are created.
226+
// When this condition is false, it indicates that there was an error when cloning the infrastructure/bootstrap template or
227+
// when generating the machine object.
228+
MachinesCreatedCondition ConditionType = "MachinesCreated"
229+
230+
// MachinesReadyCondition reports an aggregate of current status of the machines controlled by the MachineSet.
231+
MachinesReadyCondition ConditionType = "MachinesReady"
232+
233+
// BootstrapTemplateCloningFailedReason (Severity=Error) documents a MachineSet failing to
234+
// clone the bootstrap template.
235+
BootstrapTemplateCloningFailedReason = "BootstrapTemplateCloningFailed"
236+
237+
// InfrastructureTemplateCloningFailedReason (Severity=Error) documents a MachineSet failing to
238+
// clone the infrastructure template.
239+
InfrastructureTemplateCloningFailedReason = "InfrastructureTemplateCloningFailed"
240+
241+
// MachineCreationFailedReason (Severity=Error) documents a MachineSet failing to
242+
// generate a machine object.
243+
MachineCreationFailedReason = "MachineCreationFailed"
244+
245+
// ResizedCondition documents a MachineSet is resizing the set of controlled machines.
246+
ResizedCondition ConditionType = "Resized"
247+
248+
// ScalingUpReason (Severity=Info) documents a MachineSet is increasing the number of replicas.
249+
ScalingUpReason = "ScalingUp"
250+
251+
// ScalingDownReason (Severity=Info) documents a MachineSet is decreasing the number of replicas.
252+
ScalingDownReason = "ScalingDown"
253+
)

api/v1alpha4/machineset_types.go

+13
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ type MachineSetStatus struct {
163163
FailureReason *capierrors.MachineSetStatusError `json:"failureReason,omitempty"`
164164
// +optional
165165
FailureMessage *string `json:"failureMessage,omitempty"`
166+
// Conditions defines current service state of the MachineSet.
167+
// +optional
168+
Conditions Conditions `json:"conditions,omitempty"`
166169
}
167170

168171
// ANCHOR_END: MachineSetStatus
@@ -222,3 +225,13 @@ type MachineSetList struct {
222225
func init() {
223226
SchemeBuilder.Register(&MachineSet{}, &MachineSetList{})
224227
}
228+
229+
// GetConditions returns the set of conditions for the MachineSet.
230+
func (m *MachineSet) GetConditions() Conditions {
231+
return m.Status.Conditions
232+
}
233+
234+
// SetConditions updates the set of conditions on the MachineSet.
235+
func (m *MachineSet) SetConditions(conditions Conditions) {
236+
m.Status.Conditions = conditions
237+
}

api/v1alpha4/zz_generated.deepcopy.go

+7
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_machinesets.yaml

+44
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,50 @@ spec:
743743
minReadySeconds) for this MachineSet.
744744
format: int32
745745
type: integer
746+
conditions:
747+
description: Conditions defines current service state of the MachineSet.
748+
items:
749+
description: Condition defines an observation of a Cluster API resource
750+
operational state.
751+
properties:
752+
lastTransitionTime:
753+
description: Last time the condition transitioned from one status
754+
to another. This should be when the underlying condition changed.
755+
If that is not known, then using the time when the API field
756+
changed is acceptable.
757+
format: date-time
758+
type: string
759+
message:
760+
description: A human readable message indicating details about
761+
the transition. This field may be empty.
762+
type: string
763+
reason:
764+
description: The reason for the condition's last transition
765+
in CamelCase. The specific API may choose whether or not this
766+
field is considered a guaranteed API. This field may not be
767+
empty.
768+
type: string
769+
severity:
770+
description: Severity provides an explicit classification of
771+
Reason code, so the users or machines can immediately understand
772+
the current situation and act accordingly. The Severity field
773+
MUST be set only when Status=False.
774+
type: string
775+
status:
776+
description: Status of the condition, one of True, False, Unknown.
777+
type: string
778+
type:
779+
description: Type of condition in CamelCase or in foo.example.com/CamelCase.
780+
Many .condition.type values are consistent across resources
781+
like Available, but because arbitrary conditions can be useful
782+
(see .node.status.conditions), the ability to deconflict is
783+
important.
784+
type: string
785+
required:
786+
- status
787+
- type
788+
type: object
789+
type: array
746790
failureMessage:
747791
type: string
748792
failureReason:

controllers/machineset_controller.go

+53-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"sigs.k8s.io/cluster-api/controllers/remote"
3636
"sigs.k8s.io/cluster-api/util"
3737
"sigs.k8s.io/cluster-api/util/annotations"
38+
"sigs.k8s.io/cluster-api/util/collections"
3839
"sigs.k8s.io/cluster-api/util/conditions"
3940
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
4041
"sigs.k8s.io/cluster-api/util/patch"
@@ -140,7 +141,7 @@ func (r *MachineSetReconciler) Reconcile(ctx context.Context, req ctrl.Request)
140141

141142
defer func() {
142143
// Always attempt to patch the object and status after each reconciliation.
143-
if err := patchHelper.Patch(ctx, machineSet); err != nil {
144+
if err := patchMachineSet(ctx, patchHelper, machineSet); err != nil {
144145
reterr = kerrors.NewAggregate([]error{reterr, err})
145146
}
146147
}()
@@ -159,6 +160,28 @@ func (r *MachineSetReconciler) Reconcile(ctx context.Context, req ctrl.Request)
159160
return result, err
160161
}
161162

163+
func patchMachineSet(ctx context.Context, patchHelper *patch.Helper, machineSet *clusterv1.MachineSet, options ...patch.Option) error {
164+
// Always update the readyCondition by summarizing the state of other conditions.
165+
conditions.SetSummary(machineSet,
166+
conditions.WithConditions(
167+
clusterv1.MachinesCreatedCondition,
168+
clusterv1.ResizedCondition,
169+
clusterv1.MachinesReadyCondition,
170+
),
171+
)
172+
173+
// Patch the object, ignoring conflicts on the conditions owned by this controller.
174+
options = append(options,
175+
patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
176+
clusterv1.ReadyCondition,
177+
clusterv1.MachinesCreatedCondition,
178+
clusterv1.ResizedCondition,
179+
clusterv1.MachinesReadyCondition,
180+
}},
181+
)
182+
return patchHelper.Patch(ctx, machineSet, options...)
183+
}
184+
162185
func (r *MachineSetReconciler) reconcile(ctx context.Context, cluster *clusterv1.Cluster, machineSet *clusterv1.MachineSet) (ctrl.Result, error) {
163186
log := ctrl.LoggerFrom(ctx)
164187
log.V(4).Info("Reconcile MachineSet")
@@ -347,6 +370,7 @@ func (r *MachineSetReconciler) syncReplicas(ctx context.Context, ms *clusterv1.M
347370
Labels: machine.Labels,
348371
})
349372
if err != nil {
373+
conditions.MarkFalse(ms, clusterv1.MachinesCreatedCondition, clusterv1.BootstrapTemplateCloningFailedReason, clusterv1.ConditionSeverityError, err.Error())
350374
return errors.Wrapf(err, "failed to clone bootstrap configuration for MachineSet %q in namespace %q", ms.Name, ms.Namespace)
351375
}
352376
machine.Spec.Bootstrap.ConfigRef = bootstrapRef
@@ -361,6 +385,7 @@ func (r *MachineSetReconciler) syncReplicas(ctx context.Context, ms *clusterv1.M
361385
Annotations: machine.Annotations,
362386
})
363387
if err != nil {
388+
conditions.MarkFalse(ms, clusterv1.MachinesCreatedCondition, clusterv1.InfrastructureTemplateCloningFailedReason, clusterv1.ConditionSeverityError, err.Error())
364389
return errors.Wrapf(err, "failed to clone infrastructure configuration for MachineSet %q in namespace %q", ms.Name, ms.Namespace)
365390
}
366391
machine.Spec.InfrastructureRef = *infraRef
@@ -369,6 +394,8 @@ func (r *MachineSetReconciler) syncReplicas(ctx context.Context, ms *clusterv1.M
369394
log.Error(err, "Unable to create Machine", "machine", machine.Name)
370395
r.recorder.Eventf(ms, corev1.EventTypeWarning, "FailedCreate", "Failed to create machine %q: %v", machine.Name, err)
371396
errs = append(errs, err)
397+
conditions.MarkFalse(ms, clusterv1.MachinesCreatedCondition, clusterv1.MachineCreationFailedReason,
398+
clusterv1.ConditionSeverityError, err.Error())
372399

373400
// Try to cleanup the external objects if the Machine creation failed.
374401
if err := r.Client.Delete(ctx, util.ObjectReferenceToUnstructured(*infraRef)); !apierrors.IsNotFound(err) {
@@ -598,6 +625,7 @@ func (r *MachineSetReconciler) updateStatus(ctx context.Context, cluster *cluste
598625
fullyLabeledReplicasCount := 0
599626
readyReplicasCount := 0
600627
availableReplicasCount := 0
628+
desiredReplicas := *ms.Spec.Replicas
601629
templateLabel := labels.Set(ms.Spec.Template.Labels).AsSelectorPreValidated()
602630

603631
for _, machine := range filteredMachines {
@@ -641,12 +669,35 @@ func (r *MachineSetReconciler) updateStatus(ctx context.Context, cluster *cluste
641669
newStatus.DeepCopyInto(&ms.Status)
642670

643671
log.V(4).Info(fmt.Sprintf("Updating status for %v: %s/%s, ", ms.Kind, ms.Namespace, ms.Name) +
644-
fmt.Sprintf("replicas %d->%d (need %d), ", ms.Status.Replicas, newStatus.Replicas, *ms.Spec.Replicas) +
672+
fmt.Sprintf("replicas %d->%d (need %d), ", ms.Status.Replicas, newStatus.Replicas, desiredReplicas) +
645673
fmt.Sprintf("fullyLabeledReplicas %d->%d, ", ms.Status.FullyLabeledReplicas, newStatus.FullyLabeledReplicas) +
646674
fmt.Sprintf("readyReplicas %d->%d, ", ms.Status.ReadyReplicas, newStatus.ReadyReplicas) +
647675
fmt.Sprintf("availableReplicas %d->%d, ", ms.Status.AvailableReplicas, newStatus.AvailableReplicas) +
648676
fmt.Sprintf("sequence No: %v->%v", ms.Status.ObservedGeneration, newStatus.ObservedGeneration))
649677
}
678+
switch {
679+
// We are scaling up
680+
case newStatus.Replicas < desiredReplicas:
681+
conditions.MarkFalse(ms, clusterv1.ResizedCondition, clusterv1.ScalingUpReason, clusterv1.ConditionSeverityWarning, "Scaling up MachineSet to %d replicas (actual %d)", desiredReplicas, newStatus.Replicas)
682+
// We are scaling down
683+
case newStatus.Replicas > desiredReplicas:
684+
conditions.MarkFalse(ms, clusterv1.ResizedCondition, clusterv1.ScalingDownReason, clusterv1.ConditionSeverityWarning, "Scaling down MachineSet to %d replicas (actual %d)", desiredReplicas, newStatus.Replicas)
685+
// This means that there was no error in generating the desired number of machine objects
686+
conditions.MarkTrue(ms, clusterv1.MachinesCreatedCondition)
687+
default:
688+
// Make sure last resize operation is marked as completed.
689+
// NOTE: we are checking the number of machines ready so we report resize completed only when the machines
690+
// are actually provisioned (vs reporting completed immediately after the last machine object is created). This convention is also used by KCP.
691+
if newStatus.ReadyReplicas == newStatus.Replicas {
692+
conditions.MarkTrue(ms, clusterv1.ResizedCondition)
693+
}
694+
// This means that there was no error in generating the desired number of machine objects
695+
conditions.MarkTrue(ms, clusterv1.MachinesCreatedCondition)
696+
}
697+
698+
// Aggregate the operational state of all the machines; while aggregating we are adding the
699+
// source ref (reason@machine/name) so the problem can be easily tracked down to its source machine.
700+
conditions.SetAggregate(ms, clusterv1.MachinesReadyCondition, collections.FromMachines(filteredMachines...).ConditionGetters(), conditions.AddSourceRef(), conditions.WithStepCounterIf(false))
650701

651702
return nil
652703
}

0 commit comments

Comments
 (0)