Skip to content

Commit 84f9faf

Browse files
⚠️ Implement v1beta2 contract in cluster controller, KCP, CABPK (#12094)
* Align control plane and infra cluster contract utils to v1beta2 * Align cluster controller to v1beta2 * Move test builders to v1beta2 * Fix unit tests * Implement v1beta2 contract in CABPK * Implement v1beta2 contract in KCP * Move CABPK to v1beta2 * Address comments * Use v1beta2 boostrap contract in machine pools
1 parent dd105a3 commit 84f9faf

File tree

62 files changed

+938
-437
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+938
-437
lines changed

bootstrap/kubeadm/api/v1beta1/conversion.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *boot
6868
out.FailureMessage = in.Deprecated.V1Beta1.FailureMessage
6969
}
7070

71+
// Move initialization to old fields
72+
if in.Initialization != nil {
73+
out.Ready = in.Initialization.DataSecretCreated
74+
}
75+
7176
// Move new conditions (v1beta2) to the v1beta2 field.
7277
if in.Conditions == nil {
7378
return nil
@@ -103,6 +108,14 @@ func Convert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in *Kube
103108
}
104109
out.Deprecated.V1Beta1.FailureReason = in.FailureReason
105110
out.Deprecated.V1Beta1.FailureMessage = in.FailureMessage
111+
112+
// Move ready to Initialization
113+
if in.Ready {
114+
if out.Initialization == nil {
115+
out.Initialization = &bootstrapv1.KubeadmConfigInitializationStatus{}
116+
}
117+
out.Initialization.DataSecretCreated = in.Ready
118+
}
106119
return nil
107120
}
108121

bootstrap/kubeadm/api/v1beta1/conversion_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c fuzz.Continue
6060
if in.Deprecated.V1Beta1 == nil {
6161
in.Deprecated.V1Beta1 = &bootstrapv1.KubeadmConfigV1Beta1DeprecatedStatus{}
6262
}
63+
64+
// Drop empty structs with only omit empty fields.
65+
if in.Initialization != nil {
66+
if reflect.DeepEqual(in.Initialization, &bootstrapv1.KubeadmConfigInitializationStatus{}) {
67+
in.Initialization = nil
68+
}
69+
}
6370
}
6471

