Skip to content

Commit bf3d451

Browse files
Add conditions to the Machine object
1 parent b9f2389 commit bf3d451

10 files changed

+168
-10
lines changed

api/v1alpha2/conversion.go

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ func (src *Machine) ConvertTo(dstRaw conversion.Hub) error {
118118
}
119119
restoreMachineSpec(&restored.Spec, &dst.Spec)
120120
dst.Status.ObservedGeneration = restored.Status.ObservedGeneration
121+
dst.Status.Conditions = restored.Status.Conditions
121122

122123
return nil
123124
}

api/v1alpha2/zz_generated.conversion.go

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

api/v1alpha3/condition_consts.go

+31
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,34 @@ const (
2525
)
2626

2727
// ANCHOR_END: CommonConditions
28+
29+
// Conditions and condition Reasons for the Machine object
30+
31+
// MachineConditionsCount defines the total number of conditions on the Machine object.
32+
const MachineConditionsCount = 2
33+
34+
const (
35+
// BootstrapReadyCondition reports a summary of current status of the bootstrap object defined for this machine.
36+
// This condition is mirrored from the Ready condition in the bootstrap ref object, and
37+
// the absence of this condition might signal problems in the reconcile external loops or the fact that
38+
// the bootstrap provider does not not implements the Ready condition yet.
39+
BootstrapReadyCondition ConditionType = "BootstrapReady"
40+
41+
// WaitingForDataSecretFallbackReason (Severity=Info) documents a machine waiting for the bootstrap data secret
42+
// to be available.
43+
// NOTE: This reason is used only as a fallback when the bootstrap object is not reporting its own ready condition.
44+
WaitingForDataSecretFallbackReason = "WaitingForDataSecret"
45+
)
46+
47+
const (
48+
// InfrastructureReadyCondition reports a summary of current status of the infrastructure object defined for this machine.
49+
// This condition is mirrored from the Ready condition in the infrastructure ref object, and
50+
// the absence of this condition might signal problems in the reconcile external loops or the fact that
51+
// the infrastructure provider does not not implements the Ready condition yet.
52+
InfrastructureReadyCondition ConditionType = "InfrastructureReady"
53+
54+
// WaitingForInfrastructureFallbackReason (Severity=Info) documents a machine waiting for the machine infrastructure
55+
// to be available.
56+
// NOTE: This reason is used only as a fallback when the infrastructure object is not reporting its own ready condition.
57+
WaitingForInfrastructureFallbackReason = "WaitingForInfrastructure"
58+
)

api/v1alpha3/machine_types.go

+12
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ type MachineStatus struct {
159159
// ObservedGeneration is the latest generation observed by the controller.
160160
// +optional
161161
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
162+
163+
// Conditions defines current service state of the Machine.
164+
// +optional
165+
Conditions Conditions `json:"conditions,omitempty"`
162166
}
163167

164168
// ANCHOR_END: MachineStatus
@@ -231,6 +235,14 @@ type Machine struct {
231235
Status MachineStatus `json:"status,omitempty"`
232236
}
233237

238+
func (m *Machine) GetConditions() Conditions {
239+
return m.Status.Conditions
240+
}
241+
242+
func (m *Machine) SetConditions(conditions Conditions) {
243+
m.Status.Conditions = conditions
244+
}
245+
234246
// +kubebuilder:object:root=true
235247

236248
// MachineList contains a list of Machine

api/v1alpha3/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_machines.yaml

