Skip to content

Commit 11bd893

Browse files
Add conditions to the Machine object
1 parent 69cb7e9 commit 11bd893

9 files changed

+141
-17
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+
// MachineSummaryConditionsCount defines the total number of conditions on the Machine object.
32+
const MachineSummaryConditionsCount = 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 first, then the others.
173+
conditions.WithConditionOrder(clusterv1.InfrastructureReadyCondition),
174+
conditions.WithStepCounter(clusterv1.MachineSummaryConditionsCount),
175+
)
176+
169177
r.reconcilePhase(ctx, m)
170178
r.reconcileMetrics(ctx, m)
171179

controllers/machine_controller_phases.go

+27-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"
@@ -158,6 +159,14 @@ func (r *MachineReconciler) reconcileExternal(ctx context.Context, cluster *clus
158159

159160
// reconcileBootstrap reconciles the Spec.Bootstrap.ConfigRef object on a Machine.
160161
func (r *MachineReconciler) reconcileBootstrap(ctx context.Context, cluster *clusterv1.Cluster, m *clusterv1.Machine) error {
162+
// If the bootstrap data is populated, set ready and return.
163+
if m.Spec.Bootstrap.DataSecretName != nil {
164+
m.Status.BootstrapReady = true
165+
conditions.MarkTrue(m, clusterv1.BootstrapReadyCondition)
166+
return nil
167+
}
168+
169+
// If the Boostrap ref is nil (and so the machine should use user generated data secret), return.
161170
if m.Spec.Bootstrap.ConfigRef == nil {
162171
return nil
163172
}
@@ -172,12 +181,6 @@ func (r *MachineReconciler) reconcileBootstrap(ctx context.Context, cluster *clu
172181
}
173182
bootstrapConfig := externalResult.Result
174183

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-
181184
// If the bootstrap config is being deleted, return early.
182185
if !bootstrapConfig.GetDeletionTimestamp().IsZero() {
183186
return nil
@@ -187,7 +190,16 @@ func (r *MachineReconciler) reconcileBootstrap(ctx context.Context, cluster *clu
187190
ready, err := external.IsReady(bootstrapConfig)
188191
if err != nil {
189192
return err
190-
} else if !ready {
193+
}
194+
195+
// Report a summary of current status of the bootstrap object defined for this machine.
196+
conditions.SetMirror(m, clusterv1.BootstrapReadyCondition,
197+
conditions.UnstructuredGetter(bootstrapConfig),
198+
conditions.WithFallbackValue(ready, clusterv1.WaitingForDataSecretFallbackReason, clusterv1.ConditionSeverityInfo, ""),
199+
)
200+
201+
// If the bootstrap provider is not ready, requeue.
202+
if !ready {
191203
return errors.Wrapf(&capierrors.RequeueAfterError{RequeueAfter: externalReadyWait},
192204
"Bootstrap provider for Machine %q in namespace %q is not ready, requeuing", m.Name, m.Namespace)
193205
}
@@ -236,6 +248,14 @@ func (r *MachineReconciler) reconcileInfrastructure(ctx context.Context, cluster
236248
return err
237249
}
238250
m.Status.InfrastructureReady = ready
251+
252+
// Report a summary of current status of the infrastructure object defined for this machine.
253+
conditions.SetMirror(m, clusterv1.InfrastructureReadyCondition,
254+
conditions.UnstructuredGetter(infraConfig),
255+
conditions.WithFallbackValue(ready, clusterv1.WaitingForInfrastructureFallbackReason, clusterv1.ConditionSeverityInfo, ""),
256+
)
257+
258+
// If the infrastructure provider is not ready, return early.
239259
if !ready {
240260
return errors.Wrapf(&capierrors.RequeueAfterError{RequeueAfter: externalReadyWait},
241261
"Infrastructure provider for Machine %q in namespace %q is not ready, requeuing", m.Name, m.Namespace,

controllers/machine_controller_test.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import (
3131
"k8s.io/utils/pointer"
3232
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3333
"sigs.k8s.io/cluster-api/controllers/external"
34+
"sigs.k8s.io/cluster-api/test/helpers"
3435
"sigs.k8s.io/cluster-api/util"
3536
"sigs.k8s.io/controller-runtime/pkg/client"
36-
"sigs.k8s.io/controller-runtime/pkg/client/fake"
3737
"sigs.k8s.io/controller-runtime/pkg/handler"
3838
"sigs.k8s.io/controller-runtime/pkg/log"
3939
"sigs.k8s.io/controller-runtime/pkg/metrics"
@@ -105,7 +105,7 @@ func TestMachineFinalizer(t *testing.T) {
105105
g := NewWithT(t)
106106

107107
mr := &MachineReconciler{
108-
Client: fake.NewFakeClientWithScheme(
108+
Client: helpers.NewFakeClientWithScheme(
109109
scheme.Scheme,
110110
clusterCorrectMeta,
111111
machineValidCluster,
@@ -266,7 +266,7 @@ func TestMachineOwnerReference(t *testing.T) {
266266
g := NewWithT(t)
267267

268268
mr := &MachineReconciler{
269-
Client: fake.NewFakeClientWithScheme(
269+
Client: helpers.NewFakeClientWithScheme(
270270
scheme.Scheme,
271271
testCluster,
272272
machineInvalidCluster,
@@ -419,11 +419,11 @@ func TestReconcileRequest(t *testing.T) {
419419
t.Run("machine should be "+tc.machine.Name, func(t *testing.T) {
420420
g := NewWithT(t)
421421

422-
clientFake := fake.NewFakeClientWithScheme(
422+
clientFake := helpers.NewFakeClientWithScheme(
423423
scheme.Scheme,
424424
&testCluster,
425425
&tc.machine,
426-
external.TestGenericInfrastructureCRD,
426+
external.TestGenericInfrastructureCRD(),
427427
&infraConfig,
428428
)
429429

@@ -546,7 +546,7 @@ func TestReconcileDeleteExternal(t *testing.T) {
546546
}
547547

548548
r := &MachineReconciler{
549-
Client: fake.NewFakeClientWithScheme(scheme.Scheme, objs...),
549+
Client: helpers.NewFakeClientWithScheme(scheme.Scheme, objs...),
550550
Log: log.Log,
551551
scheme: scheme.Scheme,
552552
}
@@ -590,7 +590,7 @@ func TestRemoveMachineFinalizerAfterDeleteReconcile(t *testing.T) {
590590
}
591591
key := client.ObjectKey{Namespace: m.Namespace, Name: m.Name}
592592
mr := &MachineReconciler{
593-
Client: fake.NewFakeClientWithScheme(scheme.Scheme, testCluster, m),
593+
Client: helpers.NewFakeClientWithScheme(scheme.Scheme, testCluster, m),
594594
Log: log.Log,
595595
scheme: scheme.Scheme,
596596
}
@@ -667,7 +667,7 @@ func TestReconcileMetrics(t *testing.T) {
667667
objs = append(objs, machine)
668668

669669
r := &MachineReconciler{
670-
Client: fake.NewFakeClientWithScheme(scheme.Scheme, objs...),
670+
Client: helpers.NewFakeClientWithScheme(scheme.Scheme, objs...),
671671
Log: log.Log,
672672
scheme: scheme.Scheme,
673673
}
@@ -778,7 +778,7 @@ func Test_clusterToActiveMachines(t *testing.T) {
778778
objs = append(objs, m2)
779779

780780
r := &MachineReconciler{
781-
Client: fake.NewFakeClientWithScheme(scheme.Scheme, objs...),
781+
Client: helpers.NewFakeClientWithScheme(scheme.Scheme, objs...),
782782
Log: log.Log,
783783
scheme: scheme.Scheme,
784784
}
@@ -952,7 +952,7 @@ func TestIsDeleteNodeAllowed(t *testing.T) {
952952
}
953953

954954
mr := &MachineReconciler{
955-
Client: fake.NewFakeClientWithScheme(
955+
Client: helpers.NewFakeClientWithScheme(
956956
scheme.Scheme,
957957
tc.cluster,
958958
tc.machine,

0 commit comments

Comments
 (0)