kubeadM: allow conversion of TimeoutForControlPlane

v1beta3.ClusterConfiguration.APIServer.TimeoutForControlPlane
must be migrated to {Init|Join}Configuration.Timeouts.
.ControlPlaneComponentHealthCheck.

To achieve this sort of cross-Kind migration do the following:
- Use a temporary, thread-safe variable in timeoututils.go
- Make the order of GVKs in documentMapToInitConfiguration
deterministic.
This commit is contained in:
Lubomir I. Ivanov 2024-06-04 15:12:18 +03:00
parent 4af99cd676
commit 09078d4810
7 changed files with 123 additions and 31 deletions

View File

@ -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.CIImageRepository = "" // This fields doesn't exists in public API >> using default to get the roundtrip test pass
obj.KubernetesVersion = "qux" obj.KubernetesVersion = "qux"
obj.CIKubernetesVersion = "" // This fields doesn't exists in public API >> using default to get the roundtrip test pass obj.CIKubernetesVersion = "" // This fields doesn't exists in public API >> using default to get the roundtrip test pass
obj.APIServer.TimeoutForControlPlane = &metav1.Duration{ obj.APIServer.TimeoutForControlPlane = &metav1.Duration{}
Duration: 0,
}
obj.ControllerManager.ExtraEnvs = nil obj.ControllerManager.ExtraEnvs = nil
obj.APIServer.ExtraEnvs = nil obj.APIServer.ExtraEnvs = nil
obj.Scheduler.ExtraEnvs = nil obj.Scheduler.ExtraEnvs = nil

View File