+44
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,50 @@ spec:
560560
bootstrapReady:
561561
description: BootstrapReady is the state of the bootstrap provider.
562562
type: boolean
563+
conditions:
564+
description: Conditions defines current service state of the Machine.
565+
items:
566+
description: Condition defines an observation of a Cluster API resource
567+
operational state.
568+
properties:
569+
lastTransitionTime:
570+
description: Last time the condition transitioned from one status
571+
to another. This should be when the underlying condition changed.
572+
If that is not known, then using the time when the API field
573+
changed is acceptable.
574+
format: date-time
575+
type: string
576+
message:
577+
description: A human readable message indicating details about
578+
the transition. This field may be empty.
579+
type: string
580+
reason:
581+
description: The reason for the condition's last transition
582+
in CamelCase. The specific API may choose whether or not this
583+
field is considered a guaranteed API. This field may not be
584+
empty.
585+
type: string
586+
severity:
587+
description: Severity provides an explicit classification of
588+
Reason code, so the users or machines can immediately understand
589+
the current situation and act accordingly. The Severity field
590+
MUST be set only when Status=False.
591+
type: string
592+
status:
593+
description: Status of the condition, one of True, False, Unknown.
594+
type: string
595+
type:
596+
description: Type of condition in CamelCase or in foo.example.com/CamelCase.
597+
Many .condition.type values are consistent across resources
598+
like Available, but because arbitrary conditions can be useful
599+
(see .node.status.conditions), the ability to deconflict is
600+
important.
601+
type: string
602+
required:
603+
- status
604+
- type
605+
type: object
606+
type: array
563607
failureMessage:
564608
description: "FailureMessage will be set in the event that there is
565609
a terminal problem reconciling the Machine and will contain a more

controllers/machine_controller.go

+8
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
kubedrain "sigs.k8s.io/cluster-api/third_party/kubernetes-drain"
4444
"sigs.k8s.io/cluster-api/util"
4545
"sigs.k8s.io/cluster-api/util/annotations"
46+
"sigs.k8s.io/cluster-api/util/conditions"
4647
"sigs.k8s.io/cluster-api/util/patch"
4748
"sigs.k8s.io/cluster-api/util/predicates"
4849
ctrl "sigs.k8s.io/controller-runtime"
@@ -166,6 +167,13 @@ func (r *MachineReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, reterr e
166167
}
167168

