Skip to content

Commit ed3b35c

Browse files
committed
bootstrap: ensure v1beta2 conditions are always set
1 parent 8065fc4 commit ed3b35c

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,30 @@ func (r *KubeadmConfigReconciler) reconcile(ctx context.Context, scope *Scope, c
312312
Status: metav1.ConditionTrue,
313313
Reason: bootstrapv1.KubeadmConfigDataSecretAvailableV1Beta2Reason,
314314
})
315+
v1beta2conditions.Set(scope.Config, metav1.Condition{
316+
Type: bootstrapv1.KubeadmConfigCertificatesAvailableV1Beta2Condition,
317+
Status: metav1.ConditionTrue,
318+
Reason: bootstrapv1.KubeadmConfigCertificatesAvailableV1Beta2Reason,
319+
})
315320
return ctrl.Result{}, nil
316321
// Status is ready means a config has been generated.
322+
// This also solves the upgrade scenario to a version which includes v1beta2 to ensure v1beta2 conditions are properly set.
317323
case config.Status.Ready:
324+
// Based on existing code paths status.Ready is only true if status.dataSecretName is set
325+
// So we can assume that the DataSecret is available.
326+
conditions.MarkTrue(config, bootstrapv1.DataSecretAvailableCondition)
327+
v1beta2conditions.Set(scope.Config, metav1.Condition{
328+
Type: bootstrapv1.KubeadmConfigDataSecretAvailableV1Beta2Condition,
329+
Status: metav1.ConditionTrue,
330+
Reason: bootstrapv1.KubeadmConfigDataSecretAvailableV1Beta2Reason,
331+
})
332+
// Same applies for the CertificatesAvailable, which must have been the case to generate
333+
// the DataSecret.
334+
v1beta2conditions.Set(scope.Config, metav1.Condition{
335+
Type: bootstrapv1.KubeadmConfigCertificatesAvailableV1Beta2Condition,
336+
Status: metav1.ConditionTrue,
337+
Reason: bootstrapv1.KubeadmConfigCertificatesAvailableV1Beta2Reason,
338+
})
318339
if config.Spec.JoinConfiguration != nil && config.Spec.JoinConfiguration.Discovery.BootstrapToken != nil {
319340
if !configOwner.HasNodeRefs() {
320341
// If the BootstrapToken has been generated for a join but the config owner has no nodeRefs,

bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"sigs.k8s.io/cluster-api/util"
4747
"sigs.k8s.io/cluster-api/util/certs"
4848
"sigs.k8s.io/cluster-api/util/conditions"
49+
v1beta2conditions "sigs.k8s.io/cluster-api/util/conditions/v1beta2"
4950
"sigs.k8s.io/cluster-api/util/patch"
5051
"sigs.k8s.io/cluster-api/util/secret"
5152
"sigs.k8s.io/cluster-api/util/test/builder"
@@ -2744,3 +2745,96 @@ func assertHasTrueCondition(g *WithT, myclient client.Client, req ctrl.Request,
27442745
g.Expect(c).ToNot(BeNil())
27452746
g.Expect(c.Status).To(Equal(corev1.ConditionTrue))
27462747
}
2748+
2749+
func TestKubeadmConfigReconciler_Reconcile_v1beta2_conditions(t *testing.T) {
2750+
// Setup work for an initialized cluster
2751+
clusterName := "my-cluster"
2752+
cluster := builder.Cluster(metav1.NamespaceDefault, clusterName).Build()
2753+
conditions.MarkTrue(cluster, clusterv1.ControlPlaneInitializedCondition)
2754+
cluster.Status.InfrastructureReady = true
2755+
cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
2756+
Host: "example.com",
2757+
Port: 6443,
2758+
}
2759+
2760+
machine := builder.Machine(metav1.NamespaceDefault, "my-machine").
2761+
WithVersion("v1.19.1").
2762+
WithClusterName(cluster.Name).
2763+
WithBootstrapTemplate(bootstrapbuilder.KubeadmConfig(metav1.NamespaceDefault, "").Unstructured()).
2764+
Build()
2765+
2766+
kubeadmConfig := newKubeadmConfig(metav1.NamespaceDefault, "kubeadmconfig")
2767+
2768+
tests := []struct {
2769+
name string
2770+
config *bootstrapv1.KubeadmConfig
2771+
machine *clusterv1.Machine
2772+
}{
2773+
{
2774+
name: "conditions should be true again after reconciling",
2775+
config: kubeadmConfig.DeepCopy(),
2776+
machine: machine.DeepCopy(),
2777+
},
2778+
{
2779+
name: "conditions should be true again after status got emptied out",
2780+
config: kubeadmConfig.DeepCopy(),
2781+
machine: func() *clusterv1.Machine {
2782+
m := machine.DeepCopy()
2783+
m.Spec.Bootstrap.DataSecretName = ptr.To("foo")
2784+
return m
2785+
}(),
2786+
},
2787+
{
2788+
name: "conditions should be true after upgrading to v1beta2",
2789+
config: func() *bootstrapv1.KubeadmConfig {
2790+
c := kubeadmConfig.DeepCopy()
2791+
c.Status.Ready = true
2792+
return c
2793+
}(),
2794+
machine: machine.DeepCopy(),
2795+
},
2796+
}
2797+
for _, tt := range tests {
2798+
t.Run(tt.name, func(t *testing.T) {
2799+
g := NewWithT(t)
2800+
cluster := cluster.DeepCopy()
2801+
tt.config.SetOwnerReferences([]metav1.OwnerReference{{
2802+
APIVersion: clusterv1.GroupVersion.String(),
2803+
Kind: "Machine",
2804+
Name: tt.machine.Name,
2805+
}})
2806+
2807+
objects := []client.Object{cluster, tt.machine, tt.config}
2808+
objects = append(objects, createSecrets(t, cluster, tt.config)...)
2809+
2810+
myclient := fake.NewClientBuilder().WithObjects(objects...).WithStatusSubresource(&bootstrapv1.KubeadmConfig{}).Build()
2811+
2812+
r := &KubeadmConfigReconciler{
2813+
Client: myclient,
2814+
SecretCachingClient: myclient,
2815+
ClusterCache: clustercache.NewFakeClusterCache(myclient, client.ObjectKey{Name: cluster.Name, Namespace: cluster.Namespace}),
2816+
KubeadmInitLock: &myInitLocker{},
2817+
}
2818+
2819+
key := client.ObjectKey{Namespace: tt.config.Namespace, Name: tt.config.Name}
2820+
_, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: key})
2821+
g.Expect(err).ToNot(HaveOccurred())
2822+
2823+
newConfig := &bootstrapv1.KubeadmConfig{}
2824+
g.Expect(myclient.Get(ctx, key, newConfig)).To(Succeed())
2825+
2826+
for _, conditionType := range []string{bootstrapv1.KubeadmConfigReadyV1Beta2Condition, bootstrapv1.KubeadmConfigCertificatesAvailableV1Beta2Condition, bootstrapv1.KubeadmConfigDataSecretAvailableV1Beta2Condition} {
2827+
condition := v1beta2conditions.Get(newConfig, conditionType)
2828+
g.Expect(condition).ToNot(BeNil(), "condition %s is missing", conditionType)
2829+
g.Expect(condition.Status).To(Equal(metav1.ConditionTrue))
2830+
g.Expect(condition.Message).To(BeEmpty())
2831+
}
2832+
for _, conditionType := range []string{clusterv1.PausedV1Beta2Condition} {
2833+
condition := v1beta2conditions.Get(newConfig, conditionType)
2834+
g.Expect(condition).ToNot(BeNil(), "condition %s is missing", conditionType)
2835+
g.Expect(condition.Status).To(Equal(metav1.ConditionFalse))
2836+
g.Expect(condition.Message).To(BeEmpty())
2837+
}
2838+
})
2839+
}
2840+
}

0 commit comments

Comments
 (0)