@ -39,6 +39,12 @@ func SetDefaultTimeouts(t **Timeouts) {
var ( var (
activeTimeouts *Timeouts = nil activeTimeouts *Timeouts = nil
timeoutMutex = &sync.RWMutex{} 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() { func init() {
@ -58,3 +64,19 @@ func SetActiveTimeouts(timeouts *Timeouts) {
activeTimeouts = timeouts.DeepCopy() activeTimeouts = timeouts.DeepCopy()
timeoutMutex.Unlock() 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()
}

View File

@ -77,6 +77,9 @@ func Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *Cl
out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048 out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048
out.CertificateValidityPeriod = &metav1.Duration{Duration: constants.CertificateValidityPeriod} out.CertificateValidityPeriod = &metav1.Duration{Duration: constants.CertificateValidityPeriod}
out.CACertificateValidityPeriod = &metav1.Duration{Duration: constants.CACertificateValidityPeriod} 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) return autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s)
} }

View File

@ -25,7 +25,12 @@ import (
// Convert_kubeadm_InitConfiguration_To_v1beta4_InitConfiguration converts a private InitConfiguration to a public InitConfiguration. // 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 { 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. // 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 // 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 { 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{} 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. // 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 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 // 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 { 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) return autoConvert_kubeadm_Discovery_To_v1beta4_Discovery(in, out, s)

View File

@ -159,11 +159,6 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err 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 { 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) return Convert_v1beta4_JoinControlPlane_To_kubeadm_JoinControlPlane(a.(*JoinControlPlane), b.(*kubeadm.JoinControlPlane), scope)
}); err != nil { }); err != nil {
@ -309,6 +304,11 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err 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 { 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) return Convert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(a.(*ClusterConfiguration), b.(*kubeadm.ClusterConfiguration), scope)
}); err != nil { }); err != nil {
@ -768,11 +768,6 @@ func autoConvert_kubeadm_JoinConfiguration_To_v1beta4_JoinConfiguration(in *kube
return nil 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 { 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 { if err := Convert_v1beta4_APIEndpoint_To_kubeadm_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil {
return err return err

View File

@ -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. // Test the migration of all breaking changes in v1beta4, marked as "MIGRATED" in the YAML below:
// Another breaking change is the removal of ClusterConfiguration.TimeoutForControlPlane, but this field is not // - ExtraArgs
// migrated to InitConfiguration.Timeouts.ControlPlaneComponentHealthCheck due to API machinery limitations. // - ClusterConfiguration.APIServer.TimeoutForControlPlane -> {Init|Join}Configuration.Timeout.ControlPlaneComponentHealthCheck
// Remove this test once v1beta3 is removed. // - JoinConfiguration.Discovery.Timeout -> JoinConfiguration.Timeout.Discovery
func TestMigrateV1Beta3ExtraArgs(t *testing.T) { func TestMigrateV1Beta3WithBreakingChanges(t *testing.T) {
var ( var (
gv = kubeadmapiv1old.SchemeGroupVersion.String() gv = kubeadmapiv1old.SchemeGroupVersion.String()
gvNew = kubeadmapiv1.SchemeGroupVersion.String() gvNew = kubeadmapiv1.SchemeGroupVersion.String()
@ -465,27 +465,45 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) {
advertiseAddress: 1.2.3.4 advertiseAddress: 1.2.3.4
bindPort: 6443 bindPort: 6443
nodeRegistration: nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock criSocket: unix:///some-socket-path
kubeletExtraArgs: kubeletExtraArgs: # MIGRATED
foo: bar foo: bar
name: node name: node
--- ---
apiServer: apiServer:
timeoutForControlPlane: 2m0s ### note: this is note migrated! timeoutForControlPlane: 2m32s # MIGRATED
extraArgs: extraArgs: # MIGRATED
foo: bar foo: bar
apiVersion: %[1]s apiVersion: %[1]s
controllerManager: controllerManager:
extraArgs: extraArgs: # MIGRATED
foo: bar foo: bar
etcd: etcd:
local: local:
extraArgs: extraArgs: # MIGRATED
foo: bar foo: bar
kind: ClusterConfiguration kind: ClusterConfiguration
kubernetesVersion: v1.10.0
scheduler: scheduler:
extraArgs: extraArgs: # MIGRATED
foo: bar 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)) `, gv))
expectedOutput = dedent.Dedent(fmt.Sprintf(` expectedOutput = dedent.Dedent(fmt.Sprintf(`
@ -503,7 +521,7 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) {
advertiseAddress: 1.2.3.4 advertiseAddress: 1.2.3.4
bindPort: 6443 bindPort: 6443
nodeRegistration: nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock criSocket: unix:///some-socket-path
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
imagePullSerial: true imagePullSerial: true
kubeletExtraArgs: kubeletExtraArgs:
@ -514,7 +532,7 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) {
- effect: NoSchedule - effect: NoSchedule
key: node-role.kubernetes.io/control-plane key: node-role.kubernetes.io/control-plane
timeouts: timeouts:
controlPlaneComponentHealthCheck: 4m0s controlPlaneComponentHealthCheck: 2m32s
discovery: 5m0s discovery: 5m0s
etcdAPICall: 2m0s etcdAPICall: 2m0s
kubeletHealthCheck: 4m0s kubeletHealthCheck: 4m0s
@ -545,7 +563,7 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) {
value: bar value: bar
imageRepository: registry.k8s.io imageRepository: registry.k8s.io
kind: ClusterConfiguration kind: ClusterConfiguration
kubernetesVersion: v1.30.1 kubernetesVersion: v1.10.0
networking: networking:
dnsDomain: cluster.local dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12 serviceSubnet: 10.96.0.0/12
@ -554,6 +572,33 @@ func TestMigrateV1Beta3ExtraArgs(t *testing.T) {
extraArgs: extraArgs:
- name: foo - name: foo
value: bar 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)) `, gvNew))
) )

View File

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"net" "net"
"os" "os"
"sort"
"strconv" "strconv"
"strings" "strings"
@ -307,7 +308,19 @@ func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecat
var initcfg *kubeadmapi.InitConfiguration var initcfg *kubeadmapi.InitConfiguration
var clustercfg *kubeadmapi.ClusterConfiguration 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 // first, check if this GVK is supported and possibly not deprecated
if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated, allowExperimental); err != nil { if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated, allowExperimental); err != nil {
return nil, err return nil, err