Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/bootstrap/kubeadm/v1beta2/kubeadm_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,11 @@ type Discovery struct {
TLSBootstrapToken string `json:"tlsBootstrapToken,omitempty"`
}

// IsDefined returns true if the Discovery is defined.
func (r *Discovery) IsDefined() bool {
return !reflect.DeepEqual(r, &Discovery{})
}

// BootstrapTokenDiscovery is used to set the options for bootstrap token based discovery.
// +kubebuilder:validation:MinProperties=1
type BootstrapTokenDiscovery struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,12 @@ func Test_createRequest(t *testing.T) {
},
},
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
KubeletExtraArgs: []bootstrapv1.Arg{{
Name: "v",
Expand All @@ -635,6 +641,7 @@ func Test_createRequest(t *testing.T) {
currentKubeadmConfigCleanedUp.SetGroupVersionKind(bootstrapv1.GroupVersion.WithKind("KubeadmConfig")) // cleanupKubeadmConfig adds GVK.
currentKubeadmConfigCleanedUp.Status = bootstrapv1.KubeadmConfigStatus{} // cleanupKubeadmConfig drops status.
defaulting.ApplyPreviousKubeadmConfigDefaults(&currentKubeadmConfigCleanedUp.Spec) // PrepareKubeadmConfigsForDiff applies defaults.
currentKubeadmConfigCleanedUp.Spec.JoinConfiguration.Discovery = bootstrapv1.Discovery{} // PrepareKubeadmConfigsForDiff cleans up Discovery.
currentKubeadmConfigWithOutdatedLabelsAndAnnotations := currentKubeadmConfig.DeepCopy()
currentKubeadmConfigWithOutdatedLabelsAndAnnotations.Labels["outdated-label-1"] = "outdated-label-value-1"
currentKubeadmConfigWithOutdatedLabelsAndAnnotations.Annotations["outdated-annotation-1"] = "outdated-annotation-value-1"
Expand All @@ -648,6 +655,7 @@ func Test_createRequest(t *testing.T) {
desiredKubeadmConfigCleanedUp.SetGroupVersionKind(bootstrapv1.GroupVersion.WithKind("KubeadmConfig")) // cleanupKubeadmConfig adds GVK.
desiredKubeadmConfigCleanedUp.Status = bootstrapv1.KubeadmConfigStatus{} // cleanupKubeadmConfig drops status.
defaulting.ApplyPreviousKubeadmConfigDefaults(&desiredKubeadmConfigCleanedUp.Spec) // PrepareKubeadmConfigsForDiff applies defaults.
desiredKubeadmConfigCleanedUp.Spec.JoinConfiguration.Discovery = bootstrapv1.Discovery{} // PrepareKubeadmConfigsForDiff cleans up Discovery.

currentInfraMachine := &unstructured.Unstructured{
Object: map[string]interface{}{
Expand Down
22 changes: 18 additions & 4 deletions controlplane/kubeadm/internal/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,16 @@ func PrepareKubeadmConfigsForDiff(desiredKubeadmConfig, currentKubeadmConfig *bo
desiredKubeadmConfig.Spec.JoinConfiguration.Discovery = bootstrapv1.Discovery{}
currentKubeadmConfig.Spec.JoinConfiguration.Discovery = bootstrapv1.Discovery{}

// Cleanup ControlPlaneComponentHealthCheckSeconds from desiredKubeadmConfig and currentKubeadmConfig,
// because through conversion apiServer.timeoutForControlPlane in v1beta1 is converted to
// initConfiguration/joinConfiguration.timeouts.controlPlaneComponentHealthCheckSeconds in v1beta2 and
// this can lead to a diff here that would lead to a rollout.
// Note: Changes to ControlPlaneComponentHealthCheckSeconds will apply for the next join, but they will not lead to a rollout.
desiredKubeadmConfig.Spec.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = nil
desiredKubeadmConfig.Spec.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = nil
currentKubeadmConfig.Spec.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = nil
currentKubeadmConfig.Spec.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = nil

// If KCP JoinConfiguration.ControlPlane is nil and the Machine JoinConfiguration.ControlPlane is empty,
// set Machine JoinConfiguration.ControlPlane to nil.
// NOTE: This is required because CABPK applies an empty JoinConfiguration.ControlPlane in case it is nil.
Expand Down Expand Up @@ -531,12 +541,16 @@ func dropOmittableFields(spec *bootstrapv1.KubeadmConfigSpec) {

// isKubeadmConfigForJoin returns true if the KubeadmConfig is for a control plane
// or a worker machine that joined an existing cluster.
// Note: this check is based on the assumption that KubeadmConfig for joining
// control plane and workers nodes always have a non-empty join configuration, while
// instead the join configuration for the first control plane machine in the
// Note: This check is based on the assumption that KubeadmConfig for joining
// control plane and workers nodes always have a non-empty JoinConfiguration.Discovery, while
// instead the JoinConfiguration for the first control plane machine in the
// cluster is emptied out by KCP.
// Note: Previously we checked if the entire JoinConfiguration is defined, but that
// is not safe because apiServer.timeoutForControlPlane in v1beta1 is also converted to
// joinConfiguration.timeouts.controlPlaneComponentHealthCheckSeconds in v1beta2 and
// accordingly we would also detect init KubeadmConfigs as join.
func isKubeadmConfigForJoin(c *bootstrapv1.KubeadmConfig) bool {
return c.Spec.JoinConfiguration.IsDefined()
return c.Spec.JoinConfiguration.Discovery.IsDefined()
}

// isKubeadmConfigForInit returns true if the KubeadmConfig is for the first control plane
Expand Down
93 changes: 77 additions & 16 deletions controlplane/kubeadm/internal/filters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
ClusterConfiguration: bootstrapv1.ClusterConfiguration{},
JoinConfiguration: bootstrapv1.JoinConfiguration{
Timeouts: bootstrapv1.Timeouts{
// ControlPlaneComponentHealthCheckSeconds is different, but it is ignored for the diff
ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](5),
KubernetesAPICallSeconds: ptr.To[int32](7),
},
Expand Down Expand Up @@ -392,7 +393,8 @@ func TestMatchesKubeadmConfig(t *testing.T) {
// InitConfiguration will be converted to JoinConfiguration and then compared against the JoinConfiguration from KCP.
InitConfiguration: bootstrapv1.InitConfiguration{
Timeouts: bootstrapv1.Timeouts{
ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](5),
// ControlPlaneComponentHealthCheckSeconds is different, but it is ignored for the diff
ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](1),
KubernetesAPICallSeconds: ptr.To[int32](7),
},
Patches: bootstrapv1.Patches{
Expand Down Expand Up @@ -420,7 +422,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
Expand Down Expand Up @@ -478,7 +480,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
Expand Down Expand Up @@ -536,7 +538,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeFalse())
g.Expect(reason).To(BeComparableTo(`Machine KubeadmConfig is outdated: diff: &v1beta2.KubeadmConfigSpec{
ClusterConfiguration: {},
Expand Down Expand Up @@ -597,6 +599,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
Spec: bootstrapv1.KubeadmConfigSpec{
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "A new name",
},
Expand All @@ -608,11 +616,11 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
t.Run("returns true if JoinConfiguration is equal apart from discovery", func(t *testing.T) {
t.Run("returns true if JoinConfiguration is equal apart from Discovery and Timeouts", func(t *testing.T) {
g := NewWithT(t)
kcp := &controlplanev1.KubeadmControlPlane{
Spec: controlplanev1.KubeadmControlPlaneSpec{
Expand All @@ -625,6 +633,9 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
// Discovery gets removed because Discovery is not relevant for the rollout decision.
Discovery: bootstrapv1.Discovery{TLSBootstrapToken: "aaa"},
Timeouts: bootstrapv1.Timeouts{
ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](1),
},
},
},
Version: "v1.30.0",
Expand Down Expand Up @@ -656,8 +667,16 @@ func TestMatchesKubeadmConfig(t *testing.T) {
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "A new name",
},
// Discovery gets removed because Discovery is not relevant for the rollout decision.
Discovery: bootstrapv1.Discovery{TLSBootstrapToken: "bbb"},
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
// Discovery gets removed only for the diff because Discovery is not relevant for the rollout decision.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
Timeouts: bootstrapv1.Timeouts{
ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](11),
},
},
},
},
Expand All @@ -666,7 +685,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
Expand Down Expand Up @@ -710,6 +729,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
Spec: bootstrapv1.KubeadmConfigSpec{
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "A new name",
},
Expand All @@ -722,7 +747,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
Expand Down Expand Up @@ -780,7 +805,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeFalse())
g.Expect(reason).To(Equal(`Machine KubeadmConfig is outdated: diff: &v1beta2.KubeadmConfigSpec{
ClusterConfiguration: {},
Expand Down Expand Up @@ -842,6 +867,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
Spec: bootstrapv1.KubeadmConfigSpec{
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "name",
},
Expand All @@ -859,7 +890,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeFalse())
g.Expect(reason).To(Equal(`Machine KubeadmConfig is outdated: diff: &v1beta2.KubeadmConfigSpec{
ClusterConfiguration: {},
Expand Down Expand Up @@ -920,6 +951,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
Spec: bootstrapv1.KubeadmConfigSpec{
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "An old name", // This is a change
},
Expand All @@ -931,7 +968,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeFalse())
g.Expect(reason).To(BeComparableTo(`Machine KubeadmConfig is outdated: diff: &v1beta2.KubeadmConfigSpec{
ClusterConfiguration: {},
Expand Down Expand Up @@ -988,6 +1025,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
Spec: bootstrapv1.KubeadmConfigSpec{
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "An old name", // This is a change
},
Expand Down Expand Up @@ -1071,6 +1114,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
ClusterConfiguration: bootstrapv1.ClusterConfiguration{},
InitConfiguration: bootstrapv1.InitConfiguration{},
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "name",
},
Expand All @@ -1082,7 +1131,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
Expand Down Expand Up @@ -1125,6 +1174,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
Spec: bootstrapv1.KubeadmConfigSpec{
Format: "",
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "name",
},
Expand All @@ -1136,7 +1191,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeTrue())
g.Expect(reason).To(BeEmpty())
})
Expand Down Expand Up @@ -1180,6 +1235,12 @@ func TestMatchesKubeadmConfig(t *testing.T) {
},
Spec: bootstrapv1.KubeadmConfigSpec{
JoinConfiguration: bootstrapv1.JoinConfiguration{
// This field is technically set by CABPK, but adding it here so that matchesKubeadmConfig detects this correctly as a join KubeadmConfig.
Discovery: bootstrapv1.Discovery{
BootstrapToken: bootstrapv1.BootstrapTokenDiscovery{
APIServerEndpoint: "1.2.3.4:6443",
},
},
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
Name: "name",
},
Expand All @@ -1191,7 +1252,7 @@ func TestMatchesKubeadmConfig(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(currentKubeadmConfig).ToNot(BeNil())
g.Expect(desiredKubeadmConfig).ToNot(BeNil())
g.Expect(isKubeadmConfigForJoin(desiredKubeadmConfig)).To(BeTrue())
g.Expect(desiredKubeadmConfig.Spec.InitConfiguration).To(Equal(bootstrapv1.InitConfiguration{})) // Verify that this is a join.
g.Expect(match).To(BeFalse())
g.Expect(reason).To(BeComparableTo(`Machine KubeadmConfig is outdated: diff: &v1beta2.KubeadmConfigSpec{
ClusterConfiguration: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ spec:
apiServer:
extraArgs:
v: "0"
timeoutForControlPlane: 4m
# host.docker.internal is required by kubetest when running on MacOS because of the way ports are proxied.
certSANs: [localhost, host.docker.internal, "::", "::1", "127.0.0.1", "0.0.0.0"]
initConfiguration:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ spec:
apiServer:
extraArgs:
v: "0"
timeoutForControlPlane: 4m
# host.docker.internal is required by kubetest when running on MacOS because of the way ports are proxied.
certSANs: [localhost, host.docker.internal, "::", "::1", "127.0.0.1", "0.0.0.0"]
initConfiguration:
Expand Down
Loading