6572
func spokeKubeadmConfigStatus(in *KubeadmConfigStatus, c fuzz.Continue) {

bootstrap/kubeadm/api/v1beta1/zz_generated.conversion.go

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

bootstrap/kubeadm/api/v1beta2/kubeadmconfig_types.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,10 @@ type KubeadmConfigStatus struct {
460460
// +kubebuilder:validation:MaxItems=32
461461
Conditions []metav1.Condition `json:"conditions,omitempty"`
462462

463-
// ready indicates the BootstrapData field is ready to be consumed
463+
// initialization provides observations of the KubeadmConfig initialization process.
464+
// NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning.
464465
// +optional
465-
Ready bool `json:"ready"`
466+
Initialization *KubeadmConfigInitializationStatus `json:"initialization,omitempty"`
466467

467468
// dataSecretName is the name of the secret that stores the bootstrap data script.
468469
// +optional
@@ -479,6 +480,14 @@ type KubeadmConfigStatus struct {
479480
Deprecated *KubeadmConfigDeprecatedStatus `json:"deprecated,omitempty"`
480481
}
481482

483+
// KubeadmConfigInitializationStatus provides observations of the KubeadmConfig initialization process.
484+
type KubeadmConfigInitializationStatus struct {
485+
// dataSecretCreated is true when the Machine's boostrap secret is created.
486+
// NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Machine provisioning.
487+
// +optional
488+
DataSecretCreated bool `json:"dataSecretCreated,omitempty"`
489+
}
490+
482491
// KubeadmConfigDeprecatedStatus groups all the status fields that are deprecated and will be removed in a future version.
483492
// See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context.
484493
type KubeadmConfigDeprecatedStatus struct {

bootstrap/kubeadm/api/v1beta2/zz_generated.deepcopy.go

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

bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml

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

bootstrap/kubeadm/config/crd/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
labels:
22
- pairs:
3-
cluster.x-k8s.io/v1beta1: v1beta2
3+
cluster.x-k8s.io/v1beta2: v1beta2
44

55
# This kustomization.yaml is not intended to be run by itself,
66
# since it depends on service name and namespace that are out of this kustomize package.

bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,11 @@ func (r *KubeadmConfigReconciler) reconcile(ctx context.Context, scope *Scope, c
304304
return ctrl.Result{}, nil
305305
// Reconcile status for machines that already have a secret reference, but our status isn't up to date.
306306
// This case solves the pivoting scenario (or a backup restore) which doesn't preserve the status subresource on objects.
307-
case configOwner.DataSecretName() != nil && (!config.Status.Ready || config.Status.DataSecretName == nil):
308-
config.Status.Ready = true
307+
case configOwner.DataSecretName() != nil && (!(config.Status.Initialization != nil && config.Status.Initialization.DataSecretCreated) || config.Status.DataSecretName == nil):
308+
if config.Status.Initialization == nil {
309+
config.Status.Initialization = &bootstrapv1.KubeadmConfigInitializationStatus{}
310+
}
311+
config.Status.Initialization.DataSecretCreated = true
309312
config.Status.DataSecretName = configOwner.DataSecretName()
310313
v1beta1conditions.MarkTrue(config, bootstrapv1.DataSecretAvailableCondition)
311314
conditions.Set(scope.Config, metav1.Condition{
@@ -321,7 +324,7 @@ func (r *KubeadmConfigReconciler) reconcile(ctx context.Context, scope *Scope, c
321324
return ctrl.Result{}, nil
322325
// Status is ready means a config has been generated.
323326
// This also solves the upgrade scenario to a version which includes v1beta2 to ensure v1beta2 conditions are properly set.
324-
case config.Status.Ready:
327+
case config.Status.Initialization != nil && config.Status.Initialization.DataSecretCreated:
325328
// Based on existing code paths status.Ready is only true if status.dataSecretName is set
326329
// So we can assume that the DataSecret is available.
327330
v1beta1conditions.MarkTrue(config, bootstrapv1.DataSecretAvailableCondition)
@@ -1416,7 +1419,10 @@ func (r *KubeadmConfigReconciler) storeBootstrapData(ctx context.Context, scope
14161419
}
14171420
}
14181421
scope.Config.Status.DataSecretName = ptr.To(secret.Name)
1419-
scope.Config.Status.Ready = true
1422+
if scope.Config.Status.Initialization == nil {
1423+
scope.Config.Status.Initialization = &bootstrapv1.KubeadmConfigInitializationStatus{}
1424+
}
1425+
scope.Config.Status.Initialization.DataSecretCreated = true
14201426
v1beta1conditions.MarkTrue(scope.Config, bootstrapv1.DataSecretAvailableCondition)
14211427
conditions.Set(scope.Config, metav1.Condition{
14221428
Type: bootstrapv1.KubeadmConfigDataSecretAvailableV1Beta2Condition,

bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func TestKubeadmConfigReconciler_Reconcile_ReturnEarlyIfKubeadmConfigIsReady(t *
102102
config := newKubeadmConfig(metav1.NamespaceDefault, "cfg")
103103
addKubeadmConfigToMachine(config, machine)
104104

105-
config.Status.Ready = true
105+
config.Status.Initialization = &bootstrapv1.KubeadmConfigInitializationStatus{DataSecretCreated: true}
106106

107107
objects := []client.Object{
108108
cluster,
@@ -157,7 +157,7 @@ func TestKubeadmConfigReconciler_TestSecretOwnerReferenceReconciliation(t *testi
157157
},
158158
Type: corev1.SecretTypeBootstrapToken,
159159
}
160-
config.Status.Ready = true
160+
config.Status.Initialization = &bootstrapv1.KubeadmConfigInitializationStatus{DataSecretCreated: true}
161161

162162
objects := []client.Object{
163163
config,
@@ -532,7 +532,8 @@ func TestKubeadmConfigReconciler_Reconcile_GenerateCloudConfigData(t *testing.T)
532532

533533
cfg, err := getKubeadmConfig(myclient, "control-plane-init-cfg", metav1.NamespaceDefault)
534534
g.Expect(err).ToNot(HaveOccurred())
535-
g.Expect(cfg.Status.Ready).To(BeTrue())
535+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
536+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
536537
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
537538
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
538539
assertHasTrueCondition(g, myclient, request, bootstrapv1.CertificatesAvailableCondition)
@@ -714,7 +715,8 @@ func TestReconcileIfJoinCertificatesAvailableConditioninNodesAndControlPlaneIsRe
714715

715716
cfg, err := getKubeadmConfig(myclient, rt.configName, metav1.NamespaceDefault)
716717
g.Expect(err).ToNot(HaveOccurred())
717-
g.Expect(cfg.Status.Ready).To(BeTrue())
718+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
719+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
718720
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
719721
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
720722
assertHasTrueCondition(g, myclient, request, bootstrapv1.DataSecretAvailableCondition)
@@ -791,7 +793,8 @@ func TestReconcileIfJoinNodePoolsAndControlPlaneIsReady(t *testing.T) {
791793

792794
cfg, err := getKubeadmConfig(myclient, rt.configName, metav1.NamespaceDefault)
793795
g.Expect(err).ToNot(HaveOccurred())
794-
g.Expect(cfg.Status.Ready).To(BeTrue())
796+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
797+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
795798
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
796799
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
797800

@@ -892,7 +895,8 @@ func TestBootstrapDataFormat(t *testing.T) {
892895
// Verify the KubeadmConfig resource state is correct.
893896
cfg, err := getKubeadmConfig(myclient, configName, metav1.NamespaceDefault)
894897
g.Expect(err).ToNot(HaveOccurred())
895-
g.Expect(cfg.Status.Ready).To(BeTrue())
898+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
899+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
896900
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
897901

898902
// Read the secret containing the bootstrap data which was generated by the
@@ -997,7 +1001,8 @@ func TestKubeadmConfigSecretCreatedStatusNotPatched(t *testing.T) {
9971001

9981002
cfg, err := getKubeadmConfig(myclient, "worker-join-cfg", metav1.NamespaceDefault)
9991003
g.Expect(err).ToNot(HaveOccurred())
1000-
g.Expect(cfg.Status.Ready).To(BeTrue())
1004+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
1005+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
10011006
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
10021007
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
10031008
}
@@ -1051,7 +1056,8 @@ func TestBootstrapTokenTTLExtension(t *testing.T) {
10511056

10521057
cfg, err := getKubeadmConfig(myclient, "worker-join-cfg", metav1.NamespaceDefault)
10531058
g.Expect(err).ToNot(HaveOccurred())
1054-
g.Expect(cfg.Status.Ready).To(BeTrue())
1059+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
1060+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
10551061
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
10561062
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
10571063

@@ -1067,7 +1073,8 @@ func TestBootstrapTokenTTLExtension(t *testing.T) {
10671073

10681074
cfg, err = getKubeadmConfig(myclient, "control-plane-join-cfg", metav1.NamespaceDefault)
10691075
g.Expect(err).ToNot(HaveOccurred())
1070-
g.Expect(cfg.Status.Ready).To(BeTrue())
1076+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
1077+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
10711078
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
10721079
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
10731080

@@ -1297,7 +1304,8 @@ func TestBootstrapTokenRotationMachinePool(t *testing.T) {
12971304

12981305
cfg, err := getKubeadmConfig(myclient, "workerpool-join-cfg", metav1.NamespaceDefault)
12991306
g.Expect(err).ToNot(HaveOccurred())
1300-
g.Expect(cfg.Status.Ready).To(BeTrue())
1307+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
1308+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
13011309
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
13021310
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
13031311

@@ -1489,7 +1497,8 @@ func TestBootstrapTokenRefreshIfTokenSecretCleaned(t *testing.T) {
14891497

14901498
cfg, err := getKubeadmConfig(myclient, "worker-join-cfg", metav1.NamespaceDefault)
14911499
g.Expect(err).ToNot(HaveOccurred())
1492-
g.Expect(cfg.Status.Ready).To(BeTrue())
1500+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
1501+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
14931502
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
14941503
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
14951504
g.Expect(cfg.Spec.JoinConfiguration.Discovery.BootstrapToken.Token).ToNot(BeEmpty())
@@ -1562,7 +1571,8 @@ func TestBootstrapTokenRefreshIfTokenSecretCleaned(t *testing.T) {
15621571

15631572
cfg, err := getKubeadmConfig(myclient, "workerpool-join-cfg", metav1.NamespaceDefault)
15641573
g.Expect(err).ToNot(HaveOccurred())
1565-
g.Expect(cfg.Status.Ready).To(BeTrue())
1574+
g.Expect(cfg.Status.Initialization).ToNot(BeNil())
1575+
g.Expect(cfg.Status.Initialization.DataSecretCreated).To(BeTrue())
15661576
g.Expect(cfg.Status.DataSecretName).NotTo(BeNil())
15671577
g.Expect(cfg.Status.ObservedGeneration).NotTo(BeNil())
15681578
g.Expect(cfg.Spec.JoinConfiguration.Discovery.BootstrapToken.Token).ToNot(BeEmpty())
@@ -2791,7 +2801,7 @@ func TestKubeadmConfigReconciler_Reconcile_v1beta2_conditions(t *testing.T) {
27912801
name: "conditions should be true after upgrading to v1beta2",
27922802
config: func() *bootstrapv1.KubeadmConfig {
27932803
c := kubeadmConfig.DeepCopy()
2794-
c.Status.Ready = true
2804+
c.Status.Initialization = &bootstrapv1.KubeadmConfigInitializationStatus{DataSecretCreated: true}
27952805
return c
27962806
}(),
27972807
machine: machine.DeepCopy(),

controllers/external/util.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,3 @@ func IsReady(obj *unstructured.Unstructured) (bool, error) {
247247
}
248248
return ready && found, nil
249249
}
250-
251-
// IsInitialized returns true if the Status.Initialized field on an external object is true.
252-
func IsInitialized(obj *unstructured.Unstructured) (bool, error) {
253-
initialized, found, err := unstructured.NestedBool(obj.Object, "status", "initialized")
254-
if err != nil {
255-
return false, errors.Wrapf(err, "failed to determine %v %q initialized",
256-
obj.GroupVersionKind(), obj.GetName())
257-
}
258-
return initialized && found, nil
259-
}

controlplane/kubeadm/api/v1beta1/conversion.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ func Convert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneSta
7777
out.UnavailableReplicas = in.Deprecated.V1Beta1.UnavailableReplicas
7878
}
7979

80+
// Move initialized to ControlPlaneInitialized, rebuild ready
81+
if in.Initialization != nil {
82+
out.Initialized = in.Initialization.ControlPlaneInitialized
83+
}
84+
out.Ready = in.Deprecated.V1Beta1.ReadyReplicas > 0
85+
8086
// Move new conditions (v1beta2) and replica counter to the v1beta2 field.
8187
if in.Conditions == nil && in.ReadyReplicas == nil && in.AvailableReplicas == nil && in.UpToDateReplicas == nil {
8288
return nil
@@ -125,6 +131,14 @@ func Convert_v1beta1_KubeadmControlPlaneStatus_To_v1beta2_KubeadmControlPlaneSta
125131
out.Deprecated.V1Beta1.UpdatedReplicas = in.UpdatedReplicas
126132
out.Deprecated.V1Beta1.ReadyReplicas = in.ReadyReplicas
127133
out.Deprecated.V1Beta1.UnavailableReplicas = in.UnavailableReplicas
134+
135+
// Move initialized to ControlPlaneInitialized
136+
if in.Initialized {
137+
if out.Initialization == nil {
138+
out.Initialization = &controlplanev1.KubeadmControlPlaneInitializationStatus{}
139+
}
140+
out.Initialization.ControlPlaneInitialized = in.Initialized
141+
}
128142
return nil
129143
}
130144

controlplane/kubeadm/api/v1beta1/conversion_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ func hubKubeadmControlPlaneStatus(in *controlplanev1.KubeadmControlPlaneStatus,
6060
if in.Deprecated.V1Beta1 == nil {
6161
in.Deprecated.V1Beta1 = &controlplanev1.KubeadmControlPlaneV1Beta1DeprecatedStatus{}
6262
}
63+
64+
// Drop empty structs with only omit empty fields.
65+
if in.Initialization != nil {
66+
if reflect.DeepEqual(in.Initialization, &controlplanev1.KubeadmControlPlaneInitializationStatus{}) {
67+
in.Initialization = nil
68+
}
69+
}
6370
}
6471

6572
func spokeKubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, c fuzz.Continue) {
@@ -70,4 +77,7 @@ func spokeKubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, c fuzz.Contin
7077
in.V1Beta2 = nil
7178
}
7279
}
80+
81+
// Make sure ready is consistent with ready replicas, so we can rebuild the info after the round trip.
82+
in.Ready = in.ReadyReplicas > 0
7383
}

0 commit comments

Comments
 (0)