168169
defer func() {
170+
// always update the readyCondition with the summary of the machine conditions.
171+
conditions.SetSummary(m,
172+
// we want to surface infrastructure problems as firsts, then the others.
173+
conditions.WithConditionOrder(clusterv1.InfrastructureReadyCondition),
174+
conditions.WithStepCounter(clusterv1.MachineConditionsCount),
175+
)
176+
169177
r.reconcilePhase(ctx, m)
170178
r.reconcileMetrics(ctx, m)
171179

controllers/machine_controller_phases.go

+25-7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3030
"k8s.io/utils/pointer"
3131
"sigs.k8s.io/cluster-api/util/annotations"
32+
"sigs.k8s.io/cluster-api/util/conditions"
3233
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
3334
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3435
"sigs.k8s.io/controller-runtime/pkg/handler"
@@ -172,12 +173,6 @@ func (r *MachineReconciler) reconcileBootstrap(ctx context.Context, cluster *clu
172173
}
173174
bootstrapConfig := externalResult.Result
174175

175-
// If the bootstrap data is populated, set ready and return.
176-
if m.Spec.Bootstrap.DataSecretName != nil {
177-
m.Status.BootstrapReady = true
178-
return nil
179-
}
180-
181176
// If the bootstrap config is being deleted, return early.
182177
if !bootstrapConfig.GetDeletionTimestamp().IsZero() {
183178
return nil
@@ -187,7 +182,22 @@ func (r *MachineReconciler) reconcileBootstrap(ctx context.Context, cluster *clu
187182
ready, err := external.IsReady(bootstrapConfig)
188183
if err != nil {
189184
return err
190-
} else if !ready {
185+
}
186+
187+
// Report a summary of current status of the bootstrap object defined for this machine.
188+
conditions.SetMirrorCondition(m, clusterv1.BootstrapReadyCondition,
189+
conditions.UnstructuredGetter(bootstrapConfig),
190+
conditions.WithFallbackValue(ready, clusterv1.WaitingForDataSecretFallbackReason, clusterv1.ConditionSeverityInfo, ""),
191+
)
192+
193+
// If the bootstrap data is populated, set ready and return.
194+
if m.Spec.Bootstrap.DataSecretName != nil {
195+
m.Status.BootstrapReady = true
196+
return nil
197+
}
198+
199+
// If the bootstrap provider is not ready, requeue.
200+
if !ready {
191201
return errors.Wrapf(&capierrors.RequeueAfterError{RequeueAfter: externalReadyWait},
192202
"Bootstrap provider for Machine %q in namespace %q is not ready, requeuing", m.Name, m.Namespace)
193203
}
@@ -236,6 +246,14 @@ func (r *MachineReconciler) reconcileInfrastructure(ctx context.Context, cluster
236246
return err
237247
}
238248
m.Status.InfrastructureReady = ready
249+
250+
// Report a summary of current status of the infrastructure object defined for this machine.
251+
conditions.SetMirrorCondition(m, clusterv1.InfrastructureReadyCondition,
252+
conditions.UnstructuredGetter(infraConfig),
253+
conditions.WithFallbackValue(ready, clusterv1.WaitingForInfrastructureFallbackReason, clusterv1.ConditionSeverityInfo, ""),
254+
)
255+
256+
// If the infrastructure provider is not ready, return early.
239257
if !ready {
240258
return errors.Wrapf(&capierrors.RequeueAfterError{RequeueAfter: externalReadyWait},
241259
"Infrastructure provider for Machine %q in namespace %q is not ready, requeuing", m.Name, m.Namespace,

util/conditions/getter.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,47 @@ func Summary(from Getter, options ...MergeOption) *clusterv1.Condition {
137137
return merge(conditionsInScope, clusterv1.ReadyCondition, mergeOpt)
138138
}
139139

140+
// mirrorOptions allows to set options for the mirror operation.
141+
type mirrorOptions struct {
142+
fallbackTo *bool
143+
fallbackReason string
144+
fallbackSeverity clusterv1.ConditionSeverity
145+
fallbackMessage string
146+
}
147+
148+
// MirrorOptions defines an option for mirroring conditions.
149+
type MirrorOptions func(*mirrorOptions)
150+
151+
// WithFallbackValue specify a fallback value to use in case the mirrored condition does not exists;
152+
// in case the fallbackValue is false, given values for reason, severity and message will be used.
153+
func WithFallbackValue(fallbackValue bool, reason string, severity clusterv1.ConditionSeverity, message string) MirrorOptions {
154+
return func(c *mirrorOptions) {
155+
c.fallbackTo = &fallbackValue
156+
c.fallbackReason = reason
157+
c.fallbackSeverity = severity
158+
c.fallbackMessage = message
159+
}
160+
}
161+
140162
// Mirror mirrors the Ready condition from a dependent object into the target condition;
141163
// if the Ready condition does not exists in the source object, no target conditions is generated.
142-
func Mirror(from Getter, targetCondition clusterv1.ConditionType) *clusterv1.Condition {
164+
func Mirror(from Getter, targetCondition clusterv1.ConditionType, options ...MirrorOptions) *clusterv1.Condition {
165+
mirrorOpt := &mirrorOptions{}
166+
for _, o := range options {
167+
o(mirrorOpt)
168+
}
169+
143170
condition := Get(from, clusterv1.ReadyCondition)
144171

172+
if mirrorOpt.fallbackTo != nil && condition == nil {
173+
switch *mirrorOpt.fallbackTo {
174+
case true:
175+
condition = TrueCondition(targetCondition)
176+
case false:
177+
condition = FalseCondition(targetCondition, mirrorOpt.fallbackReason, mirrorOpt.fallbackSeverity, mirrorOpt.fallbackMessage)
178+
}
179+
}
180+
145181
if condition != nil {
146182
condition.Type = targetCondition
147183
}

util/conditions/setter.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ func SetSummary(to Setter, options ...MergeOption) {
120120

121121
// SetMirrorCondition creates a new condition by mirroring the the Ready condition from a dependent object;
122122
// if the Ready condition does not exists in the source object, no target conditions is generated.
123-
func SetMirrorCondition(to Setter, targetCondition clusterv1.ConditionType, from Getter) {
124-
Set(to, Mirror(from, targetCondition))
123+
func SetMirrorCondition(to Setter, targetCondition clusterv1.ConditionType, from Getter, options ...MirrorOptions) {
124+
Set(to, Mirror(from, targetCondition, options...))
125125
}
126126

127127
// SetAggregateCondition creates a new condition with the aggregation of all the the Ready condition

0 commit comments

Comments
 (0)