diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 41a6b27c5e6..b65672f6884 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -89,9 +89,7 @@ func fuzzClusterConfiguration(obj *kubeadm.ClusterConfiguration, c fuzz.Continue obj.CIImageRepository = "" // This fields doesn't exists in public API >> using default to get the roundtrip test pass obj.KubernetesVersion = "qux" obj.CIKubernetesVersion = "" // This fields doesn't exists in public API >> using default to get the roundtrip test pass - obj.APIServer.TimeoutForControlPlane = &metav1.Duration{ - Duration: 0, - } + obj.APIServer.TimeoutForControlPlane = &metav1.Duration{} obj.ControllerManager.ExtraEnvs = nil obj.APIServer.ExtraEnvs = nil obj.Scheduler.ExtraEnvs = nil diff --git a/cmd/kubeadm/app/apis/kubeadm/timeoututils.go b/cmd/kubeadm/app/apis/kubeadm/timeoututils.go index bbfd804a9c2..c0a1adfcd46 100644 --- a/cmd/kubeadm/app/apis/kubeadm/timeoututils.go +++ b/cmd/kubeadm/app/apis/kubeadm/timeoututils.go @@ -39,6 +39,12 @@ func SetDefaultTimeouts(t **Timeouts) { var ( activeTimeouts *Timeouts = nil timeoutMutex = &sync.RWMutex{} + + // conversionTimeoutControlPlane is a variable used when converting the v1beta3 field + // ClusterConfiguration.APIServer.TimeoutForControlPlane to + // v1beta4 {Init|Join}Configuration.Timeouts.ControlPlaneComponentHealthCheck. + // TODO: remove this once v1beta3 is removed. + conversionTimeoutControlPlane *metav1.Duration ) func init() { @@ -58,3 +64,19 @@ func SetActiveTimeouts(timeouts *Timeouts) { activeTimeouts = timeouts.DeepCopy() timeoutMutex.Unlock() } + +// TODO: remove these once v1beta3 is removed + +// GetConversionTimeoutControlPlane returns conversionTimeoutControlPlane. +func GetConversionTimeoutControlPlane() *metav1.Duration { + timeoutMutex.RLock() + defer timeoutMutex.RUnlock() + return conversionTimeoutControlPlane +} + +// SetConversionTimeoutControlPlane stores t into conversionTimeoutControlPlane. +func SetConversionTimeoutControlPlane(t *metav1.Duration) { + timeoutMutex.Lock() + conversionTimeoutControlPlane = t.DeepCopy() + timeoutMutex.Unlock() +} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go index 80516553aae..ee882cf669d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go @@ -77,6 +77,9 @@ func Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *Cl out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048 out.CertificateValidityPeriod = &metav1.Duration{Duration: constants.CertificateValidityPeriod} out.CACertificateValidityPeriod = &metav1.Duration{Duration: constants.CACertificateValidityPeriod} + if in.APIServer.TimeoutForControlPlane != nil && in.APIServer.TimeoutForControlPlane.Duration != 0 { + kubeadm.SetConversionTimeoutControlPlane(in.APIServer.TimeoutForControlPlane) + } return autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s) } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/conversion.go index 97ce6008ffa..fa0ef37f3b1 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/conversion.go @@ -25,7 +25,12 @@ import ( // Convert_kubeadm_InitConfiguration_To_v1beta4_InitConfiguration converts a private InitConfiguration to a public InitConfiguration. func Convert_kubeadm_InitConfiguration_To_v1beta4_InitConfiguration(in *kubeadm.InitConfiguration, out *InitConfiguration, s conversion.Scope) error { - return autoConvert_kubeadm_InitConfiguration_To_v1beta4_InitConfiguration(in, out, s) + err := autoConvert_kubeadm_InitConfiguration_To_v1beta4_InitConfiguration(in, out, s) + timeoutControlPlane := kubeadm.GetConversionTimeoutControlPlane() // Remove with v1beta3. + if timeoutControlPlane != nil { + out.Timeouts.ControlPlaneComponentHealthCheck = timeoutControlPlane + } + return err } // Convert_v1beta4_InitConfiguration_To_kubeadm_InitConfiguration converts a public InitConfiguration to a private InitConfiguration. @@ -46,8 +51,9 @@ func Convert_kubeadm_APIServer_To_v1beta4_APIServer(in *kubeadm.APIServer, out * // Convert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration is required due to missing TimeoutForControlPlane in v1beta4 func Convert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *ClusterConfiguration, out *kubeadm.ClusterConfiguration, s conversion.Scope) error { + err := autoConvert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s) out.APIServer.TimeoutForControlPlane = &metav1.Duration{} - return autoConvert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s) + return err } // Convert_v1beta4_JoinConfiguration_To_kubeadm_JoinConfiguration converts a public JoinConfiguration to a private JoinConfiguration. @@ -57,6 +63,16 @@ func Convert_v1beta4_JoinConfiguration_To_kubeadm_JoinConfiguration(in *JoinConf return err } +// Convert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration converts a private JoinConfinguration to a public JoinCOnfiguration. +func Convert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(in *kubeadm.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { + err := autoConvert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(in, out, s) + timeoutControlPlane := kubeadm.GetConversionTimeoutControlPlane() // Remove with v1beta3. + if timeoutControlPlane != nil { + out.Timeouts.ControlPlaneComponentHealthCheck = timeoutControlPlane + } + return err +} + // Convert_kubeadm_Discovery_To_v1beta4_Discovery is required because there is no Discovery.Timeout in v1beta4 func Convert_kubeadm_Discovery_To_v1beta4_Discovery(in *kubeadm.Discovery, out *Discovery, s conversion.Scope) error { return autoConvert_kubeadm_Discovery_To_v1beta4_Discovery(in, out, s) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go index c6e14260ffc..cdfa252a6c1 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go @@ -159,11 +159,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*kubeadm.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(a.(*kubeadm.JoinConfiguration), b.(*JoinConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*JoinControlPlane)(nil), (*kubeadm.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta4_JoinControlPlane_To_kubeadm_JoinControlPlane(a.(*JoinControlPlane), b.(*kubeadm.JoinControlPlane), scope) }); err != nil { @@ -309,6 +304,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*kubeadm.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(a.(*kubeadm.JoinConfiguration), b.(*JoinConfiguration), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*ClusterConfiguration)(nil), (*kubeadm.ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(a.(*ClusterConfiguration), b.(*kubeadm.ClusterConfiguration), scope) }); err != nil { @@ -768,11 +768,6 @@ func autoConvert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(in *kube return nil } -// Convert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration is an autogenerated conversion function. -func Convert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(in *kubeadm.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { - return autoConvert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(in, out, s) -} - func autoConvert_v1beta4_JoinControlPlane_To_kubeadm_JoinControlPlane(in *JoinControlPlane, out *kubeadm.JoinControlPlane, s conversion.Scope) error { if err := Convert_v1beta4_APIEndpoint_To_kubeadm_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { return err diff --git a/cmd/kubeadm/app/util/config/common_test.go b/cmd/kubeadm/app/util/config/common_test.go index 375aeb75e32..c17d7a7ea7e 100644 --- a/cmd/kubeadm/app/util/config/common_test.go +++ b/cmd/kubeadm/app/util/config/common_test.go @@ -441,11 +441,11 @@ func TestMigrateOldConfig(t *testing.T) { } } -// Test the migration of extra args from v1beta3 to v1beta4, as this is the only breaking change that is migrated. -// Another breaking change is the removal of ClusterConfiguration.TimeoutForControlPlane, but this field is not -// migrated to InitConfiguration.Timeouts.ControlPlaneComponentHealthCheck due to API machinery limitations. -// Remove this test once v1beta3 is removed. -func TestMigrateV1Beta3ExtraArgs(t *testing.T) { +// Test the migration of all breaking changes in v1beta4, marked as "MIGRATED" in the YAML below: +// - ExtraArgs +// - ClusterConfiguration.APIServer.TimeoutForControlPlane -> {Init|Join}Configuration.Timeout.ControlPlaneComponentHealthCheck +// - JoinConfiguration.Discovery.Timeout -> JoinConfiguration.Timeout.Discovery +func TestMigrateV1Beta3WithBreakingChanges(t *testing.T) { var ( gv = kubeadmapiv1old.SchemeGroupVersion.String() gvNew = kubeadmapiv1.SchemeGroupVersion.String() @@ -465,27 +465,45 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) { advertiseAddress: 1.2.3.4 bindPort: 6443 nodeRegistration: - criSocket: unix:///var/run/containerd/containerd.sock - kubeletExtraArgs: + criSocket: unix:///some-socket-path + kubeletExtraArgs: # MIGRATED foo: bar name: node --- apiServer: - timeoutForControlPlane: 2m0s ### note: this is note migrated! - extraArgs: + timeoutForControlPlane: 2m32s # MIGRATED + extraArgs: # MIGRATED foo: bar apiVersion: %[1]s controllerManager: - extraArgs: + extraArgs: # MIGRATED foo: bar etcd: local: - extraArgs: + extraArgs: # MIGRATED foo: bar kind: ClusterConfiguration + kubernetesVersion: v1.10.0 scheduler: - extraArgs: + extraArgs: # MIGRATED foo: bar + --- + apiVersion: %[1]s + kind: JoinConfiguration + nodeRegistration: + criSocket: unix:///some-socket-path + imagePullPolicy: IfNotPresent + kubeletExtraArgs: # MIGRATED + foo: baz + name: foo + taints: null + discovery: + bootstrapToken: + apiServerEndpoint: some-address:6443 + token: abcdef.0123456789abcdef + unsafeSkipCAVerification: true + tlsBootstrapToken: abcdef.0123456789abcdef + timeout: 2m10s # MIGRATED `, gv)) expectedOutput = dedent.Dedent(fmt.Sprintf(` @@ -503,7 +521,7 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) { advertiseAddress: 1.2.3.4 bindPort: 6443 nodeRegistration: - criSocket: unix:///var/run/containerd/containerd.sock + criSocket: unix:///some-socket-path imagePullPolicy: IfNotPresent imagePullSerial: true kubeletExtraArgs: @@ -514,7 +532,7 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) { - effect: NoSchedule key: node-role.kubernetes.io/control-plane timeouts: - controlPlaneComponentHealthCheck: 4m0s + controlPlaneComponentHealthCheck: 2m32s discovery: 5m0s etcdAPICall: 2m0s kubeletHealthCheck: 4m0s @@ -545,7 +563,7 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) { value: bar imageRepository: registry.k8s.io kind: ClusterConfiguration - kubernetesVersion: v1.30.1 + kubernetesVersion: v1.10.0 networking: dnsDomain: cluster.local serviceSubnet: 10.96.0.0/12 @@ -554,6 +572,33 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) { extraArgs: - name: foo value: bar + --- + apiVersion: %[1]s + caCertPath: /etc/kubernetes/pki/ca.crt + discovery: + bootstrapToken: + apiServerEndpoint: some-address:6443 + token: abcdef.0123456789abcdef + unsafeSkipCAVerification: true + tlsBootstrapToken: abcdef.0123456789abcdef + kind: JoinConfiguration + nodeRegistration: + criSocket: unix:///some-socket-path + imagePullPolicy: IfNotPresent + imagePullSerial: true + kubeletExtraArgs: + - name: foo + value: baz + name: foo + taints: null + timeouts: + controlPlaneComponentHealthCheck: 2m32s + discovery: 2m10s + etcdAPICall: 2m0s + kubeletHealthCheck: 4m0s + kubernetesAPICall: 1m0s + tlsBootstrap: 5m0s + upgradeManifests: 5m0s `, gvNew)) ) diff --git a/cmd/kubeadm/app/util/config/initconfiguration.go b/cmd/kubeadm/app/util/config/initconfiguration.go index f1c2db29182..8d2af14e394 100644 --- a/cmd/kubeadm/app/util/config/initconfiguration.go +++ b/cmd/kubeadm/app/util/config/initconfiguration.go @@ -20,6 +20,7 @@ import ( "bytes" "net" "os" + "sort" "strconv" "strings" @@ -307,7 +308,19 @@ func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecat var initcfg *kubeadmapi.InitConfiguration var clustercfg *kubeadmapi.ClusterConfiguration - for gvk, fileContent := range gvkmap { + // Sort the GVKs deterministically by GVK string. + // This allows ClusterConfiguration to be decoded first. + gvks := make([]schema.GroupVersionKind, 0, len(gvkmap)) + for gvk := range gvkmap { + gvks = append(gvks, gvk) + } + sort.Slice(gvks, func(i, j int) bool { + return gvks[i].String() < gvks[j].String() + }) + + for _, gvk := range gvks { + fileContent := gvkmap[gvk] + // first, check if this GVK is supported and possibly not deprecated if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated, allowExperimental); err != nil { return nil, err