diff --git a/api/bootstrap/kubeadm/v1beta1/conversion.go b/api/bootstrap/kubeadm/v1beta1/conversion.go index 390c0070b258..5867c529afad 100644 --- a/api/bootstrap/kubeadm/v1beta1/conversion.go +++ b/api/bootstrap/kubeadm/v1beta1/conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v1beta1 import ( + "reflect" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -24,30 +26,153 @@ import ( bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.KubeadmConfig) + if err := Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(src, dst, nil); err != nil { + return err + } - return Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(src, dst, nil) + // Manually restore data. + restored := &bootstrapv1.KubeadmConfig{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + if ok { + RestoreKubeadmConfigSpec(&restored.Spec, &dst.Spec) + } + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.ConvertTo(&dst.Spec) + return nil +} + +func RestoreKubeadmConfigSpec(restored *bootstrapv1.KubeadmConfigSpec, dst *bootstrapv1.KubeadmConfigSpec) { + // Restore fields added in v1beta2 + if restored.InitConfiguration != nil && restored.InitConfiguration.Timeouts != nil { + if dst.InitConfiguration == nil { + dst.InitConfiguration = &bootstrapv1.InitConfiguration{} + } + dst.InitConfiguration.Timeouts = restored.InitConfiguration.Timeouts + } + if restored.JoinConfiguration != nil && restored.JoinConfiguration.Timeouts != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + dst.JoinConfiguration.Timeouts = restored.JoinConfiguration.Timeouts + } +} + +func (src *KubeadmConfigSpec) ConvertTo(dst *bootstrapv1.KubeadmConfigSpec) { + // Override with timeouts values already existing in v1beta1. + var initControlPlaneComponentHealthCheckSeconds *int32 + if src.ClusterConfiguration != nil && src.ClusterConfiguration.APIServer.TimeoutForControlPlane != nil { + if dst.InitConfiguration == nil { + dst.InitConfiguration = &bootstrapv1.InitConfiguration{} + } + if dst.InitConfiguration.Timeouts == nil { + dst.InitConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.ClusterConfiguration.APIServer.TimeoutForControlPlane) + initControlPlaneComponentHealthCheckSeconds = dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if dst.JoinConfiguration.Timeouts == nil { + dst.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + dst.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds + if src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil { + dst.JoinConfiguration.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(src.JoinConfiguration.Discovery.Timeout) + } + } + + if reflect.DeepEqual(dst.ClusterConfiguration, &bootstrapv1.ClusterConfiguration{}) { + dst.ClusterConfiguration = nil + } } func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.KubeadmConfig) + if err := Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.ConvertFrom(&src.Spec) - return Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(src, dst, nil) + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) +} + +func (dst *KubeadmConfigSpec) ConvertFrom(src *bootstrapv1.KubeadmConfigSpec) { + // Convert timeouts moved from one struct to another. + if src.InitConfiguration != nil && src.InitConfiguration.Timeouts != nil && src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil { + if dst.ClusterConfiguration == nil { + dst.ClusterConfiguration = &ClusterConfiguration{} + } + dst.ClusterConfiguration.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + } + if reflect.DeepEqual(dst.InitConfiguration, &InitConfiguration{}) { + dst.InitConfiguration = nil + } + if src.JoinConfiguration != nil && src.JoinConfiguration.Timeouts != nil && src.JoinConfiguration.Timeouts.TLSBootstrapSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &JoinConfiguration{} + } + dst.JoinConfiguration.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(src.JoinConfiguration.Timeouts.TLSBootstrapSeconds) + } + if reflect.DeepEqual(dst.JoinConfiguration, &JoinConfiguration{}) { + dst.JoinConfiguration = nil + } } func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.KubeadmConfigTemplate) + if err := Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &bootstrapv1.KubeadmConfigTemplate{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + if ok { + RestoreKubeadmConfigSpec(&restored.Spec.Template.Spec, &dst.Spec.Template.Spec) + } - return Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(src, dst, nil) + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.ConvertTo(&dst.Spec.Template.Spec) + return nil } func (dst *KubeadmConfigTemplate) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.KubeadmConfigTemplate) + if err := Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.ConvertFrom(&src.Spec.Template.Spec) + + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) +} + +func Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { + // Timeouts requires conversion at an upper level + return autoConvert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s) +} - return Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(src, dst, nil) +func Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { + // Timeouts requires conversion at an upper level + return autoConvert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in, out, s) } func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, out *KubeadmConfigStatus, s apimachineryconversion.Scope) error { @@ -82,11 +207,54 @@ func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *boot return nil } +func Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) + return autoConvert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in, out, s) +} + +func Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) + return autoConvert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in, out, s) +} + +func Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { + // Following fields require a custom conversions. + out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs) + return autoConvert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s) +} + +func Convert_v1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error { + // TimeoutForControlPlane has been removed in v1beta2 + return autoConvert_v1beta1_APIServer_To_v1beta2_APIServer(in, out, s) +} + +func Convert_v1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error { + // Timeout has been removed in v1beta2 + return autoConvert_v1beta1_Discovery_To_v1beta2_Discovery(in, out, s) +} + func Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *KubeadmConfigSpec, out *bootstrapv1.KubeadmConfigSpec, s apimachineryconversion.Scope) error { // NOTE: v1beta2 KubeadmConfigSpec does not have UseExperimentalRetryJoin anymore, so it's fine to just lose this field. return autoConvert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in, out, s) } +func Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) +} + +func Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + +func Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs) + return autoConvert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + func Convert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in *KubeadmConfigStatus, out *bootstrapv1.KubeadmConfigStatus, s apimachineryconversion.Scope) error { if err := autoConvert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in, out, s); err != nil { return err diff --git a/api/bootstrap/kubeadm/v1beta1/conversion_test.go b/api/bootstrap/kubeadm/v1beta1/conversion_test.go index 6b406b22d4b5..6c78736b4a07 100644 --- a/api/bootstrap/kubeadm/v1beta1/conversion_test.go +++ b/api/bootstrap/kubeadm/v1beta1/conversion_test.go @@ -21,15 +21,23 @@ package v1beta1 import ( "reflect" "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) +const ( + fakeID = "abcdef" + fakeSecret = "abcdef0123456789" +) + // Test is disabled when the race detector is enabled (via "//go:build !race" above) because otherwise the fuzz tests would just time out. func TestFuzzyConversion(t *testing.T) { @@ -48,11 +56,37 @@ func TestFuzzyConversion(t *testing.T) { func KubeadmConfigFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ hubKubeadmConfigStatus, + spokeAPIServer, + spokeDiscovery, spokeKubeadmConfigSpec, spokeKubeadmConfigStatus, + hubBootstrapTokenString, + spokeBootstrapTokenString, + hubKubeadmConfigSpec, } } +func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + spokeAPIServer, + spokeDiscovery, + spokeKubeadmConfigSpec, + spokeBootstrapTokenString, + hubBootstrapTokenString, + hubKubeadmConfigSpec, + } +} + +func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret +} + +func spokeBootstrapTokenString(in *BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret +} + func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c randfill.Continue) { c.FillNoCustom(in) // Always create struct with at least one mandatory fields. @@ -71,6 +105,25 @@ func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c randfill.Cont } } +func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) { + c.FillNoCustom(in) + + // enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration + var initControlPlaneComponentHealthCheckSeconds *int32 + if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil { + initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if in.JoinConfiguration == nil { + in.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if in.JoinConfiguration.Timeouts == nil { + in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds + } +} + func spokeKubeadmConfigSpec(in *KubeadmConfigSpec, c randfill.Continue) { c.FillNoCustom(in) @@ -78,6 +131,22 @@ func spokeKubeadmConfigSpec(in *KubeadmConfigSpec, c randfill.Continue) { in.UseExperimentalRetryJoin = false } +func spokeAPIServer(in *APIServer, c randfill.Continue) { + c.FillNoCustom(in) + + if in.TimeoutForControlPlane != nil { + in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeDiscovery(in *Discovery, c randfill.Continue) { + c.FillNoCustom(in) + + if in.Timeout != nil { + in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + func spokeKubeadmConfigStatus(in *KubeadmConfigStatus, c randfill.Continue) { c.FillNoCustom(in) // Drop empty structs with only omit empty fields. @@ -87,9 +156,3 @@ func spokeKubeadmConfigStatus(in *KubeadmConfigStatus, c randfill.Continue) { } } } - -func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { - return []interface{}{ - spokeKubeadmConfigSpec, - } -} diff --git a/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go b/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go index 5d4a74cfbca7..0019c2599c4a 100644 --- a/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go +++ b/api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go @@ -50,11 +50,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_APIServer_To_v1beta1_APIServer(a.(*v1beta2.APIServer), b.(*APIServer), scope) }); err != nil { @@ -120,16 +115,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) }); err != nil { @@ -140,11 +125,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_Discovery_To_v1beta1_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) }); err != nil { @@ -275,21 +255,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(a.(*v1beta2.InitConfiguration), b.(*InitConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*JoinControlPlane)(nil), (*v1beta2.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta2.JoinControlPlane), scope) }); err != nil { @@ -415,16 +385,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*v1beta2.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(a.(*v1beta2.LocalEtcd), b.(*LocalEtcd), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*NTP)(nil), (*v1beta2.NTP)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_NTP_To_v1beta2_NTP(a.(*NTP), b.(*v1beta2.NTP), scope) }); err != nil { @@ -445,16 +405,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*v1beta2.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*v1beta2.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*Partition)(nil), (*v1beta2.Partition)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_Partition_To_v1beta2_Partition(a.(*Partition), b.(*v1beta2.Partition), scope) }); err != nil { @@ -520,11 +470,26 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*corev1beta1.Condition)(nil), (*v1.Condition)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_Condition_To_v1_Condition(a.(*corev1beta1.Condition), b.(*v1.Condition), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*KubeadmConfigSpec)(nil), (*v1beta2.KubeadmConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(a.(*KubeadmConfigSpec), b.(*v1beta2.KubeadmConfigSpec), scope) }); err != nil { @@ -535,16 +500,51 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*corev1beta1.ObjectMeta)(nil), (*corev1beta2.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_ObjectMeta_To_v1beta2_ObjectMeta(a.(*corev1beta1.ObjectMeta), b.(*corev1beta2.ObjectMeta), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(a.(*v1beta2.InitConfiguration), b.(*InitConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.KubeadmConfigStatus)(nil), (*KubeadmConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(a.(*v1beta2.KubeadmConfigStatus), b.(*KubeadmConfigStatus), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta2.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(a.(*v1beta2.LocalEtcd), b.(*LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*v1beta2.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*corev1beta2.ObjectMeta)(nil), (*corev1beta1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_ObjectMeta_To_v1beta1_ObjectMeta(a.(*corev1beta2.ObjectMeta), b.(*corev1beta1.ObjectMeta), scope) }); err != nil { @@ -580,21 +580,15 @@ func autoConvert_v1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1be return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) + // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_APIServer_To_v1beta2_APIServer is an autogenerated conversion function. -func Convert_v1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1beta2.APIServer, s conversion.Scope) error { - return autoConvert_v1beta1_APIServer_To_v1beta2_APIServer(in, out, s) -} - func autoConvert_v1beta2_APIServer_To_v1beta1_APIServer(in *v1beta2.APIServer, out *APIServer, s conversion.Scope) error { if err := Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) return nil } @@ -790,29 +784,19 @@ func Convert_v1beta2_ContainerLinuxConfig_To_v1beta1_ContainerLinuxConfig(in *v1 } func autoConvert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ExtraVolumes = *(*[]v1beta2.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) out.ExtraEnvs = *(*[]v1beta2.EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) return nil } -// Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent is an autogenerated conversion function. -func Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - return autoConvert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) -} - func autoConvert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) out.ExtraEnvs = *(*[]EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) return nil } -// Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent is an autogenerated conversion function. -func Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - return autoConvert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in, out, s) -} - func autoConvert_v1beta1_DNS_To_v1beta2_DNS(in *DNS, out *v1beta2.DNS, s conversion.Scope) error { if err := Convert_v1beta1_ImageMeta_To_v1beta2_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err @@ -841,20 +825,14 @@ func autoConvert_v1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1be out.BootstrapToken = (*v1beta2.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) out.File = (*v1beta2.FileDiscovery)(unsafe.Pointer(in.File)) out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_Discovery_To_v1beta2_Discovery is an autogenerated conversion function. -func Convert_v1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1beta2.Discovery, s conversion.Scope) error { - return autoConvert_v1beta1_Discovery_To_v1beta2_Discovery(in, out, s) -} - func autoConvert_v1beta2_Discovery_To_v1beta1_Discovery(in *v1beta2.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) out.File = (*FileDiscovery)(unsafe.Pointer(in.File)) out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil } @@ -906,7 +884,15 @@ func Convert_v1beta2_EnvVar_To_v1beta1_EnvVar(in *v1beta2.EnvVar, out *EnvVar, s } func autoConvert_v1beta1_Etcd_To_v1beta2_Etcd(in *Etcd, out *v1beta2.Etcd, s conversion.Scope) error { - out.Local = (*v1beta2.LocalEtcd)(unsafe.Pointer(in.Local)) + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(v1beta2.LocalEtcd) + if err := Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(*in, *out, s); err != nil { + return err + } + } else { + out.Local = nil + } out.External = (*v1beta2.ExternalEtcd)(unsafe.Pointer(in.External)) return nil } @@ -917,7 +903,15 @@ func Convert_v1beta1_Etcd_To_v1beta2_Etcd(in *Etcd, out *v1beta2.Etcd, s convers } func autoConvert_v1beta2_Etcd_To_v1beta1_Etcd(in *v1beta2.Etcd, out *Etcd, s conversion.Scope) error { - out.Local = (*LocalEtcd)(unsafe.Pointer(in.Local)) + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(LocalEtcd) + if err := Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(*in, *out, s); err != nil { + return err + } + } else { + out.Local = nil + } out.External = (*ExternalEtcd)(unsafe.Pointer(in.External)) return nil } @@ -1187,14 +1181,10 @@ func autoConvert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *v1be } out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration is an autogenerated conversion function. -func Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *v1beta2.InitConfiguration, out *InitConfiguration, s conversion.Scope) error { - return autoConvert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s) -} - func autoConvert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *v1beta2.JoinConfiguration, s conversion.Scope) error { if err := Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { return err @@ -1225,14 +1215,10 @@ func autoConvert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in *v1be out.ControlPlane = (*JoinControlPlane)(unsafe.Pointer(in.ControlPlane)) out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration is an autogenerated conversion function. -func Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { - return autoConvert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in, out, s) -} - func autoConvert_v1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *v1beta2.JoinControlPlane, s conversion.Scope) error { if err := Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { return err @@ -1454,9 +1440,33 @@ func Convert_v1beta2_KubeadmConfigList_To_v1beta1_KubeadmConfigList(in *v1beta2. } func autoConvert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *KubeadmConfigSpec, out *v1beta2.KubeadmConfigSpec, s conversion.Scope) error { - out.ClusterConfiguration = (*v1beta2.ClusterConfiguration)(unsafe.Pointer(in.ClusterConfiguration)) - out.InitConfiguration = (*v1beta2.InitConfiguration)(unsafe.Pointer(in.InitConfiguration)) - out.JoinConfiguration = (*v1beta2.JoinConfiguration)(unsafe.Pointer(in.JoinConfiguration)) + if in.ClusterConfiguration != nil { + in, out := &in.ClusterConfiguration, &out.ClusterConfiguration + *out = new(v1beta2.ClusterConfiguration) + if err := Convert_v1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(*in, *out, s); err != nil { + return err + } + } else { + out.ClusterConfiguration = nil + } + if in.InitConfiguration != nil { + in, out := &in.InitConfiguration, &out.InitConfiguration + *out = new(v1beta2.InitConfiguration) + if err := Convert_v1beta1_InitConfiguration_To_v1beta2_InitConfiguration(*in, *out, s); err != nil { + return err + } + } else { + out.InitConfiguration = nil + } + if in.JoinConfiguration != nil { + in, out := &in.JoinConfiguration, &out.JoinConfiguration + *out = new(v1beta2.JoinConfiguration) + if err := Convert_v1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(*in, *out, s); err != nil { + return err + } + } else { + out.JoinConfiguration = nil + } out.Files = *(*[]v1beta2.File)(unsafe.Pointer(&in.Files)) out.DiskSetup = (*v1beta2.DiskSetup)(unsafe.Pointer(in.DiskSetup)) out.Mounts = *(*[]v1beta2.MountPoints)(unsafe.Pointer(&in.Mounts)) @@ -1473,9 +1483,33 @@ func autoConvert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *Kube } func autoConvert_v1beta2_KubeadmConfigSpec_To_v1beta1_KubeadmConfigSpec(in *v1beta2.KubeadmConfigSpec, out *KubeadmConfigSpec, s conversion.Scope) error { - out.ClusterConfiguration = (*ClusterConfiguration)(unsafe.Pointer(in.ClusterConfiguration)) - out.InitConfiguration = (*InitConfiguration)(unsafe.Pointer(in.InitConfiguration)) - out.JoinConfiguration = (*JoinConfiguration)(unsafe.Pointer(in.JoinConfiguration)) + if in.ClusterConfiguration != nil { + in, out := &in.ClusterConfiguration, &out.ClusterConfiguration + *out = new(ClusterConfiguration) + if err := Convert_v1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(*in, *out, s); err != nil { + return err + } + } else { + out.ClusterConfiguration = nil + } + if in.InitConfiguration != nil { + in, out := &in.InitConfiguration, &out.InitConfiguration + *out = new(InitConfiguration) + if err := Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(*in, *out, s); err != nil { + return err + } + } else { + out.InitConfiguration = nil + } + if in.JoinConfiguration != nil { + in, out := &in.JoinConfiguration, &out.JoinConfiguration + *out = new(JoinConfiguration) + if err := Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(*in, *out, s); err != nil { + return err + } + } else { + out.JoinConfiguration = nil + } out.Files = *(*[]File)(unsafe.Pointer(&in.Files)) out.DiskSetup = (*DiskSetup)(unsafe.Pointer(in.DiskSetup)) out.Mounts = *(*[]MountPoints)(unsafe.Pointer(&in.Mounts)) @@ -1662,35 +1696,25 @@ func autoConvert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1be return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ExtraEnvs = *(*[]v1beta2.EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } -// Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd is an autogenerated conversion function. -func Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { - return autoConvert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) -} - func autoConvert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { if err := Convert_v1beta2_ImageMeta_To_v1beta1_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.ExtraEnvs = *(*[]EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } -// Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd is an autogenerated conversion function. -func Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { - return autoConvert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in, out, s) -} - func autoConvert_v1beta1_NTP_To_v1beta2_NTP(in *NTP, out *v1beta2.NTP, s conversion.Scope) error { out.Servers = *(*[]string)(unsafe.Pointer(&in.Servers)) out.Enabled = (*bool)(unsafe.Pointer(in.Enabled)) @@ -1741,34 +1765,24 @@ func autoConvert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOpti out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) - out.ImagePullPolicy = in.ImagePullPolicy + out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) return nil } -// Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions is an autogenerated conversion function. -func Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { - return autoConvert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) -} - func autoConvert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) - out.ImagePullPolicy = in.ImagePullPolicy + out.ImagePullPolicy = string(in.ImagePullPolicy) out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) return nil } -// Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions is an autogenerated conversion function. -func Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { - return autoConvert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s) -} - func autoConvert_v1beta1_Partition_To_v1beta2_Partition(in *Partition, out *v1beta2.Partition, s conversion.Scope) error { out.Device = in.Device out.Layout = in.Layout diff --git a/api/bootstrap/kubeadm/v1beta2/conversion.go b/api/bootstrap/kubeadm/v1beta2/conversion.go index 537810b77572..f46aa5597b8c 100644 --- a/api/bootstrap/kubeadm/v1beta2/conversion.go +++ b/api/bootstrap/kubeadm/v1beta2/conversion.go @@ -16,6 +16,15 @@ limitations under the License. package v1beta2 +import ( + "math" + "sort" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + func (*KubeadmConfig) Hub() {} func (*KubeadmConfigTemplate) Hub() {} @@ -23,3 +32,61 @@ func (*ClusterConfiguration) Hub() {} func (*ClusterStatus) Hub() {} func (*InitConfiguration) Hub() {} func (*JoinConfiguration) Hub() {} + +// ConvertToArgs takes a argument map and converts it to a slice of arguments. +// The resulting argument slice is sorted alpha-numerically. +// NOTE: this is a util function intended only for usage in API conversions. +func ConvertToArgs(in map[string]string) []Arg { + if in == nil { + return nil + } + args := make([]Arg, 0, len(in)) + for k, v := range in { + args = append(args, Arg{Name: k, Value: v}) + } + sort.Slice(args, func(i, j int) bool { + if args[i].Name == args[j].Name { + return args[i].Value < args[j].Value + } + return args[i].Name < args[j].Name + }) + return args +} + +// ConvertFromArgs takes a slice of arguments and returns an argument map. +// Duplicate argument keys will be de-duped, where later keys will take precedence. +// NOTE: this is a util function intended only for usage in API conversions. +func ConvertFromArgs(in []Arg) map[string]string { + if in == nil { + return nil + } + args := make(map[string]string, len(in)) + for _, arg := range in { + args[arg.Name] = arg.Value + } + return args +} + +// ConvertToSeconds takes *metav1.Duration and returns a *int32. +// Durations longer than MaxInt32 are capped. +// NOTE: this is a util function intended only for usage in API conversions. +func ConvertToSeconds(in *metav1.Duration) *int32 { + if in == nil { + return nil + } + seconds := math.Trunc(in.Seconds()) + if seconds > math.MaxInt32 { + return ptr.To[int32](math.MaxInt32) + } + return ptr.To(int32(seconds)) +} + +// ConvertFromSeconds takes *int32 and returns a *metav1.Duration. +// Durations longer than MaxInt32 are capped. +// NOTE: this is a util function intended only for usage in API conversions. +func ConvertFromSeconds(in *int32) *metav1.Duration { + if in == nil { + return nil + } + return ptr.To(metav1.Duration{Duration: time.Duration(*in) * time.Second}) +} diff --git a/api/bootstrap/kubeadm/v1beta2/conversion_test.go b/api/bootstrap/kubeadm/v1beta2/conversion_test.go new file mode 100644 index 000000000000..48d04dbaef89 --- /dev/null +++ b/api/bootstrap/kubeadm/v1beta2/conversion_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + "math" + "testing" + "time" + + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +func TestConvertArgs(t *testing.T) { + g := NewWithT(t) + + argList := []Arg{ + { + Name: "foo", + Value: "1", + }, + { + Name: "bar", + Value: "1", + }, + { + Name: "foo", + Value: "2", + }, + } + argMap := ConvertFromArgs(argList) + + argList = ConvertToArgs(argMap) + g.Expect(argList).To(HaveLen(2)) + g.Expect(argList).To(ConsistOf( + Arg{ + Name: "bar", + Value: "1"}, + Arg{ + Name: "foo", + Value: "2", + }, + )) +} + +func TestConvertSeconds(t *testing.T) { + g := NewWithT(t) + + seconds := ptr.To[int32](100) + duration := ConvertFromSeconds(seconds) + g.Expect(ConvertToSeconds(duration)).To(Equal(seconds)) + + seconds = nil + duration = ConvertFromSeconds(seconds) + g.Expect(ConvertToSeconds(duration)).To(Equal(seconds)) + + // Durations longer than MaxInt32 are capped. + duration = ptr.To(metav1.Duration{Duration: (math.MaxInt32 + 1) * time.Second}) + g.Expect(ConvertToSeconds(duration)).To(Equal(ptr.To[int32](math.MaxInt32))) +} diff --git a/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go b/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go index 7f487f62c841..874d42aa9f13 100644 --- a/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go +++ b/api/bootstrap/kubeadm/v1beta2/kubeadm_types.go @@ -113,6 +113,10 @@ type InitConfiguration struct { // "kubeadm init". The minimum kubernetes version needed to support Patches is v1.22 // +optional Patches *Patches `json:"patches,omitempty"` + + // timeouts holds various timeouts that apply to kubeadm commands. + // +optional + Timeouts *Timeouts `json:"timeouts,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -207,10 +211,17 @@ type ClusterConfiguration struct { // ControlPlaneComponent holds settings common to control plane component of the cluster. type ControlPlaneComponent struct { - // extraArgs is an extra set of flags to pass to the control plane component. - // TODO: This is temporary and ideally we would like to switch all components to use ComponentConfig + ConfigMaps. - // +optional - ExtraArgs map[string]string `json:"extraArgs,omitempty"` + // extraArgs is a list of args to pass to the control plane component. + // The arg name must match the command line flag name except without leading dash(es). + // Extra arguments will override existing default arguments set by kubeadm. + // +optional + // +listType=map + // +listMapKey=name + // +listMapKey=value + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:XValidation:rule="self.all(x, self.exists_one(y, x.name == y.name))",message="extraArgs name must be unique" + ExtraArgs []Arg `json:"extraArgs,omitempty"` // extraVolumes is an extra set of host volumes, mounted to the control plane component. // +optional @@ -235,10 +246,6 @@ type APIServer struct { // +kubebuilder:validation:items:MinLength=1 // +kubebuilder:validation:items:MaxLength=253 CertSANs []string `json:"certSANs,omitempty"` - - // timeoutForControlPlane controls the timeout that we use for API server to appear - // +optional - TimeoutForControlPlane *metav1.Duration `json:"timeoutForControlPlane,omitempty"` } // DNS defines the DNS addon that should be used in the cluster. @@ -322,13 +329,20 @@ type NodeRegistrationOptions struct { // +kubebuilder:validation:MaxItems=100 Taints []corev1.Taint `json:"taints,omitempty"` - // kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - // kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - // Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. + // kubeletExtraArgs is a list of args to pass to kubelet. + // The arg name must match the command line flag name except without leading dash(es). + // Extra arguments will override existing default arguments set by kubeadm. // +optional - KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + // +listType=map + // +listMapKey=name + // +listMapKey=value + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:XValidation:rule="self.all(x, self.exists_one(y, x.name == y.name))",message="kubeletExtraArgs name must be unique" + KubeletExtraArgs []Arg `json:"kubeletExtraArgs,omitempty"` - // ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered. + // ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + // Value 'all' ignores errors from all checks. // +optional // +kubebuilder:validation:MaxItems=50 // +kubebuilder:validation:items:MinLength=1 @@ -342,7 +356,7 @@ type NodeRegistrationOptions struct { // with Kubernetes version equal to 1.22 and later. // +kubebuilder:validation:Enum=Always;IfNotPresent;Never // +optional - ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` // imagePullSerial specifies if image pulling performed by kubeadm must be done serially or in parallel. // This option takes effect only on Kubernetes >=1.31.0. @@ -367,9 +381,9 @@ func (n *NodeRegistrationOptions) MarshalJSON() ([]byte, error) { Name string `json:"name,omitempty"` CRISocket string `json:"criSocket,omitempty"` Taints []corev1.Taint `json:"taints"` - KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + KubeletExtraArgs []Arg `json:"kubeletExtraArgs,omitempty"` IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"` - ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` ImagePullSerial *bool `json:"imagePullSerial,omitempty"` }{ Name: n.Name, @@ -387,9 +401,9 @@ func (n *NodeRegistrationOptions) MarshalJSON() ([]byte, error) { Name string `json:"name,omitempty"` CRISocket string `json:"criSocket,omitempty"` Taints []corev1.Taint `json:"taints,omitempty"` - KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + KubeletExtraArgs []Arg `json:"kubeletExtraArgs,omitempty"` IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"` - ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` ImagePullSerial *bool `json:"imagePullSerial,omitempty"` }{ Name: n.Name, @@ -487,12 +501,19 @@ type LocalEtcd struct { // +kubebuilder:validation:MaxLength=512 DataDir string `json:"dataDir,omitempty"` - // extraArgs are extra arguments provided to the etcd binary - // when run inside a static pod. + // extraArgs is a list of args to pass to etcd. + // The arg name must match the command line flag name except without leading dash(es). + // Extra arguments will override existing default arguments set by kubeadm. // +optional - ExtraArgs map[string]string `json:"extraArgs,omitempty"` + // +listType=map + // +listMapKey=name + // +listMapKey=value + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + // +kubebuilder:validation:XValidation:rule="self.all(x, self.exists_one(y, x.name == y.name))",message="extraArgs name must be unique" + ExtraArgs []Arg `json:"extraArgs,omitempty"` - // extraEnvs is an extra set of environment variables to pass to the control plane component. + // extraEnvs is an extra set of environment variables to pass to etcd. // Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. // This option takes effect only on Kubernetes >=1.31.0. // +optional @@ -559,7 +580,7 @@ type JoinConfiguration struct { NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"` // caCertPath is the path to the SSL certificate authority used to - // secure comunications between node and control-plane. + // secure communications between node and control-plane. // Defaults to "/etc/kubernetes/pki/ca.crt". // +optional // TODO: revisit when there is defaulting from k/k @@ -590,6 +611,10 @@ type JoinConfiguration struct { // "kubeadm join". The minimum kubernetes version needed to support Patches is v1.22 // +optional Patches *Patches `json:"patches,omitempty"` + + // timeouts holds various timeouts that apply to kubeadm commands. + // +optional + Timeouts *Timeouts `json:"timeouts,omitempty"` } // JoinControlPlane contains elements describing an additional control plane instance to be deployed on the joining node. @@ -618,10 +643,6 @@ type Discovery struct { // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=512 TLSBootstrapToken string `json:"tlsBootstrapToken,omitempty"` - - // timeout modifies the discovery timeout - // +optional - Timeout *metav1.Duration `json:"timeout,omitempty"` } // BootstrapTokenDiscovery is used to set the options for bootstrap token based discovery. @@ -931,7 +952,67 @@ type Patches struct { Directory string `json:"directory,omitempty"` } +// Arg represents an argument with a name and a value. +type Arg struct { + // name is the Name of the extraArg. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Name string `json:"name"` + + // value is the Value of the extraArg. + // +required + // +kubebuilder:validation:MinLength=0 + // +kubebuilder:validation:MaxLength=1024 + Value string `json:"value"` +} + // EnvVar represents an environment variable present in a Container. type EnvVar struct { corev1.EnvVar `json:",inline"` } + +// Timeouts holds various timeouts that apply to kubeadm commands. +type Timeouts struct { + // controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + // component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + // If not set, it defaults to 4m (240s). + // +kubebuilder:validation:Minimum=0 + // +optional + ControlPlaneComponentHealthCheckSeconds *int32 `json:"controlPlaneComponentHealthCheckSeconds,omitempty"` + + // kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + // during "kubeadm init" and "kubeadm join". + // If not set, it defaults to 4m (240s). + // +kubebuilder:validation:Minimum=0 + // +optional + KubeletHealthCheckSeconds *int32 `json:"kubeletHealthCheckSeconds,omitempty"` + + // kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + // the API server. This applies to all types of methods (GET, POST, etc). + // If not set, it defaults to 1m (60s). + // +kubebuilder:validation:Minimum=0 + // +optional + KubernetesAPICallSeconds *int32 `json:"kubernetesAPICallSeconds,omitempty"` + + // etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + // the etcd cluster. + // If not set, it defaults to 2m (120s). + // +kubebuilder:validation:Minimum=0 + // +optional + EtcdAPICallSeconds *int32 `json:"etcdAPICallSeconds,omitempty"` + + // tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + // for a joining node. + // If not set, it defaults to 5m (300s). + // +kubebuilder:validation:Minimum=0 + // +optional + TLSBootstrapSeconds *int32 `json:"tlsBootstrapSeconds,omitempty"` + + // discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + // for a joining node. + // If not set, it defaults to 5m (300s). + // +kubebuilder:validation:Minimum=0 + // +optional + DiscoverySeconds *int32 `json:"discoverySeconds,omitempty"` +} diff --git a/api/bootstrap/kubeadm/v1beta2/kubeadm_types_test.go b/api/bootstrap/kubeadm/v1beta2/kubeadm_types_test.go index ea393d411224..f41a87dcf0b3 100644 --- a/api/bootstrap/kubeadm/v1beta2/kubeadm_types_test.go +++ b/api/bootstrap/kubeadm/v1beta2/kubeadm_types_test.go @@ -38,10 +38,10 @@ func TestNodeRegistrationOptionsMarshalJSON(t *testing.T) { Name: "node-1", CRISocket: "unix:///var/run/containerd/containerd.sock", Taints: nil, - KubeletExtraArgs: map[string]string{"abc": "def"}, + KubeletExtraArgs: []Arg{{Name: "abc", Value: "def"}}, IgnorePreflightErrors: []string{"ignore-1"}, }, - expected: `{"name":"node-1","criSocket":"unix:///var/run/containerd/containerd.sock","kubeletExtraArgs":{"abc":"def"},"ignorePreflightErrors":["ignore-1"]}`, + expected: `{"name":"node-1","criSocket":"unix:///var/run/containerd/containerd.sock","kubeletExtraArgs":[{"name":"abc","value":"def"}],"ignorePreflightErrors":["ignore-1"]}`, }, { name: "marshal empty taints", @@ -49,10 +49,10 @@ func TestNodeRegistrationOptionsMarshalJSON(t *testing.T) { Name: "node-1", CRISocket: "unix:///var/run/containerd/containerd.sock", Taints: []corev1.Taint{}, - KubeletExtraArgs: map[string]string{"abc": "def"}, + KubeletExtraArgs: []Arg{{Name: "abc", Value: "def"}}, IgnorePreflightErrors: []string{"ignore-1"}, }, - expected: `{"name":"node-1","criSocket":"unix:///var/run/containerd/containerd.sock","taints":[],"kubeletExtraArgs":{"abc":"def"},"ignorePreflightErrors":["ignore-1"]}`, + expected: `{"name":"node-1","criSocket":"unix:///var/run/containerd/containerd.sock","taints":[],"kubeletExtraArgs":[{"name":"abc","value":"def"}],"ignorePreflightErrors":["ignore-1"]}`, }, { name: "marshal regular taints", @@ -66,10 +66,10 @@ func TestNodeRegistrationOptionsMarshalJSON(t *testing.T) { Effect: "effect", }, }, - KubeletExtraArgs: map[string]string{"abc": "def"}, + KubeletExtraArgs: []Arg{{Name: "abc", Value: "def"}}, IgnorePreflightErrors: []string{"ignore-1"}, }, - expected: `{"name":"node-1","criSocket":"unix:///var/run/containerd/containerd.sock","taints":[{"key":"key","value":"value","effect":"effect"}],"kubeletExtraArgs":{"abc":"def"},"ignorePreflightErrors":["ignore-1"]}`, + expected: `{"name":"node-1","criSocket":"unix:///var/run/containerd/containerd.sock","taints":[{"key":"key","value":"value","effect":"effect"}],"kubeletExtraArgs":[{"name":"abc","value":"def"}],"ignorePreflightErrors":["ignore-1"]}`, }, } for _, tt := range tests { diff --git a/api/bootstrap/kubeadm/v1beta2/kubeadmconfig_types.go b/api/bootstrap/kubeadm/v1beta2/kubeadmconfig_types.go index b85ebb5314ad..42e4a200c216 100644 --- a/api/bootstrap/kubeadm/v1beta2/kubeadmconfig_types.go +++ b/api/bootstrap/kubeadm/v1beta2/kubeadmconfig_types.go @@ -184,6 +184,31 @@ func (c *KubeadmConfigSpec) Validate(pathPrefix *field.Path) field.ErrorList { } } + // Validate timeouts + // Note: When v1beta1 will be removed, we can drop this limitation. + tInit := "unset" + if c.InitConfiguration != nil && c.InitConfiguration.Timeouts != nil && c.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil { + tInit = fmt.Sprintf("%d", *c.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + } + tJoin := "unset" + if c.JoinConfiguration != nil && c.JoinConfiguration.Timeouts != nil && c.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil { + tJoin = fmt.Sprintf("%d", *c.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + } + if tInit != tJoin { + allErrs = append(allErrs, + field.Invalid( + pathPrefix.Child("initConfiguration", "timeouts", "controlPlaneComponentHealthCheckSeconds"), + tInit, + fmt.Sprintf("controlPlaneComponentHealthCheckSeconds must be set to the same value both in initConfiguration.timeouts (%s) and in joinConfiguration.timeouts (%s)", tInit, tJoin), + ), + field.Invalid( + pathPrefix.Child("joinConfiguration", "timeouts", "controlPlaneComponentHealthCheckSeconds"), + tJoin, + fmt.Sprintf("controlPlaneComponentHealthCheckSeconds must be set to the same value both in initConfiguration.timeouts (%s) and in joinConfiguration.timeouts (%s)", tInit, tJoin), + ), + ) + } + return allErrs } diff --git a/api/bootstrap/kubeadm/v1beta2/zz_generated.deepcopy.go b/api/bootstrap/kubeadm/v1beta2/zz_generated.deepcopy.go index 2a212b30a978..6a2e075a0b53 100644 --- a/api/bootstrap/kubeadm/v1beta2/zz_generated.deepcopy.go +++ b/api/bootstrap/kubeadm/v1beta2/zz_generated.deepcopy.go @@ -21,8 +21,8 @@ limitations under the License. package v1beta2 import ( - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" corev1beta2 "sigs.k8s.io/cluster-api/api/core/v1beta2" ) @@ -51,11 +51,6 @@ func (in *APIServer) DeepCopyInto(out *APIServer) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.TimeoutForControlPlane != nil { - in, out := &in.TimeoutForControlPlane, &out.TimeoutForControlPlane - *out = new(v1.Duration) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIServer. @@ -68,6 +63,21 @@ func (in *APIServer) DeepCopy() *APIServer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Arg) DeepCopyInto(out *Arg) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Arg. +func (in *Arg) DeepCopy() *Arg { + if in == nil { + return nil + } + out := new(Arg) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { *out = *in @@ -78,7 +88,7 @@ func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { } if in.TTL != nil { in, out := &in.TTL, &out.TTL - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.Expires != nil { @@ -230,10 +240,8 @@ func (in *ControlPlaneComponent) DeepCopyInto(out *ControlPlaneComponent) { *out = *in if in.ExtraArgs != nil { in, out := &in.ExtraArgs, &out.ExtraArgs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + *out = make([]Arg, len(*in)) + copy(*out, *in) } if in.ExtraVolumes != nil { in, out := &in.ExtraVolumes, &out.ExtraVolumes @@ -288,11 +296,6 @@ func (in *Discovery) DeepCopyInto(out *Discovery) { *out = new(FileDiscovery) (*in).DeepCopyInto(*out) } - if in.Timeout != nil { - in, out := &in.Timeout, &out.Timeout - *out = new(v1.Duration) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Discovery. @@ -580,6 +583,11 @@ func (in *InitConfiguration) DeepCopyInto(out *InitConfiguration) { *out = new(Patches) **out = **in } + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(Timeouts) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InitConfiguration. @@ -621,6 +629,11 @@ func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) { *out = new(Patches) **out = **in } + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(Timeouts) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JoinConfiguration. @@ -953,7 +966,7 @@ func (in *KubeadmConfigStatus) DeepCopyInto(out *KubeadmConfigStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1104,10 +1117,8 @@ func (in *LocalEtcd) DeepCopyInto(out *LocalEtcd) { out.ImageMeta = in.ImageMeta if in.ExtraArgs != nil { in, out := &in.ExtraArgs, &out.ExtraArgs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + *out = make([]Arg, len(*in)) + copy(*out, *in) } if in.ExtraEnvs != nil { in, out := &in.ExtraEnvs, &out.ExtraEnvs @@ -1202,17 +1213,15 @@ func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { *out = *in if in.Taints != nil { in, out := &in.Taints, &out.Taints - *out = make([]corev1.Taint, len(*in)) + *out = make([]v1.Taint, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.KubeletExtraArgs != nil { in, out := &in.KubeletExtraArgs, &out.KubeletExtraArgs - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + *out = make([]Arg, len(*in)) + copy(*out, *in) } if in.IgnorePreflightErrors != nil { in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors @@ -1322,6 +1331,51 @@ func (in *SecretPasswdSource) DeepCopy() *SecretPasswdSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timeouts) DeepCopyInto(out *Timeouts) { + *out = *in + if in.ControlPlaneComponentHealthCheckSeconds != nil { + in, out := &in.ControlPlaneComponentHealthCheckSeconds, &out.ControlPlaneComponentHealthCheckSeconds + *out = new(int32) + **out = **in + } + if in.KubeletHealthCheckSeconds != nil { + in, out := &in.KubeletHealthCheckSeconds, &out.KubeletHealthCheckSeconds + *out = new(int32) + **out = **in + } + if in.KubernetesAPICallSeconds != nil { + in, out := &in.KubernetesAPICallSeconds, &out.KubernetesAPICallSeconds + *out = new(int32) + **out = **in + } + if in.EtcdAPICallSeconds != nil { + in, out := &in.EtcdAPICallSeconds, &out.EtcdAPICallSeconds + *out = new(int32) + **out = **in + } + if in.TLSBootstrapSeconds != nil { + in, out := &in.TLSBootstrapSeconds, &out.TLSBootstrapSeconds + *out = new(int32) + **out = **in + } + if in.DiscoverySeconds != nil { + in, out := &in.DiscoverySeconds, &out.DiscoverySeconds + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timeouts. +func (in *Timeouts) DeepCopy() *Timeouts { + if in == nil { + return nil + } + out := new(Timeouts) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *User) DeepCopyInto(out *User) { *out = *in diff --git a/api/controlplane/kubeadm/v1beta1/conversion.go b/api/controlplane/kubeadm/v1beta1/conversion.go index b2356cab246a..6c328704eb04 100644 --- a/api/controlplane/kubeadm/v1beta1/conversion.go +++ b/api/controlplane/kubeadm/v1beta1/conversion.go @@ -26,30 +26,75 @@ import ( controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*controlplanev1.KubeadmControlPlane) + if err := Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &controlplanev1.KubeadmControlPlane{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + if ok { + bootstrapv1beta1.RestoreKubeadmConfigSpec(&restored.Spec.KubeadmConfigSpec, &dst.Spec.KubeadmConfigSpec) + } - return Convert_v1beta1_KubeadmControlPlane_To_v1beta2_KubeadmControlPlane(src, dst, nil) + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.KubeadmConfigSpec) + return nil } func (dst *KubeadmControlPlane) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*controlplanev1.KubeadmControlPlane) + if err := Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.KubeadmConfigSpec) - return Convert_v1beta2_KubeadmControlPlane_To_v1beta1_KubeadmControlPlane(src, dst, nil) + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) } func (src *KubeadmControlPlaneTemplate) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*controlplanev1.KubeadmControlPlaneTemplate) + if err := Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(src, dst, nil); err != nil { + return err + } + + // Manually restore data. + restored := &controlplanev1.KubeadmControlPlaneTemplate{} + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { + return err + } + if ok { + bootstrapv1beta1.RestoreKubeadmConfigSpec(&restored.Spec.Template.Spec.KubeadmConfigSpec, &dst.Spec.Template.Spec.KubeadmConfigSpec) + } - return Convert_v1beta1_KubeadmControlPlaneTemplate_To_v1beta2_KubeadmControlPlaneTemplate(src, dst, nil) + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.Template.Spec.KubeadmConfigSpec) + return nil } func (dst *KubeadmControlPlaneTemplate) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*controlplanev1.KubeadmControlPlaneTemplate) + if err := Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(src, dst, nil); err != nil { + return err + } + + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.Template.Spec.KubeadmConfigSpec) - return Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1beta1_KubeadmControlPlaneTemplate(src, dst, nil) + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) } func Convert_v1beta2_KubeadmControlPlaneStatus_To_v1beta1_KubeadmControlPlaneStatus(in *controlplanev1.KubeadmControlPlaneStatus, out *KubeadmControlPlaneStatus, s apimachineryconversion.Scope) error { diff --git a/api/controlplane/kubeadm/v1beta1/conversion_test.go b/api/controlplane/kubeadm/v1beta1/conversion_test.go index 40dc3877732a..30abe76e7da0 100644 --- a/api/controlplane/kubeadm/v1beta1/conversion_test.go +++ b/api/controlplane/kubeadm/v1beta1/conversion_test.go @@ -21,17 +21,25 @@ package v1beta1 import ( "reflect" "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) +const ( + fakeID = "abcdef" + fakeSecret = "abcdef0123456789" +) + // Test is disabled when the race detector is enabled (via "//go:build !race" above) because otherwise the fuzz tests would just time out. func TestFuzzyConversion(t *testing.T) { @@ -52,9 +60,54 @@ func KubeadmControlPlaneFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{ hubKubeadmControlPlaneStatus, spokeKubeadmControlPlaneStatus, spokeKubeadmConfigSpec, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func KubeadmControlPlaneTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + spokeKubeadmConfigSpec, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) { + c.FillNoCustom(in) + + // enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration + var initControlPlaneComponentHealthCheckSeconds *int32 + if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil { + initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if in.JoinConfiguration == nil { + in.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if in.JoinConfiguration.Timeouts == nil { + in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds } } +func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret +} + +func spokeBootstrapTokenString(in *bootstrapv1beta1.BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret +} + func hubKubeadmControlPlaneStatus(in *controlplanev1.KubeadmControlPlaneStatus, c randfill.Continue) { c.FillNoCustom(in) // Always create struct with at least one mandatory fields. @@ -92,9 +145,19 @@ func spokeKubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, c randfill.Co in.Ready = in.ReadyReplicas > 0 } -func KubeadmControlPlaneTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { - return []interface{}{ - spokeKubeadmConfigSpec, +func spokeAPIServer(in *bootstrapv1beta1.APIServer, c randfill.Continue) { + c.FillNoCustom(in) + + if in.TimeoutForControlPlane != nil { + in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeDiscovery(in *bootstrapv1beta1.Discovery, c randfill.Continue) { + c.FillNoCustom(in) + + if in.Timeout != nil { + in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) } } diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml index 6c9341925b5e..c13911304706 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml @@ -4147,11 +4147,38 @@ spec: maxItems: 100 type: array extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to pass to - the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name and + a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -4315,10 +4342,6 @@ spec: type: object maxItems: 100 type: array - timeoutForControlPlane: - description: timeoutForControlPlane controls the timeout that - we use for API server to appear - type: string type: object apiVersion: description: |- @@ -4361,11 +4384,38 @@ spec: controller manager control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to pass to - the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name and + a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -4607,15 +4657,41 @@ spec: minLength: 1 type: string extraArgs: - additionalProperties: - type: string description: |- - extraArgs are extra arguments provided to the etcd binary - when run inside a static pod. - type: object + extraArgs is a list of args to pass to etcd. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- - extraEnvs is an extra set of environment variables to pass to the control plane component. + extraEnvs is an extra set of environment variables to pass to etcd. Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. This option takes effect only on Kubernetes >=1.31.0. items: @@ -4841,11 +4917,38 @@ spec: control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to pass to - the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name and + a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -5320,8 +5423,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a slice of pre-flight - errors to be ignored when the current node is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -5347,13 +5451,38 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name and + a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -5431,6 +5560,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that apply to kubeadm + commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object joinConfiguration: description: joinConfiguration is the kubeadm configuration for the @@ -5446,7 +5628,7 @@ spec: caCertPath: description: |- caCertPath is the path to the SSL certificate authority used to - secure comunications between node and control-plane. + secure communications between node and control-plane. Defaults to "/etc/kubernetes/pki/ca.crt". maxLength: 512 minLength: 1 @@ -5687,9 +5869,6 @@ spec: required: - kubeConfigPath type: object - timeout: - description: timeout modifies the discovery timeout - type: string tlsBootstrapToken: description: |- tlsBootstrapToken is a token used for TLS bootstrapping. @@ -5721,8 +5900,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a slice of pre-flight - errors to be ignored when the current node is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -5748,13 +5928,38 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name and + a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -5832,6 +6037,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that apply to kubeadm + commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object mounts: description: mounts specifies a list of mount points to be setup. diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml index af5b60ed6596..7895b84b2d6a 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml @@ -4015,11 +4015,38 @@ spec: maxItems: 100 type: array extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to - pass to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -4189,10 +4216,6 @@ spec: type: object maxItems: 100 type: array - timeoutForControlPlane: - description: timeoutForControlPlane controls the timeout - that we use for API server to appear - type: string type: object apiVersion: description: |- @@ -4235,11 +4258,38 @@ spec: for the controller manager control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to - pass to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -4488,15 +4538,42 @@ spec: minLength: 1 type: string extraArgs: - additionalProperties: - type: string description: |- - extraArgs are extra arguments provided to the etcd binary - when run inside a static pod. - type: object + extraArgs is a list of args to pass to etcd. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with + a name and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) extraEnvs: description: |- - extraEnvs is an extra set of environment variables to pass to the control plane component. + extraEnvs is an extra set of environment variables to pass to etcd. Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. This option takes effect only on Kubernetes >=1.31.0. items: @@ -4728,11 +4805,38 @@ spec: scheduler control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to - pass to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -5221,9 +5325,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a slice - of pre-flight errors to be ignored when the current - node is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -5249,13 +5353,38 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -5333,6 +5462,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that apply + to kubeadm commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object joinConfiguration: description: joinConfiguration is the kubeadm configuration @@ -5348,7 +5530,7 @@ spec: caCertPath: description: |- caCertPath is the path to the SSL certificate authority used to - secure comunications between node and control-plane. + secure communications between node and control-plane. Defaults to "/etc/kubernetes/pki/ca.crt". maxLength: 512 minLength: 1 @@ -5596,9 +5778,6 @@ spec: required: - kubeConfigPath type: object - timeout: - description: timeout modifies the discovery timeout - type: string tlsBootstrapToken: description: |- tlsBootstrapToken is a token used for TLS bootstrapping. @@ -5630,9 +5809,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a slice - of pre-flight errors to be ignored when the current - node is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -5658,13 +5837,38 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -5742,6 +5946,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that apply + to kubeadm commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object mounts: description: mounts specifies a list of mount points to be diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go index 6686231481a1..24b6a278250c 100644 --- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go @@ -549,12 +549,6 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex // injects into config.ClusterConfiguration values from top level object r.reconcileTopLevelObjectSettings(ctx, scope.Cluster, machine, scope.Config) - clusterdata, err := kubeadmtypes.MarshalClusterConfigurationForVersion(scope.Config.Spec.ClusterConfiguration, parsedVersion) - if err != nil { - scope.Error(err, "Failed to marshal cluster configuration") - return ctrl.Result{}, err - } - if scope.Config.Spec.InitConfiguration == nil { scope.Config.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{ TypeMeta: metav1.TypeMeta{ @@ -564,9 +558,13 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex } } - // NOTE: It is required to provide in input the ClusterConfiguration because clusterConfiguration.APIServer.TimeoutForControlPlane - // has been migrated to InitConfiguration in the kubeadm v1beta4 API version. - initdata, err := kubeadmtypes.MarshalInitConfigurationForVersion(scope.Config.Spec.ClusterConfiguration, scope.Config.Spec.InitConfiguration, parsedVersion) + clusterdata, err := kubeadmtypes.MarshalClusterConfigurationForVersion(scope.Config.Spec.InitConfiguration, scope.Config.Spec.ClusterConfiguration, parsedVersion) + if err != nil { + scope.Error(err, "Failed to marshal cluster configuration") + return ctrl.Result{}, err + } + + initdata, err := kubeadmtypes.MarshalInitConfigurationForVersion(scope.Config.Spec.InitConfiguration, parsedVersion) if err != nil { scope.Error(err, "Failed to marshal init configuration") return ctrl.Result{}, err @@ -740,7 +738,7 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) // NOTE: It is not required to provide in input ClusterConfiguration because only clusterConfiguration.APIServer.TimeoutForControlPlane // has been migrated to JoinConfiguration in the kubeadm v1beta4 API version, and this field does not apply to workers. - joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(nil, joinConfiguration, parsedVersion) + joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(joinConfiguration, parsedVersion) if err != nil { scope.Error(err, "Failed to marshal join configuration") return ctrl.Result{}, err @@ -894,9 +892,7 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", kubernetesVersion) } - // NOTE: It is required to provide in input the ClusterConfiguration because clusterConfiguration.APIServer.TimeoutForControlPlane - // has been migrated to JoinConfiguration in the kubeadm v1beta4 API version. - joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(scope.Config.Spec.ClusterConfiguration, scope.Config.Spec.JoinConfiguration, parsedVersion) + joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(scope.Config.Spec.JoinConfiguration, parsedVersion) if err != nil { scope.Error(err, "Failed to marshal join configuration") return ctrl.Result{}, err diff --git a/bootstrap/kubeadm/internal/webhooks/kubeadmconfig_test.go b/bootstrap/kubeadm/internal/webhooks/kubeadmconfig_test.go index 13a7db2b084a..f5661124c917 100644 --- a/bootstrap/kubeadm/internal/webhooks/kubeadmconfig_test.go +++ b/bootstrap/kubeadm/internal/webhooks/kubeadmconfig_test.go @@ -469,6 +469,42 @@ func TestKubeadmConfigValidate(t *testing.T) { }, }, }, + "invalid ControlPlaneComponentHealthCheckSeconds": { + in: &bootstrapv1.KubeadmConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "baz", + Namespace: metav1.NamespaceDefault, + }, + Spec: bootstrapv1.KubeadmConfigSpec{ + InitConfiguration: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10), + }, + }, + }, + }, + expectErr: true, + }, + "valid ControlPlaneComponentHealthCheckSeconds": { + in: &bootstrapv1.KubeadmConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "baz", + Namespace: metav1.NamespaceDefault, + }, + Spec: bootstrapv1.KubeadmConfigSpec{ + InitConfiguration: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10), + }, + }, + JoinConfiguration: &bootstrapv1.JoinConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10), + }, + }, + }, + }, + }, } for name, tt := range cases { diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go index 87c7a6a951c2..a2db33862521 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go @@ -17,6 +17,7 @@ limitations under the License. package upstreamv1beta1 import ( + "github.com/pkg/errors" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -70,35 +71,92 @@ func Convert_upstreamv1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguratio return autoConvert_upstreamv1beta1_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in, out, s) } +func Convert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *bootstrapv1.JoinConfiguration, s apimachineryconversion.Scope) error { + if err := autoConvert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s); err != nil { + return err + } + if in.Discovery.Timeout != nil { + if out.Timeouts == nil { + out.Timeouts = &bootstrapv1.Timeouts{} + } + out.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(in.Discovery.Timeout) + } + return nil +} + func Convert_upstreamv1beta1_DNS_To_v1beta2_DNS(in *DNS, out *bootstrapv1.DNS, s apimachineryconversion.Scope) error { // DNS.Type does not exist in CABPK v1beta1 version, because it always was CoreDNS. return autoConvert_upstreamv1beta1_DNS_To_v1beta2_DNS(in, out, s) } +func Convert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) +} + +func Convert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error { + // APIServer.TimeoutForControlPlane does not exist in CABPK, because CABPK aligns to upstreamV1Beta4. + return autoConvert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(in, out, s) +} + +func Convert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error { + // Discovery.Timeout does not exist in CABPK, because CABPK aligns to upstreamV1Beta4. + return autoConvert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(in, out, s) +} + +func Convert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + +func Convert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs) + return autoConvert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + // Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta1. func Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta1_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { // ControlPlaneComponent.ExtraEnvs does not exist in kubeadm v1beta1, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta1 API does not allow this use case. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta1_ControlPlaneComponent(in, out, s) } func Convert_v1beta2_LocalEtcd_To_upstreamv1beta1_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { // LocalEtcd.ExtraEnvs does not exist in kubeadm v1beta1, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta1 API does not allow this use case. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta1_LocalEtcd(in, out, s) } func Convert_v1beta2_InitConfiguration_To_upstreamv1beta1_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.SkipPhases and Patches do not exist in kubeadm v1beta1, dropping those info. + // InitConfiguration.SkipPhases, InitConfiguration.Timeouts and Patches do not exist in kubeadm v1beta1, dropping those info. return autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta1_InitConfiguration(in, out, s) } func Convert_v1beta2_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { - // JoinConfiguration.SkipPhases and Patches do not exist in kubeadm v1beta1, dropping those info. - return autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(in, out, s) + // JoinConfiguration.SkipPhases, JoinConfiguration.Timeouts and Patches do not exist in kubeadm v1beta1, dropping those info. + if err := autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(in, out, s); err != nil { + return err + } + + if in.Timeouts != nil { + out.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(in.Timeouts.TLSBootstrapSeconds) + } + return nil } func Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { // NodeRegistrationOptions.IgnorePreflightErrors, ImagePullPolicy and ImagePullSerial do not exist in kubeadm v1beta1, dropping those info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta1 API does not allow this use case. + out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs) return autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in, out, s) } @@ -106,3 +164,29 @@ func Convert_v1beta2_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in *bootstra // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. return autoConvert_v1beta2_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in, out, s) } + +// Custom conversions to handle fields migrated from ClusterConfiguration to Init and JoinConfiguration in the kubeadm v1beta4 API version. + +func (dst *ClusterConfiguration) ConvertFromInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error { + if initConfiguration == nil || initConfiguration.Timeouts == nil || initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds == nil { + return nil + } + + dst.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + return nil +} + +func (src *ClusterConfiguration) ConvertToInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error { + if src.APIServer.TimeoutForControlPlane == nil { + return nil + } + + if initConfiguration == nil { + return errors.New("cannot convert ClusterConfiguration to a nil InitConfiguration") + } + if initConfiguration.Timeouts == nil { + initConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.APIServer.TimeoutForControlPlane) + return nil +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_no_fuzz_test.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_no_fuzz_test.go new file mode 100644 index 000000000000..178278c3e408 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_no_fuzz_test.go @@ -0,0 +1,50 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package upstreamv1beta1 + +import ( + "testing" + + . "github.com/onsi/gomega" + "k8s.io/utils/ptr" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" +) + +// This test case has been moved out of conversion_test.go because it should be run with the race detector. +// Note: The tests in conversion_test.go are disabled when the race detector is enabled (via "//go:build !race") because otherwise the fuzz tests would just time out. + +func TestTimeoutForControlPlaneMigration(t *testing.T) { + t.Run("from InitConfiguration to ClusterConfiguration and back", func(t *testing.T) { + g := NewWithT(t) + + initConfiguration := &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](123), + }, + } + + clusterConfiguration := &ClusterConfiguration{} + err := clusterConfiguration.ConvertFromInitConfiguration(initConfiguration) + g.Expect(err).ToNot(HaveOccurred()) + + initConfiguration = &bootstrapv1.InitConfiguration{} + err = clusterConfiguration.ConvertToInitConfiguration(initConfiguration) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds).To(Equal(ptr.To[int32](123))) + }) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go index adf06ad65827..2142299415af 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go @@ -20,9 +20,12 @@ package upstreamv1beta1 import ( "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" @@ -64,13 +67,15 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - clusterConfigurationFuzzer, - dnsFuzzer, - bootstrapv1ControlPlaneComponentFuzzer, - bootstrapv1LocalEtcdFuzzer, - bootstrapv1InitConfigurationFuzzer, - bootstrapv1JoinConfigurationFuzzer, - bootstrapv1NodeRegistrationOptionsFuzzer, + spokeJoinConfigurationFuzzer, + spokeClusterConfigurationFuzzer, + spokeControlPlaneComponentFuzzer, + spokeDNSFuzzer, + spokeAPIServerFuzzer, + hubLocalEtcdFuzzer, + hubInitConfigurationFuzzer, + hubJoinConfigurationFuzzer, + hubNodeRegistrationOptionsFuzzer, } } @@ -78,42 +83,57 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { // NOTES: // - When fields do does not exist in cabpk v1beta1 types, pinning it to avoid kubeadm v1beta1 --> cabpk v1beta1 --> kubeadm v1beta1 round trip errors. -func clusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) { +func spokeClusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.UseHyperKubeImage = false } -func dnsFuzzer(obj *DNS, c randfill.Continue) { +func spokeDNSFuzzer(obj *DNS, c randfill.Continue) { c.FillNoCustom(obj) obj.Type = "" } +func spokeAPIServerFuzzer(obj *APIServer, c randfill.Continue) { + c.FillNoCustom(obj) + + obj.TimeoutForControlPlane = nil +} + // Custom fuzzers for CABPK v1beta1 types. // NOTES: // - When fields do not exist in kubeadm v1beta1 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta1 --> cabpk v1beta1 round trip errors. -func bootstrapv1ControlPlaneComponentFuzzer(obj *bootstrapv1.ControlPlaneComponent, c randfill.Continue) { +func spokeJoinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { + c.FillNoCustom(obj) + + if obj.Discovery.Timeout != nil { + obj.Discovery.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeControlPlaneComponentFuzzer(obj *bootstrapv1.ControlPlaneComponent, c randfill.Continue) { c.FillNoCustom(obj) obj.ExtraEnvs = nil } -func bootstrapv1LocalEtcdFuzzer(obj *bootstrapv1.LocalEtcd, c randfill.Continue) { +func hubLocalEtcdFuzzer(obj *bootstrapv1.LocalEtcd, c randfill.Continue) { c.FillNoCustom(obj) obj.ExtraEnvs = nil } -func bootstrapv1InitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c randfill.Continue) { +func hubInitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.Patches = nil obj.SkipPhases = nil + obj.Timeouts = nil } -func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { +func hubJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.Patches = nil @@ -122,9 +142,16 @@ func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c ra if obj.Discovery.File != nil { obj.Discovery.File.KubeConfig = nil } + if obj.Timeouts != nil { + if obj.Timeouts.TLSBootstrapSeconds != nil { + obj.Timeouts = &bootstrapv1.Timeouts{TLSBootstrapSeconds: obj.Timeouts.TLSBootstrapSeconds} + } else { + obj.Timeouts = nil + } + } } -func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c randfill.Continue) { +func hubNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c randfill.Continue) { c.FillNoCustom(obj) obj.IgnorePreflightErrors = nil diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go index 7951fab75cd2..cda6c6412b64 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go @@ -48,11 +48,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_APIServer_To_upstreamv1beta1_APIServer(a.(*v1beta2.APIServer), b.(*APIServer), scope) }); err != nil { @@ -103,21 +98,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.DNS)(nil), (*DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_DNS_To_upstreamv1beta1_DNS(a.(*v1beta2.DNS), b.(*DNS), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_Discovery_To_upstreamv1beta1_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) }); err != nil { @@ -173,11 +158,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*JoinControlPlane)(nil), (*v1beta2.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta1_JoinControlPlane_To_v1beta2_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta2.JoinControlPlane), scope) }); err != nil { @@ -188,11 +168,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*Networking)(nil), (*v1beta2.Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta1_Networking_To_v1beta2_Networking(a.(*Networking), b.(*v1beta2.Networking), scope) }); err != nil { @@ -203,8 +178,8 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + if err := s.AddConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) }); err != nil { return err } @@ -213,11 +188,36 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta1_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta1_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) }); err != nil { @@ -278,21 +278,15 @@ func autoConvert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, o return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) + // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type return nil } -// Convert_upstreamv1beta1_APIServer_To_v1beta2_APIServer is an autogenerated conversion function. -func Convert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1beta2.APIServer, s conversion.Scope) error { - return autoConvert_upstreamv1beta1_APIServer_To_v1beta2_APIServer(in, out, s) -} - func autoConvert_v1beta2_APIServer_To_upstreamv1beta1_APIServer(in *v1beta2.APIServer, out *APIServer, s conversion.Scope) error { if err := Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta1_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) return nil } @@ -462,18 +456,13 @@ func Convert_v1beta2_ClusterStatus_To_upstreamv1beta1_ClusterStatus(in *v1beta2. } func autoConvert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ExtraVolumes = *(*[]v1beta2.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) return nil } -// Convert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent is an autogenerated conversion function. -func Convert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - return autoConvert_upstreamv1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) -} - func autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta1_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type return nil @@ -511,15 +500,10 @@ func autoConvert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, o out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil } -// Convert_upstreamv1beta1_Discovery_To_v1beta2_Discovery is an autogenerated conversion function. -func Convert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1beta2.Discovery, s conversion.Scope) error { - return autoConvert_upstreamv1beta1_Discovery_To_v1beta2_Discovery(in, out, s) -} - func autoConvert_v1beta2_Discovery_To_upstreamv1beta1_Discovery(in *v1beta2.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) if in.File != nil { @@ -532,7 +516,6 @@ func autoConvert_v1beta2_Discovery_To_upstreamv1beta1_Discovery(in *v1beta2.Disc out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil } @@ -697,6 +680,7 @@ func autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta1_InitConfiguration( } // WARNING: in.SkipPhases requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } @@ -712,11 +696,6 @@ func autoConvert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration( return nil } -// Convert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration is an autogenerated conversion function. -func Convert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *v1beta2.JoinConfiguration, s conversion.Scope) error { - return autoConvert_upstreamv1beta1_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s) -} - func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { if err := Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { return err @@ -728,6 +707,7 @@ func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration( out.ControlPlane = (*JoinControlPlane)(unsafe.Pointer(in.ControlPlane)) // WARNING: in.SkipPhases requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } @@ -760,23 +740,18 @@ func autoConvert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, o return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } -// Convert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd is an autogenerated conversion function. -func Convert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { - return autoConvert_upstreamv1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) -} - func autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta1_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { if err := Convert_v1beta2_ImageMeta_To_upstreamv1beta1_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) @@ -811,20 +786,15 @@ func autoConvert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistra out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) return nil } -// Convert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions is an autogenerated conversion function. -func Convert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { - return autoConvert_upstreamv1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) -} - func autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) // WARNING: in.IgnorePreflightErrors requires manual conversion: does not exist in peer-type // WARNING: in.ImagePullPolicy requires manual conversion: does not exist in peer-type // WARNING: in.ImagePullSerial requires manual conversion: does not exist in peer-type diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go index 3ce4b8c30763..a77953d53ebc 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go @@ -17,6 +17,7 @@ limitations under the License. package upstreamv1beta2 import ( + "github.com/pkg/errors" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -70,6 +71,19 @@ func Convert_upstreamv1beta2_ClusterConfiguration_To_v1beta2_ClusterConfiguratio return autoConvert_upstreamv1beta2_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in, out, s) } +func Convert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *bootstrapv1.JoinConfiguration, s apimachineryconversion.Scope) error { + if err := autoConvert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s); err != nil { + return err + } + if in.Discovery.Timeout != nil { + if out.Timeouts == nil { + out.Timeouts = &bootstrapv1.Timeouts{} + } + out.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(in.Discovery.Timeout) + } + return nil +} + func Convert_upstreamv1beta2_DNS_To_v1beta2_DNS(in *DNS, out *bootstrapv1.DNS, s apimachineryconversion.Scope) error { // DNS.Type does not exist in CABPK v1beta1 version, because it always was CoreDNS. return autoConvert_upstreamv1beta2_DNS_To_v1beta2_DNS(in, out, s) @@ -85,30 +99,74 @@ func Convert_upstreamv1beta2_JoinControlPlane_To_v1beta2_JoinControlPlane(in *Jo return autoConvert_upstreamv1beta2_JoinControlPlane_To_v1beta2_JoinControlPlane(in, out, s) } +func Convert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error { + // APIServer.TimeoutForControlPlane does not exist in CABPK, because CABPK aligns to upstreamV1Beta4. + return autoConvert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(in, out, s) +} + +func Convert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error { + // Discovery.Timeout does not exist in CABPK, because CABPK aligns to upstreamV1Beta4. + return autoConvert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(in, out, s) +} + +func Convert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) +} + +func Convert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + +func Convert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs) + return autoConvert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + // Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta2. func Convert_v1beta2_InitConfiguration_To_upstreamv1beta2_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.SkipPhases and Patches do not exist in kubeadm v1beta2, dropping those info. + // InitConfiguration.SkipPhases, InitConfiguration.Timeouts and Patches do not exist in kubeadm v1beta2, dropping those info. return autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta2_InitConfiguration(in, out, s) } func Convert_v1beta2_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { - // JoinConfiguration.SkipPhases and Patches do not exist in kubeadm v1beta2, dropping those info. - return autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in, out, s) + // JoinConfiguration.SkipPhases, JoinConfiguration.Timeouts and Patches do not exist in kubeadm v1beta2, dropping those info. + if err := autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in, out, s); err != nil { + return err + } + + if in.Timeouts != nil { + out.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(in.Timeouts.TLSBootstrapSeconds) + } + return nil } func Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { // NodeRegistrationOptions.ImagePullPolicy and ImagePullSerial do not exist in kubeadm v1beta2, dropping those info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta2 API does not allow this use case. + out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs) return autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in, out, s) } func Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta2_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { // ControlPlaneComponent.ExtraEnvs does not exist in kubeadm v1beta2, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta2 API does not allow this use case. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta2_ControlPlaneComponent(in, out, s) } func Convert_v1beta2_LocalEtcd_To_upstreamv1beta2_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { // LocalEtcd.ExtraEnvs does not exist in kubeadm v1beta2, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta2 API does not allow this use case. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta2_LocalEtcd(in, out, s) } @@ -116,3 +174,29 @@ func Convert_v1beta2_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in *bootstra // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. return autoConvert_v1beta2_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in, out, s) } + +// Custom conversions to handle fields migrated from ClusterConfiguration to Init and JoinConfiguration in the kubeadm v1beta4 API version. + +func (dst *ClusterConfiguration) ConvertFromInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error { + if initConfiguration == nil || initConfiguration.Timeouts == nil || initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds == nil { + return nil + } + + dst.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + return nil +} + +func (src *ClusterConfiguration) ConvertToInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error { + if src.APIServer.TimeoutForControlPlane == nil { + return nil + } + + if initConfiguration == nil { + return errors.New("cannot convert ClusterConfiguration to a nil InitConfiguration") + } + if initConfiguration.Timeouts == nil { + initConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.APIServer.TimeoutForControlPlane) + return nil +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_no_fuzz_test.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_no_fuzz_test.go new file mode 100644 index 000000000000..4b6b45804383 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_no_fuzz_test.go @@ -0,0 +1,50 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package upstreamv1beta2 + +import ( + "testing" + + . "github.com/onsi/gomega" + "k8s.io/utils/ptr" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" +) + +// This test case has been moved out of conversion_test.go because it should be run with the race detector. +// Note: The tests in conversion_test.go are disabled when the race detector is enabled (via "//go:build !race") because otherwise the fuzz tests would just time out. + +func TestTimeoutForControlPlaneMigration(t *testing.T) { + t.Run("from InitConfiguration to ClusterConfiguration and back", func(t *testing.T) { + g := NewWithT(t) + + initConfiguration := &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](123), + }, + } + + clusterConfiguration := &ClusterConfiguration{} + err := clusterConfiguration.ConvertFromInitConfiguration(initConfiguration) + g.Expect(err).ToNot(HaveOccurred()) + + initConfiguration = &bootstrapv1.InitConfiguration{} + err = clusterConfiguration.ConvertToInitConfiguration(initConfiguration) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds).To(Equal(ptr.To[int32](123))) + }) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go index 9469c8d2ecac..7a9741e8054a 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go @@ -20,9 +20,12 @@ package upstreamv1beta2 import ( "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" @@ -64,15 +67,17 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - clusterConfigurationFuzzer, - dnsFuzzer, - initConfigurationFuzzer, - joinControlPlanesFuzzer, - bootstrapv1ControlPlaneComponentFuzzer, - bootstrapv1LocalEtcdFuzzer, - bootstrapv1InitConfigurationFuzzer, - bootstrapv1JoinConfigurationFuzzer, - bootstrapv1NodeRegistrationOptionsFuzzer, + spokeClusterConfigurationFuzzer, + spokeDNSFuzzer, + spokeInitConfigurationFuzzer, + spokeJoinConfigurationFuzzer, + spokeJoinControlPlanesFuzzer, + spokeAPIServerFuzzer, + hubControlPlaneComponentFuzzer, + hubLocalEtcdFuzzer, + hubInitConfigurationFuzzer, + hubJoinConfigurationFuzzer, + hubNodeRegistrationOptionsFuzzer, } } @@ -80,54 +85,69 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { // NOTES: // - When fields do does not exist in cabpk v1beta1 types, pinning it to avoid kubeadm v1beta2 --> cabpk v1beta1 --> kubeadm v1beta2 round trip errors. -func clusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) { +func spokeClusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.UseHyperKubeImage = false } -func dnsFuzzer(obj *DNS, c randfill.Continue) { +func spokeDNSFuzzer(obj *DNS, c randfill.Continue) { c.FillNoCustom(obj) obj.Type = "" } -func initConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { +func spokeInitConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.CertificateKey = "" } -func joinControlPlanesFuzzer(obj *JoinControlPlane, c randfill.Continue) { +func spokeJoinControlPlanesFuzzer(obj *JoinControlPlane, c randfill.Continue) { c.FillNoCustom(obj) obj.CertificateKey = "" } +func spokeJoinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { + c.FillNoCustom(obj) + + if obj.Discovery.Timeout != nil { + obj.Discovery.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeAPIServerFuzzer(obj *APIServer, c randfill.Continue) { + c.FillNoCustom(obj) + + obj.TimeoutForControlPlane = nil +} + // Custom fuzzers for CABPK v1beta1 types. // NOTES: // - When fields do not exist in kubeadm v1beta2 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta2 --> cabpk v1beta1 round trip errors. -func bootstrapv1ControlPlaneComponentFuzzer(obj *bootstrapv1.ControlPlaneComponent, c randfill.Continue) { +func hubControlPlaneComponentFuzzer(obj *bootstrapv1.ControlPlaneComponent, c randfill.Continue) { c.FillNoCustom(obj) obj.ExtraEnvs = nil } -func bootstrapv1LocalEtcdFuzzer(obj *bootstrapv1.LocalEtcd, c randfill.Continue) { +func hubLocalEtcdFuzzer(obj *bootstrapv1.LocalEtcd, c randfill.Continue) { c.FillNoCustom(obj) obj.ExtraEnvs = nil } -func bootstrapv1InitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c randfill.Continue) { +func hubInitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.Patches = nil obj.SkipPhases = nil + obj.Timeouts = nil } -func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { +func hubJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.Patches = nil @@ -136,9 +156,16 @@ func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c ra if obj.Discovery.File != nil { obj.Discovery.File.KubeConfig = nil } + if obj.Timeouts != nil { + if obj.Timeouts.TLSBootstrapSeconds != nil { + obj.Timeouts = &bootstrapv1.Timeouts{TLSBootstrapSeconds: obj.Timeouts.TLSBootstrapSeconds} + } else { + obj.Timeouts = nil + } + } } -func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c randfill.Continue) { +func hubNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c randfill.Continue) { c.FillNoCustom(obj) obj.ImagePullPolicy = "" diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go index bb2971bc1b59..ea5e7d43bcbc 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go @@ -48,11 +48,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_APIServer_To_upstreamv1beta2_APIServer(a.(*v1beta2.APIServer), b.(*APIServer), scope) }); err != nil { @@ -103,21 +98,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.DNS)(nil), (*DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_DNS_To_upstreamv1beta2_DNS(a.(*v1beta2.DNS), b.(*DNS), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_Discovery_To_upstreamv1beta2_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) }); err != nil { @@ -168,21 +153,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.JoinControlPlane)(nil), (*JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_JoinControlPlane_To_upstreamv1beta2_JoinControlPlane(a.(*v1beta2.JoinControlPlane), b.(*JoinControlPlane), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*Networking)(nil), (*v1beta2.Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta2_Networking_To_v1beta2_Networking(a.(*Networking), b.(*v1beta2.Networking), scope) }); err != nil { @@ -193,8 +168,8 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + if err := s.AddConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) }); err != nil { return err } @@ -203,21 +178,46 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta2_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*InitConfiguration)(nil), (*v1beta2.InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta2_InitConfiguration_To_v1beta2_InitConfiguration(a.(*InitConfiguration), b.(*v1beta2.InitConfiguration), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*JoinControlPlane)(nil), (*v1beta2.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta2_JoinControlPlane_To_v1beta2_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta2.JoinControlPlane), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta2_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) }); err != nil { @@ -278,21 +278,15 @@ func autoConvert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(in *APIServer, o return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) + // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type return nil } -// Convert_upstreamv1beta2_APIServer_To_v1beta2_APIServer is an autogenerated conversion function. -func Convert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1beta2.APIServer, s conversion.Scope) error { - return autoConvert_upstreamv1beta2_APIServer_To_v1beta2_APIServer(in, out, s) -} - func autoConvert_v1beta2_APIServer_To_upstreamv1beta2_APIServer(in *v1beta2.APIServer, out *APIServer, s conversion.Scope) error { if err := Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta2_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) return nil } @@ -462,18 +456,13 @@ func Convert_v1beta2_ClusterStatus_To_upstreamv1beta2_ClusterStatus(in *v1beta2. } func autoConvert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ExtraVolumes = *(*[]v1beta2.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) return nil } -// Convert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent is an autogenerated conversion function. -func Convert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - return autoConvert_upstreamv1beta2_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) -} - func autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta2_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type return nil @@ -511,15 +500,10 @@ func autoConvert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(in *Discovery, o out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil } -// Convert_upstreamv1beta2_Discovery_To_v1beta2_Discovery is an autogenerated conversion function. -func Convert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1beta2.Discovery, s conversion.Scope) error { - return autoConvert_upstreamv1beta2_Discovery_To_v1beta2_Discovery(in, out, s) -} - func autoConvert_v1beta2_Discovery_To_upstreamv1beta2_Discovery(in *v1beta2.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) if in.File != nil { @@ -532,7 +516,6 @@ func autoConvert_v1beta2_Discovery_To_upstreamv1beta2_Discovery(in *v1beta2.Disc out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil } @@ -693,6 +676,7 @@ func autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta2_InitConfiguration( } // WARNING: in.SkipPhases requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } @@ -716,11 +700,6 @@ func autoConvert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration( return nil } -// Convert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration is an autogenerated conversion function. -func Convert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *v1beta2.JoinConfiguration, s conversion.Scope) error { - return autoConvert_upstreamv1beta2_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s) -} - func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { if err := Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { return err @@ -740,6 +719,7 @@ func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration( } // WARNING: in.SkipPhases requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } @@ -768,23 +748,18 @@ func autoConvert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, o return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } -// Convert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd is an autogenerated conversion function. -func Convert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { - return autoConvert_upstreamv1beta2_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) -} - func autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta2_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { if err := Convert_v1beta2_ImageMeta_To_upstreamv1beta2_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) @@ -819,21 +794,16 @@ func autoConvert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistra out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) return nil } -// Convert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions is an autogenerated conversion function. -func Convert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { - return autoConvert_upstreamv1beta2_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) -} - func autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) // WARNING: in.ImagePullPolicy requires manual conversion: does not exist in peer-type // WARNING: in.ImagePullSerial requires manual conversion: does not exist in peer-type diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go index 10b6cdf3f611..025e4bcf1da7 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go @@ -17,6 +17,7 @@ limitations under the License. package upstreamv1beta3 import ( + "github.com/pkg/errors" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -56,17 +57,72 @@ func (dst *JoinConfiguration) ConvertFrom(srcRaw conversion.Hub) error { // Custom conversion from this API, kubeadm v1beta3, to the hub version, CABPK v1beta1. func Convert_upstreamv1beta3_InitConfiguration_To_v1beta2_InitConfiguration(in *InitConfiguration, out *bootstrapv1.InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.CertificateKey does not exist in CABPK v1beta1 version, because Cluster API does not use automatic copy certs. + // InitConfiguration.CertificateKey does not exist in CABPK, because Cluster API does not use automatic copy certs. return autoConvert_upstreamv1beta3_InitConfiguration_To_v1beta2_InitConfiguration(in, out, s) } +func Convert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *bootstrapv1.JoinConfiguration, s apimachineryconversion.Scope) error { + if err := autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s); err != nil { + return err + } + if in.Discovery.Timeout != nil { + if out.Timeouts == nil { + out.Timeouts = &bootstrapv1.Timeouts{} + } + out.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(in.Discovery.Timeout) + } + return nil +} + func Convert_upstreamv1beta3_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *bootstrapv1.JoinControlPlane, s apimachineryconversion.Scope) error { - // JoinControlPlane.CertificateKey does not exist in CABPK v1beta1 version, because Cluster API does not use automatic copy certs. + // JoinControlPlane.CertificateKey does not exist in CABPK, because Cluster API does not use automatic copy certs. return autoConvert_upstreamv1beta3_JoinControlPlane_To_v1beta2_JoinControlPlane(in, out, s) } +func Convert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error { + // APIServer.TimeoutForControlPlane does not exist in CABPK, because CABPK aligns to upstreamV1Beta4. + return autoConvert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(in, out, s) +} + +func Convert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error { + // Discovery.Timeout does not exist in CABPK, because CABPK aligns to upstreamV1Beta4. + return autoConvert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(in, out, s) +} + +func Convert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) +} + +func Convert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + +func Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs) + return autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + // Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta3. +func Convert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { + // InitConfiguration.Timeouts does not exist in kubeadm v1beta3, dropping this info. + return autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration(in, out, s) +} + +func Convert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { + // JoinConfiguration.Timeouts does not exist in kubeadm v1beta3, dropping this info. + if err := autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(in, out, s); err != nil { + return err + } + + if in.Timeouts != nil { + out.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(in.Timeouts.TLSBootstrapSeconds) + } + return nil +} + func Convert_v1beta2_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apimachineryconversion.Scope) error { // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. return autoConvert_v1beta2_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in, out, s) @@ -74,15 +130,53 @@ func Convert_v1beta2_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in *bootstra func Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta3_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { // ControlPlaneComponent.ExtraEnvs does not exist in kubeadm v1beta3, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta3 API does not allow this use case. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta3_ControlPlaneComponent(in, out, s) } func Convert_v1beta2_LocalEtcd_To_upstreamv1beta3_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { // LocalEtcd.ExtraEnvs does not exist in kubeadm v1beta3, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta3 API does not allow this use case. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta3_LocalEtcd(in, out, s) } func Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { // NodeRegistrationOptions.ImagePullSerial does not exist in kubeadm v1beta3, dropping this info. + + // Following fields require a custom conversions. + // Note: there is a potential info loss when there are two values for the same arg but this is accepted because the kubeadm v1beta3 API does not allow this use case. + out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs) return autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(in, out, s) } + +// Custom conversions to handle fields migrated from ClusterConfiguration to Init and JoinConfiguration in the kubeadm v1beta4 API version. + +func (dst *ClusterConfiguration) ConvertFromInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error { + if initConfiguration == nil || initConfiguration.Timeouts == nil || initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds == nil { + return nil + } + + dst.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + return nil +} + +func (src *ClusterConfiguration) ConvertToInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error { + if src.APIServer.TimeoutForControlPlane == nil { + return nil + } + + if initConfiguration == nil { + return errors.New("cannot convert ClusterConfiguration to a nil InitConfiguration") + } + if initConfiguration.Timeouts == nil { + initConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.APIServer.TimeoutForControlPlane) + return nil +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_no_fuzz_test.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_no_fuzz_test.go new file mode 100644 index 000000000000..5c2048c15057 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_no_fuzz_test.go @@ -0,0 +1,50 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package upstreamv1beta3 + +import ( + "testing" + + . "github.com/onsi/gomega" + "k8s.io/utils/ptr" + + bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" +) + +// This test case has been moved out of conversion_test.go because it should be run with the race detector. +// Note: The tests in conversion_test.go are disabled when the race detector is enabled (via "//go:build !race") because otherwise the fuzz tests would just time out. + +func TestTimeoutForControlPlaneMigration(t *testing.T) { + t.Run("from InitConfiguration to ClusterConfiguration and back", func(t *testing.T) { + g := NewWithT(t) + + initConfiguration := &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](123), + }, + } + + clusterConfiguration := &ClusterConfiguration{} + err := clusterConfiguration.ConvertFromInitConfiguration(initConfiguration) + g.Expect(err).ToNot(HaveOccurred()) + + initConfiguration = &bootstrapv1.InitConfiguration{} + err = clusterConfiguration.ConvertToInitConfiguration(initConfiguration) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(initConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds).To(Equal(ptr.To[int32](123))) + }) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go index d5f4bf09c88b..7488d95f63d0 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go @@ -20,11 +20,14 @@ package upstreamv1beta3 import ( "testing" + "time" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" @@ -67,14 +70,16 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - initConfigurationFuzzer, - joinConfigurationFuzzer, - bootstrapv1JoinConfigurationFuzzer, - nodeRegistrationOptionsFuzzer, - joinControlPlanesFuzzer, - bootstrapv1ControlPlaneComponentFuzzer, - bootstrapv1LocalEtcdFuzzer, - bootstrapv1NodeRegistrationOptionsFuzzer, + spokeInitConfigurationFuzzer, + spokeJoinConfigurationFuzzer, + spokeAPIServerFuzzer, + hubJoinConfigurationFuzzer, + spokeNodeRegistrationOptionsFuzzer, + spokeJoinControlPlanesFuzzer, + hubInitConfigurationFuzzer, + hubControlPlaneComponentFuzzer, + hubLocalEtcdFuzzer, + hubNodeRegistrationOptionsFuzzer, } } @@ -82,34 +87,50 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { // NOTES: // - When fields do not exist in cabpk v1beta1 types, pinning it to avoid kubeadm v1beta3 --> cabpk v1beta1 --> kubeadm v1beta3 round trip errors. -func initConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { +func spokeInitConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.CertificateKey = "" obj.SkipPhases = nil } -func joinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { +func spokeJoinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.SkipPhases = nil + if obj.Discovery.Timeout != nil { + obj.Discovery.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeAPIServerFuzzer(obj *APIServer, c randfill.Continue) { + c.FillNoCustom(obj) + + obj.TimeoutForControlPlane = nil } -func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { +func hubJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { c.FillNoCustom(obj) if obj.Discovery.File != nil { obj.Discovery.File.KubeConfig = nil } + if obj.Timeouts != nil { + if obj.Timeouts.TLSBootstrapSeconds != nil { + obj.Timeouts = &bootstrapv1.Timeouts{TLSBootstrapSeconds: obj.Timeouts.TLSBootstrapSeconds} + } else { + obj.Timeouts = nil + } + } } -func nodeRegistrationOptionsFuzzer(obj *NodeRegistrationOptions, c randfill.Continue) { +func spokeNodeRegistrationOptionsFuzzer(obj *NodeRegistrationOptions, c randfill.Continue) { c.FillNoCustom(obj) obj.IgnorePreflightErrors = nil } -func joinControlPlanesFuzzer(obj *JoinControlPlane, c randfill.Continue) { +func spokeJoinControlPlanesFuzzer(obj *JoinControlPlane, c randfill.Continue) { c.FillNoCustom(obj) obj.CertificateKey = "" @@ -119,20 +140,26 @@ func joinControlPlanesFuzzer(obj *JoinControlPlane, c randfill.Continue) { // NOTES: // - When fields do not exist in kubeadm v1beta4 types, pinning them to avoid cabpk v1beta1 --> kubeadm v1beta4 --> cabpk v1beta1 round trip errors. -func bootstrapv1ControlPlaneComponentFuzzer(obj *bootstrapv1.ControlPlaneComponent, c randfill.Continue) { +func hubControlPlaneComponentFuzzer(obj *bootstrapv1.ControlPlaneComponent, c randfill.Continue) { c.FillNoCustom(obj) obj.ExtraEnvs = nil } -func bootstrapv1LocalEtcdFuzzer(obj *bootstrapv1.LocalEtcd, c randfill.Continue) { +func hubLocalEtcdFuzzer(obj *bootstrapv1.LocalEtcd, c randfill.Continue) { c.FillNoCustom(obj) obj.ExtraEnvs = nil } -func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c randfill.Continue) { +func hubNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c randfill.Continue) { c.FillNoCustom(obj) obj.ImagePullSerial = nil } + +func hubInitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c randfill.Continue) { + c.FillNoCustom(obj) + + obj.Timeouts = nil +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go index 67c07e2ce483..cdb3a5225824 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go @@ -48,11 +48,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_APIServer_To_upstreamv1beta3_APIServer(a.(*v1beta2.APIServer), b.(*APIServer), scope) }); err != nil { @@ -98,11 +93,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta3_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) }); err != nil { @@ -113,11 +103,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_Discovery_To_upstreamv1beta3_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) }); err != nil { @@ -168,63 +153,68 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration(a.(*v1beta2.InitConfiguration), b.(*InitConfiguration), scope) + if err := s.AddGeneratedConversionFunc((*v1beta2.JoinControlPlane)(nil), (*JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_JoinControlPlane_To_upstreamv1beta3_JoinControlPlane(a.(*v1beta2.JoinControlPlane), b.(*JoinControlPlane), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) + if err := s.AddGeneratedConversionFunc((*Networking)(nil), (*v1beta2.Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_Networking_To_v1beta2_Networking(a.(*Networking), b.(*v1beta2.Networking), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) + if err := s.AddGeneratedConversionFunc((*v1beta2.Networking)(nil), (*Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Networking_To_upstreamv1beta3_Networking(a.(*v1beta2.Networking), b.(*Networking), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.JoinControlPlane)(nil), (*JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_JoinControlPlane_To_upstreamv1beta3_JoinControlPlane(a.(*v1beta2.JoinControlPlane), b.(*JoinControlPlane), scope) + if err := s.AddGeneratedConversionFunc((*Patches)(nil), (*v1beta2.Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_Patches_To_v1beta2_Patches(a.(*Patches), b.(*v1beta2.Patches), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + if err := s.AddGeneratedConversionFunc((*v1beta2.Patches)(nil), (*Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Patches_To_upstreamv1beta3_Patches(a.(*v1beta2.Patches), b.(*Patches), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Networking)(nil), (*v1beta2.Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_Networking_To_v1beta2_Networking(a.(*Networking), b.(*v1beta2.Networking), scope) + if err := s.AddConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.Networking)(nil), (*Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_Networking_To_upstreamv1beta3_Networking(a.(*v1beta2.Networking), b.(*Networking), scope) + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + if err := s.AddConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Patches)(nil), (*v1beta2.Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_Patches_To_v1beta2_Patches(a.(*Patches), b.(*v1beta2.Patches), scope) + if err := s.AddConversionFunc((*InitConfiguration)(nil), (*v1beta2.InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_InitConfiguration_To_v1beta2_InitConfiguration(a.(*InitConfiguration), b.(*v1beta2.InitConfiguration), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.Patches)(nil), (*Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_Patches_To_upstreamv1beta3_Patches(a.(*v1beta2.Patches), b.(*Patches), scope) + if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*v1beta2.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta2.JoinConfiguration), scope) }); err != nil { return err } - if err := s.AddConversionFunc((*InitConfiguration)(nil), (*v1beta2.InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_InitConfiguration_To_v1beta2_InitConfiguration(a.(*InitConfiguration), b.(*v1beta2.InitConfiguration), scope) + if err := s.AddConversionFunc((*JoinControlPlane)(nil), (*v1beta2.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_JoinControlPlane_To_v1beta2_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta2.JoinControlPlane), scope) }); err != nil { return err } - if err := s.AddConversionFunc((*JoinControlPlane)(nil), (*v1beta2.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_JoinControlPlane_To_v1beta2_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta2.JoinControlPlane), scope) + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) }); err != nil { return err } @@ -238,6 +228,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta2.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration(a.(*v1beta2.InitConfiguration), b.(*InitConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_LocalEtcd_To_upstreamv1beta3_LocalEtcd(a.(*v1beta2.LocalEtcd), b.(*LocalEtcd), scope) }); err != nil { @@ -278,21 +278,15 @@ func autoConvert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(in *APIServer, o return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) + // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type return nil } -// Convert_upstreamv1beta3_APIServer_To_v1beta2_APIServer is an autogenerated conversion function. -func Convert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1beta2.APIServer, s conversion.Scope) error { - return autoConvert_upstreamv1beta3_APIServer_To_v1beta2_APIServer(in, out, s) -} - func autoConvert_v1beta2_APIServer_To_upstreamv1beta3_APIServer(in *v1beta2.APIServer, out *APIServer, s conversion.Scope) error { if err := Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta3_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) return nil } @@ -446,18 +440,13 @@ func Convert_v1beta2_ClusterConfiguration_To_upstreamv1beta3_ClusterConfiguratio } func autoConvert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ExtraVolumes = *(*[]v1beta2.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) return nil } -// Convert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent is an autogenerated conversion function. -func Convert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - return autoConvert_upstreamv1beta3_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) -} - func autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta3_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type return nil @@ -499,15 +488,10 @@ func autoConvert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(in *Discovery, o out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil } -// Convert_upstreamv1beta3_Discovery_To_v1beta2_Discovery is an autogenerated conversion function. -func Convert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1beta2.Discovery, s conversion.Scope) error { - return autoConvert_upstreamv1beta3_Discovery_To_v1beta2_Discovery(in, out, s) -} - func autoConvert_v1beta2_Discovery_To_upstreamv1beta3_Discovery(in *v1beta2.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) if in.File != nil { @@ -520,7 +504,6 @@ func autoConvert_v1beta2_Discovery_To_upstreamv1beta3_Discovery(in *v1beta2.Disc out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil } @@ -683,14 +666,10 @@ func autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration( } out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration is an autogenerated conversion function. -func Convert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration(in *v1beta2.InitConfiguration, out *InitConfiguration, s conversion.Scope) error { - return autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta3_InitConfiguration(in, out, s) -} - func autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *v1beta2.JoinConfiguration, s conversion.Scope) error { if err := Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { return err @@ -713,11 +692,6 @@ func autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration( return nil } -// Convert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration is an autogenerated conversion function. -func Convert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *v1beta2.JoinConfiguration, s conversion.Scope) error { - return autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s) -} - func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { if err := Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { return err @@ -737,14 +711,10 @@ func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration( } out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration is an autogenerated conversion function. -func Convert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { - return autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(in, out, s) -} - func autoConvert_upstreamv1beta3_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *v1beta2.JoinControlPlane, s conversion.Scope) error { if err := Convert_upstreamv1beta3_APIEndpoint_To_v1beta2_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { return err @@ -770,23 +740,18 @@ func autoConvert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, o return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } -// Convert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd is an autogenerated conversion function. -func Convert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { - return autoConvert_upstreamv1beta3_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) -} - func autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta3_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { if err := Convert_v1beta2_ImageMeta_To_upstreamv1beta3_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) @@ -821,24 +786,19 @@ func autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistra out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) - out.ImagePullPolicy = in.ImagePullPolicy + out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) return nil } -// Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions is an autogenerated conversion function. -func Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { - return autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) -} - func autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) - out.ImagePullPolicy = in.ImagePullPolicy + out.ImagePullPolicy = string(in.ImagePullPolicy) // WARNING: in.ImagePullSerial requires manual conversion: does not exist in peer-type return nil } diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go index 34e4667d368c..89e3fbf29d18 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go @@ -17,9 +17,6 @@ limitations under the License. package upstreamv1beta4 import ( - "sort" - - "github.com/pkg/errors" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -67,20 +64,6 @@ func Convert_upstreamv1beta4_ClusterConfiguration_To_v1beta2_ClusterConfiguratio return autoConvert_upstreamv1beta4_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in, out, s) } -func Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { - // Following fields exists in CABPK v1beta1 but they need a custom conversions. - // Note: there is a potential info loss when there are two values for the same arg but this is not an issue because the CAPBK v1beta1 does not allow this use case. - out.ExtraArgs = convertFromArgs(in.ExtraArgs) - return autoConvert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) -} - -func Convert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { - // Following fields require a custom conversions. - // Note: there is a potential info loss when there are two values for the same arg but this is not an issue because the CAPBK v1beta1 does not allow this use case. - out.ExtraArgs = convertFromArgs(in.ExtraArgs) - return autoConvert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) -} - func Convert_upstreamv1beta4_DNS_To_v1beta2_DNS(in *DNS, out *bootstrapv1.DNS, s apimachineryconversion.Scope) error { // Following fields do not exist in CABPK v1beta1 version: // - Disabled (Not supported yet) @@ -98,22 +81,7 @@ func Convert_upstreamv1beta4_InitConfiguration_To_v1beta2_InitConfiguration(in * func Convert_upstreamv1beta4_JoinConfiguration_To_v1beta2_JoinConfiguration(in *JoinConfiguration, out *bootstrapv1.JoinConfiguration, s apimachineryconversion.Scope) error { // Following fields do not exist in CABPK v1beta1 version: // - DryRun (Does not make sense for CAPBK) - // - Timeouts (Not supported yet) - err := autoConvert_upstreamv1beta4_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s) - - // Handle migration of JoinConfiguration.Timeouts.TLSBootstrap to Discovery.Timeout. - if in.Timeouts != nil && in.Timeouts.TLSBootstrap != nil { - out.Discovery.Timeout = in.Timeouts.TLSBootstrap - } - - return err -} - -func Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { - // Following fields require a custom conversions. - // Note: there is a potential info loss when there are two values for the same arg but this is not an issue because the CAPBK v1beta1 does not allow this use case. - out.KubeletExtraArgs = convertFromArgs(in.KubeletExtraArgs) - return autoConvert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) + return autoConvert_upstreamv1beta4_JoinConfiguration_To_v1beta2_JoinConfiguration(in, out, s) } func Convert_upstreamv1beta4_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *bootstrapv1.JoinControlPlane, s apimachineryconversion.Scope) error { @@ -122,45 +90,28 @@ func Convert_upstreamv1beta4_JoinControlPlane_To_v1beta2_JoinControlPlane(in *Jo return autoConvert_upstreamv1beta4_JoinControlPlane_To_v1beta2_JoinControlPlane(in, out, s) } -// Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta4. +func Convert_upstreamv1beta4_Timeouts_To_v1beta2_Timeouts(in *Timeouts, out *bootstrapv1.Timeouts, s apimachineryconversion.Scope) error { + if err := autoConvert_upstreamv1beta4_Timeouts_To_v1beta2_Timeouts(in, out, s); err != nil { + return err + } -func Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { - // Following fields require a custom conversions. - out.ExtraArgs = convertToArgs(in.ExtraArgs) - return autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in, out, s) + out.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(in.ControlPlaneComponentHealthCheck) + out.KubeletHealthCheckSeconds = bootstrapv1.ConvertToSeconds(in.KubeletHealthCheck) + out.KubernetesAPICallSeconds = bootstrapv1.ConvertToSeconds(in.KubernetesAPICall) + out.DiscoverySeconds = bootstrapv1.ConvertToSeconds(in.Discovery) + out.EtcdAPICallSeconds = bootstrapv1.ConvertToSeconds(in.EtcdAPICall) + out.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(in.TLSBootstrap) + return nil } +// Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta4. + func Convert_v1beta2_APIServer_To_upstreamv1beta4_APIServer(in *bootstrapv1.APIServer, out *APIServer, s apimachineryconversion.Scope) error { // Following fields do not exist in kubeadm v1beta4 version: // - TimeoutForControlPlane (this field has been migrated to Init/JoinConfiguration; migration is handled by ConvertFromClusterConfiguration custom converters. return autoConvert_v1beta2_APIServer_To_upstreamv1beta4_APIServer(in, out, s) } -func Convert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { - // Following fields require a custom conversions. - out.ExtraArgs = convertToArgs(in.ExtraArgs) - return autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in, out, s) -} - -func Convert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { - err := autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(in, out, s) - - // Handle migration of Discovery.Timeout to JoinConfiguration.Timeouts.TLSBootstrap. - if in.Discovery.Timeout != nil { - if out.Timeouts == nil { - out.Timeouts = &Timeouts{} - } - out.Timeouts.TLSBootstrap = in.Discovery.Timeout - } - return err -} - -func Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { - // Following fields exists in kubeadm v1beta4 types and can be converted to CAPBK v1beta1. - out.KubeletExtraArgs = convertToArgs(in.KubeletExtraArgs) - return autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in, out, s) -} - func Convert_v1beta2_Discovery_To_upstreamv1beta4_Discovery(in *bootstrapv1.Discovery, out *Discovery, s apimachineryconversion.Scope) error { // Following fields do not exist in kubeadm v1beta4 version: // - Timeout (this field has been migrated to JoinConfiguration.Timeouts.TLSBootstrap, the conversion is handled in Convert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration) @@ -172,84 +123,16 @@ func Convert_v1beta2_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in *bootstra return autoConvert_v1beta2_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in, out, s) } -// convertToArgs takes a argument map and converts it to a slice of arguments. -// Te resulting argument slice is sorted alpha-numerically. -func convertToArgs(in map[string]string) []Arg { - if in == nil { - return nil - } - args := make([]Arg, 0, len(in)) - for k, v := range in { - args = append(args, Arg{Name: k, Value: v}) - } - sort.Slice(args, func(i, j int) bool { - if args[i].Name == args[j].Name { - return args[i].Value < args[j].Value - } - return args[i].Name < args[j].Name - }) - return args -} - -// convertFromArgs takes a slice of arguments and returns an argument map. -// Duplicate argument keys will be de-duped, where later keys will take precedence. -func convertFromArgs(in []Arg) map[string]string { - if in == nil { - return nil - } - args := make(map[string]string, len(in)) - for _, arg := range in { - args[arg.Name] = arg.Value - } - return args -} - -// Custom conversions to handle fields migrated from ClusterConfiguration to Init and JoinConfiguration in the kubeadm v1beta4 API version. - -func (dst *InitConfiguration) ConvertFromClusterConfiguration(clusterConfiguration *bootstrapv1.ClusterConfiguration) error { - if clusterConfiguration == nil || clusterConfiguration.APIServer.TimeoutForControlPlane == nil { - return nil +func Convert_v1beta2_Timeouts_To_upstreamv1beta4_Timeouts(in *bootstrapv1.Timeouts, out *Timeouts, s apimachineryconversion.Scope) error { + if err := autoConvert_v1beta2_Timeouts_To_upstreamv1beta4_Timeouts(in, out, s); err != nil { + return err } - if dst.Timeouts == nil { - dst.Timeouts = &Timeouts{} - } - dst.Timeouts.ControlPlaneComponentHealthCheck = clusterConfiguration.APIServer.TimeoutForControlPlane - return nil -} - -func (dst *JoinConfiguration) ConvertFromClusterConfiguration(clusterConfiguration *bootstrapv1.ClusterConfiguration) error { - if clusterConfiguration == nil || clusterConfiguration.APIServer.TimeoutForControlPlane == nil { - return nil - } - - if dst.Timeouts == nil { - dst.Timeouts = &Timeouts{} - } - dst.Timeouts.ControlPlaneComponentHealthCheck = clusterConfiguration.APIServer.TimeoutForControlPlane - return nil -} - -func (src *InitConfiguration) ConvertToClusterConfiguration(clusterConfiguration *bootstrapv1.ClusterConfiguration) error { - if src.Timeouts == nil || src.Timeouts.ControlPlaneComponentHealthCheck == nil { - return nil - } - - if clusterConfiguration == nil { - return errors.New("cannot convert InitConfiguration to a nil ClusterConfiguration") - } - clusterConfiguration.APIServer.TimeoutForControlPlane = src.Timeouts.ControlPlaneComponentHealthCheck - return nil -} - -func (src *JoinConfiguration) ConvertToClusterConfiguration(clusterConfiguration *bootstrapv1.ClusterConfiguration) error { - if src.Timeouts == nil || src.Timeouts.ControlPlaneComponentHealthCheck == nil { - return nil - } - - if clusterConfiguration == nil { - return errors.New("cannot convert JoinConfiguration to a nil ClusterConfiguration") - } - clusterConfiguration.APIServer.TimeoutForControlPlane = src.Timeouts.ControlPlaneComponentHealthCheck + out.ControlPlaneComponentHealthCheck = bootstrapv1.ConvertFromSeconds(in.ControlPlaneComponentHealthCheckSeconds) + out.KubeletHealthCheck = bootstrapv1.ConvertFromSeconds(in.KubeletHealthCheckSeconds) + out.KubernetesAPICall = bootstrapv1.ConvertFromSeconds(in.KubernetesAPICallSeconds) + out.EtcdAPICall = bootstrapv1.ConvertFromSeconds(in.EtcdAPICallSeconds) + out.TLSBootstrap = bootstrapv1.ConvertFromSeconds(in.TLSBootstrapSeconds) + out.Discovery = bootstrapv1.ConvertFromSeconds(in.DiscoverySeconds) return nil } diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_no_fuzz_test.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_no_fuzz_test.go deleted file mode 100644 index a4d60935a645..000000000000 --- a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_no_fuzz_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package upstreamv1beta4 - -import ( - "testing" - "time" - - . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" -) - -// This test case has been moved out of conversion_test.go because it should be run with the race detector. -// Note: The tests in conversion_test.go are disabled when the race detector is enabled (via "//go:build !race") because otherwise the fuzz tests would just time out. - -func TestTimeoutForControlPlaneMigration(t *testing.T) { - timeout := metav1.Duration{Duration: 10 * time.Second} - t.Run("from ClusterConfiguration to InitConfiguration and back", func(t *testing.T) { - g := NewWithT(t) - - clusterConfiguration := &bootstrapv1.ClusterConfiguration{ - APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, - } - - initConfiguration := &InitConfiguration{} - err := initConfiguration.ConvertFromClusterConfiguration(clusterConfiguration) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(initConfiguration.Timeouts.ControlPlaneComponentHealthCheck).To(Equal(&timeout)) - - clusterConfiguration = &bootstrapv1.ClusterConfiguration{} - err = initConfiguration.ConvertToClusterConfiguration(clusterConfiguration) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(clusterConfiguration.APIServer.TimeoutForControlPlane).To(Equal(&timeout)) - }) - t.Run("from ClusterConfiguration to JoinConfiguration and back", func(t *testing.T) { - g := NewWithT(t) - - clusterConfiguration := &bootstrapv1.ClusterConfiguration{ - APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, - } - - joinConfiguration := &JoinConfiguration{} - err := joinConfiguration.ConvertFromClusterConfiguration(clusterConfiguration) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(joinConfiguration.Timeouts.ControlPlaneComponentHealthCheck).To(Equal(&timeout)) - - clusterConfiguration = &bootstrapv1.ClusterConfiguration{} - err = joinConfiguration.ConvertToClusterConfiguration(clusterConfiguration) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(clusterConfiguration.APIServer.TimeoutForControlPlane).To(Equal(&timeout)) - }) -} diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go index 4e47b5f53e5c..e62bd6e949b0 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go @@ -20,11 +20,14 @@ package upstreamv1beta4 import ( "testing" + "time" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" @@ -67,13 +70,13 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - clusterConfigurationFuzzer, - dnsFuzzer, - initConfigurationFuzzer, - joinConfigurationFuzzer, - joinControlPlaneFuzzer, - bootstrapv1APIServerFuzzer, - bootstrapv1JoinConfigurationFuzzer, + spokeClusterConfigurationFuzzer, + spokeDNSFuzzer, + spokeInitConfigurationFuzzer, + spokeJoinConfigurationFuzzer, + spokeJoinControlPlaneFuzzer, + spokeTimeoutsFuzzer, + hubJoinConfigurationFuzzer, } } @@ -81,7 +84,7 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { // NOTES: // - When fields do not exist in cabpk v1beta1 types, pinning them to avoid kubeadm v1beta4 --> cabpk v1beta1 --> kubeadm v1beta4 round trip errors. -func clusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) { +func spokeClusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.Proxy = Proxy{} @@ -90,13 +93,13 @@ func clusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Continue) obj.CertificateValidityPeriod = nil } -func dnsFuzzer(obj *DNS, c randfill.Continue) { +func spokeDNSFuzzer(obj *DNS, c randfill.Continue) { c.FillNoCustom(obj) obj.Disabled = false } -func initConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { +func spokeInitConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.DryRun = false @@ -104,7 +107,7 @@ func initConfigurationFuzzer(obj *InitConfiguration, c randfill.Continue) { obj.Timeouts = nil } -func joinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { +func spokeJoinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { c.FillNoCustom(obj) obj.DryRun = false @@ -123,23 +126,38 @@ func joinConfigurationFuzzer(obj *JoinConfiguration, c randfill.Continue) { obj.Timeouts = supportedTimeouts } -func joinControlPlaneFuzzer(obj *JoinControlPlane, c randfill.Continue) { +func spokeJoinControlPlaneFuzzer(obj *JoinControlPlane, c randfill.Continue) { c.FillNoCustom(obj) obj.CertificateKey = "" } -// Custom fuzzers for CABPK v1beta1 types. -// NOTES: -// - When fields do not exist in kubeadm v1beta4 types, pinning them to avoid cabpk v1beta1 --> kubeadm v1beta4 --> cabpk v1beta1 round trip errors. - -func bootstrapv1APIServerFuzzer(obj *bootstrapv1.APIServer, c randfill.Continue) { +func spokeTimeoutsFuzzer(obj *Timeouts, c randfill.Continue) { c.FillNoCustom(obj) - obj.TimeoutForControlPlane = nil + if c.Bool() { + obj.ControlPlaneComponentHealthCheck = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + obj.KubeletHealthCheck = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + obj.KubernetesAPICall = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + obj.EtcdAPICall = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + obj.TLSBootstrap = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + obj.Discovery = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } else { + obj.ControlPlaneComponentHealthCheck = nil + obj.KubeletHealthCheck = nil + obj.KubernetesAPICall = nil + obj.EtcdAPICall = nil + obj.TLSBootstrap = nil + obj.Discovery = nil + } + obj.UpgradeManifests = nil } -func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { +// Custom fuzzers for CABPK v1beta1 types. +// NOTES: +// - When fields do not exist in kubeadm v1beta4 types, pinning them to avoid cabpk v1beta1 --> kubeadm v1beta4 --> cabpk v1beta1 round trip errors. + +func hubJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c randfill.Continue) { c.FillNoCustom(obj) if obj.Discovery.File != nil { diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/types.go b/bootstrap/kubeadm/types/upstreamv1beta4/types.go index 19f937b897d4..903c8c4b2997 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/types.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/types.go @@ -147,7 +147,7 @@ type ClusterConfiguration struct { ClusterName string `json:"clusterName,omitempty"` // EncryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. - // Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096" or "ECDSA-P256". + // Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384" . // +optional EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"` @@ -397,7 +397,7 @@ type JoinConfiguration struct { NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"` // CACertPath is the path to the SSL certificate authority used to - // secure comunications between node and control-plane. + // secure communications between node and control-plane. // Defaults to "/etc/kubernetes/pki/ca.crt". // +optional CACertPath string `json:"caCertPath,omitempty"` @@ -539,6 +539,8 @@ type EncryptionAlgorithmType string const ( // EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256. EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256" + // EncryptionAlgorithmECDSAP384 defines the ECDSA encryption algorithm type with curve P384. + EncryptionAlgorithmECDSAP384 EncryptionAlgorithmType = "ECDSA-P384" // EncryptionAlgorithmRSA2048 defines the RSA encryption algorithm type with key size 2048 bits. EncryptionAlgorithmRSA2048 EncryptionAlgorithmType = "RSA-2048" // EncryptionAlgorithmRSA3072 defines the RSA encryption algorithm type with key size 3072 bits. diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go index c47d38041a09..fb289363379d 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go @@ -53,6 +53,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*Arg)(nil), (*v1beta2.Arg)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_Arg_To_v1beta2_Arg(a.(*Arg), b.(*v1beta2.Arg), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.Arg)(nil), (*Arg)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Arg_To_upstreamv1beta4_Arg(a.(*v1beta2.Arg), b.(*Arg), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*BootstrapToken)(nil), (*v1beta2.BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta4_BootstrapToken_To_v1beta2_BootstrapToken(a.(*BootstrapToken), b.(*v1beta2.BootstrapToken), scope) }); err != nil { @@ -88,6 +98,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta2.DNS)(nil), (*DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_DNS_To_upstreamv1beta4_DNS(a.(*v1beta2.DNS), b.(*DNS), scope) }); err != nil { @@ -158,11 +178,26 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta2.JoinControlPlane)(nil), (*JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane(a.(*v1beta2.JoinControlPlane), b.(*JoinControlPlane), scope) }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(a.(*v1beta2.LocalEtcd), b.(*LocalEtcd), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*Networking)(nil), (*v1beta2.Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta4_Networking_To_v1beta2_Networking(a.(*Networking), b.(*v1beta2.Networking), scope) }); err != nil { @@ -173,6 +208,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta2.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(a.(*v1beta2.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*Patches)(nil), (*v1beta2.Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta4_Patches_To_v1beta2_Patches(a.(*Patches), b.(*v1beta2.Patches), scope) }); err != nil { @@ -188,11 +233,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta4_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) }); err != nil { @@ -213,13 +253,8 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) - }); err != nil { - return err - } - if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + if err := s.AddConversionFunc((*Timeouts)(nil), (*v1beta2.Timeouts)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_Timeouts_To_v1beta2_Timeouts(a.(*Timeouts), b.(*v1beta2.Timeouts), scope) }); err != nil { return err } @@ -228,11 +263,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_Discovery_To_upstreamv1beta4_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) }); err != nil { @@ -243,18 +273,8 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*v1beta2.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(a.(*v1beta2.JoinConfiguration), b.(*JoinConfiguration), scope) - }); err != nil { - return err - } - if err := s.AddConversionFunc((*v1beta2.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(a.(*v1beta2.LocalEtcd), b.(*LocalEtcd), scope) - }); err != nil { - return err - } - if err := s.AddConversionFunc((*v1beta2.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(a.(*v1beta2.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) + if err := s.AddConversionFunc((*v1beta2.Timeouts)(nil), (*Timeouts)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Timeouts_To_upstreamv1beta4_Timeouts(a.(*v1beta2.Timeouts), b.(*Timeouts), scope) }); err != nil { return err } @@ -301,10 +321,31 @@ func autoConvert_v1beta2_APIServer_To_upstreamv1beta4_APIServer(in *v1beta2.APIS return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type return nil } +func autoConvert_upstreamv1beta4_Arg_To_v1beta2_Arg(in *Arg, out *v1beta2.Arg, s conversion.Scope) error { + out.Name = in.Name + out.Value = in.Value + return nil +} + +// Convert_upstreamv1beta4_Arg_To_v1beta2_Arg is an autogenerated conversion function. +func Convert_upstreamv1beta4_Arg_To_v1beta2_Arg(in *Arg, out *v1beta2.Arg, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_Arg_To_v1beta2_Arg(in, out, s) +} + +func autoConvert_v1beta2_Arg_To_upstreamv1beta4_Arg(in *v1beta2.Arg, out *Arg, s conversion.Scope) error { + out.Name = in.Name + out.Value = in.Value + return nil +} + +// Convert_v1beta2_Arg_To_upstreamv1beta4_Arg is an autogenerated conversion function. +func Convert_v1beta2_Arg_To_upstreamv1beta4_Arg(in *v1beta2.Arg, out *Arg, s conversion.Scope) error { + return autoConvert_v1beta2_Arg_To_upstreamv1beta4_Arg(in, out, s) +} + func autoConvert_upstreamv1beta4_BootstrapToken_To_v1beta2_BootstrapToken(in *BootstrapToken, out *v1beta2.BootstrapToken, s conversion.Scope) error { out.Token = (*v1beta2.BootstrapTokenString)(unsafe.Pointer(in.Token)) out.Description = in.Description @@ -449,19 +490,29 @@ func Convert_v1beta2_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguratio } func autoConvert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg vs map[string]string) + out.ExtraArgs = *(*[]v1beta2.Arg)(unsafe.Pointer(&in.ExtraArgs)) out.ExtraVolumes = *(*[]v1beta2.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) out.ExtraEnvs = *(*[]v1beta2.EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) return nil } +// Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent is an autogenerated conversion function. +func Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) +} + func autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg) + out.ExtraArgs = *(*[]Arg)(unsafe.Pointer(&in.ExtraArgs)) out.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) out.ExtraEnvs = *(*[]EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) return nil } +// Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent is an autogenerated conversion function. +func Convert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { + return autoConvert_v1beta2_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in, out, s) +} + func autoConvert_upstreamv1beta4_DNS_To_v1beta2_DNS(in *DNS, out *v1beta2.DNS, s conversion.Scope) error { if err := Convert_upstreamv1beta4_ImageMeta_To_v1beta2_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err @@ -514,7 +565,6 @@ func autoConvert_v1beta2_Discovery_To_upstreamv1beta4_Discovery(in *v1beta2.Disc out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil } @@ -539,15 +589,7 @@ func Convert_v1beta2_EnvVar_To_upstreamv1beta4_EnvVar(in *v1beta2.EnvVar, out *E } func autoConvert_upstreamv1beta4_Etcd_To_v1beta2_Etcd(in *Etcd, out *v1beta2.Etcd, s conversion.Scope) error { - if in.Local != nil { - in, out := &in.Local, &out.Local - *out = new(v1beta2.LocalEtcd) - if err := Convert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(*in, *out, s); err != nil { - return err - } - } else { - out.Local = nil - } + out.Local = (*v1beta2.LocalEtcd)(unsafe.Pointer(in.Local)) out.External = (*v1beta2.ExternalEtcd)(unsafe.Pointer(in.External)) return nil } @@ -558,15 +600,7 @@ func Convert_upstreamv1beta4_Etcd_To_v1beta2_Etcd(in *Etcd, out *v1beta2.Etcd, s } func autoConvert_v1beta2_Etcd_To_upstreamv1beta4_Etcd(in *v1beta2.Etcd, out *Etcd, s conversion.Scope) error { - if in.Local != nil { - in, out := &in.Local, &out.Local - *out = new(LocalEtcd) - if err := Convert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(*in, *out, s); err != nil { - return err - } - } else { - out.Local = nil - } + out.Local = (*LocalEtcd)(unsafe.Pointer(in.Local)) out.External = (*ExternalEtcd)(unsafe.Pointer(in.External)) return nil } @@ -680,7 +714,15 @@ func autoConvert_upstreamv1beta4_InitConfiguration_To_v1beta2_InitConfiguration( // WARNING: in.CertificateKey requires manual conversion: does not exist in peer-type out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*v1beta2.Patches)(unsafe.Pointer(in.Patches)) - // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(v1beta2.Timeouts) + if err := Convert_upstreamv1beta4_Timeouts_To_v1beta2_Timeouts(*in, *out, s); err != nil { + return err + } + } else { + out.Timeouts = nil + } return nil } @@ -694,6 +736,15 @@ func autoConvert_v1beta2_InitConfiguration_To_upstreamv1beta4_InitConfiguration( } out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(Timeouts) + if err := Convert_v1beta2_Timeouts_To_upstreamv1beta4_Timeouts(*in, *out, s); err != nil { + return err + } + } else { + out.Timeouts = nil + } return nil } @@ -722,7 +773,15 @@ func autoConvert_upstreamv1beta4_JoinConfiguration_To_v1beta2_JoinConfiguration( } out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*v1beta2.Patches)(unsafe.Pointer(in.Patches)) - // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(v1beta2.Timeouts) + if err := Convert_upstreamv1beta4_Timeouts_To_v1beta2_Timeouts(*in, *out, s); err != nil { + return err + } + } else { + out.Timeouts = nil + } return nil } @@ -745,9 +804,23 @@ func autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration( } out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + if in.Timeouts != nil { + in, out := &in.Timeouts, &out.Timeouts + *out = new(Timeouts) + if err := Convert_v1beta2_Timeouts_To_upstreamv1beta4_Timeouts(*in, *out, s); err != nil { + return err + } + } else { + out.Timeouts = nil + } return nil } +// Convert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration is an autogenerated conversion function. +func Convert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(in *v1beta2.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { + return autoConvert_v1beta2_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(in, out, s) +} + func autoConvert_upstreamv1beta4_JoinControlPlane_To_v1beta2_JoinControlPlane(in *JoinControlPlane, out *v1beta2.JoinControlPlane, s conversion.Scope) error { if err := Convert_upstreamv1beta4_APIEndpoint_To_v1beta2_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { return err @@ -773,25 +846,35 @@ func autoConvert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, o return err } out.DataDir = in.DataDir - // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg vs map[string]string) + out.ExtraArgs = *(*[]v1beta2.Arg)(unsafe.Pointer(&in.ExtraArgs)) out.ExtraEnvs = *(*[]v1beta2.EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } +// Convert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd is an autogenerated conversion function. +func Convert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + func autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { if err := Convert_v1beta2_ImageMeta_To_upstreamv1beta4_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err } out.DataDir = in.DataDir - // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg) + out.ExtraArgs = *(*[]Arg)(unsafe.Pointer(&in.ExtraArgs)) out.ExtraEnvs = *(*[]EnvVar)(unsafe.Pointer(&in.ExtraEnvs)) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } +// Convert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd is an autogenerated conversion function. +func Convert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { + return autoConvert_v1beta2_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in, out, s) +} + func autoConvert_upstreamv1beta4_Networking_To_v1beta2_Networking(in *Networking, out *v1beta2.Networking, s conversion.Scope) error { out.ServiceSubnet = in.ServiceSubnet out.PodSubnet = in.PodSubnet @@ -820,24 +903,34 @@ func autoConvert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistra out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg vs map[string]string) + out.KubeletExtraArgs = *(*[]v1beta2.Arg)(unsafe.Pointer(&in.KubeletExtraArgs)) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) - out.ImagePullPolicy = string(in.ImagePullPolicy) + out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) return nil } +// Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions is an autogenerated conversion function. +func Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + func autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg) + out.KubeletExtraArgs = *(*[]Arg)(unsafe.Pointer(&in.KubeletExtraArgs)) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) return nil } +// Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions is an autogenerated conversion function. +func Convert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { + return autoConvert_v1beta2_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in, out, s) +} + func autoConvert_upstreamv1beta4_Patches_To_v1beta2_Patches(in *Patches, out *v1beta2.Patches, s conversion.Scope) error { out.Directory = in.Directory return nil @@ -857,3 +950,24 @@ func autoConvert_v1beta2_Patches_To_upstreamv1beta4_Patches(in *v1beta2.Patches, func Convert_v1beta2_Patches_To_upstreamv1beta4_Patches(in *v1beta2.Patches, out *Patches, s conversion.Scope) error { return autoConvert_v1beta2_Patches_To_upstreamv1beta4_Patches(in, out, s) } + +func autoConvert_upstreamv1beta4_Timeouts_To_v1beta2_Timeouts(in *Timeouts, out *v1beta2.Timeouts, s conversion.Scope) error { + // WARNING: in.ControlPlaneComponentHealthCheck requires manual conversion: does not exist in peer-type + // WARNING: in.KubeletHealthCheck requires manual conversion: does not exist in peer-type + // WARNING: in.KubernetesAPICall requires manual conversion: does not exist in peer-type + // WARNING: in.EtcdAPICall requires manual conversion: does not exist in peer-type + // WARNING: in.TLSBootstrap requires manual conversion: does not exist in peer-type + // WARNING: in.Discovery requires manual conversion: does not exist in peer-type + // WARNING: in.UpgradeManifests requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_Timeouts_To_upstreamv1beta4_Timeouts(in *v1beta2.Timeouts, out *Timeouts, s conversion.Scope) error { + // WARNING: in.ControlPlaneComponentHealthCheckSeconds requires manual conversion: does not exist in peer-type + // WARNING: in.KubeletHealthCheckSeconds requires manual conversion: does not exist in peer-type + // WARNING: in.KubernetesAPICallSeconds requires manual conversion: does not exist in peer-type + // WARNING: in.EtcdAPICallSeconds requires manual conversion: does not exist in peer-type + // WARNING: in.TLSBootstrapSeconds requires manual conversion: does not exist in peer-type + // WARNING: in.DiscoverySeconds requires manual conversion: does not exist in peer-type + return nil +} diff --git a/bootstrap/kubeadm/types/utils.go b/bootstrap/kubeadm/types/utils.go index 96205ca3661f..03318c5fa78e 100644 --- a/bootstrap/kubeadm/types/utils.go +++ b/bootstrap/kubeadm/types/utils.go @@ -62,16 +62,16 @@ var ( } ) -// ConvertibleFromClusterConfiguration defines capabilities of a type that during conversions gets values from ClusterConfiguration. -// NOTE: this interface is specifically designed to handle fields migrated from ClusterConfiguration to Init and JoinConfiguration in the kubeadm v1beta4 API version. -type ConvertibleFromClusterConfiguration interface { - ConvertFromClusterConfiguration(clusterConfiguration *bootstrapv1.ClusterConfiguration) error +// ConvertibleFromInitConfiguration defines capabilities of a type that during conversions gets values from initConfiguration. +// NOTE: this interface is specifically designed to handle fields migrated from ClusterConfiguration to InitConfiguration in the kubeadm v1beta4 API version. +type ConvertibleFromInitConfiguration interface { + ConvertFromInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error } -// ConvertibleToClusterConfiguration defines capabilities of a type that during conversions sets values to ClusterConfiguration. -// NOTE: this interface is specifically designed to handle fields migrated from ClusterConfiguration to Init and JoinConfiguration in the kubeadm v1beta4 API version. -type ConvertibleToClusterConfiguration interface { - ConvertToClusterConfiguration(clusterConfiguration *bootstrapv1.ClusterConfiguration) error +// ConvertibleToInitConfiguration defines capabilities of a type that during conversions sets values to initConfiguration. +// NOTE: this interface is specifically designed to handle fields migrated from ClusterConfiguration to InitConfiguration in the kubeadm v1beta4 API version. +type ConvertibleToInitConfiguration interface { + ConvertToInitConfiguration(initConfiguration *bootstrapv1.InitConfiguration) error } // KubeVersionToKubeadmAPIGroupVersion maps a Kubernetes version to the correct Kubeadm API Group supported. @@ -86,7 +86,7 @@ func KubeVersionToKubeadmAPIGroupVersion(v semver.Version) (schema.GroupVersion, // NOTE: All the Kubernetes version >= v1.22 and < v1.31 should use the kubeadm API version v1beta3 return upstreamv1beta3.GroupVersion, nil default: - // NOTE: All the Kubernetes version greater or equal to v1.31 (not yet released at the time of writing this code) + // NOTE: All the Kubernetes version greater or equal to v1.31 // are assumed to use the kubeadm API version v1beta4 or to work with Cluster API using it. // This should be reconsidered whenever kubeadm introduces a new API version. return upstreamv1beta4.GroupVersion, nil @@ -96,8 +96,8 @@ func KubeVersionToKubeadmAPIGroupVersion(v semver.Version) (schema.GroupVersion, // MarshalClusterConfigurationForVersion converts a Cluster API ClusterConfiguration type to the kubeadm API type // for the given Kubernetes Version. // NOTE: This assumes Kubernetes Version equals to kubeadm version. -func MarshalClusterConfigurationForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, version semver.Version) (string, error) { - return marshalForVersion(nil, clusterConfiguration, version, clusterConfigurationVersionTypeMap) +func MarshalClusterConfigurationForVersion(initConfiguration *bootstrapv1.InitConfiguration, clusterConfiguration *bootstrapv1.ClusterConfiguration, version semver.Version) (string, error) { + return marshalForVersion(initConfiguration, clusterConfiguration, version, clusterConfigurationVersionTypeMap) } // MarshalClusterStatusForVersion converts a Cluster API ClusterStatus type to the kubeadm API type @@ -110,18 +110,18 @@ func MarshalClusterStatusForVersion(clusterStatus *bootstrapv1.ClusterStatus, ve // MarshalInitConfigurationForVersion converts a Cluster API InitConfiguration type to the kubeadm API type // for the given Kubernetes Version. // NOTE: This assumes Kubernetes Version equals to kubeadm version. -func MarshalInitConfigurationForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, initConfiguration *bootstrapv1.InitConfiguration, version semver.Version) (string, error) { - return marshalForVersion(clusterConfiguration, initConfiguration, version, initConfigurationVersionTypeMap) +func MarshalInitConfigurationForVersion(initConfiguration *bootstrapv1.InitConfiguration, version semver.Version) (string, error) { + return marshalForVersion(nil, initConfiguration, version, initConfigurationVersionTypeMap) } // MarshalJoinConfigurationForVersion converts a Cluster API JoinConfiguration type to the kubeadm API type // for the given Kubernetes Version. // NOTE: This assumes Kubernetes Version equals to kubeadm version. -func MarshalJoinConfigurationForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, joinConfiguration *bootstrapv1.JoinConfiguration, version semver.Version) (string, error) { - return marshalForVersion(clusterConfiguration, joinConfiguration, version, joinConfigurationVersionTypeMap) +func MarshalJoinConfigurationForVersion(joinConfiguration *bootstrapv1.JoinConfiguration, version semver.Version) (string, error) { + return marshalForVersion(nil, joinConfiguration, version, joinConfigurationVersionTypeMap) } -func marshalForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, obj conversion.Hub, version semver.Version, kubeadmObjVersionTypeMap map[schema.GroupVersion]conversion.Convertible) (string, error) { +func marshalForVersion(initConfiguration *bootstrapv1.InitConfiguration, obj conversion.Hub, version semver.Version, kubeadmObjVersionTypeMap map[schema.GroupVersion]conversion.Convertible) (string, error) { kubeadmAPIGroupVersion, err := KubeVersionToKubeadmAPIGroupVersion(version) if err != nil { return "", err @@ -137,9 +137,9 @@ func marshalForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, o return "", errors.Wrapf(err, "failed to convert to KubeadmAPI type for version %s", kubeadmAPIGroupVersion) } - if convertibleFromClusterConfigurationObj, ok := targetKubeadmObj.(ConvertibleFromClusterConfiguration); ok { - if err := convertibleFromClusterConfigurationObj.ConvertFromClusterConfiguration(clusterConfiguration); err != nil { - return "", errors.Wrapf(err, "failed to convert from ClusterConfiguration to KubeadmAPI type for version %s", kubeadmAPIGroupVersion) + if convertibleFromInitConfigurationObj, ok := targetKubeadmObj.(ConvertibleFromInitConfiguration); ok { + if err := convertibleFromInitConfigurationObj.ConvertFromInitConfiguration(initConfiguration); err != nil { + return "", errors.Wrapf(err, "failed to convert from InitConfiguration to KubeadmAPI type for version %s", kubeadmAPIGroupVersion) } } @@ -177,9 +177,9 @@ func toYaml(obj runtime.Object, gv runtime.GroupVersioner, codecs serializer.Cod // UnmarshalClusterConfiguration tries to translate a Kubeadm API yaml back to the Cluster API ClusterConfiguration type. // NOTE: The yaml could be any of the known formats for the kubeadm ClusterConfiguration type. -func UnmarshalClusterConfiguration(yaml string) (*bootstrapv1.ClusterConfiguration, error) { +func UnmarshalClusterConfiguration(yaml string, initConfiguration *bootstrapv1.InitConfiguration) (*bootstrapv1.ClusterConfiguration, error) { obj := &bootstrapv1.ClusterConfiguration{} - if err := unmarshalFromVersions(yaml, clusterConfigurationVersionTypeMap, nil, obj); err != nil { + if err := unmarshalFromVersions(yaml, clusterConfigurationVersionTypeMap, initConfiguration, obj); err != nil { return nil, err } return obj, nil @@ -197,9 +197,9 @@ func UnmarshalClusterStatus(yaml string) (*bootstrapv1.ClusterStatus, error) { // UnmarshalInitConfiguration tries to translate a Kubeadm API yaml back to the InitConfiguration type. // NOTE: The yaml could be any of the known formats for the kubeadm InitConfiguration type. -func UnmarshalInitConfiguration(yaml string, clusterConfiguration *bootstrapv1.ClusterConfiguration) (*bootstrapv1.InitConfiguration, error) { +func UnmarshalInitConfiguration(yaml string) (*bootstrapv1.InitConfiguration, error) { obj := &bootstrapv1.InitConfiguration{} - if err := unmarshalFromVersions(yaml, initConfigurationVersionTypeMap, clusterConfiguration, obj); err != nil { + if err := unmarshalFromVersions(yaml, initConfigurationVersionTypeMap, nil, obj); err != nil { return nil, err } return obj, nil @@ -207,15 +207,15 @@ func UnmarshalInitConfiguration(yaml string, clusterConfiguration *bootstrapv1.C // UnmarshalJoinConfiguration tries to translate a Kubeadm API yaml back to the JoinConfiguration type. // NOTE: The yaml could be any of the known formats for the kubeadm JoinConfiguration type. -func UnmarshalJoinConfiguration(yaml string, clusterConfiguration *bootstrapv1.ClusterConfiguration) (*bootstrapv1.JoinConfiguration, error) { +func UnmarshalJoinConfiguration(yaml string) (*bootstrapv1.JoinConfiguration, error) { obj := &bootstrapv1.JoinConfiguration{} - if err := unmarshalFromVersions(yaml, joinConfigurationVersionTypeMap, clusterConfiguration, obj); err != nil { + if err := unmarshalFromVersions(yaml, joinConfigurationVersionTypeMap, nil, obj); err != nil { return nil, err } return obj, nil } -func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersion]conversion.Convertible, clusterConfiguration *bootstrapv1.ClusterConfiguration, capiObj conversion.Hub) error { +func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersion]conversion.Convertible, initConfiguration *bootstrapv1.InitConfiguration, capiObj conversion.Hub) error { // For each know kubeadm API version for gv, obj := range kubeadmAPIVersions { // Tries conversion from yaml to the corresponding kubeadmObj @@ -228,13 +228,13 @@ func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersi _, _, err = codecs.UniversalDeserializer().Decode([]byte(yaml), &gvk, kubeadmObj) if err == nil { - if convertibleToClusterConfigurationObj, ok := kubeadmObj.(ConvertibleToClusterConfiguration); ok { - if err := convertibleToClusterConfigurationObj.ConvertToClusterConfiguration(clusterConfiguration); err != nil { - return errors.Wrapf(err, "failed to convert to ClusterConfiguration from KubeadmAPI type for version %s", gvk) + if convertibleToInitConfigurationObj, ok := kubeadmObj.(ConvertibleToInitConfiguration); ok { + if err := convertibleToInitConfigurationObj.ConvertToInitConfiguration(initConfiguration); err != nil { + return errors.Wrapf(err, "failed to convert to InitConfiguration from KubeadmAPI type for version %s", gvk) } } - // If conversion worked, then converts the kubeadmObj (spoke) back to the Cluster API ClusterConfiguration type (hub). + // If conversion worked, then converts the kubeadmObj (spoke) back to the Cluster API type (hub). if err := kubeadmObj.(conversion.Convertible).ConvertTo(capiObj); err != nil { return errors.Wrapf(err, "failed to convert kubeadm types to Cluster API types") } diff --git a/bootstrap/kubeadm/types/utils_test.go b/bootstrap/kubeadm/types/utils_test.go index 3fb04357e851..95b4529cc0d2 100644 --- a/bootstrap/kubeadm/types/utils_test.go +++ b/bootstrap/kubeadm/types/utils_test.go @@ -18,13 +18,12 @@ package utils import ( "testing" - "time" "github.com/blang/semver/v4" "github.com/google/go-cmp/cmp" . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/ptr" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta2" @@ -147,8 +146,9 @@ func TestKubeVersionToKubeadmAPIGroupVersion(t *testing.T) { func TestMarshalClusterConfigurationForVersion(t *testing.T) { type args struct { - capiObj *bootstrapv1.ClusterConfiguration - version semver.Version + initConfiguration *bootstrapv1.InitConfiguration + capiObj *bootstrapv1.ClusterConfiguration + version semver.Version } tests := []struct { name string @@ -172,6 +172,28 @@ func TestMarshalClusterConfigurationForVersion(t *testing.T) { "scheduler: {}\n", wantErr: false, }, + { + name: "Generates a v1beta2 kubeadm configuration with data from init configuration", + args: args{ + initConfiguration: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](30), + }, + }, + capiObj: &bootstrapv1.ClusterConfiguration{}, + version: semver.MustParse("1.15.0"), + }, + want: "apiServer:\n" + + " timeoutForControlPlane: 30s\n" + + "apiVersion: kubeadm.k8s.io/v1beta2\n" + + "controllerManager: {}\n" + + "dns: {}\n" + + "etcd: {}\n" + + "kind: ClusterConfiguration\n" + + "networking: {}\n" + + "scheduler: {}\n", + wantErr: false, + }, { name: "Generates a v1beta3 kubeadm configuration", args: args{ @@ -188,6 +210,28 @@ func TestMarshalClusterConfigurationForVersion(t *testing.T) { "scheduler: {}\n", wantErr: false, }, + { + name: "Generates a v1beta3 kubeadm configuration with data from init configuration", + args: args{ + initConfiguration: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](30), + }, + }, + capiObj: &bootstrapv1.ClusterConfiguration{}, + version: semver.MustParse("1.22.0"), + }, + want: "apiServer:\n" + + " timeoutForControlPlane: 30s\n" + + "apiVersion: kubeadm.k8s.io/v1beta3\n" + + "controllerManager: {}\n" + + "dns: {}\n" + + "etcd: {}\n" + + "kind: ClusterConfiguration\n" + + "networking: {}\n" + + "scheduler: {}\n", + wantErr: false, + }, { name: "Generates a v1beta4 kubeadm configuration", args: args{ @@ -210,7 +254,7 @@ func TestMarshalClusterConfigurationForVersion(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := MarshalClusterConfigurationForVersion(tt.args.capiObj, tt.args.version) + got, err := MarshalClusterConfigurationForVersion(tt.args.initConfiguration, tt.args.capiObj, tt.args.version) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return @@ -268,11 +312,9 @@ func TestMarshalClusterStatusForVersion(t *testing.T) { } func TestMarshalInitConfigurationForVersion(t *testing.T) { - timeout := metav1.Duration{Duration: 10 * time.Second} type args struct { - clusterConfiguration *bootstrapv1.ClusterConfiguration - initConfiguration *bootstrapv1.InitConfiguration - version semver.Version + initConfiguration *bootstrapv1.InitConfiguration + version semver.Version } tests := []struct { name string @@ -324,27 +366,8 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { NodeRegistration: bootstrapv1.NodeRegistrationOptions{ IgnorePreflightErrors: []string{"some-preflight-check"}, }, - }, - version: semver.MustParse("1.31.0"), - }, - want: "apiVersion: kubeadm.k8s.io/v1beta4\n" + - "kind: InitConfiguration\n" + - "localAPIEndpoint: {}\n" + - "nodeRegistration:\n" + - " ignorePreflightErrors:\n" + - " - some-preflight-check\n" + - " taints: null\n", - wantErr: false, - }, - { - name: "Generates a v1beta4 kubeadm init configuration with data from cluster configuration", - args: args{ - clusterConfiguration: &bootstrapv1.ClusterConfiguration{ - APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, - }, - initConfiguration: &bootstrapv1.InitConfiguration{ - NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - IgnorePreflightErrors: []string{"some-preflight-check"}, + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](50), }, }, version: semver.MustParse("1.31.0"), @@ -357,7 +380,7 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { " - some-preflight-check\n" + " taints: null\n" + "timeouts:\n" + - " controlPlaneComponentHealthCheck: 10s\n", + " controlPlaneComponentHealthCheck: 50s\n", wantErr: false, }, } @@ -365,7 +388,7 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := MarshalInitConfigurationForVersion(tt.args.clusterConfiguration, tt.args.initConfiguration, tt.args.version) + got, err := MarshalInitConfigurationForVersion(tt.args.initConfiguration, tt.args.version) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return @@ -377,11 +400,9 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { } func TestMarshalJoinConfigurationForVersion(t *testing.T) { - timeout := metav1.Duration{Duration: 10 * time.Second} type args struct { - clusterConfiguration *bootstrapv1.ClusterConfiguration - joinConfiguration *bootstrapv1.JoinConfiguration - version semver.Version + joinConfiguration *bootstrapv1.JoinConfiguration + version semver.Version } tests := []struct { name string @@ -433,27 +454,8 @@ func TestMarshalJoinConfigurationForVersion(t *testing.T) { NodeRegistration: bootstrapv1.NodeRegistrationOptions{ IgnorePreflightErrors: []string{"some-preflight-check"}, }, - }, - version: semver.MustParse("1.31.0"), - }, - want: "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + - "discovery: {}\n" + - "kind: JoinConfiguration\n" + - "nodeRegistration:\n" + - " ignorePreflightErrors:\n" + - " - some-preflight-check\n" + - " taints: null\n", - wantErr: false, - }, - { - name: "Generates a v1beta4 kubeadm join configuration with data from cluster configuration", - args: args{ - clusterConfiguration: &bootstrapv1.ClusterConfiguration{ - APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, - }, - joinConfiguration: &bootstrapv1.JoinConfiguration{ - NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - IgnorePreflightErrors: []string{"some-preflight-check"}, + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](50), }, }, version: semver.MustParse("1.31.0"), @@ -466,7 +468,7 @@ func TestMarshalJoinConfigurationForVersion(t *testing.T) { " - some-preflight-check\n" + " taints: null\n" + "timeouts:\n" + - " controlPlaneComponentHealthCheck: 10s\n", + " controlPlaneComponentHealthCheck: 50s\n", wantErr: false, }, } @@ -474,7 +476,7 @@ func TestMarshalJoinConfigurationForVersion(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := MarshalJoinConfigurationForVersion(tt.args.clusterConfiguration, tt.args.joinConfiguration, tt.args.version) + got, err := MarshalJoinConfigurationForVersion(tt.args.joinConfiguration, tt.args.version) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return @@ -490,10 +492,11 @@ func TestUnmarshalClusterConfiguration(t *testing.T) { yaml string } tests := []struct { - name string - args args - want *bootstrapv1.ClusterConfiguration - wantErr bool + name string + args args + want *bootstrapv1.ClusterConfiguration + wantInitConfiguration *bootstrapv1.InitConfiguration + wantErr bool }{ { name: "Parses a v1beta2 kubeadm configuration", @@ -507,7 +510,30 @@ func TestUnmarshalClusterConfiguration(t *testing.T) { "networking: {}\n" + "scheduler: {}\n", }, - want: &bootstrapv1.ClusterConfiguration{}, + want: &bootstrapv1.ClusterConfiguration{}, + wantInitConfiguration: &bootstrapv1.InitConfiguration{}, + wantErr: false, + }, + { + name: "Parses a v1beta2 kubeadm configuration with data to init configuration", + args: args{ + yaml: "apiServer: {\n" + + " timeoutForControlPlane: 50s\n" + + "}\n" + + "apiVersion: kubeadm.k8s.io/v1beta2\n" + "" + + "controllerManager: {}\n" + + "dns: {}\n" + + "etcd: {}\n" + + "kind: ClusterConfiguration\n" + + "networking: {}\n" + + "scheduler: {}\n", + }, + want: &bootstrapv1.ClusterConfiguration{}, + wantInitConfiguration: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](50), + }, + }, wantErr: false, }, { @@ -522,7 +548,30 @@ func TestUnmarshalClusterConfiguration(t *testing.T) { "networking: {}\n" + "scheduler: {}\n", }, - want: &bootstrapv1.ClusterConfiguration{}, + want: &bootstrapv1.ClusterConfiguration{}, + wantInitConfiguration: &bootstrapv1.InitConfiguration{}, + wantErr: false, + }, + { + name: "Parses a v1beta3 kubeadm configuration with data to init configuration", + args: args{ + yaml: "apiServer: {\n" + + " timeoutForControlPlane: 50s\n" + + "}\n" + + "apiVersion: kubeadm.k8s.io/v1beta3\n" + "" + + "controllerManager: {}\n" + + "dns: {}\n" + + "etcd: {}\n" + + "kind: ClusterConfiguration\n" + + "networking: {}\n" + + "scheduler: {}\n", + }, + want: &bootstrapv1.ClusterConfiguration{}, + wantInitConfiguration: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](50), + }, + }, wantErr: false, }, { @@ -538,21 +587,24 @@ func TestUnmarshalClusterConfiguration(t *testing.T) { "proxy: {}\n" + "scheduler: {}\n", }, - want: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, + want: &bootstrapv1.ClusterConfiguration{}, + wantInitConfiguration: &bootstrapv1.InitConfiguration{}, + wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := UnmarshalClusterConfiguration(tt.args.yaml) + gotInitConfiguration := &bootstrapv1.InitConfiguration{} + got, err := UnmarshalClusterConfiguration(tt.args.yaml, gotInitConfiguration) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return } g.Expect(err).ToNot(HaveOccurred()) g.Expect(got).To(BeComparableTo(tt.want), cmp.Diff(tt.want, got)) + g.Expect(gotInitConfiguration).To(BeComparableTo(tt.wantInitConfiguration), cmp.Diff(tt.wantInitConfiguration, gotInitConfiguration)) }) } } @@ -612,16 +664,14 @@ func TestUnmarshalClusterStatus(t *testing.T) { } func TestUnmarshalInitConfiguration(t *testing.T) { - timeout := metav1.Duration{Duration: 10 * time.Second} type args struct { yaml string } tests := []struct { - name string - args args - want *bootstrapv1.InitConfiguration - wantClusterConfiguration *bootstrapv1.ClusterConfiguration - wantErr bool + name string + args args + want *bootstrapv1.InitConfiguration + wantErr bool }{ { name: "Parses a v1beta2 kubeadm configuration", @@ -631,9 +681,8 @@ func TestUnmarshalInitConfiguration(t *testing.T) { "localAPIEndpoint: {}\n" + "nodeRegistration: {}\n", }, - want: &bootstrapv1.InitConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, + want: &bootstrapv1.InitConfiguration{}, + wantErr: false, }, { name: "Parses a v1beta3 kubeadm configuration", @@ -643,35 +692,24 @@ func TestUnmarshalInitConfiguration(t *testing.T) { "localAPIEndpoint: {}\n" + "nodeRegistration: {}\n", }, - want: &bootstrapv1.InitConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, + want: &bootstrapv1.InitConfiguration{}, + wantErr: false, }, { name: "Parses a v1beta4 kubeadm configuration", - args: args{ - yaml: "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + - "kind: InitConfiguration\n" + - "localAPIEndpoint: {}\n" + - "nodeRegistration: {}\n", - }, - want: &bootstrapv1.InitConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, - }, - { - name: "Parses a v1beta4 kubeadm configuration with data to cluster configuration", args: args{ yaml: "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + "kind: InitConfiguration\n" + "localAPIEndpoint: {}\n" + "nodeRegistration: {}\n" + - "timeouts:\n" + - " controlPlaneComponentHealthCheck: 10s\n", + "timeouts: {\n" + + " controlPlaneComponentHealthCheck: 50s\n" + + "}\n", }, - want: &bootstrapv1.InitConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{ - APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, + want: &bootstrapv1.InitConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](50), + }, }, wantErr: false, }, @@ -680,30 +718,26 @@ func TestUnmarshalInitConfiguration(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - gotClusterConfiguration := &bootstrapv1.ClusterConfiguration{} - got, err := UnmarshalInitConfiguration(tt.args.yaml, gotClusterConfiguration) + got, err := UnmarshalInitConfiguration(tt.args.yaml) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return } g.Expect(err).ToNot(HaveOccurred()) g.Expect(got).To(BeComparableTo(tt.want), cmp.Diff(tt.want, got)) - g.Expect(gotClusterConfiguration).To(BeComparableTo(tt.wantClusterConfiguration), cmp.Diff(tt.wantClusterConfiguration, gotClusterConfiguration)) }) } } func TestUnmarshalJoinConfiguration(t *testing.T) { - timeout := metav1.Duration{Duration: 10 * time.Second} type args struct { yaml string } tests := []struct { - name string - args args - want *bootstrapv1.JoinConfiguration - wantClusterConfiguration *bootstrapv1.ClusterConfiguration - wantErr bool + name string + args args + want *bootstrapv1.JoinConfiguration + wantErr bool }{ { name: "Parses a v1beta2 kubeadm configuration", @@ -713,9 +747,8 @@ func TestUnmarshalJoinConfiguration(t *testing.T) { "discovery: {}\n" + "kind: JoinConfiguration\n", }, - want: &bootstrapv1.JoinConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, + want: &bootstrapv1.JoinConfiguration{}, + wantErr: false, }, { name: "Parses a v1beta3 kubeadm configuration", @@ -725,35 +758,24 @@ func TestUnmarshalJoinConfiguration(t *testing.T) { "discovery: {}\n" + "kind: JoinConfiguration\n", }, - want: &bootstrapv1.JoinConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, + want: &bootstrapv1.JoinConfiguration{}, + wantErr: false, }, { name: "Parses a v1beta4 kubeadm configuration", - args: args{ - yaml: "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + - "caCertPath: \"\"\n" + - "discovery: {}\n" + - "kind: JoinConfiguration\n", - }, - want: &bootstrapv1.JoinConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, - wantErr: false, - }, - { - name: "Parses a v1beta4 kubeadm configuration with data to cluster configuration", args: args{ yaml: "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + "caCertPath: \"\"\n" + "discovery: {}\n" + "kind: JoinConfiguration\n" + - "timeouts:\n" + - " controlPlaneComponentHealthCheck: 10s\n", + "timeouts: {\n" + + " controlPlaneComponentHealthCheck: 50s\n" + + "}\n", }, - want: &bootstrapv1.JoinConfiguration{}, - wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{ - APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, + want: &bootstrapv1.JoinConfiguration{ + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](50), + }, }, wantErr: false, }, @@ -762,15 +784,13 @@ func TestUnmarshalJoinConfiguration(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - gotClusterConfiguration := &bootstrapv1.ClusterConfiguration{} - got, err := UnmarshalJoinConfiguration(tt.args.yaml, gotClusterConfiguration) + got, err := UnmarshalJoinConfiguration(tt.args.yaml) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return } g.Expect(err).ToNot(HaveOccurred()) g.Expect(got).To(BeComparableTo(tt.want), cmp.Diff(tt.want, got)) - g.Expect(gotClusterConfiguration).To(BeComparableTo(tt.wantClusterConfiguration), cmp.Diff(tt.wantClusterConfiguration, gotClusterConfiguration)) }) } } diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml index 58b9771a8bb8..45f87bb07a57 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml @@ -5069,11 +5069,38 @@ spec: maxItems: 100 type: array extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to pass - to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -5239,10 +5266,6 @@ spec: type: object maxItems: 100 type: array - timeoutForControlPlane: - description: timeoutForControlPlane controls the timeout - that we use for API server to appear - type: string type: object apiVersion: description: |- @@ -5285,11 +5308,38 @@ spec: the controller manager control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to pass - to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -5534,15 +5584,41 @@ spec: minLength: 1 type: string extraArgs: - additionalProperties: - type: string description: |- - extraArgs are extra arguments provided to the etcd binary - when run inside a static pod. - type: object + extraArgs is a list of args to pass to etcd. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- - extraEnvs is an extra set of environment variables to pass to the control plane component. + extraEnvs is an extra set of environment variables to pass to etcd. Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. This option takes effect only on Kubernetes >=1.31.0. items: @@ -5771,11 +5847,38 @@ spec: control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags to pass - to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -6256,9 +6359,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a slice of - pre-flight errors to be ignored when the current node - is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -6284,13 +6387,38 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -6368,6 +6496,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that apply to + kubeadm commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object joinConfiguration: description: joinConfiguration is the kubeadm configuration for @@ -6383,7 +6564,7 @@ spec: caCertPath: description: |- caCertPath is the path to the SSL certificate authority used to - secure comunications between node and control-plane. + secure communications between node and control-plane. Defaults to "/etc/kubernetes/pki/ca.crt". maxLength: 512 minLength: 1 @@ -6627,9 +6808,6 @@ spec: required: - kubeConfigPath type: object - timeout: - description: timeout modifies the discovery timeout - type: string tlsBootstrapToken: description: |- tlsBootstrapToken is a token used for TLS bootstrapping. @@ -6661,9 +6839,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a slice of - pre-flight errors to be ignored when the current node - is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -6689,13 +6867,38 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with a name + and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -6773,6 +6976,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that apply to + kubeadm commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object mounts: description: mounts specifies a list of mount points to be setup. diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml index 4e6af06e0457..b97cd9057957 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml @@ -3429,11 +3429,39 @@ spec: maxItems: 100 type: array extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags - to pass to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with + a name and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -3606,10 +3634,6 @@ spec: type: object maxItems: 100 type: array - timeoutForControlPlane: - description: timeoutForControlPlane controls the - timeout that we use for API server to appear - type: string type: object apiVersion: description: |- @@ -3652,11 +3676,39 @@ spec: for the controller manager control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags - to pass to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with + a name and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -3908,15 +3960,44 @@ spec: minLength: 1 type: string extraArgs: - additionalProperties: - type: string description: |- - extraArgs are extra arguments provided to the etcd binary - when run inside a static pod. - type: object + extraArgs is a list of args to pass to etcd. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument + with a name and a value. + properties: + name: + description: name is the Name of the + extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the + extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) extraEnvs: description: |- - extraEnvs is an extra set of environment variables to pass to the control plane component. + extraEnvs is an extra set of environment variables to pass to etcd. Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. This option takes effect only on Kubernetes >=1.31.0. items: @@ -4153,11 +4234,39 @@ spec: the scheduler control plane component properties: extraArgs: - additionalProperties: - type: string - description: extraArgs is an extra set of flags - to pass to the control plane component. - type: object + description: |- + extraArgs is a list of args to pass to the control plane component. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with + a name and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: extraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) extraEnvs: description: |- extraEnvs is an extra set of environment variables to pass to the control plane component. @@ -4652,9 +4761,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a - slice of pre-flight errors to be ignored when - the current node is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -4680,13 +4789,39 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with + a name and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -4764,6 +4899,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that + apply to kubeadm commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object joinConfiguration: description: joinConfiguration is the kubeadm configuration @@ -4779,7 +4967,7 @@ spec: caCertPath: description: |- caCertPath is the path to the SSL certificate authority used to - secure comunications between node and control-plane. + secure communications between node and control-plane. Defaults to "/etc/kubernetes/pki/ca.crt". maxLength: 512 minLength: 1 @@ -5028,9 +5216,6 @@ spec: required: - kubeConfigPath type: object - timeout: - description: timeout modifies the discovery timeout - type: string tlsBootstrapToken: description: |- tlsBootstrapToken is a token used for TLS bootstrapping. @@ -5062,9 +5247,9 @@ spec: minLength: 1 type: string ignorePreflightErrors: - description: ignorePreflightErrors provides a - slice of pre-flight errors to be ignored when - the current node is registered. + description: |- + ignorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. + Value 'all' ignores errors from all checks. items: maxLength: 512 minLength: 1 @@ -5090,13 +5275,39 @@ spec: Default: true (defaulted in kubeadm) type: boolean kubeletExtraArgs: - additionalProperties: - type: string description: |- - kubeletExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file - kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap - Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. - type: object + kubeletExtraArgs is a list of args to pass to kubelet. + The arg name must match the command line flag name except without leading dash(es). + Extra arguments will override existing default arguments set by kubeadm. + items: + description: Arg represents an argument with + a name and a value. + properties: + name: + description: name is the Name of the extraArg. + maxLength: 256 + minLength: 1 + type: string + value: + description: value is the Value of the extraArg. + maxLength: 1024 + minLength: 0 + type: string + required: + - name + - value + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + - value + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: kubeletExtraArgs name must be unique + rule: self.all(x, self.exists_one(y, x.name + == y.name)) name: description: |- name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. @@ -5174,6 +5385,59 @@ spec: type: string maxItems: 50 type: array + timeouts: + description: timeouts holds various timeouts that + apply to kubeadm commands. + properties: + controlPlaneComponentHealthCheckSeconds: + description: |- + controlPlaneComponentHealthCheckSeconds is the amount of time to wait for a control plane + component, such as the API server, to be healthy during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + discoverySeconds: + description: |- + discoverySeconds is the amount of time to wait for kubeadm to validate the API server identity + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + etcdAPICallSeconds: + description: |- + etcdAPICallSeconds is the amount of time to wait for the kubeadm etcd client to complete a request to + the etcd cluster. + If not set, it defaults to 2m (120s). + format: int32 + minimum: 0 + type: integer + kubeletHealthCheckSeconds: + description: |- + kubeletHealthCheckSeconds is the amount of time to wait for the kubelet to be healthy + during "kubeadm init" and "kubeadm join". + If not set, it defaults to 4m (240s). + format: int32 + minimum: 0 + type: integer + kubernetesAPICallSeconds: + description: |- + kubernetesAPICallSeconds is the amount of time to wait for the kubeadm client to complete a request to + the API server. This applies to all types of methods (GET, POST, etc). + If not set, it defaults to 1m (60s). + format: int32 + minimum: 0 + type: integer + tlsBootstrapSeconds: + description: |- + tlsBootstrapSeconds is the amount of time to wait for the kubelet to complete TLS bootstrap + for a joining node. + If not set, it defaults to 5m (300s). + format: int32 + minimum: 0 + type: integer + type: object type: object mounts: description: mounts specifies a list of mount points to diff --git a/controlplane/kubeadm/internal/controllers/helpers.go b/controlplane/kubeadm/internal/controllers/helpers.go index f10e4720dcd3..bb21d29e5326 100644 --- a/controlplane/kubeadm/internal/controllers/helpers.go +++ b/controlplane/kubeadm/internal/controllers/helpers.go @@ -18,7 +18,6 @@ package controllers import ( "context" - "encoding/json" "strings" "github.com/pkg/errors" @@ -385,11 +384,11 @@ func (r *KubeadmControlPlaneReconciler) computeDesiredMachine(kcp *controlplanev // Machine's bootstrap config may be missing ClusterConfiguration if it is not the first machine in the control plane. // We store ClusterConfiguration as annotation here to detect any changes in KCP ClusterConfiguration and rollout the machine if any. // Nb. This annotation is read when comparing the KubeadmConfig to check if a machine needs to be rolled out. - clusterConfig, err := json.Marshal(kcp.Spec.KubeadmConfigSpec.ClusterConfiguration) + clusterConfigurationAnnotation, err := internal.ClusterConfigurationToMachineAnnotationValue(kcp.Spec.KubeadmConfigSpec.ClusterConfiguration) if err != nil { - return nil, errors.Wrap(err, "failed to marshal cluster configuration") + return nil, err } - annotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = string(clusterConfig) + annotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = clusterConfigurationAnnotation // In case this machine is being created as a consequence of a remediation, then add an annotation // tracking remediating data. @@ -406,8 +405,20 @@ func (r *KubeadmControlPlaneReconciler) computeDesiredMachine(kcp *controlplanev // For existing machine only set the ClusterConfiguration annotation if the machine already has it. // We should not add the annotation if it was missing in the first place because we do not have enough // information. - if clusterConfig, ok := existingMachine.Annotations[controlplanev1.KubeadmClusterConfigurationAnnotation]; ok { - annotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = clusterConfig + if clusterConfigurationAnnotation, ok := existingMachine.Annotations[controlplanev1.KubeadmClusterConfigurationAnnotation]; ok { + // In case the annotation is outdated, update it. + if internal.ClusterConfigurationAnnotationFromMachineIsOutdated(clusterConfigurationAnnotation) { + clusterConfiguration, err := internal.ClusterConfigurationFromMachine(existingMachine) + if err != nil { + return nil, err + } + + clusterConfigurationAnnotation, err = internal.ClusterConfigurationToMachineAnnotationValue(clusterConfiguration) + if err != nil { + return nil, err + } + } + annotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = clusterConfigurationAnnotation } // If the machine already has remediation data then preserve it. diff --git a/controlplane/kubeadm/internal/controllers/helpers_test.go b/controlplane/kubeadm/internal/controllers/helpers_test.go index 02faa9c86ff2..903892eaef02 100644 --- a/controlplane/kubeadm/internal/controllers/helpers_test.go +++ b/controlplane/kubeadm/internal/controllers/helpers_test.go @@ -498,8 +498,6 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { } kcpMachineTemplateObjectMetaCopy := kcpMachineTemplateObjectMeta.DeepCopy() - clusterConfigurationString := "{\"etcd\":{},\"networking\":{},\"apiServer\":{},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}" - infraRef := &corev1.ObjectReference{ Kind: "InfraKind", APIVersion: clusterv1.GroupVersionInfrastructure.String(), @@ -514,11 +512,13 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { } tests := []struct { - name string - kcp *controlplanev1.KubeadmControlPlane - isUpdatingExistingMachine bool - want []gomegatypes.GomegaMatcher - wantErr bool + name string + kcp *controlplanev1.KubeadmControlPlane + isUpdatingExistingMachine bool + existingClusterConfigurationAnnotation string + want []gomegatypes.GomegaMatcher + wantClusterConfigurationAnnotation string + wantErr bool }{ { name: "should return the correct Machine object when creating a new Machine", @@ -553,7 +553,8 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { HavePrefix(kcpName + namingTemplateKey), Not(HaveSuffix("00000")), }, - wantErr: false, + wantClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantErr: false, }, { name: "should return error when creating a new Machine when '.random' is not added in template", @@ -613,7 +614,8 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { ContainSubstring(fmt.Sprintf("%053d", 0)), Not(HaveSuffix("00000")), }, - wantErr: false, + wantClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantErr: false, }, { name: "should return error when creating a new Machine with invalid template", @@ -671,6 +673,7 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { HavePrefix(kcpName), Not(HaveSuffix("00000")), }, + wantClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", }, { name: "should return the correct Machine object when creating a new Machine with additional kcp readinessGates", @@ -695,11 +698,45 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { }, }, }, - isUpdatingExistingMachine: false, - wantErr: false, + isUpdatingExistingMachine: false, + wantClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantErr: false, + }, + { + name: "should return the correct Machine object when updating an existing Machine (empty ClusterConfiguration annotation)", + kcp: &controlplanev1.KubeadmControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: kcpName, + Namespace: cluster.Namespace, + }, + Spec: controlplanev1.KubeadmControlPlaneSpec{ + Version: "v1.16.6", + MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{ + ObjectMeta: kcpMachineTemplateObjectMeta, + NodeDrainTimeout: duration5s, + NodeDeletionTimeout: duration5s, + NodeVolumeDetachTimeout: duration5s, + ReadinessGates: []clusterv1.MachineReadinessGate{ + {ConditionType: "Foo"}, + }, + }, + KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{ + ClusterConfiguration: &bootstrapv1.ClusterConfiguration{ + ClusterName: clusterName, + }, + }, + MachineNamingStrategy: &controlplanev1.MachineNamingStrategy{ + Template: "{{ .kubeadmControlPlane.name }}" + namingTemplateKey + "-{{ .random }}", + }, + }, + }, + isUpdatingExistingMachine: true, + existingClusterConfigurationAnnotation: "", + wantClusterConfigurationAnnotation: "", + wantErr: false, }, { - name: "should return the correct Machine object when updating an existing Machine", + name: "should return the correct Machine object when updating an existing Machine (outdated ClusterConfiguration annotation)", kcp: &controlplanev1.KubeadmControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: kcpName, @@ -727,7 +764,43 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { }, }, isUpdatingExistingMachine: true, - wantErr: false, + + existingClusterConfigurationAnnotation: "{\"etcd\":{},\"networking\":{},\"apiServer\":{\"extraArgs\":{\"foo\":\"bar\"}},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantErr: false, + }, + { + name: "should return the correct Machine object when updating an existing Machine (up to date ClusterConfiguration annotation)", + kcp: &controlplanev1.KubeadmControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: kcpName, + Namespace: cluster.Namespace, + }, + Spec: controlplanev1.KubeadmControlPlaneSpec{ + Version: "v1.16.6", + MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{ + ObjectMeta: kcpMachineTemplateObjectMeta, + NodeDrainTimeout: duration5s, + NodeDeletionTimeout: duration5s, + NodeVolumeDetachTimeout: duration5s, + ReadinessGates: []clusterv1.MachineReadinessGate{ + {ConditionType: "Foo"}, + }, + }, + KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{ + ClusterConfiguration: &bootstrapv1.ClusterConfiguration{ + ClusterName: clusterName, + }, + }, + MachineNamingStrategy: &controlplanev1.MachineNamingStrategy{ + Template: "{{ .kubeadmControlPlane.name }}" + namingTemplateKey + "-{{ .random }}", + }, + }, + }, + isUpdatingExistingMachine: true, + existingClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantClusterConfigurationAnnotation: "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{},\"clusterName\":\"testCluster\"}", + wantErr: false, }, } @@ -745,7 +818,6 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { machineUID := types.UID("abc-123-existing-machine") // Use different ClusterConfiguration string than the information present in KCP // to verify that for an existing machine we do not override this information. - existingClusterConfigurationString := "existing-cluster-configuration-information" remediationData := "remediation-data" machineVersion := ptr.To("v1.25.3") existingMachine := &clusterv1.Machine{ @@ -753,8 +825,7 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { Name: machineName, UID: machineUID, Annotations: map[string]string{ - controlplanev1.KubeadmClusterConfigurationAnnotation: existingClusterConfigurationString, - controlplanev1.RemediationForAnnotation: remediationData, + controlplanev1.RemediationForAnnotation: remediationData, }, }, Spec: clusterv1.MachineSpec{ @@ -770,6 +841,10 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { ReadinessGates: []clusterv1.MachineReadinessGate{{ConditionType: "Foo"}}, }, } + if tt.existingClusterConfigurationAnnotation != "" { + existingMachine.Annotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = tt.existingClusterConfigurationAnnotation + } + desiredMachine, err = (&KubeadmControlPlaneReconciler{}).computeDesiredMachine( tt.kcp, cluster, existingMachine.Spec.FailureDomain, existingMachine, @@ -801,7 +876,9 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { for k, v := range kcpMachineTemplateObjectMeta.Annotations { expectedAnnotations[k] = v } - expectedAnnotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = existingClusterConfigurationString + if tt.wantClusterConfigurationAnnotation != "" { + expectedAnnotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = tt.wantClusterConfigurationAnnotation + } expectedAnnotations[controlplanev1.RemediationForAnnotation] = remediationData // The pre-terminate annotation should always be added expectedAnnotations[controlplanev1.PreTerminateHookCleanupAnnotation] = "" @@ -835,7 +912,7 @@ func TestKubeadmControlPlaneReconciler_computeDesiredMachine(t *testing.T) { for k, v := range kcpMachineTemplateObjectMeta.Annotations { expectedAnnotations[k] = v } - expectedAnnotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = clusterConfigurationString + expectedAnnotations[controlplanev1.KubeadmClusterConfigurationAnnotation] = tt.wantClusterConfigurationAnnotation // The pre-terminate annotation should always be added expectedAnnotations[controlplanev1.PreTerminateHookCleanupAnnotation] = "" g.Expect(desiredMachine.Annotations).To(Equal(expectedAnnotations)) diff --git a/controlplane/kubeadm/internal/filters.go b/controlplane/kubeadm/internal/filters.go index de38919d9bed..2b0544cfcb06 100644 --- a/controlplane/kubeadm/internal/filters.go +++ b/controlplane/kubeadm/internal/filters.go @@ -20,11 +20,14 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/klog/v2" + bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" @@ -197,18 +200,17 @@ func matchesKubeadmBootstrapConfig(machineConfigs map[string]*bootstrapv1.Kubead // made in KCP's ClusterConfiguration given that we don't have enough information to make a decision. // Users should use KCP.Spec.RolloutAfter field to force a rollout in this case. func matchClusterConfiguration(kcp *controlplanev1.KubeadmControlPlane, machine *clusterv1.Machine) (bool, string, error) { - machineClusterConfigStr, ok := machine.GetAnnotations()[controlplanev1.KubeadmClusterConfigurationAnnotation] - if !ok { + if _, ok := machine.GetAnnotations()[controlplanev1.KubeadmClusterConfigurationAnnotation]; !ok { // We don't have enough information to make a decision; don't' trigger a roll out. return true, "", nil } - machineClusterConfig := &bootstrapv1.ClusterConfiguration{} - // ClusterConfiguration annotation is not correct, only solution is to rollout. - // The call to json.Unmarshal has to take a pointer to the pointer struct defined above, - // otherwise we won't be able to handle a nil ClusterConfiguration (that is serialized into "null"). - // See https://github.com/kubernetes-sigs/cluster-api/issues/3353. - if err := json.Unmarshal([]byte(machineClusterConfigStr), &machineClusterConfig); err != nil { + machineClusterConfig, err := ClusterConfigurationFromMachine(machine) + if err != nil { + // ClusterConfiguration annotation is not correct, only solution is to rollout. + // The call to json.Unmarshal has to take a pointer to the pointer struct defined above, + // otherwise we won't be able to handle a nil ClusterConfiguration (that is serialized into "null"). + // See https://github.com/kubernetes-sigs/cluster-api/issues/3353. return false, "", nil //nolint:nilerr // Intentionally not returning the error here } @@ -233,6 +235,69 @@ func matchClusterConfiguration(kcp *controlplanev1.KubeadmControlPlane, machine return match, diff, nil } +type versionedClusterConfiguration struct { + MarshalVersion string `json:"marshalVersion,omitempty"` + *bootstrapv1.ClusterConfiguration +} + +// ClusterConfigurationAnnotationFromMachineIsOutdated return true if the annotation is outdated. +// Note: this is intentionally implemented with a string check to prevent an additional json.Unmarshal operation. +func ClusterConfigurationAnnotationFromMachineIsOutdated(annotation string) bool { + return !strings.Contains(annotation, fmt.Sprintf("\"marshalVersion\":%q", bootstrapv1.GroupVersion.Version)) +} + +// ClusterConfigurationToMachineAnnotationValue returns an annotation valued to add on machines for +// tracking the ClusterConfiguration value at the time the machine was created. +func ClusterConfigurationToMachineAnnotationValue(clusterConfiguration *bootstrapv1.ClusterConfiguration) (string, error) { + machineClusterConfig := &versionedClusterConfiguration{ + MarshalVersion: bootstrapv1.GroupVersion.Version, + ClusterConfiguration: clusterConfiguration, + } + + annotationBytes, err := json.Marshal(machineClusterConfig) + if err != nil { + return "", errors.Wrap(err, "failed to marshal cluster configuration") + } + return string(annotationBytes), nil +} + +// ClusterConfigurationFromMachine returns the ClusterConfiguration value at the time the machine was created. +// Note: In case the annotation was created with an older version of the KCP API, the value is converted to the current API version. +func ClusterConfigurationFromMachine(machine *clusterv1.Machine) (*bootstrapv1.ClusterConfiguration, error) { + machineClusterConfigStr, ok := machine.GetAnnotations()[controlplanev1.KubeadmClusterConfigurationAnnotation] + if !ok { + return nil, nil + } + + if ClusterConfigurationAnnotationFromMachineIsOutdated(machineClusterConfigStr) { + // Note: Only conversion from v1beta1 is supported as of today. + machineClusterConfigV1Beta1 := &bootstrapv1beta1.ClusterConfiguration{} + if err := json.Unmarshal([]byte(machineClusterConfigStr), &machineClusterConfigV1Beta1); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal ClusterConfiguration from Machine %s", klog.KObj(machine)) + } + + kubeadmConfigV1Beta1 := &bootstrapv1beta1.KubeadmConfig{ + Spec: bootstrapv1beta1.KubeadmConfigSpec{ + ClusterConfiguration: machineClusterConfigV1Beta1, + }, + } + kubeadmConfig := &bootstrapv1.KubeadmConfig{} + err := kubeadmConfigV1Beta1.ConvertTo(kubeadmConfig) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert ClusterConfiguration from Machine %s", klog.KObj(machine)) + } + + machineClusterConfig := kubeadmConfig.Spec.ClusterConfiguration + return machineClusterConfig, nil + } + + machineClusterConfig := &versionedClusterConfiguration{} + if err := json.Unmarshal([]byte(machineClusterConfigStr), &machineClusterConfig); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal ClusterConfiguration from Machine %s", klog.KObj(machine)) + } + return machineClusterConfig.ClusterConfiguration, nil +} + // matchInitOrJoinConfiguration verifies if KCP and machine InitConfiguration or JoinConfiguration matches. // NOTE: By extension this method takes care of detecting changes in other fields of the KubeadmConfig configuration (e.g. Files, Mounts etc.) func matchInitOrJoinConfiguration(machineConfig *bootstrapv1.KubeadmConfig, kcp *controlplanev1.KubeadmControlPlane) (bool, string, error) { diff --git a/controlplane/kubeadm/internal/filters_test.go b/controlplane/kubeadm/internal/filters_test.go index 54b9d3939c99..43962356e36b 100644 --- a/controlplane/kubeadm/internal/filters_test.go +++ b/controlplane/kubeadm/internal/filters_test.go @@ -17,7 +17,7 @@ limitations under the License. package internal import ( - "encoding/json" + "fmt" "testing" "time" @@ -32,6 +32,99 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ) +func TestClusterConfigurationAnnotation(t *testing.T) { + t.Run("ClusterConfigurationToMachineAnnotationValue", func(t *testing.T) { + g := NewWithT(t) + kcp := &controlplanev1.KubeadmControlPlane{ + Spec: controlplanev1.KubeadmControlPlaneSpec{ + KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{ + ClusterConfiguration: &bootstrapv1.ClusterConfiguration{ + APIServer: bootstrapv1.APIServer{ + ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, + }, + }, + KubernetesVersion: "v1.33.0", + }, + }, + }, + } + + annotations, err := ClusterConfigurationToMachineAnnotationValue(kcp.Spec.KubeadmConfigSpec.ClusterConfiguration) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(annotations).To(Equal("{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"kubernetesVersion\":\"v1.33.0\",\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{}}")) + }) + t.Run("ClusterConfigurationFromMachineIsOutdated", func(t *testing.T) { + g := NewWithT(t) + + // Without annotation. + annotation := "" + g.Expect(ClusterConfigurationAnnotationFromMachineIsOutdated(annotation)).To(BeTrue()) + + // v1beta1 annotation (without marshalVersion) + annotation = "{\"etcd\":{},\"networking\":{},\"kubernetesVersion\":\"v1.33.0\",\"apiServer\":{\"extraArgs\":{\"foo\":\"bar\"}},\"controllerManager\":{},\"scheduler\":{},\"dns\":{}}" + g.Expect(ClusterConfigurationAnnotationFromMachineIsOutdated(annotation)).To(BeTrue()) + + // up to date annotation (marshalVersion equal to current version) + annotation = fmt.Sprintf("{\"marshalVersion\":%q,\"etcd\":{},\"networking\":{},\"kubernetesVersion\":\"v1.33.0\",\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{}}", bootstrapv1.GroupVersion.Version) + g.Expect(ClusterConfigurationAnnotationFromMachineIsOutdated(annotation)).To(BeFalse()) + + // marshalVersion not equal to the current version (this should not happen because marshalVersion has been introduced with the v1beta2 API) + annotation = "{\"marshalVersion\":\"foo\",\"etcd\":{},\"networking\":{},\"kubernetesVersion\":\"v1.33.0\",\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{}}" + g.Expect(ClusterConfigurationAnnotationFromMachineIsOutdated(annotation)).To(BeTrue()) + }) + t.Run("ClusterConfigurationFromMachine", func(t *testing.T) { + g := NewWithT(t) + m1 := &clusterv1.Machine{} + + // Without annotation. + clusterConfiguration, err := ClusterConfigurationFromMachine(m1) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(clusterConfiguration).To(BeNil()) + + // v1beta1 annotation (without marshalVersion) + m1.SetAnnotations(map[string]string{controlplanev1.KubeadmClusterConfigurationAnnotation: "{\"etcd\":{},\"networking\":{},\"kubernetesVersion\":\"v1.33.0\",\"apiServer\":{\"extraArgs\":{\"foo\":\"bar\"}},\"controllerManager\":{},\"scheduler\":{},\"dns\":{}}"}) + clusterConfiguration, err = ClusterConfigurationFromMachine(m1) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(clusterConfiguration).To(Equal(&bootstrapv1.ClusterConfiguration{ + APIServer: bootstrapv1.APIServer{ + ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ + ExtraArgs: []bootstrapv1.Arg{ // Extra args converted from old format to new format. + { + Name: "foo", + Value: "bar", + }, + }, + }, + }, + KubernetesVersion: "v1.33.0", + })) + + // up to date annotation (marshalVersion equal to current version) + m1.SetAnnotations(map[string]string{controlplanev1.KubeadmClusterConfigurationAnnotation: fmt.Sprintf("{\"marshalVersion\":%q,\"etcd\":{},\"networking\":{},\"kubernetesVersion\":\"v1.33.0\",\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{},\"scheduler\":{},\"dns\":{}}", bootstrapv1.GroupVersion.Version)}) + clusterConfiguration, err = ClusterConfigurationFromMachine(m1) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(clusterConfiguration).To(Equal(&bootstrapv1.ClusterConfiguration{ + APIServer: bootstrapv1.APIServer{ + ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, + }, + }, + KubernetesVersion: "v1.33.0", + })) + }) +} + func TestMatchClusterConfiguration(t *testing.T) { t.Run("machine without the ClusterConfiguration annotation should match (not enough information to make a decision)", func(t *testing.T) { g := NewWithT(t) @@ -164,14 +257,29 @@ func TestMatchClusterConfiguration(t *testing.T) { ClusterConfiguration: &bootstrapv1.ClusterConfiguration{ APIServer: bootstrapv1.APIServer{ ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{"foo": "bar"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, }, }, ControllerManager: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{"foo": "bar"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, }, Scheduler: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{"foo": "bar"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, }, DNS: bootstrapv1.DNS{ ImageMeta: bootstrapv1.ImageMeta{ @@ -187,18 +295,18 @@ func TestMatchClusterConfiguration(t *testing.T) { // This is a point in time snapshot of how a serialized ClusterConfiguration looks like; // we are hardcoding this in the test so we can detect if a change in the API impacts serialization. // NOTE: changes in the json representation do not always trigger a rollout in KCP, but they are an heads up that should be investigated. - clusterConfigCheckPoint := []byte("{\"etcd\":{},\"networking\":{},\"apiServer\":{\"extraArgs\":{\"foo\":\"bar\"}},\"controllerManager\":{\"extraArgs\":{\"foo\":\"bar\"}},\"scheduler\":{\"extraArgs\":{\"foo\":\"bar\"}},\"dns\":{\"imageRepository\":\"gcr.io/capi-test\",\"imageTag\":\"v1.10.1\"}}") + annotationsCheckPoint := "{\"marshalVersion\":\"v1beta2\",\"etcd\":{},\"networking\":{},\"apiServer\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"controllerManager\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"scheduler\":{\"extraArgs\":[{\"name\":\"foo\",\"value\":\"bar\"}]},\"dns\":{\"imageRepository\":\"gcr.io/capi-test\",\"imageTag\":\"v1.10.1\"}}" // compute how a serialized ClusterConfiguration looks like now - clusterConfig, err := json.Marshal(kcp.Spec.KubeadmConfigSpec.ClusterConfiguration) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(clusterConfig).To(Equal(clusterConfigCheckPoint)) + annotations, err := ClusterConfigurationToMachineAnnotationValue(kcp.Spec.KubeadmConfigSpec.ClusterConfiguration) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(annotations).To(Equal(annotationsCheckPoint)) // check the match function detects if a Machine with the annotation string above matches the object it originates from (round trip). m := &clusterv1.Machine{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - controlplanev1.KubeadmClusterConfigurationAnnotation: string(clusterConfig), + controlplanev1.KubeadmClusterConfigurationAnnotation: annotationsCheckPoint, }, }, } @@ -546,7 +654,7 @@ func TestMatchInitOrJoinConfiguration(t *testing.T) { }, LocalAPIEndpoint: {}, SkipPhases: nil, - Patches: nil, + ... // 2 identical fields }, JoinConfiguration: nil, Files: nil, @@ -671,7 +779,7 @@ func TestMatchInitOrJoinConfiguration(t *testing.T) { }, CACertPath: "", Discovery: {}, - ... // 3 identical fields + ... // 4 identical fields }, Files: nil, DiskSetup: nil, @@ -918,7 +1026,7 @@ func TestMatchesKubeadmBootstrapConfig(t *testing.T) { }, LocalAPIEndpoint: {}, SkipPhases: nil, - Patches: nil, + ... // 2 identical fields }, JoinConfiguration: nil, Files: nil, @@ -1043,7 +1151,7 @@ func TestMatchesKubeadmBootstrapConfig(t *testing.T) { }, CACertPath: "", Discovery: {}, - ... // 3 identical fields + ... // 4 identical fields }, Files: nil, DiskSetup: nil, diff --git a/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go b/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go index bd5a21fb4293..3c19542348e5 100644 --- a/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go +++ b/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane.go @@ -154,6 +154,7 @@ const ( ignition = "ignition" diskSetup = "diskSetup" featureGates = "featureGates" + timeouts = "timeouts" ) const minimumCertificatesExpiryDays = 7 @@ -190,6 +191,8 @@ func (webhook *KubeadmControlPlane) ValidateUpdate(_ context.Context, oldObj, ne {spec, kubeadmConfigSpec, initConfiguration, "bootstrapTokens"}, {spec, kubeadmConfigSpec, initConfiguration, "localAPIEndpoint"}, {spec, kubeadmConfigSpec, initConfiguration, "localAPIEndpoint", "*"}, + {spec, kubeadmConfigSpec, initConfiguration, timeouts}, + {spec, kubeadmConfigSpec, initConfiguration, timeouts, "*"}, // spec.kubeadmConfigSpec.joinConfiguration {spec, kubeadmConfigSpec, joinConfiguration, nodeRegistration}, {spec, kubeadmConfigSpec, joinConfiguration, nodeRegistration, "*"}, @@ -201,6 +204,8 @@ func (webhook *KubeadmControlPlane) ValidateUpdate(_ context.Context, oldObj, ne {spec, kubeadmConfigSpec, joinConfiguration, "controlPlane", "*"}, {spec, kubeadmConfigSpec, joinConfiguration, "discovery"}, {spec, kubeadmConfigSpec, joinConfiguration, "discovery", "*"}, + {spec, kubeadmConfigSpec, joinConfiguration, timeouts}, + {spec, kubeadmConfigSpec, joinConfiguration, timeouts, "*"}, // spec.kubeadmConfigSpec {spec, kubeadmConfigSpec, bootCommands}, {spec, kubeadmConfigSpec, preKubeadmCommands}, diff --git a/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane_test.go b/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane_test.go index 463bfd6eb801..8efbfaab71c9 100644 --- a/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane_test.go +++ b/controlplane/kubeadm/internal/webhooks/kubeadm_control_plane_test.go @@ -171,6 +171,13 @@ func TestKubeadmControlPlaneValidateCreate(t *testing.T) { "/invalid-key": "foo", } + invalidControlPlaneComponentHealthCheckSeconds := valid.DeepCopy() + invalidControlPlaneComponentHealthCheckSeconds.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{Timeouts: &bootstrapv1.Timeouts{ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10)}} + + validControlPlaneComponentHealthCheckSeconds := valid.DeepCopy() + validControlPlaneComponentHealthCheckSeconds.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{Timeouts: &bootstrapv1.Timeouts{ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10)}} + validControlPlaneComponentHealthCheckSeconds.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{Timeouts: &bootstrapv1.Timeouts{ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10)}} + tests := []struct { name string enableIgnitionFeature bool @@ -261,6 +268,15 @@ func TestKubeadmControlPlaneValidateCreate(t *testing.T) { expectErr: true, kcp: invalidMetadata, }, + { + name: "should return error for invalid Timeouts.ControlPlaneComponentHealthCheckSeconds", + expectErr: true, + kcp: invalidControlPlaneComponentHealthCheckSeconds, + }, + { + name: "should pass for valid Timeouts.ControlPlaneComponentHealthCheckSeconds", + kcp: validControlPlaneComponentHealthCheckSeconds, + }, } for _, tt := range tests { @@ -322,6 +338,10 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) { NodeRegistration: bootstrapv1.NodeRegistrationOptions{ Name: "test", }, + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10), + KubeletHealthCheckSeconds: ptr.To[int32](40), + }, }, ClusterConfiguration: &bootstrapv1.ClusterConfiguration{ ClusterName: "test", @@ -333,14 +353,13 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) { }, }, JoinConfiguration: &bootstrapv1.JoinConfiguration{ - Discovery: bootstrapv1.Discovery{ - Timeout: &metav1.Duration{ - Duration: 10 * time.Minute, - }, - }, NodeRegistration: bootstrapv1.NodeRegistrationOptions{ Name: "test", }, + Timeouts: &bootstrapv1.Timeouts{ + ControlPlaneComponentHealthCheckSeconds: ptr.To[int32](10), + KubeletHealthCheckSeconds: ptr.To[int32](40), + }, }, PreKubeadmCommands: []string{ "test", "foo", @@ -491,22 +510,36 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) { apiServer := before.DeepCopy() apiServer.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer = bootstrapv1.APIServer{ ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{"foo": "bar"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, ExtraVolumes: []bootstrapv1.HostPathMount{{Name: "mount1"}}, }, - TimeoutForControlPlane: &metav1.Duration{Duration: 5 * time.Minute}, - CertSANs: []string{"foo", "bar"}, + CertSANs: []string{"foo", "bar"}, } controllerManager := before.DeepCopy() controllerManager.Spec.KubeadmConfigSpec.ClusterConfiguration.ControllerManager = bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{"controller manager field": "controller manager value"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "controller manager field", + Value: "controller manager value", + }, + }, ExtraVolumes: []bootstrapv1.HostPathMount{{Name: "mount", HostPath: "/foo", MountPath: "bar", ReadOnly: true, PathType: "File"}}, } scheduler := before.DeepCopy() scheduler.Spec.KubeadmConfigSpec.ClusterConfiguration.Scheduler = bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{"scheduler field": "scheduler value"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "scheduler field", + Value: "scheduler value", + }, + }, ExtraVolumes: []bootstrapv1.HostPathMount{{Name: "mount", HostPath: "/foo", MountPath: "bar", ReadOnly: true, PathType: "File"}}, } @@ -611,7 +644,12 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) { localExtraArgs := before.DeepCopy() localExtraArgs.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local = &bootstrapv1.LocalEtcd{ - ExtraArgs: map[string]string{"an arg": "a value"}, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "an arg", + Value: "a value", + }, + }, } beforeExternalEtcdCluster := before.DeepCopy() @@ -718,6 +756,18 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) { "/invalid-key": "foo", } + changeTimeouts := before.DeepCopy() + changeTimeouts.Spec.KubeadmConfigSpec.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = ptr.To[int32](20) // before 10 + changeTimeouts.Spec.KubeadmConfigSpec.InitConfiguration.Timeouts.KubeletHealthCheckSeconds = nil // before set + changeTimeouts.Spec.KubeadmConfigSpec.InitConfiguration.Timeouts.EtcdAPICallSeconds = ptr.To[int32](20) // before not set + changeTimeouts.Spec.KubeadmConfigSpec.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = ptr.To[int32](20) // before 10 + changeTimeouts.Spec.KubeadmConfigSpec.JoinConfiguration.Timeouts.KubeletHealthCheckSeconds = nil // before set + changeTimeouts.Spec.KubeadmConfigSpec.JoinConfiguration.Timeouts.EtcdAPICallSeconds = ptr.To[int32](20) // before not set + + unsetTimeouts := before.DeepCopy() + unsetTimeouts.Spec.KubeadmConfigSpec.InitConfiguration.Timeouts = nil + unsetTimeouts.Spec.KubeadmConfigSpec.JoinConfiguration.Timeouts = nil + tests := []struct { name string enableIgnitionFeature bool @@ -1081,6 +1131,24 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) { before: before, kcp: invalidMetadata, }, + { + name: "should succeed when changing timeouts", + expectErr: false, + before: before, + kcp: changeTimeouts, + }, + { + name: "should succeed when unsetting timeouts", + expectErr: false, + before: before, + kcp: unsetTimeouts, + }, + { + name: "should succeed when setting timeouts", + expectErr: false, + before: unsetTimeouts, + kcp: changeTimeouts, + }, } for _, tt := range tests { diff --git a/controlplane/kubeadm/internal/webhooks/scale_test.go b/controlplane/kubeadm/internal/webhooks/scale_test.go index 9e292c8cd9b6..b2067db4b1d4 100644 --- a/controlplane/kubeadm/internal/webhooks/scale_test.go +++ b/controlplane/kubeadm/internal/webhooks/scale_test.go @@ -91,11 +91,6 @@ func TestKubeadmControlPlaneValidateScale(t *testing.T) { }, }, JoinConfiguration: &bootstrapv1.JoinConfiguration{ - Discovery: bootstrapv1.Discovery{ - Timeout: &metav1.Duration{ - Duration: 10 * time.Minute, - }, - }, NodeRegistration: bootstrapv1.NodeRegistrationOptions{ Name: "kcp-managed-etcd", }, diff --git a/controlplane/kubeadm/internal/workload_cluster.go b/controlplane/kubeadm/internal/workload_cluster.go index 7d6f2314bde1..75c0d0ac89ca 100644 --- a/controlplane/kubeadm/internal/workload_cluster.go +++ b/controlplane/kubeadm/internal/workload_cluster.go @@ -378,7 +378,8 @@ func (w *Workload) UpdateClusterConfiguration(ctx context.Context, version semve return errors.Errorf("unable to find %q in the kubeadm-config ConfigMap", clusterConfigurationKey) } - currentObj, err := kubeadmtypes.UnmarshalClusterConfiguration(currentData) + initConfiguration := &bootstrapv1.InitConfiguration{} + currentObj, err := kubeadmtypes.UnmarshalClusterConfiguration(currentData, initConfiguration) if err != nil { return errors.Wrapf(err, "unable to decode %q in the kubeadm-config ConfigMap's from YAML", clusterConfigurationKey) } @@ -389,7 +390,7 @@ func (w *Workload) UpdateClusterConfiguration(ctx context.Context, version semve } if !reflect.DeepEqual(currentObj, updatedObj) { - updatedData, err := kubeadmtypes.MarshalClusterConfigurationForVersion(updatedObj, version) + updatedData, err := kubeadmtypes.MarshalClusterConfigurationForVersion(initConfiguration, updatedObj, version) if err != nil { return errors.Wrapf(err, "unable to encode %q kubeadm-config ConfigMap's to YAML", clusterConfigurationKey) } diff --git a/controlplane/kubeadm/internal/workload_cluster_etcd_test.go b/controlplane/kubeadm/internal/workload_cluster_etcd_test.go index 6401f9721959..4fc543053b43 100644 --- a/controlplane/kubeadm/internal/workload_cluster_etcd_test.go +++ b/controlplane/kubeadm/internal/workload_cluster_etcd_test.go @@ -132,12 +132,14 @@ func TestUpdateEtcdExternalInKubeadmConfigMap(t *testing.T) { func TestUpdateEtcdLocalInKubeadmConfigMap(t *testing.T) { tests := []struct { name string + version semver.Version clusterConfigurationData string localEtcd *bootstrapv1.LocalEtcd wantClusterConfiguration string }{ { - name: "it should set local etcd configuration with local etcd", + name: "it should set local etcd configuration with local etcd (<1.31)", + version: semver.MustParse("1.19.1"), clusterConfigurationData: utilyaml.Raw(` apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration @@ -149,8 +151,11 @@ func TestUpdateEtcdLocalInKubeadmConfigMap(t *testing.T) { ImageRepository: "example.com/k8s", ImageTag: "v1.6.0", }, - ExtraArgs: map[string]string{ - "foo": "bar", + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, }, }, wantClusterConfiguration: utilyaml.Raw(` @@ -170,7 +175,8 @@ func TestUpdateEtcdLocalInKubeadmConfigMap(t *testing.T) { `), }, { - name: "no op when external etcd configuration already exists", + name: "no op when external etcd configuration already exists (<1.31)", + version: semver.MustParse("1.19.1"), clusterConfigurationData: utilyaml.Raw(` apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration @@ -182,8 +188,11 @@ func TestUpdateEtcdLocalInKubeadmConfigMap(t *testing.T) { ImageRepository: "example.com/k8s", ImageTag: "v1.6.0", }, - ExtraArgs: map[string]string{ - "foo": "bar", + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, }, }, wantClusterConfiguration: utilyaml.Raw(` @@ -193,6 +202,74 @@ func TestUpdateEtcdLocalInKubeadmConfigMap(t *testing.T) { external: {} `), }, + { + name: "it should set local etcd configuration with local etcd (>=1.31)", + version: semver.MustParse("1.31.1"), + clusterConfigurationData: utilyaml.Raw(` + apiVersion: kubeadm.k8s.io/v1beta4 + kind: ClusterConfiguration + etcd: + local: {} + `), + localEtcd: &bootstrapv1.LocalEtcd{ + ImageMeta: bootstrapv1.ImageMeta{ + ImageRepository: "example.com/k8s", + ImageTag: "v1.6.0", + }, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, + }, + wantClusterConfiguration: utilyaml.Raw(` + apiServer: {} + apiVersion: kubeadm.k8s.io/v1beta4 + controllerManager: {} + dns: {} + etcd: + local: + dataDir: "" + extraArgs: + - name: foo + value: bar + imageRepository: example.com/k8s + imageTag: v1.6.0 + kind: ClusterConfiguration + networking: {} + proxy: {} + scheduler: {} + `), + }, + { + name: "no op when external etcd configuration already exists (>=1.31)", + version: semver.MustParse("1.31.1"), + clusterConfigurationData: utilyaml.Raw(` + apiVersion: kubeadm.k8s.io/v1beta4 + kind: ClusterConfiguration + etcd: + external: {} + `), + localEtcd: &bootstrapv1.LocalEtcd{ + ImageMeta: bootstrapv1.ImageMeta{ + ImageRepository: "example.com/k8s", + ImageTag: "v1.6.0", + }, + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "foo", + Value: "bar", + }, + }, + }, + wantClusterConfiguration: utilyaml.Raw(` + apiVersion: kubeadm.k8s.io/v1beta4 + kind: ClusterConfiguration + etcd: + external: {} + `), + }, } for _, tt := range tests { @@ -211,7 +288,7 @@ func TestUpdateEtcdLocalInKubeadmConfigMap(t *testing.T) { w := &Workload{ Client: fakeClient, } - err := w.UpdateClusterConfiguration(ctx, semver.MustParse("1.19.1"), w.UpdateEtcdLocalInKubeadmConfigMap(tt.localEtcd)) + err := w.UpdateClusterConfiguration(ctx, tt.version, w.UpdateEtcdLocalInKubeadmConfigMap(tt.localEtcd)) g.Expect(err).ToNot(HaveOccurred()) var actualConfig corev1.ConfigMap diff --git a/controlplane/kubeadm/internal/workload_cluster_test.go b/controlplane/kubeadm/internal/workload_cluster_test.go index 2bca4a1ae736..a9abad8d853b 100644 --- a/controlplane/kubeadm/internal/workload_cluster_test.go +++ b/controlplane/kubeadm/internal/workload_cluster_test.go @@ -826,21 +826,29 @@ func TestUpdateImageRepositoryInKubeadmConfigMap(t *testing.T) { func TestUpdateApiServerInKubeadmConfigMap(t *testing.T) { tests := []struct { name string + version semver.Version clusterConfigurationData string newAPIServer bootstrapv1.APIServer wantClusterConfiguration string }{ { - name: "it should set the api server config", + name: "it should set the api server config (< 1.31)", + version: semver.MustParse("1.19.1"), clusterConfigurationData: utilyaml.Raw(` apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration `), newAPIServer: bootstrapv1.APIServer{ ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{ - "bar": "baz", - "someKey": "someVal", + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "bar", + Value: "baz", + }, + { + Name: "someKey", + Value: "someVal", + }, }, ExtraVolumes: []bootstrapv1.HostPathMount{ { @@ -869,6 +877,55 @@ func TestUpdateApiServerInKubeadmConfigMap(t *testing.T) { scheduler: {} `), }, + { + name: "it should set the api server config (>=1.31)", + version: semver.MustParse("1.31.1"), + clusterConfigurationData: utilyaml.Raw(` + apiVersion: kubeadm.k8s.io/v1beta4 + kind: ClusterConfiguration + `), + newAPIServer: bootstrapv1.APIServer{ + ControlPlaneComponent: bootstrapv1.ControlPlaneComponent{ + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "bar", + Value: "baz", + }, + { + Name: "someKey", + Value: "someVal", + }, + }, + ExtraVolumes: []bootstrapv1.HostPathMount{ + { + Name: "mount2", + HostPath: "/bar/baz", + MountPath: "/foo/bar", + }, + }, + }, + }, + wantClusterConfiguration: utilyaml.Raw(` + apiServer: + extraArgs: + - name: bar + value: baz + - name: someKey + value: someVal + extraVolumes: + - hostPath: /bar/baz + mountPath: /foo/bar + name: mount2 + apiVersion: kubeadm.k8s.io/v1beta4 + controllerManager: {} + dns: {} + etcd: {} + kind: ClusterConfiguration + networking: {} + proxy: {} + scheduler: {} + `), + }, } for _, tt := range tests { @@ -887,7 +944,7 @@ func TestUpdateApiServerInKubeadmConfigMap(t *testing.T) { w := &Workload{ Client: fakeClient, } - err := w.UpdateClusterConfiguration(ctx, semver.MustParse("1.19.1"), w.UpdateAPIServerInKubeadmConfigMap(tt.newAPIServer)) + err := w.UpdateClusterConfiguration(ctx, tt.version, w.UpdateAPIServerInKubeadmConfigMap(tt.newAPIServer)) g.Expect(err).ToNot(HaveOccurred()) var actualConfig corev1.ConfigMap @@ -915,9 +972,15 @@ func TestUpdateControllerManagerInKubeadmConfigMap(t *testing.T) { kind: ClusterConfiguration `), newControllerManager: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{ - "bar": "baz", - "someKey": "someVal", + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "bar", + Value: "baz", + }, + { + Name: "someKey", + Value: "someVal", + }, }, ExtraVolumes: []bootstrapv1.HostPathMount{ { @@ -991,9 +1054,15 @@ func TestUpdateSchedulerInKubeadmConfigMap(t *testing.T) { kind: ClusterConfiguration `), newScheduler: bootstrapv1.ControlPlaneComponent{ - ExtraArgs: map[string]string{ - "bar": "baz", - "someKey": "someVal", + ExtraArgs: []bootstrapv1.Arg{ + { + Name: "bar", + Value: "baz", + }, + { + Name: "someKey", + Value: "someVal", + }, }, ExtraVolumes: []bootstrapv1.HostPathMount{ { diff --git a/docs/book/src/developer/providers/migrations/v1.10-to-v1.11.md b/docs/book/src/developer/providers/migrations/v1.10-to-v1.11.md index ccabddf1f66a..a2fcbbb8df5b 100644 --- a/docs/book/src/developer/providers/migrations/v1.10-to-v1.11.md +++ b/docs/book/src/developer/providers/migrations/v1.10-to-v1.11.md @@ -22,7 +22,10 @@ proposal because most of the changes described below are a consequence of the wo * [MachineHealthCheck](#machinehealthcheck) * [ClusterClass](#clusterclass) * [KubeadmConfig](#kubeadmconfig) + * [KubeadmConfigTemplate](#kubeadmconfigtemplate) * [KubeadmControlPlane](#kubeadmcontrolplane) + * [KubeadmControlPlaneTemplate](#kubeadmcontrolplanetemplate) + * [ClusterResourceSetBinding](#clusterresourcesetbinding) * [Cluster API Contract changes](#cluster-api-contract-changes) * [Contract rules for InfraCluster](#contract-rules-for-infracluster) * [Contract rules for InfraMachine](#contract-rules-for-inframachine) @@ -186,16 +189,69 @@ proposal because most of the changes described below are a consequence of the wo - KubeadmConfig (and the entire CABPK provider) now implements the v1beta2 Cluster API contract. - See changes that apply to [all CRDs](#all-crds) -- The `spec.useExperimentalRetryJoin` field (deprecated in CAPI v1.2!) has been removed. +- ExtraArg field types have been changed from `map[string]sting` to `[]Arg`, thus aligning with kubeadm v1beta4 API; + however, using multiple args with the same name will be enabled only when v1beta1 is removed, tentatively in August 2026 + - `spec.clusterConfiguration.apiServer.extraArgs` type has been changed to `[]Arg` + - `spec.clusterConfiguration.controllerManager.extraArgs` type has been changed to `[]Arg` + - `spec.clusterConfiguration.scheduler.extraArgs` type has been changed to `[]Arg` + - `spec.clusterConfiguration.etcd.local.extraArgs` type has been changed to `[]Arg` + - `spec.initConfiguration.nodeRegistration.kubeletExtraArgs` type has been changed to `[]Arg` + - `spec.joinConfiguration.nodeRegistration.kubeletExtraArgs` type has been changed to `[]Arg` +- ImagePullPolicy field types have been changed from `string` to `corev1.PullPolicy`, thus aligning with kubeadm v1beta4 API + - `spec.initConfiguration.nodeRegistration.imagePullPolicy` type has been changed to `corev1.PullPolicy` + - `spec.joinConfiguration.nodeRegistration.imagePullPolicy` type has been changed to `corev1.PullPolicy` +- Timeout fields have been aligned with kubeadm v1beta4 API, but field names and types have been adapted according to API guidelines. + - `spec.initConfiguration.timeouts` struct has been added with the following fields: + - `controlPlaneComponentHealthCheckSeconds` + - `kubeletHealthCheckSeconds` + - `kubernetesAPICallSeconds` + - `etcdAPICallSeconds` + - `tlsBootstrapSeconds` + - `discoverySeconds` + - `spec.joinConfiguration.timeouts` field has been added with the same set of timeouts listed above. + - `spec.clusterConfiguration.apiServer.timeoutForControlPlane` field has been removed. + Use `spec.initConfiguration.timeouts.controlPlaneComponentHealthCheckSeconds` and `spec.joinConfiguration.timeouts.controlPlaneComponentHealthCheckSeconds` instead; + however, using different timeouts for init and join will be enabled only when v1beta1 is removed. + - `spec.joinConfiguration.discovery.timeout` field has been removed. Use `spec.joinConfiguration.timeouts.tlsBootstrapSeconds` instead. +- The `spec.useExperimentalRetryJoin` field (deprecated in CAPI v1.2!) has been removed. - Information about the initial provisioning process is now surfacing under the new `status.initialization` field. - `status.ready` has been replaced by `status.initialization.dataSecretCreated` - Support for terminal errors has been dropped (see [dataSecretCreated](#cluster-api-contract-changes)). - `status.failureReason` and `status.failureMessage` will continue to exist temporarily under `status.deprecated.v1beta1`. + +### KubeadmConfigTemplate + +KubeadmConfigTemplate `spec.template.spec` has been aligned to changes in the [KubeadmConfig](#kubeadmconfig) `spec` struct. ### KubeadmControlPlane - KubeadmControlPlane (and the entire KCP provider) now implements the v1beta2 Cluster API contract. - See changes that apply to [all CRDs](#all-crds) +- ExtraArg field types have been changed from `map[string]sting` to `[]Arg`, thus aligning with kubeadm v1beta4 API; + however, using multiple args with the same name will be enabled only when v1beta1 is removed, tentatively in August 2026 + - `spec.kubeadmConfigSpec.clusterConfiguration.apiServer.extraArgs` type has been changed to `[]Arg` + - `spec.kubeadmConfigSpec.clusterConfiguration.controllerManager.extraArgs` type has been changed to `[]Arg` + - `spec.kubeadmConfigSpec.clusterConfiguration.scheduler.extraArgs` type has been changed to `[]Arg` + - `spec.kubeadmConfigSpec.clusterConfiguration.etcd.local.extraArgs` type has been changed to `[]Arg` + - `spec.kubeadmConfigSpec.initConfiguration.nodeRegistration.kubeletExtraArgs` type has been changed to `[]Arg` + - `spec.kubeadmConfigSpec.joinConfiguration.nodeRegistration.kubeletExtraArgs` type has been changed to `[]Arg` +- ImagePullPolicy field types have been changed from `string` to `corev1.PullPolicy`, thus aligning with kubeadm v1beta4 API + - `spec.kubeadmConfigSpec.initConfiguration.nodeRegistration.imagePullPolicy` type has been changed to `corev1.PullPolicy` + - `spec.kubeadmConfigSpec.joinConfiguration.nodeRegistration.imagePullPolicy` type has been changed to `corev1.PullPolicy` +- Timeout fields have been aligned with kubeadm v1beta4 API, but field names and types have been adapted according to API guidelines. + - `spec.kubeadmConfigSpec.initConfiguration.timeouts` struct has been added with the following fields: + - `controlPlaneComponentHealthCheckSeconds` + - `kubeletHealthCheckSeconds` + - `kubernetesAPICallSeconds` + - `etcdAPICallSeconds` + - `tlsBootstrapSeconds` + - `discoverySeconds` + - `spec.kubeadmConfigSpec.joinConfiguration.timeouts` field has been added with the same set of timeouts listed above. + - `spec.kubeadmConfigSpec.clusterConfiguration.apiServer.timeoutForControlPlane` field has been removed. + Use `spec.kubeadmConfigSpec.initConfiguration.timeouts.controlPlaneComponentHealthCheckSeconds` and `spec.kubeadmConfigSpec.joinConfiguration.timeouts.controlPlaneComponentHealthCheckSeconds` instead; + however, using different timeouts for init and join will be enabled only when v1beta1 is removed. + - `spec.kubeadmConfigSpec.joinConfiguration.discovery.timeout` field has been removed. Use `spec.kubeadmConfigSpec.joinConfiguration.timeouts.tlsBootstrapSeconds` instead. +- The `spec.kubeadmConfigSpec.useExperimentalRetryJoin` field (deprecated in CAPI v1.2!) has been removed. - Replica counters fields are now consistent with replica counters from other resources. - `status.replicas` was made a pointer and omitempty was added - `status.readyReplicas` has now a new semantic based on machine's `Ready` condition @@ -208,6 +264,10 @@ proposal because most of the changes described below are a consequence of the wo - Support for terminal errors has been dropped. - `status.failureReason` and `status.failureMessage` will continue to exist temporarily under `status.deprecated.v1beta1`. +### KubeadmControlPlaneTemplate + +KubeadmControlPlaneTemplate `spec.template.spec` has been aligned to changes in the [KubeadmControlPlane](#kubeadmcontrolplane) `spec` struct. + ### ClusterResourceSetBinding - Remove deprecated `ClusterResourceSetBinding.DeleteBinding` func diff --git a/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go b/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go index 85ee9ae21143..81408213deaa 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v1alpha3 import ( + "reflect" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -57,16 +59,21 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &bootstrapv1.KubeadmConfig{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } - MergeRestoredKubeadmConfigSpec(&dst.Spec, &restored.Spec) - dst.Status.Conditions = restored.Status.Conditions + if ok { + RestoreKubeadmConfigSpec(&dst.Spec, &restored.Spec) + dst.Status.Conditions = restored.Status.Conditions + } + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.ConvertTo(&dst.Spec) return nil } -func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored *bootstrapv1.KubeadmConfigSpec) { +func RestoreKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored *bootstrapv1.KubeadmConfigSpec) { dst.Files = restored.Files dst.Users = restored.Users @@ -101,6 +108,7 @@ func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored if dst.InitConfiguration == nil { dst.InitConfiguration = &bootstrapv1.InitConfiguration{} } + dst.InitConfiguration.Timeouts = restored.InitConfiguration.Timeouts dst.InitConfiguration.Patches = restored.InitConfiguration.Patches dst.InitConfiguration.SkipPhases = restored.InitConfiguration.SkipPhases @@ -116,6 +124,7 @@ func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored if dst.JoinConfiguration == nil { dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{} } + dst.JoinConfiguration.Timeouts = restored.JoinConfiguration.Timeouts dst.JoinConfiguration.Patches = restored.JoinConfiguration.Patches dst.JoinConfiguration.SkipPhases = restored.JoinConfiguration.SkipPhases @@ -135,6 +144,37 @@ func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored } } +func (src *KubeadmConfigSpec) ConvertTo(dst *bootstrapv1.KubeadmConfigSpec) { + // Override with timeouts values already existing in v1beta1. + var initControlPlaneComponentHealthCheckSeconds *int32 + if src.ClusterConfiguration != nil && src.ClusterConfiguration.APIServer.TimeoutForControlPlane != nil { + if dst.InitConfiguration == nil { + dst.InitConfiguration = &bootstrapv1.InitConfiguration{} + } + if dst.InitConfiguration.Timeouts == nil { + dst.InitConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.ClusterConfiguration.APIServer.TimeoutForControlPlane) + initControlPlaneComponentHealthCheckSeconds = dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if dst.JoinConfiguration.Timeouts == nil { + dst.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + dst.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds + if src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil { + dst.JoinConfiguration.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(src.JoinConfiguration.Discovery.Timeout) + } + } + + if reflect.DeepEqual(dst.ClusterConfiguration, &bootstrapv1.ClusterConfiguration{}) { + dst.ClusterConfiguration = nil + } +} + func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.KubeadmConfig) @@ -162,12 +202,33 @@ func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error { dst.Status.Ready = src.Status.Initialization.DataSecretCreated } + // Convert timeouts moved from one struct to another. + dst.Spec.ConvertFrom(&src.Spec) + // Preserve Hub data on down-conversion except for metadata - if err := utilconversion.MarshalData(src, dst); err != nil { - return err - } + return utilconversion.MarshalData(src, dst) +} - return nil +func (dst *KubeadmConfigSpec) ConvertFrom(src *bootstrapv1.KubeadmConfigSpec) { + // Convert timeouts moved from one struct to another. + if src.InitConfiguration != nil && src.InitConfiguration.Timeouts != nil && src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil { + if dst.ClusterConfiguration == nil { + dst.ClusterConfiguration = &upstreamv1beta1.ClusterConfiguration{} + } + dst.ClusterConfiguration.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + } + if reflect.DeepEqual(dst.InitConfiguration, &upstreamv1beta1.InitConfiguration{}) { + dst.InitConfiguration = nil + } + if src.JoinConfiguration != nil && src.JoinConfiguration.Timeouts != nil && src.JoinConfiguration.Timeouts.TLSBootstrapSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &upstreamv1beta1.JoinConfiguration{} + } + dst.JoinConfiguration.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(src.JoinConfiguration.Timeouts.TLSBootstrapSeconds) + } + if reflect.DeepEqual(dst.JoinConfiguration, &upstreamv1beta1.JoinConfiguration{}) { + dst.JoinConfiguration = nil + } } func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { @@ -179,14 +240,17 @@ func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &bootstrapv1.KubeadmConfigTemplate{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } + if ok { + RestoreKubeadmConfigSpec(&dst.Spec.Template.Spec, &restored.Spec.Template.Spec) + dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta + } - dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta - - MergeRestoredKubeadmConfigSpec(&dst.Spec.Template.Spec, &restored.Spec.Template.Spec) - + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.ConvertTo(&dst.Spec.Template.Spec) return nil } @@ -197,12 +261,11 @@ func (dst *KubeadmConfigTemplate) ConvertFrom(srcRaw conversion.Hub) error { return err } - // Preserve Hub data on down-conversion except for metadata - if err := utilconversion.MarshalData(src, dst); err != nil { - return err - } + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.ConvertFrom(&src.Spec.Template.Spec) - return nil + // Preserve Hub data on down-conversion except for metadata. + return utilconversion.MarshalData(src, dst) } func Convert_v1alpha3_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *KubeadmConfigSpec, out *bootstrapv1.KubeadmConfigSpec, s apimachineryconversion.Scope) error { diff --git a/internal/apis/bootstrap/kubeadm/v1alpha3/conversion_test.go b/internal/apis/bootstrap/kubeadm/v1alpha3/conversion_test.go index 4d0d3835cd6e..89f750d3d88c 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha3/conversion_test.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha3/conversion_test.go @@ -21,9 +21,12 @@ package v1alpha3 import ( "reflect" "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" @@ -31,6 +34,11 @@ import ( utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) +const ( + fakeID = "abcdef" + fakeSecret = "abcdef0123456789" +) + // Test is disabled when the race detector is enabled (via "//go:build !race" above) because otherwise the fuzz tests would just time out. func TestFuzzyConversion(t *testing.T) { @@ -53,18 +61,44 @@ func KubeadmConfigFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { spokeDNS, spokeClusterConfiguration, hubKubeadmConfigStatus, - // This custom functions are needed when ConvertTo/ConvertFrom functions - // uses the json package to unmarshal the bootstrap token string. - // - // The Kubeadm BootstrapTokenString type ships with a custom - // json string representation, in particular it supplies a customized - // UnmarshalJSON function that can return an error if the string - // isn't in the correct form. - // - // This function effectively disables any fuzzing for the token by setting - // the values for ID and Secret to working alphanumeric values. - spokeKubeadmBootstrapTokenString, - hubKubeadmBootstrapTokenString, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + spokeKubeadmConfigSpec, + spokeKubeadmConfigStatus, + spokeDNS, + spokeClusterConfiguration, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) { + c.FillNoCustom(in) + + // enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration + var initControlPlaneComponentHealthCheckSeconds *int32 + if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil { + initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if in.JoinConfiguration == nil { + in.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if in.JoinConfiguration.Timeouts == nil { + in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds } } @@ -86,27 +120,6 @@ func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c randfill.Cont } } -func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { - return []interface{}{ - spokeKubeadmConfigSpec, - spokeKubeadmConfigStatus, - spokeDNS, - spokeClusterConfiguration, - // This custom functions are needed when ConvertTo/ConvertFrom functions - // uses the json package to unmarshal the bootstrap token string. - // - // The Kubeadm BootstrapTokenString type ships with a custom - // json string representation, in particular it supplies a customized - // UnmarshalJSON function that can return an error if the string - // isn't in the correct form. - // - // This function effectively disables any fuzzing for the token by setting - // the values for ID and Secret to working alphanumeric values. - spokeKubeadmBootstrapTokenString, - hubKubeadmBootstrapTokenString, - } -} - func spokeKubeadmConfigSpec(in *KubeadmConfigSpec, c randfill.Continue) { c.FillNoCustom(in) @@ -135,12 +148,28 @@ func spokeClusterConfiguration(obj *upstreamv1beta1.ClusterConfiguration, c rand obj.UseHyperKubeImage = false } -func spokeKubeadmBootstrapTokenString(in *upstreamv1beta1.BootstrapTokenString, _ randfill.Continue) { - in.ID = "abcdef" - in.Secret = "abcdef0123456789" +func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret } -func hubKubeadmBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { - in.ID = "abcdef" - in.Secret = "abcdef0123456789" +func spokeBootstrapTokenString(in *upstreamv1beta1.BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret +} + +func spokeAPIServer(in *upstreamv1beta1.APIServer, c randfill.Continue) { + c.FillNoCustom(in) + + if in.TimeoutForControlPlane != nil { + in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeDiscovery(in *upstreamv1beta1.Discovery, c randfill.Continue) { + c.FillNoCustom(in) + + if in.Timeout != nil { + in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } } diff --git a/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go b/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go index 211d105d57e5..4c8dd48517f8 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v1alpha4 import ( + "reflect" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apimachineryconversion "k8s.io/apimachinery/pkg/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -56,16 +58,21 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &bootstrapv1.KubeadmConfig{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } - MergeRestoredKubeadmConfigSpec(&dst.Spec, &restored.Spec) - dst.Status.Conditions = restored.Status.Conditions + if ok { + RestoreKubeadmConfigSpec(&dst.Spec, &restored.Spec) + dst.Status.Conditions = restored.Status.Conditions + } + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.ConvertTo(&dst.Spec) return nil } -func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored *bootstrapv1.KubeadmConfigSpec) { +func RestoreKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored *bootstrapv1.KubeadmConfigSpec) { dst.Files = restored.Files dst.Users = restored.Users @@ -100,6 +107,7 @@ func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored if dst.InitConfiguration == nil { dst.InitConfiguration = &bootstrapv1.InitConfiguration{} } + dst.InitConfiguration.Timeouts = restored.InitConfiguration.Timeouts dst.InitConfiguration.Patches = restored.InitConfiguration.Patches dst.InitConfiguration.SkipPhases = restored.InitConfiguration.SkipPhases @@ -114,6 +122,7 @@ func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored if dst.JoinConfiguration == nil { dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{} } + dst.JoinConfiguration.Timeouts = restored.JoinConfiguration.Timeouts dst.JoinConfiguration.Patches = restored.JoinConfiguration.Patches dst.JoinConfiguration.SkipPhases = restored.JoinConfiguration.SkipPhases @@ -132,6 +141,37 @@ func MergeRestoredKubeadmConfigSpec(dst *bootstrapv1.KubeadmConfigSpec, restored } } +func (src *KubeadmConfigSpec) ConvertTo(dst *bootstrapv1.KubeadmConfigSpec) { + // Override with timeouts values already existing in v1beta1. + var initControlPlaneComponentHealthCheckSeconds *int32 + if src.ClusterConfiguration != nil && src.ClusterConfiguration.APIServer.TimeoutForControlPlane != nil { + if dst.InitConfiguration == nil { + dst.InitConfiguration = &bootstrapv1.InitConfiguration{} + } + if dst.InitConfiguration.Timeouts == nil { + dst.InitConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.ClusterConfiguration.APIServer.TimeoutForControlPlane) + initControlPlaneComponentHealthCheckSeconds = dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if dst.JoinConfiguration.Timeouts == nil { + dst.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + dst.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds + if src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil { + dst.JoinConfiguration.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(src.JoinConfiguration.Discovery.Timeout) + } + } + + if reflect.DeepEqual(dst.ClusterConfiguration, &bootstrapv1.ClusterConfiguration{}) { + dst.ClusterConfiguration = nil + } +} + func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.KubeadmConfig) @@ -159,10 +199,35 @@ func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error { dst.Status.Ready = src.Status.Initialization.DataSecretCreated } + // Convert timeouts moved from one struct to another. + dst.Spec.ConvertFrom(&src.Spec) + // Preserve Hub data on down-conversion except for metadata. return utilconversion.MarshalData(src, dst) } +func (dst *KubeadmConfigSpec) ConvertFrom(src *bootstrapv1.KubeadmConfigSpec) { + // Convert timeouts moved from one struct to another. + if src.InitConfiguration != nil && src.InitConfiguration.Timeouts != nil && src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil { + if dst.ClusterConfiguration == nil { + dst.ClusterConfiguration = &ClusterConfiguration{} + } + dst.ClusterConfiguration.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds) + } + if reflect.DeepEqual(dst.InitConfiguration, &InitConfiguration{}) { + dst.InitConfiguration = nil + } + if src.JoinConfiguration != nil && src.JoinConfiguration.Timeouts != nil && src.JoinConfiguration.Timeouts.TLSBootstrapSeconds != nil { + if dst.JoinConfiguration == nil { + dst.JoinConfiguration = &JoinConfiguration{} + } + dst.JoinConfiguration.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(src.JoinConfiguration.Timeouts.TLSBootstrapSeconds) + } + if reflect.DeepEqual(dst.JoinConfiguration, &JoinConfiguration{}) { + dst.JoinConfiguration = nil + } +} + func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.KubeadmConfigTemplate) @@ -172,14 +237,17 @@ func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &bootstrapv1.KubeadmConfigTemplate{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } + if ok { + RestoreKubeadmConfigSpec(&dst.Spec.Template.Spec, &restored.Spec.Template.Spec) + dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta + } - dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta - - MergeRestoredKubeadmConfigSpec(&dst.Spec.Template.Spec, &restored.Spec.Template.Spec) - + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.ConvertTo(&dst.Spec.Template.Spec) return nil } @@ -189,6 +257,10 @@ func (dst *KubeadmConfigTemplate) ConvertFrom(srcRaw conversion.Hub) error { if err := Convert_v1beta2_KubeadmConfigTemplate_To_v1alpha4_KubeadmConfigTemplate(src, dst, nil); err != nil { return err } + + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.ConvertFrom(&src.Spec.Template.Spec) + // Preserve Hub data on down-conversion except for metadata. return utilconversion.MarshalData(src, dst) } @@ -225,8 +297,9 @@ func Convert_v1beta2_User_To_v1alpha4_User(in *bootstrapv1.User, out *User, s ap } func Convert_v1beta2_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { - // NodeRegistrationOptions.ImagePullPolicy does not exit in - // kubeadm v1alpha4 API. + // NodeRegistrationOptions.ImagePullPolicy does not exit in kubeadm v1alpha4 API. + // Following fields require a custom conversions. + out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs) return autoConvert_v1beta2_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in, out, s) } @@ -242,16 +315,20 @@ func Convert_v1beta2_FileDiscovery_To_v1alpha4_FileDiscovery(in *bootstrapv1.Fil func Convert_v1beta2_ControlPlaneComponent_To_v1alpha4_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error { // ControlPlaneComponent.ExtraEnvs does not exist in v1alpha4 APIs. + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_ControlPlaneComponent_To_v1alpha4_ControlPlaneComponent(in, out, s) } func Convert_v1beta2_LocalEtcd_To_v1alpha4_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error { // LocalEtcd.ExtraEnvs does not exist in v1alpha4 APIs. + // Following fields require a custom conversions. + out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs) return autoConvert_v1beta2_LocalEtcd_To_v1alpha4_LocalEtcd(in, out, s) } func Convert_v1beta2_KubeadmConfigStatus_To_v1alpha4_KubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, out *KubeadmConfigStatus, s apimachineryconversion.Scope) error { - // V1Beta2 was added in v1beta1. + // V1Beta2 was added in v1alpha4. return autoConvert_v1beta2_KubeadmConfigStatus_To_v1alpha4_KubeadmConfigStatus(in, out, s) } @@ -259,6 +336,31 @@ func Convert_v1alpha4_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in *Kub return autoConvert_v1alpha4_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in, out, s) } +func Convert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) +} + +func Convert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs) + return autoConvert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) +} + +func Convert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs) + return autoConvert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) +} + +func Convert_v1alpha4_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error { + // TimeoutForControlPlane has been removed in v1beta2 + return autoConvert_v1alpha4_APIServer_To_v1beta2_APIServer(in, out, s) +} + +func Convert_v1alpha4_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error { + // Timeout has been removed in v1beta2 + return autoConvert_v1alpha4_Discovery_To_v1beta2_Discovery(in, out, s) +} + // Implement local conversion func because conversion-gen is not aware of conversion func in other packages (see https://github.com/kubernetes/code-generator/issues/94) func Convert_v1_Condition_To_v1alpha4_Condition(in *metav1.Condition, out *clusterv1alpha4.Condition, s apimachineryconversion.Scope) error { diff --git a/internal/apis/bootstrap/kubeadm/v1alpha4/conversion_test.go b/internal/apis/bootstrap/kubeadm/v1alpha4/conversion_test.go index a736ebf7968d..3f0e5157a53f 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha4/conversion_test.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha4/conversion_test.go @@ -21,9 +21,12 @@ package v1alpha4 import ( "reflect" "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" "sigs.k8s.io/randfill" bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" @@ -41,12 +44,12 @@ func TestFuzzyConversion(t *testing.T) { t.Run("for KubeadmConfig", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ Hub: &bootstrapv1.KubeadmConfig{}, Spoke: &KubeadmConfig{}, - FuzzerFuncs: []fuzzer.FuzzerFuncs{KubeadmConfigFuzzFuncs, fuzzFuncs}, + FuzzerFuncs: []fuzzer.FuzzerFuncs{KubeadmConfigFuzzFuncs}, })) t.Run("for KubeadmConfigTemplate", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ Hub: &bootstrapv1.KubeadmConfigTemplate{}, Spoke: &KubeadmConfigTemplate{}, - FuzzerFuncs: []fuzzer.FuzzerFuncs{KubeadmConfigTemplateFuzzFuncs, fuzzFuncs}, + FuzzerFuncs: []fuzzer.FuzzerFuncs{KubeadmConfigTemplateFuzzFuncs}, })) } @@ -54,6 +57,41 @@ func KubeadmConfigFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ hubKubeadmConfigStatus, spokeKubeadmConfigSpec, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + spokeKubeadmConfigSpec, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) { + c.FillNoCustom(in) + + // enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration + var initControlPlaneComponentHealthCheckSeconds *int32 + if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil { + initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if in.JoinConfiguration == nil { + in.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if in.JoinConfiguration.Timeouts == nil { + in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds } } @@ -82,35 +120,28 @@ func spokeKubeadmConfigSpec(in *KubeadmConfigSpec, c randfill.Continue) { in.UseExperimentalRetryJoin = false } -func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { - return []interface{}{ - // This custom functions are needed when ConvertTo/ConvertFrom functions - // uses the json package to unmarshal the bootstrap token string. - // - // The Kubeadm BootstrapTokenString type ships with a custom - // json string representation, in particular it supplies a customized - // UnmarshalJSON function that can return an error if the string - // isn't in the correct form. - // - // This function effectively disables any fuzzing for the token by setting - // the values for ID and Secret to working alphanumeric values. - kubeadmBootstrapTokenStringFuzzerV1Beta1, - kubeadmBootstrapTokenStringFuzzerV1Alpha4, - } -} - -func kubeadmBootstrapTokenStringFuzzerV1Beta1(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { +func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { in.ID = fakeID in.Secret = fakeSecret } -func kubeadmBootstrapTokenStringFuzzerV1Alpha4(in *BootstrapTokenString, _ randfill.Continue) { +func spokeBootstrapTokenString(in *BootstrapTokenString, _ randfill.Continue) { in.ID = fakeID in.Secret = fakeSecret } -func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { - return []interface{}{ - spokeKubeadmConfigSpec, +func spokeAPIServer(in *APIServer, c randfill.Continue) { + c.FillNoCustom(in) + + if in.TimeoutForControlPlane != nil { + in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeDiscovery(in *Discovery, c randfill.Continue) { + c.FillNoCustom(in) + + if in.Timeout != nil { + in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) } } diff --git a/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go b/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go index 32b3077d2dab..15ebd4c55a97 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go @@ -49,11 +49,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha4_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_APIServer_To_v1alpha4_APIServer(a.(*v1beta2.APIServer), b.(*APIServer), scope) }); err != nil { @@ -109,11 +104,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*DNS)(nil), (*v1beta2.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_DNS_To_v1beta2_DNS(a.(*DNS), b.(*v1beta2.DNS), scope) }); err != nil { @@ -124,11 +114,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha4_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta2.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_Discovery_To_v1alpha4_Discovery(a.(*v1beta2.Discovery), b.(*Discovery), scope) }); err != nil { @@ -289,11 +274,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*NTP)(nil), (*v1beta2.NTP)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_NTP_To_v1beta2_NTP(a.(*NTP), b.(*v1beta2.NTP), scope) }); err != nil { @@ -314,11 +294,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*Partition)(nil), (*v1beta2.Partition)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_Partition_To_v1beta2_Partition(a.(*Partition), b.(*v1beta2.Partition), scope) }); err != nil { @@ -349,11 +324,26 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*APIServer)(nil), (*v1beta2.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha4_APIServer_To_v1beta2_APIServer(a.(*APIServer), b.(*v1beta2.APIServer), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*corev1alpha4.Condition)(nil), (*v1.Condition)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_Condition_To_v1_Condition(a.(*corev1alpha4.Condition), b.(*v1.Condition), scope) }); err != nil { return err } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta2.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta2.ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*Discovery)(nil), (*v1beta2.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha4_Discovery_To_v1beta2_Discovery(a.(*Discovery), b.(*v1beta2.Discovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*KubeadmConfigSpec)(nil), (*v1beta2.KubeadmConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(a.(*KubeadmConfigSpec), b.(*v1beta2.KubeadmConfigSpec), scope) }); err != nil { @@ -364,6 +354,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta2.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(a.(*LocalEtcd), b.(*v1beta2.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta2.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta2.NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_ControlPlaneComponent_To_v1alpha4_ControlPlaneComponent(a.(*v1beta2.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) }); err != nil { @@ -449,21 +449,15 @@ func autoConvert_v1alpha4_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1b return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) + // WARNING: in.TimeoutForControlPlane requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha4_APIServer_To_v1beta2_APIServer is an autogenerated conversion function. -func Convert_v1alpha4_APIServer_To_v1beta2_APIServer(in *APIServer, out *v1beta2.APIServer, s conversion.Scope) error { - return autoConvert_v1alpha4_APIServer_To_v1beta2_APIServer(in, out, s) -} - func autoConvert_v1beta2_APIServer_To_v1alpha4_APIServer(in *v1beta2.APIServer, out *APIServer, s conversion.Scope) error { if err := Convert_v1beta2_ControlPlaneComponent_To_v1alpha4_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { return err } out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) - out.TimeoutForControlPlane = (*v1.Duration)(unsafe.Pointer(in.TimeoutForControlPlane)) return nil } @@ -637,18 +631,13 @@ func Convert_v1beta2_ClusterStatus_To_v1alpha4_ClusterStatus(in *v1beta2.Cluster } func autoConvert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ExtraVolumes = *(*[]v1beta2.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) return nil } -// Convert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent is an autogenerated conversion function. -func Convert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta2.ControlPlaneComponent, s conversion.Scope) error { - return autoConvert_v1alpha4_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s) -} - func autoConvert_v1beta2_ControlPlaneComponent_To_v1alpha4_ControlPlaneComponent(in *v1beta2.ControlPlaneComponent, out *ControlPlaneComponent, s conversion.Scope) error { - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type return nil @@ -690,15 +679,10 @@ func autoConvert_v1alpha4_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1b out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha4_Discovery_To_v1beta2_Discovery is an autogenerated conversion function. -func Convert_v1alpha4_Discovery_To_v1beta2_Discovery(in *Discovery, out *v1beta2.Discovery, s conversion.Scope) error { - return autoConvert_v1alpha4_Discovery_To_v1beta2_Discovery(in, out, s) -} - func autoConvert_v1beta2_Discovery_To_v1alpha4_Discovery(in *v1beta2.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) if in.File != nil { @@ -711,7 +695,6 @@ func autoConvert_v1beta2_Discovery_To_v1alpha4_Discovery(in *v1beta2.Discovery, out.File = nil } out.TLSBootstrapToken = in.TLSBootstrapToken - out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil } @@ -980,6 +963,7 @@ func autoConvert_v1beta2_InitConfiguration_To_v1alpha4_InitConfiguration(in *v1b } // WARNING: in.SkipPhases requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } @@ -1011,6 +995,7 @@ func autoConvert_v1beta2_JoinConfiguration_To_v1alpha4_JoinConfiguration(in *v1b out.ControlPlane = (*JoinControlPlane)(unsafe.Pointer(in.ControlPlane)) // WARNING: in.SkipPhases requires manual conversion: does not exist in peer-type // WARNING: in.Patches requires manual conversion: does not exist in peer-type + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type return nil } @@ -1391,23 +1376,18 @@ func autoConvert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1b return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) return nil } -// Convert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd is an autogenerated conversion function. -func Convert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *v1beta2.LocalEtcd, s conversion.Scope) error { - return autoConvert_v1alpha4_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s) -} - func autoConvert_v1beta2_LocalEtcd_To_v1alpha4_LocalEtcd(in *v1beta2.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { if err := Convert_v1beta2_ImageMeta_To_v1alpha4_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { return err } out.DataDir = in.DataDir - out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + // WARNING: in.ExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type out.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) @@ -1464,21 +1444,16 @@ func autoConvert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOpt out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types (map[string]string vs []sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) return nil } -// Convert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions is an autogenerated conversion function. -func Convert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta2.NodeRegistrationOptions, s conversion.Scope) error { - return autoConvert_v1alpha4_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s) -} - func autoConvert_v1beta2_NodeRegistrationOptions_To_v1alpha4_NodeRegistrationOptions(in *v1beta2.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints)) - out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2.Arg vs map[string]string) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) // WARNING: in.ImagePullPolicy requires manual conversion: does not exist in peer-type // WARNING: in.ImagePullSerial requires manual conversion: does not exist in peer-type diff --git a/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go b/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go index f645ad11c559..147cc61f718b 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go +++ b/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go @@ -61,41 +61,44 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &controlplanev1.KubeadmControlPlane{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } + if ok { + dst.Spec.MachineTemplate.ObjectMeta = restored.Spec.MachineTemplate.ObjectMeta + dst.Spec.MachineTemplate.ReadinessGates = restored.Spec.MachineTemplate.ReadinessGates + dst.Spec.MachineTemplate.NodeDeletionTimeout = restored.Spec.MachineTemplate.NodeDeletionTimeout + dst.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.MachineTemplate.NodeVolumeDetachTimeout + dst.Spec.RolloutBefore = restored.Spec.RolloutBefore + + if restored.Spec.RemediationStrategy != nil { + dst.Spec.RemediationStrategy = restored.Spec.RemediationStrategy + } + if restored.Status.LastRemediation != nil { + dst.Status.LastRemediation = restored.Status.LastRemediation + } - dst.Spec.MachineTemplate.ObjectMeta = restored.Spec.MachineTemplate.ObjectMeta - dst.Spec.MachineTemplate.ReadinessGates = restored.Spec.MachineTemplate.ReadinessGates - dst.Spec.MachineTemplate.NodeDeletionTimeout = restored.Spec.MachineTemplate.NodeDeletionTimeout - dst.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.MachineTemplate.NodeVolumeDetachTimeout - dst.Spec.RolloutBefore = restored.Spec.RolloutBefore + if restored.Spec.MachineNamingStrategy != nil { + dst.Spec.MachineNamingStrategy = restored.Spec.MachineNamingStrategy + } - if restored.Spec.RemediationStrategy != nil { - dst.Spec.RemediationStrategy = restored.Spec.RemediationStrategy - } - if restored.Status.LastRemediation != nil { - dst.Status.LastRemediation = restored.Status.LastRemediation - } + bootstrapv1alpha3.RestoreKubeadmConfigSpec(&dst.Spec.KubeadmConfigSpec, &restored.Spec.KubeadmConfigSpec) - if restored.Spec.MachineNamingStrategy != nil { - dst.Spec.MachineNamingStrategy = restored.Spec.MachineNamingStrategy + dst.Status.Version = restored.Status.Version + dst.Status.Conditions = restored.Status.Conditions + dst.Status.AvailableReplicas = restored.Status.AvailableReplicas + dst.Status.ReadyReplicas = restored.Status.ReadyReplicas + dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas } - bootstrapv1alpha3.MergeRestoredKubeadmConfigSpec(&dst.Spec.KubeadmConfigSpec, &restored.Spec.KubeadmConfigSpec) - - dst.Status.Version = restored.Status.Version - dst.Status.Conditions = restored.Status.Conditions - dst.Status.AvailableReplicas = restored.Status.AvailableReplicas - dst.Status.ReadyReplicas = restored.Status.ReadyReplicas - dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas - + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.KubeadmConfigSpec) return nil } func (dst *KubeadmControlPlane) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*controlplanev1.KubeadmControlPlane) - if err := Convert_v1beta2_KubeadmControlPlane_To_v1alpha3_KubeadmControlPlane(src, dst, nil); err != nil { return err } @@ -128,12 +131,11 @@ func (dst *KubeadmControlPlane) ConvertFrom(srcRaw conversion.Hub) error { } dst.Status.Ready = src.Status.Deprecated.V1Beta1.ReadyReplicas > 0 - // Preserve Hub data on down-conversion except for metadata - if err := utilconversion.MarshalData(src, dst); err != nil { - return err - } + // Convert timeouts moved from one struct to another. + dst.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.KubeadmConfigSpec) - return nil + // Preserve Hub data on down-conversion except for metadata + return utilconversion.MarshalData(src, dst) } func Convert_v1beta2_KubeadmControlPlaneSpec_To_v1alpha3_KubeadmControlPlaneSpec(in *controlplanev1.KubeadmControlPlaneSpec, out *KubeadmControlPlaneSpec, s apimachineryconversion.Scope) error { diff --git a/internal/apis/controlplane/kubeadm/v1alpha3/conversion_test.go b/internal/apis/controlplane/kubeadm/v1alpha3/conversion_test.go index 9ec0c2c15904..3d85dec12c5e 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha3/conversion_test.go +++ b/internal/apis/controlplane/kubeadm/v1alpha3/conversion_test.go @@ -21,8 +21,10 @@ package v1alpha3 import ( "reflect" "testing" + "time" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/utils/ptr" "sigs.k8s.io/randfill" @@ -31,9 +33,15 @@ import ( controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta1" bootstrapv1alpha3 "sigs.k8s.io/cluster-api/internal/apis/bootstrap/kubeadm/v1alpha3" + bootstrapv1alpha4 "sigs.k8s.io/cluster-api/internal/apis/bootstrap/kubeadm/v1alpha4" utilconversion "sigs.k8s.io/cluster-api/util/conversion" ) +const ( + fakeID = "abcdef" + fakeSecret = "abcdef0123456789" +) + // Test is disabled when the race detector is enabled (via "//go:build !race" above) because otherwise the fuzz tests would just time out. func TestFuzzyConversion(t *testing.T) { @@ -50,19 +58,31 @@ func KubeadmControlPlaneFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{ spokeKubeadmControlPlaneStatus, spokeDNS, spokeKubeadmClusterConfiguration, - // This custom function is needed when ConvertTo/ConvertFrom functions - // uses the json package to unmarshal the bootstrap token string. - // - // The Kubeadm v1beta1.BootstrapTokenString type ships with a custom - // json string representation, in particular it supplies a customized - // UnmarshalJSON function that can return an error if the string - // isn't in the correct form. - // - // This function effectively disables any fuzzing for the token by setting - // the values for ID and Secret to working alphanumeric values. hubBootstrapTokenString, - spokeKubeadmBootstrapTokenString, + spokeBootstrapTokenString, spokeKubeadmConfigSpec, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) { + c.FillNoCustom(in) + + // enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration + var initControlPlaneComponentHealthCheckSeconds *int32 + if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil { + initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if in.JoinConfiguration == nil { + in.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if in.JoinConfiguration.Timeouts == nil { + in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds } } @@ -98,13 +118,13 @@ func spokeKubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, c randfill.Co } func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { - in.ID = "abcdef" - in.Secret = "abcdef0123456789" + in.ID = fakeID + in.Secret = fakeSecret } -func spokeKubeadmBootstrapTokenString(in *upstreamv1beta1.BootstrapTokenString, _ randfill.Continue) { - in.ID = "abcdef" - in.Secret = "abcdef0123456789" +func spokeBootstrapTokenString(in *bootstrapv1alpha4.BootstrapTokenString, _ randfill.Continue) { + in.ID = fakeID + in.Secret = fakeSecret } func spokeDNS(obj *upstreamv1beta1.DNS, c randfill.Continue) { @@ -127,3 +147,19 @@ func spokeKubeadmConfigSpec(in *bootstrapv1alpha3.KubeadmConfigSpec, c randfill. // Drop UseExperimentalRetryJoin as we intentionally don't preserve it. in.UseExperimentalRetryJoin = false } + +func spokeAPIServer(in *upstreamv1beta1.APIServer, c randfill.Continue) { + c.FillNoCustom(in) + + if in.TimeoutForControlPlane != nil { + in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeDiscovery(in *upstreamv1beta1.Discovery, c randfill.Continue) { + c.FillNoCustom(in) + + if in.Timeout != nil { + in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} diff --git a/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go b/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go index 9c517a318143..6ef766c14ca5 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go +++ b/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go @@ -62,38 +62,41 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &controlplanev1.KubeadmControlPlane{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } + if ok { + dst.Spec.MachineTemplate.ReadinessGates = restored.Spec.MachineTemplate.ReadinessGates + dst.Spec.MachineTemplate.NodeDeletionTimeout = restored.Spec.MachineTemplate.NodeDeletionTimeout + dst.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.MachineTemplate.NodeVolumeDetachTimeout + dst.Spec.RolloutBefore = restored.Spec.RolloutBefore + + if restored.Spec.RemediationStrategy != nil { + dst.Spec.RemediationStrategy = restored.Spec.RemediationStrategy + } + if restored.Status.LastRemediation != nil { + dst.Status.LastRemediation = restored.Status.LastRemediation + } - dst.Spec.MachineTemplate.ReadinessGates = restored.Spec.MachineTemplate.ReadinessGates - dst.Spec.MachineTemplate.NodeDeletionTimeout = restored.Spec.MachineTemplate.NodeDeletionTimeout - dst.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.MachineTemplate.NodeVolumeDetachTimeout - dst.Spec.RolloutBefore = restored.Spec.RolloutBefore - - if restored.Spec.RemediationStrategy != nil { - dst.Spec.RemediationStrategy = restored.Spec.RemediationStrategy - } - if restored.Status.LastRemediation != nil { - dst.Status.LastRemediation = restored.Status.LastRemediation - } + if restored.Spec.MachineNamingStrategy != nil { + dst.Spec.MachineNamingStrategy = restored.Spec.MachineNamingStrategy + } - if restored.Spec.MachineNamingStrategy != nil { - dst.Spec.MachineNamingStrategy = restored.Spec.MachineNamingStrategy + bootstrapv1alpha4.RestoreKubeadmConfigSpec(&dst.Spec.KubeadmConfigSpec, &restored.Spec.KubeadmConfigSpec) + dst.Status.Conditions = restored.Status.Conditions + dst.Status.AvailableReplicas = restored.Status.AvailableReplicas + dst.Status.ReadyReplicas = restored.Status.ReadyReplicas + dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas } - bootstrapv1alpha4.MergeRestoredKubeadmConfigSpec(&dst.Spec.KubeadmConfigSpec, &restored.Spec.KubeadmConfigSpec) - dst.Status.Conditions = restored.Status.Conditions - dst.Status.AvailableReplicas = restored.Status.AvailableReplicas - dst.Status.ReadyReplicas = restored.Status.ReadyReplicas - dst.Status.UpToDateReplicas = restored.Status.UpToDateReplicas - + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.KubeadmConfigSpec) return nil } func (dst *KubeadmControlPlane) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*controlplanev1.KubeadmControlPlane) - if err := Convert_v1beta2_KubeadmControlPlane_To_v1alpha4_KubeadmControlPlane(src, dst, nil); err != nil { return err } @@ -126,6 +129,9 @@ func (dst *KubeadmControlPlane) ConvertFrom(srcRaw conversion.Hub) error { } dst.Status.Ready = src.Status.Deprecated.V1Beta1.ReadyReplicas > 0 + // Convert timeouts moved from one struct to another. + dst.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.KubeadmConfigSpec) + // Preserve Hub data on down-conversion except for metadata return utilconversion.MarshalData(src, dst) } @@ -139,47 +145,53 @@ func (src *KubeadmControlPlaneTemplate) ConvertTo(dstRaw conversion.Hub) error { // Manually restore data. restored := &controlplanev1.KubeadmControlPlaneTemplate{} - if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + ok, err := utilconversion.UnmarshalData(src, restored) + if err != nil { return err } + if ok { + dst.Spec.Template.Spec.MachineTemplate = restored.Spec.Template.Spec.MachineTemplate - dst.Spec.Template.Spec.MachineTemplate = restored.Spec.Template.Spec.MachineTemplate + dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta + if restored.Spec.Template.Spec.MachineTemplate != nil { + dst.Spec.Template.Spec.MachineTemplate.ObjectMeta = restored.Spec.Template.Spec.MachineTemplate.ObjectMeta + } - dst.Spec.Template.ObjectMeta = restored.Spec.Template.ObjectMeta - if restored.Spec.Template.Spec.MachineTemplate != nil { - dst.Spec.Template.Spec.MachineTemplate.ObjectMeta = restored.Spec.Template.Spec.MachineTemplate.ObjectMeta - } + if dst.Spec.Template.Spec.MachineTemplate == nil { + dst.Spec.Template.Spec.MachineTemplate = restored.Spec.Template.Spec.MachineTemplate + } else if restored.Spec.Template.Spec.MachineTemplate != nil { + dst.Spec.Template.Spec.MachineTemplate.NodeDeletionTimeout = restored.Spec.Template.Spec.MachineTemplate.NodeDeletionTimeout + dst.Spec.Template.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.Template.Spec.MachineTemplate.NodeVolumeDetachTimeout + } - if dst.Spec.Template.Spec.MachineTemplate == nil { - dst.Spec.Template.Spec.MachineTemplate = restored.Spec.Template.Spec.MachineTemplate - } else if restored.Spec.Template.Spec.MachineTemplate != nil { - dst.Spec.Template.Spec.MachineTemplate.NodeDeletionTimeout = restored.Spec.Template.Spec.MachineTemplate.NodeDeletionTimeout - dst.Spec.Template.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.Template.Spec.MachineTemplate.NodeVolumeDetachTimeout - } + dst.Spec.Template.Spec.RolloutBefore = restored.Spec.Template.Spec.RolloutBefore - dst.Spec.Template.Spec.RolloutBefore = restored.Spec.Template.Spec.RolloutBefore + if restored.Spec.Template.Spec.RemediationStrategy != nil { + dst.Spec.Template.Spec.RemediationStrategy = restored.Spec.Template.Spec.RemediationStrategy + } - if restored.Spec.Template.Spec.RemediationStrategy != nil { - dst.Spec.Template.Spec.RemediationStrategy = restored.Spec.Template.Spec.RemediationStrategy - } + if restored.Spec.Template.Spec.MachineNamingStrategy != nil { + dst.Spec.Template.Spec.MachineNamingStrategy = restored.Spec.Template.Spec.MachineNamingStrategy + } - if restored.Spec.Template.Spec.MachineNamingStrategy != nil { - dst.Spec.Template.Spec.MachineNamingStrategy = restored.Spec.Template.Spec.MachineNamingStrategy + bootstrapv1alpha4.RestoreKubeadmConfigSpec(&dst.Spec.Template.Spec.KubeadmConfigSpec, &restored.Spec.Template.Spec.KubeadmConfigSpec) } - bootstrapv1alpha4.MergeRestoredKubeadmConfigSpec(&dst.Spec.Template.Spec.KubeadmConfigSpec, &restored.Spec.Template.Spec.KubeadmConfigSpec) - + // Override restored data with timeouts values already existing in v1beta1 but in other structs. + src.Spec.Template.Spec.KubeadmConfigSpec.ConvertTo(&dst.Spec.Template.Spec.KubeadmConfigSpec) return nil } func (dst *KubeadmControlPlaneTemplate) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*controlplanev1.KubeadmControlPlaneTemplate) - if err := Convert_v1beta2_KubeadmControlPlaneTemplate_To_v1alpha4_KubeadmControlPlaneTemplate(src, dst, nil); err != nil { return err } - // Preserve Hub data on down-conversion except for metadata + // Convert timeouts moved from one struct to another. + dst.Spec.Template.Spec.KubeadmConfigSpec.ConvertFrom(&src.Spec.Template.Spec.KubeadmConfigSpec) + + // Preserve Hub data on down-conversion except for metadata. return utilconversion.MarshalData(src, dst) } diff --git a/internal/apis/controlplane/kubeadm/v1alpha4/conversion_test.go b/internal/apis/controlplane/kubeadm/v1alpha4/conversion_test.go index 42effe42d269..da7e13d82144 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha4/conversion_test.go +++ b/internal/apis/controlplane/kubeadm/v1alpha4/conversion_test.go @@ -21,9 +21,11 @@ package v1alpha4 import ( "reflect" "testing" + "time" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/utils/ptr" "sigs.k8s.io/randfill" @@ -61,19 +63,43 @@ func KubeadmControlPlaneFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{ hubKubeadmControlPlaneStatus, spokeKubeadmControlPlaneStatus, spokeKubeadmControlPlaneTemplateResource, - // This custom function is needed when ConvertTo/ConvertFrom functions - // uses the json package to unmarshal the bootstrap token string. - // - // The Kubeadm v1beta1.BootstrapTokenString type ships with a custom - // json string representation, in particular it supplies a customized - // UnmarshalJSON function that can return an error if the string - // isn't in the correct form. - // - // This function effectively disables any fuzzing for the token by setting - // the values for ID and Secret to working alphanumeric values. hubBootstrapTokenString, spokeBootstrapTokenString, spokeKubeadmConfigSpec, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func KubeadmControlPlaneTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + spokeKubeadmControlPlaneTemplateResource, + hubBootstrapTokenString, + spokeBootstrapTokenString, + spokeKubeadmConfigSpec, + spokeAPIServer, + spokeDiscovery, + hubKubeadmConfigSpec, + } +} + +func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) { + c.FillNoCustom(in) + + // enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration + var initControlPlaneComponentHealthCheckSeconds *int32 + if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil { + initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds + } + if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil { + if in.JoinConfiguration == nil { + in.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + } + if in.JoinConfiguration.Timeouts == nil { + in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{} + } + in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds } } @@ -108,25 +134,6 @@ func spokeKubeadmControlPlaneStatus(in *KubeadmControlPlaneStatus, c randfill.Co in.Ready = in.ReadyReplicas > 0 } -func KubeadmControlPlaneTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { - return []interface{}{ - spokeKubeadmControlPlaneTemplateResource, - // This custom function is needed when ConvertTo/ConvertFrom functions - // uses the json package to unmarshal the bootstrap token string. - // - // The Kubeadm v1beta1.BootstrapTokenString type ships with a custom - // json string representation, in particular it supplies a customized - // UnmarshalJSON function that can return an error if the string - // isn't in the correct form. - // - // This function effectively disables any fuzzing for the token by setting - // the values for ID and Secret to working alphanumeric values. - hubBootstrapTokenString, - spokeBootstrapTokenString, - spokeKubeadmConfigSpec, - } -} - func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) { in.ID = fakeID in.Secret = fakeSecret @@ -153,3 +160,19 @@ func spokeKubeadmConfigSpec(in *bootstrapv1alpha4.KubeadmConfigSpec, c randfill. // Drop UseExperimentalRetryJoin as we intentionally don't preserve it. in.UseExperimentalRetryJoin = false } + +func spokeAPIServer(in *bootstrapv1alpha4.APIServer, c randfill.Continue) { + c.FillNoCustom(in) + + if in.TimeoutForControlPlane != nil { + in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} + +func spokeDiscovery(in *bootstrapv1alpha4.Discovery, c randfill.Continue) { + c.FillNoCustom(in) + + if in.Timeout != nil { + in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second}) + } +} diff --git a/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper_test.go b/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper_test.go index ad998d9f483e..187b8e98ed80 100644 --- a/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper_test.go +++ b/internal/controllers/topology/cluster/structuredmerge/serversidepathhelper_test.go @@ -565,8 +565,11 @@ func TestServerSideApplyWithDefaulting(t *testing.T) { Spec: bootstrapv1.KubeadmConfigSpec{ JoinConfiguration: &bootstrapv1.JoinConfiguration{ NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - KubeletExtraArgs: map[string]string{ - "eviction-hard": "nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%", + KubeletExtraArgs: []bootstrapv1.Arg{ + { + Name: "eviction-hard", + Value: "nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%", + }, }, }, }, diff --git a/test/e2e/clusterclass_rollout.go b/test/e2e/clusterclass_rollout.go index 7133a32eba7f..e709b4b9b2ff 100644 --- a/test/e2e/clusterclass_rollout.go +++ b/test/e2e/clusterclass_rollout.go @@ -46,6 +46,7 @@ import ( "sigs.k8s.io/cluster-api/test/framework/clusterctl" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/cluster-api/util/labels" "sigs.k8s.io/cluster-api/util/patch" ) @@ -392,6 +393,9 @@ func assertControlPlane(g Gomega, clusterClassObjects clusterClassObjects, clust clusterClass.Spec.ControlPlane.Metadata.Annotations, ccControlPlaneTemplateTemplateMetadata.Annotations, ), + // Note: ignoring utilconversion.DataAnnotation so we accept both control plane objects using the latest API version and + // control plane objects using older releases (with conversion data). + utilconversion.DataAnnotation, ) // ControlPlane.spec.machineTemplate.metadata @@ -515,6 +519,9 @@ func assertControlPlaneMachines(g Gomega, clusterObjects clusterObjects, cluster bootstrapConfigMetadata.Annotations, ).without(g, clusterv1.MachineCertificatesExpiryDateAnnotation), controlPlaneMachineTemplateMetadata.Annotations, + // Note: ignoring utilconversion.DataAnnotation so we accept both bootstrap config objects using the latest API version and + // control bootstrap config objects using older releases (with conversion data). + utilconversion.DataAnnotation, ) // ControlPlane Machine Node.metadata @@ -642,6 +649,9 @@ func assertMachineDeployments(g Gomega, clusterClassObjects clusterClassObjects, }, ccBootstrapConfigTemplate.GetAnnotations(), ), + // Note: ignoring utilconversion.DataAnnotation so we accept both bootstrap config template objects using the latest API version and + // bootstrap config template objects using older releases (with conversion data). + utilconversion.DataAnnotation, ) // MachineDeployment BootstrapConfigTemplate.spec.template.metadata expectMapsToBeEquivalent(g, bootstrapConfigTemplateTemplateMetadata.Labels, @@ -742,6 +752,9 @@ func assertMachinePools(g Gomega, clusterClassObjects clusterClassObjects, clust }, ccBootstrapConfigTemplateTemplateMetadata.Annotations, ), + // Note: ignoring utilconversion.DataAnnotation so we accept both bootstrap config objects using the latest API version and + // bootstrap config objects using older releases (with conversion data). + utilconversion.DataAnnotation, ) } } @@ -889,6 +902,9 @@ func assertMachineSetsMachines(g Gomega, clusterObjects clusterObjects, cluster machineSet.Spec.Template.Annotations, bootstrapConfigTemplateTemplateMetadata.Annotations, ), + // Note: ignoring utilconversion.DataAnnotation so we accept both bootstrap config objects using the latest API version and + // bootstrap config objects using older releases (with conversion data). + utilconversion.DataAnnotation, ) // MachineDeployment MachineSet Machine Node.metadata @@ -1351,12 +1367,17 @@ func modifyMachinePoolViaClusterAndWait(ctx context.Context, input modifyMachine } } -func expectMapsToBeEquivalent(g Gomega, m1, m2 map[string]string) { - if m1 == nil { - m1 = map[string]string{} +func expectMapsToBeEquivalent(g Gomega, m1, m2 map[string]string, ignoreKeys ...string) { + m1Copy, m2Copy := map[string]string{}, map[string]string{} + for k, v := range m1 { + m1Copy[k] = v } - if m2 == nil { - m2 = map[string]string{} + for k, v := range m2 { + m2Copy[k] = v } - g.ExpectWithOffset(1, m1).To(BeEquivalentTo(m2)) + for _, key := range ignoreKeys { + delete(m1Copy, key) + delete(m2Copy, key) + } + g.ExpectWithOffset(1, m1Copy).To(BeEquivalentTo(m2Copy)) } diff --git a/test/e2e/data/infrastructure-docker/main/clusterclass-quick-start.yaml b/test/e2e/data/infrastructure-docker/main/clusterclass-quick-start.yaml index e3b77e871f91..8dc1b002892f 100644 --- a/test/e2e/data/infrastructure-docker/main/clusterclass-quick-start.yaml +++ b/test/e2e/data/infrastructure-docker/main/clusterclass-quick-start.yaml @@ -399,7 +399,7 @@ spec: plugins: - name: PodSecurity configuration: - apiVersion: pod-security.admission.config.k8s.io/v1{{ if semverCompare "< v1.25" .builtin.controlPlane.version }}beta1{{ end }} + apiVersion: pod-security.admission.config.k8s.io/v1{{ if semverCompare "< v1.25-0" .builtin.controlPlane.version }}beta1{{ end }} kind: PodSecurityConfiguration defaults: enforce: "baseline" @@ -413,7 +413,7 @@ spec: runtimeClasses: [] namespaces: [kube-system] path: /etc/kubernetes/kube-apiserver-admission-pss.yaml - enabledIf: '{{ semverCompare ">= v1.24" .builtin.controlPlane.version }}' + enabledIf: '{{ semverCompare ">= v1.24-0" .builtin.controlPlane.version }}' - name: controlPlaneLogLevel enabledIf: "{{ if .kubeControlPlaneLogLevel }}true{{end}}" description: "Configures control plane components and kubelet to run at the log level specified in the variable `kubeControlPlaneLogLevel`." diff --git a/test/extension/handlers/topologymutation/handler.go b/test/extension/handlers/topologymutation/handler.go index a88bbb3a2e08..5d4b5ee6f7a3 100644 --- a/test/extension/handlers/topologymutation/handler.go +++ b/test/extension/handlers/topologymutation/handler.go @@ -33,8 +33,8 @@ import ( intstrutil "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" - bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" - controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" + bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + controlplanev1beta1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1" clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" "sigs.k8s.io/cluster-api/exp/runtime/topologymutation" @@ -61,13 +61,18 @@ type ExtensionHandlers struct { } // NewExtensionHandlers returns a new ExtensionHandlers for the topology mutation hook handlers. -func NewExtensionHandlers(scheme *runtime.Scheme) *ExtensionHandlers { +func NewExtensionHandlers() *ExtensionHandlers { + scheme := runtime.NewScheme() + _ = infrav1.AddToScheme(scheme) + _ = infraexpv1.AddToScheme(scheme) + _ = bootstrapv1beta1.AddToScheme(scheme) + _ = controlplanev1beta1.AddToScheme(scheme) return &ExtensionHandlers{ // Add the apiGroups being handled to the decoder decoder: serializer.NewCodecFactory(scheme).UniversalDecoder( infrav1.GroupVersion, - controlplanev1.GroupVersion, - bootstrapv1.GroupVersion, + bootstrapv1beta1.GroupVersion, + controlplanev1beta1.GroupVersion, ), } } @@ -93,13 +98,13 @@ func (h *ExtensionHandlers) GeneratePatches(ctx context.Context, req *runtimehoo log.Error(err, "Error patching DockerClusterTemplate") return errors.Wrap(err, "error patching DockerClusterTemplate") } - case *controlplanev1.KubeadmControlPlaneTemplate: + case *controlplanev1beta1.KubeadmControlPlaneTemplate: err := patchKubeadmControlPlaneTemplate(ctx, obj, variables) if err != nil { log.Error(err, "Error patching KubeadmControlPlaneTemplate") return errors.Wrapf(err, "error patching KubeadmControlPlaneTemplate") } - case *bootstrapv1.KubeadmConfigTemplate: + case *bootstrapv1beta1.KubeadmConfigTemplate: // NOTE: KubeadmConfigTemplate could be linked to one or more of the existing MachineDeployment class; // the patchKubeadmConfigTemplate func shows how to implement patches only for KubeadmConfigTemplates // linked to a specific MachineDeployment class; another option is to check the holderRef value and call @@ -124,7 +129,7 @@ func (h *ExtensionHandlers) GeneratePatches(ctx context.Context, req *runtimehoo } } return nil - }) + }, topologymutation.FailForUnknownTypes{}) } // patchDockerClusterTemplate patches the DockerClusterTemplate. @@ -151,7 +156,7 @@ func patchDockerClusterTemplate(_ context.Context, dockerClusterTemplate *infrav // NOTE: RolloutStrategy.RollingUpdate.MaxSurge patch is not required for any special reason, it is used for testing the patch machinery itself. // NOTE: cgroupfs patch is not required anymore after the introduction of the automatic setting kubeletExtraArgs for CAPD, however we keep it // as example of version aware patches. -func patchKubeadmControlPlaneTemplate(ctx context.Context, kcpTemplate *controlplanev1.KubeadmControlPlaneTemplate, templateVariables map[string]apiextensionsv1.JSON) error { +func patchKubeadmControlPlaneTemplate(ctx context.Context, kcpTemplate *controlplanev1beta1.KubeadmControlPlaneTemplate, templateVariables map[string]apiextensionsv1.JSON) error { log := ctrl.LoggerFrom(ctx) // 1) If the Kubernetes version from builtin.controlPlane.version is below 1.24.0 set "cgroup-driver": "cgroupfs" to @@ -175,7 +180,7 @@ func patchKubeadmControlPlaneTemplate(ctx context.Context, kcpTemplate *controlp log.Info(fmt.Sprintf("Setting KubeadmControlPlaneTemplate cgroup-driver to %q", cgroupDriverCgroupfs)) // Set the cgroupDriver in the InitConfiguration. if kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration == nil { - kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{} + kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1beta1.InitConfiguration{} } if kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.KubeletExtraArgs == nil { kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.KubeletExtraArgs = map[string]string{} @@ -184,7 +189,7 @@ func patchKubeadmControlPlaneTemplate(ctx context.Context, kcpTemplate *controlp // Set the cgroupDriver in the JoinConfiguration. if kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration == nil { - kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1beta1.JoinConfiguration{} } if kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.KubeletExtraArgs == nil { kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.KubeletExtraArgs = map[string]string{} @@ -206,10 +211,10 @@ func patchKubeadmControlPlaneTemplate(ctx context.Context, kcpTemplate *controlp kubeadmControlPlaneMaxSurgeIntOrString := intstrutil.Parse(kcpControlPlaneMaxSurge) log.Info(fmt.Sprintf("Setting KubeadmControlPlaneMaxSurge to %q", kubeadmControlPlaneMaxSurgeIntOrString.String())) if kcpTemplate.Spec.Template.Spec.RolloutStrategy == nil { - kcpTemplate.Spec.Template.Spec.RolloutStrategy = &controlplanev1.RolloutStrategy{} + kcpTemplate.Spec.Template.Spec.RolloutStrategy = &controlplanev1beta1.RolloutStrategy{} } if kcpTemplate.Spec.Template.Spec.RolloutStrategy.RollingUpdate == nil { - kcpTemplate.Spec.Template.Spec.RolloutStrategy.RollingUpdate = &controlplanev1.RollingUpdate{} + kcpTemplate.Spec.Template.Spec.RolloutStrategy.RollingUpdate = &controlplanev1beta1.RollingUpdate{} } kcpTemplate.Spec.Template.Spec.RolloutStrategy.RollingUpdate.MaxSurge = &kubeadmControlPlaneMaxSurgeIntOrString return nil @@ -220,7 +225,7 @@ func patchKubeadmControlPlaneTemplate(ctx context.Context, kcpTemplate *controlp // to cgroupfs for Kubernetes < 1.24; this patch is required for tests to work with older kind images. // NOTE: cgroupfs patch is not required anymore after the introduction of the automatic setting kubeletExtraArgs for CAPD, however we keep it // as example of version aware patches. -func patchKubeadmConfigTemplate(ctx context.Context, k *bootstrapv1.KubeadmConfigTemplate, templateVariables map[string]apiextensionsv1.JSON) error { +func patchKubeadmConfigTemplate(ctx context.Context, k *bootstrapv1beta1.KubeadmConfigTemplate, templateVariables map[string]apiextensionsv1.JSON) error { log := ctrl.LoggerFrom(ctx) // Only patch the customImage if this DockerMachineTemplate belongs to a MachineDeployment or MachinePool with class "default-class" @@ -265,7 +270,7 @@ func patchKubeadmConfigTemplate(ctx context.Context, k *bootstrapv1.KubeadmConfi // Set the cgroupDriver in the JoinConfiguration. if k.Spec.Template.Spec.JoinConfiguration == nil { - k.Spec.Template.Spec.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + k.Spec.Template.Spec.JoinConfiguration = &bootstrapv1beta1.JoinConfiguration{} } if k.Spec.Template.Spec.JoinConfiguration.NodeRegistration.KubeletExtraArgs == nil { k.Spec.Template.Spec.JoinConfiguration.NodeRegistration.KubeletExtraArgs = map[string]string{} @@ -299,7 +304,7 @@ func patchKubeadmConfigTemplate(ctx context.Context, k *bootstrapv1.KubeadmConfi // Set the cgroupDriver in the JoinConfiguration. if k.Spec.Template.Spec.JoinConfiguration == nil { - k.Spec.Template.Spec.JoinConfiguration = &bootstrapv1.JoinConfiguration{} + k.Spec.Template.Spec.JoinConfiguration = &bootstrapv1beta1.JoinConfiguration{} } if k.Spec.Template.Spec.JoinConfiguration.NodeRegistration.KubeletExtraArgs == nil { k.Spec.Template.Spec.JoinConfiguration.NodeRegistration.KubeletExtraArgs = map[string]string{} @@ -389,7 +394,7 @@ func patchDockerMachinePoolTemplate(ctx context.Context, dockerMachinePoolTempla return errors.Wrap(err, "could not set customImage to MachinePool DockerMachinePoolTemplate") } - semVer, err := version.ParseMajorMinorPatchTolerant(mpVersion) + semVer, err := semver.ParseTolerant(mpVersion) if err != nil { return errors.Wrap(err, "could not parse MachinePool version") } diff --git a/test/extension/handlers/topologymutation/handler_integration_test.go b/test/extension/handlers/topologymutation/handler_integration_test.go index d86a1bde436f..7345b72db645 100644 --- a/test/extension/handlers/topologymutation/handler_integration_test.go +++ b/test/extension/handlers/topologymutation/handler_integration_test.go @@ -85,7 +85,7 @@ func TestHandler(t *testing.T) { // Create a RuntimeClient that is backed by our Runtime Extension. runtimeClient := &injectRuntimeClient{ - runtimeExtension: NewExtensionHandlers(testScheme), + runtimeExtension: NewExtensionHandlers(), } // Create a ClusterClassReconciler. diff --git a/test/extension/handlers/topologymutation/handler_test.go b/test/extension/handlers/topologymutation/handler_test.go index aebffa9039f3..c6a1fb12474f 100644 --- a/test/extension/handlers/topologymutation/handler_test.go +++ b/test/extension/handlers/topologymutation/handler_test.go @@ -30,8 +30,8 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" - bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2" - controlplanev1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta2" + bootstrapv1beta1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta1" + controlplanev1beta1 "sigs.k8s.io/cluster-api/api/controlplane/kubeadm/v1beta1" runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1" infrav1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1" infraexpv1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/exp/api/v1beta1" @@ -43,8 +43,9 @@ var ( func init() { _ = infrav1.AddToScheme(testScheme) - _ = controlplanev1.AddToScheme(testScheme) - _ = bootstrapv1.AddToScheme(testScheme) + _ = infraexpv1.AddToScheme(testScheme) + _ = controlplanev1beta1.AddToScheme(testScheme) + _ = bootstrapv1beta1.AddToScheme(testScheme) } func Test_patchDockerClusterTemplate(t *testing.T) { @@ -102,21 +103,21 @@ func Test_patchKubeadmControlPlaneTemplate(t *testing.T) { tests := []struct { name string - template *controlplanev1.KubeadmControlPlaneTemplate + template *controlplanev1beta1.KubeadmControlPlaneTemplate variables map[string]apiextensionsv1.JSON - expectedTemplate *controlplanev1.KubeadmControlPlaneTemplate + expectedTemplate *controlplanev1beta1.KubeadmControlPlaneTemplate expectedErr bool }{ { name: "fails if builtin.controlPlane.version is not set", - template: &controlplanev1.KubeadmControlPlaneTemplate{}, + template: &controlplanev1beta1.KubeadmControlPlaneTemplate{}, variables: nil, - expectedTemplate: &controlplanev1.KubeadmControlPlaneTemplate{}, + expectedTemplate: &controlplanev1beta1.KubeadmControlPlaneTemplate{}, expectedErr: true, }, { name: "sets KubeletExtraArgs[cgroup-driver] to cgroupfs for Kubernetes < 1.24", - template: &controlplanev1.KubeadmControlPlaneTemplate{}, + template: &controlplanev1beta1.KubeadmControlPlaneTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ ControlPlane: &runtimehooksv1.ControlPlaneBuiltins{ @@ -124,19 +125,23 @@ func Test_patchKubeadmControlPlaneTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &controlplanev1.KubeadmControlPlaneTemplate{ - Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{ - Template: controlplanev1.KubeadmControlPlaneTemplateResource{ - Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{ - KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{ - InitConfiguration: &bootstrapv1.InitConfiguration{ - NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - KubeletExtraArgs: map[string]string{"cgroup-driver": "cgroupfs"}, + expectedTemplate: &controlplanev1beta1.KubeadmControlPlaneTemplate{ + Spec: controlplanev1beta1.KubeadmControlPlaneTemplateSpec{ + Template: controlplanev1beta1.KubeadmControlPlaneTemplateResource{ + Spec: controlplanev1beta1.KubeadmControlPlaneTemplateResourceSpec{ + KubeadmConfigSpec: bootstrapv1beta1.KubeadmConfigSpec{ + InitConfiguration: &bootstrapv1beta1.InitConfiguration{ + NodeRegistration: bootstrapv1beta1.NodeRegistrationOptions{ + KubeletExtraArgs: map[string]string{ + "cgroup-driver": "cgroupfs", + }, }, }, - JoinConfiguration: &bootstrapv1.JoinConfiguration{ - NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - KubeletExtraArgs: map[string]string{"cgroup-driver": "cgroupfs"}, + JoinConfiguration: &bootstrapv1beta1.JoinConfiguration{ + NodeRegistration: bootstrapv1beta1.NodeRegistrationOptions{ + KubeletExtraArgs: map[string]string{ + "cgroup-driver": "cgroupfs", + }, }, }, }, @@ -147,7 +152,7 @@ func Test_patchKubeadmControlPlaneTemplate(t *testing.T) { }, { name: "do not set KubeletExtraArgs[cgroup-driver] to cgroupfs for Kubernetes >= 1.24", - template: &controlplanev1.KubeadmControlPlaneTemplate{}, + template: &controlplanev1beta1.KubeadmControlPlaneTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ ControlPlane: &runtimehooksv1.ControlPlaneBuiltins{ @@ -155,11 +160,11 @@ func Test_patchKubeadmControlPlaneTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &controlplanev1.KubeadmControlPlaneTemplate{}, + expectedTemplate: &controlplanev1beta1.KubeadmControlPlaneTemplate{}, }, { name: "sets RolloutStrategy.RollingUpdate.MaxSurge if the kubeadmControlPlaneMaxSurge is provided", - template: &controlplanev1.KubeadmControlPlaneTemplate{}, + template: &controlplanev1beta1.KubeadmControlPlaneTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ ControlPlane: &runtimehooksv1.ControlPlaneBuiltins{ @@ -168,12 +173,12 @@ func Test_patchKubeadmControlPlaneTemplate(t *testing.T) { })}, "kubeadmControlPlaneMaxSurge": {Raw: toJSON("1")}, }, - expectedTemplate: &controlplanev1.KubeadmControlPlaneTemplate{ - Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{ - Template: controlplanev1.KubeadmControlPlaneTemplateResource{ - Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{ - RolloutStrategy: &controlplanev1.RolloutStrategy{ - RollingUpdate: &controlplanev1.RollingUpdate{MaxSurge: &intstr.IntOrString{IntVal: 1}}, + expectedTemplate: &controlplanev1beta1.KubeadmControlPlaneTemplate{ + Spec: controlplanev1beta1.KubeadmControlPlaneTemplateSpec{ + Template: controlplanev1beta1.KubeadmControlPlaneTemplateResource{ + Spec: controlplanev1beta1.KubeadmControlPlaneTemplateResourceSpec{ + RolloutStrategy: &controlplanev1beta1.RolloutStrategy{ + RollingUpdate: &controlplanev1beta1.RollingUpdate{MaxSurge: &intstr.IntOrString{IntVal: 1}}, }, }, }, @@ -199,21 +204,21 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { tests := []struct { name string - template *bootstrapv1.KubeadmConfigTemplate + template *bootstrapv1beta1.KubeadmConfigTemplate variables map[string]apiextensionsv1.JSON - expectedTemplate *bootstrapv1.KubeadmConfigTemplate + expectedTemplate *bootstrapv1beta1.KubeadmConfigTemplate expectedErr bool }{ { name: "fails if builtin variable is not set", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: nil, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, expectedErr: true, }, { name: "no op for MachineDeployment class != default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachineDeployment: &runtimehooksv1.MachineDeploymentBuiltins{ @@ -221,11 +226,11 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, }, { name: "fails if builtin.machineDeployment.version is not set for MachineDeployment class == default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachineDeployment: &runtimehooksv1.MachineDeploymentBuiltins{ @@ -233,12 +238,12 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, expectedErr: true, }, { name: "set KubeletExtraArgs[cgroup-driver] to cgroupfs for Kubernetes < 1.24 and MachineDeployment class == default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachineDeployment: &runtimehooksv1.MachineDeploymentBuiltins{ @@ -247,13 +252,15 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{ - Spec: bootstrapv1.KubeadmConfigTemplateSpec{ - Template: bootstrapv1.KubeadmConfigTemplateResource{ - Spec: bootstrapv1.KubeadmConfigSpec{ - JoinConfiguration: &bootstrapv1.JoinConfiguration{ - NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - KubeletExtraArgs: map[string]string{"cgroup-driver": "cgroupfs"}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{ + Spec: bootstrapv1beta1.KubeadmConfigTemplateSpec{ + Template: bootstrapv1beta1.KubeadmConfigTemplateResource{ + Spec: bootstrapv1beta1.KubeadmConfigSpec{ + JoinConfiguration: &bootstrapv1beta1.JoinConfiguration{ + NodeRegistration: bootstrapv1beta1.NodeRegistrationOptions{ + KubeletExtraArgs: map[string]string{ + "cgroup-driver": "cgroupfs", + }, }, }, }, @@ -263,7 +270,7 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, { name: "do not set KubeletExtraArgs[cgroup-driver] to cgroupfs for Kubernetes >= 1.24 and MachineDeployment class == default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachineDeployment: &runtimehooksv1.MachineDeploymentBuiltins{ @@ -272,11 +279,11 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, }, { name: "no op for MachinePool class != default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachinePool: &runtimehooksv1.MachinePoolBuiltins{ @@ -284,11 +291,11 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, }, { name: "fails if builtin.machinePool.version is not set for MachinePool class == default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachinePool: &runtimehooksv1.MachinePoolBuiltins{ @@ -296,12 +303,12 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, expectedErr: true, }, { name: "set KubeletExtraArgs[cgroup-driver] to cgroupfs for Kubernetes < 1.24 and MachinePool class == default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachinePool: &runtimehooksv1.MachinePoolBuiltins{ @@ -310,13 +317,15 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{ - Spec: bootstrapv1.KubeadmConfigTemplateSpec{ - Template: bootstrapv1.KubeadmConfigTemplateResource{ - Spec: bootstrapv1.KubeadmConfigSpec{ - JoinConfiguration: &bootstrapv1.JoinConfiguration{ - NodeRegistration: bootstrapv1.NodeRegistrationOptions{ - KubeletExtraArgs: map[string]string{"cgroup-driver": "cgroupfs"}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{ + Spec: bootstrapv1beta1.KubeadmConfigTemplateSpec{ + Template: bootstrapv1beta1.KubeadmConfigTemplateResource{ + Spec: bootstrapv1beta1.KubeadmConfigSpec{ + JoinConfiguration: &bootstrapv1beta1.JoinConfiguration{ + NodeRegistration: bootstrapv1beta1.NodeRegistrationOptions{ + KubeletExtraArgs: map[string]string{ + "cgroup-driver": "cgroupfs", + }, }, }, }, @@ -326,7 +335,7 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, { name: "do not set KubeletExtraArgs[cgroup-driver] to cgroupfs for Kubernetes >= 1.24 and MachinePool class == default-worker", - template: &bootstrapv1.KubeadmConfigTemplate{}, + template: &bootstrapv1beta1.KubeadmConfigTemplate{}, variables: map[string]apiextensionsv1.JSON{ runtimehooksv1.BuiltinsName: {Raw: toJSON(runtimehooksv1.Builtins{ MachinePool: &runtimehooksv1.MachinePoolBuiltins{ @@ -335,7 +344,7 @@ func Test_patchKubeadmConfigTemplate(t *testing.T) { }, })}, }, - expectedTemplate: &bootstrapv1.KubeadmConfigTemplate{}, + expectedTemplate: &bootstrapv1beta1.KubeadmConfigTemplate{}, }, } for _, tt := range tests { @@ -484,7 +493,7 @@ func Test_patchDockerMachinePoolTemplate(t *testing.T) { // NOTE: custom RuntimeExtension must test specif logic added to GeneratePatches, if any. func TestHandler_GeneratePatches(t *testing.T) { g := NewWithT(t) - h := NewExtensionHandlers(testScheme) + h := NewExtensionHandlers() controlPlaneVarsV123WithMaxSurge := []runtimehooksv1.Variable{ newVariable(runtimehooksv1.BuiltinsName, runtimehooksv1.Builtins{ ControlPlane: &runtimehooksv1.ControlPlaneBuiltins{ @@ -512,10 +521,10 @@ func TestHandler_GeneratePatches(t *testing.T) { }, }), } - kubeadmControlPlaneTemplate := controlplanev1.KubeadmControlPlaneTemplate{ + kubeadmControlPlaneTemplate := controlplanev1beta1.KubeadmControlPlaneTemplate{ TypeMeta: metav1.TypeMeta{ Kind: "KubeadmControlPlaneTemplate", - APIVersion: controlplanev1.GroupVersion.String(), + APIVersion: controlplanev1beta1.GroupVersion.String(), }, } dockerMachineTemplate := infrav1.DockerMachineTemplate{ @@ -536,10 +545,10 @@ func TestHandler_GeneratePatches(t *testing.T) { APIVersion: infrav1.GroupVersion.String(), }, } - kubeadmConfigTemplate := bootstrapv1.KubeadmConfigTemplate{ + kubeadmConfigTemplate := bootstrapv1beta1.KubeadmConfigTemplate{ TypeMeta: metav1.TypeMeta{ Kind: "KubeadmConfigTemplate", - APIVersion: bootstrapv1.GroupVersion.String(), + APIVersion: bootstrapv1beta1.GroupVersion.String(), }, } tests := []struct { @@ -581,7 +590,7 @@ func TestHandler_GeneratePatches(t *testing.T) { {"op":"add","path":"/spec/template/spec/joinConfiguration","value":{"discovery":{},"nodeRegistration":{"kubeletExtraArgs":{"cgroup-driver":"cgroupfs"}}}} ]`), responseItem("6", `[ -{"op":"add","path":"/spec/template/spec/customImage","value":"kindest/node:v1.23.0"} +{"op":"add","path":"/spec/template/spec/template/customImage","value":"kindest/node:v1.23.0"} ]`), }, }, diff --git a/test/extension/handlers/topologymutation/testdata/clusterclass-quick-start-runtimesdk.yaml b/test/extension/handlers/topologymutation/testdata/clusterclass-quick-start-runtimesdk.yaml index e8e78d83ae0f..967fd8c61682 100644 --- a/test/extension/handlers/topologymutation/testdata/clusterclass-quick-start-runtimesdk.yaml +++ b/test/extension/handlers/topologymutation/testdata/clusterclass-quick-start-runtimesdk.yaml @@ -5,7 +5,7 @@ metadata: spec: controlPlane: ref: - apiVersion: controlplane.cluster.x-k8s.io/v1beta2 + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlaneTemplate name: quick-start-control-plane machineInfrastructure: @@ -28,7 +28,7 @@ spec: template: bootstrap: ref: - apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate name: quick-start-default-worker-bootstraptemplate infrastructure: @@ -43,7 +43,7 @@ spec: template: bootstrap: ref: - apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate name: quick-start-default-worker-bootstraptemplate infrastructure: @@ -85,7 +85,7 @@ spec: loadBalancer: {} --- kind: KubeadmControlPlaneTemplate -apiVersion: controlplane.cluster.x-k8s.io/v1beta2 +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 metadata: name: quick-start-control-plane spec: @@ -137,7 +137,7 @@ spec: - containerPath: "/var/run/docker.sock" hostPath: "/var/run/docker.sock" --- -apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate metadata: name: quick-start-default-worker-bootstraptemplate diff --git a/test/extension/main.go b/test/extension/main.go index 506e167840fb..522e982b2e9f 100644 --- a/test/extension/main.go +++ b/test/extension/main.go @@ -280,7 +280,7 @@ func setupTopologyMutationHookHandlers(runtimeExtensionWebhookServer *server.Ser // Create the ExtensionHandlers for the Topology Mutation Hooks. // NOTE: it is not mandatory to group all the ExtensionHandlers using a struct, what is important // is to have HandlerFunc with the signature defined in sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1. - topologyMutationExtensionHandlers := topologymutation.NewExtensionHandlers(scheme) + topologyMutationExtensionHandlers := topologymutation.NewExtensionHandlers() if err := runtimeExtensionWebhookServer.AddExtensionHandler(server.ExtensionHandler{ Hook: runtimehooksv1.GeneratePatches, diff --git a/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go b/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go index e80e1f1d1bf2..2e5dd0c4c292 100644 --- a/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go +++ b/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go @@ -85,15 +85,14 @@ func (a *writeFilesAction) Unmarshal(userData []byte, kindMapping kind.Mapping) if len(contentSplit) != 3 { return errors.Errorf("invalid kubeadm config file, unable to parse it") } - clusterConfiguration := &bootstrapv1.ClusterConfiguration{} - initConfiguration, err := kubeadmtypes.UnmarshalInitConfiguration(contentSplit[2], clusterConfiguration) + initConfiguration, err := kubeadmtypes.UnmarshalInitConfiguration(contentSplit[2]) if err != nil { return errors.Wrapf(err, "failed to parse init configuration") } fixNodeRegistration(&initConfiguration.NodeRegistration, kindMapping) - contentSplit[2], err = kubeadmtypes.MarshalInitConfigurationForVersion(clusterConfiguration, initConfiguration, kindMapping.KubernetesVersion) + contentSplit[2], err = kubeadmtypes.MarshalInitConfigurationForVersion(initConfiguration, kindMapping.KubernetesVersion) if err != nil { return errors.Wrapf(err, "failed to marshal init configuration") } @@ -101,15 +100,14 @@ func (a *writeFilesAction) Unmarshal(userData []byte, kindMapping kind.Mapping) } if f.Path == kubeadmJoinPath { // NOTE: in case of join the kubeadmConfigFile contains only the join Configuration - clusterConfiguration := &bootstrapv1.ClusterConfiguration{} - joinConfiguration, err := kubeadmtypes.UnmarshalJoinConfiguration(f.Content, clusterConfiguration) + joinConfiguration, err := kubeadmtypes.UnmarshalJoinConfiguration(f.Content) if err != nil { return errors.Wrapf(err, "failed to parse join configuration") } fixNodeRegistration(&joinConfiguration.NodeRegistration, kindMapping) - a.Files[i].Content, err = kubeadmtypes.MarshalJoinConfigurationForVersion(clusterConfiguration, joinConfiguration, kindMapping.KubernetesVersion) + a.Files[i].Content, err = kubeadmtypes.MarshalJoinConfigurationForVersion(joinConfiguration, kindMapping.KubernetesVersion) if err != nil { return errors.Wrapf(err, "failed to marshal join configuration") } @@ -130,7 +128,7 @@ func fixNodeRegistration(nodeRegistration *bootstrapv1.NodeRegistrationOptions, } if nodeRegistration.KubeletExtraArgs == nil { - nodeRegistration.KubeletExtraArgs = map[string]string{} + nodeRegistration.KubeletExtraArgs = []bootstrapv1.Arg{} } // Disable disk resource management by default. @@ -152,14 +150,21 @@ func fixNodeRegistration(nodeRegistration *bootstrapv1.NodeRegistrationOptions, if version.Compare(kindMapping.KubernetesVersion, cgroupDriverPatchVersionCeiling) == -1 { // kubeadm for Kubernetes version <= 1.23 defaults to cgroup-driver=cgroupfs; following settings makes kubelet // to run consistently with what kubeadm expects. - nodeRegistration.KubeletExtraArgs["cgroup-driver"] = cgroupDriverCgroupfs + defaultExtraArg(nodeRegistration, "cgroup-driver", cgroupDriverCgroupfs) } } // defautExtraArg sets a default value for an extra arg if it is not already set. func defaultExtraArg(nodeRegistration *bootstrapv1.NodeRegistrationOptions, arg, value string) { - if _, ok := nodeRegistration.KubeletExtraArgs[arg]; !ok { - nodeRegistration.KubeletExtraArgs[arg] = value + found := false + for i := range nodeRegistration.KubeletExtraArgs { + if nodeRegistration.KubeletExtraArgs[i].Name == arg { + nodeRegistration.KubeletExtraArgs[i].Value = value + found = true + } + } + if !found { + nodeRegistration.KubeletExtraArgs = append(nodeRegistration.KubeletExtraArgs, bootstrapv1.Arg{Name: arg, Value: value}) } } diff --git a/util/conversion/conversion.go b/util/conversion/conversion.go index c885a3b5ce50..212481142c97 100644 --- a/util/conversion/conversion.go +++ b/util/conversion/conversion.go @@ -242,7 +242,10 @@ func FuzzTestFunc(input FuzzTestFuncInput) func(*testing.T) { input.SpokeAfterMutation(spokeAfter) } - g.Expect(apiequality.Semantic.DeepEqual(spokeBefore, spokeAfter)).To(gomega.BeTrue(), cmp.Diff(spokeBefore, spokeAfter)) + if !apiequality.Semantic.DeepEqual(spokeBefore, spokeAfter) { + diff := cmp.Diff(spokeBefore, spokeAfter) + g.Expect(false).To(gomega.BeTrue(), diff) + } } }) t.Run("hub-spoke-hub", func(t *testing.T) { @@ -266,7 +269,10 @@ func FuzzTestFunc(input FuzzTestFuncInput) func(*testing.T) { input.HubAfterMutation(hubAfter) } - g.Expect(apiequality.Semantic.DeepEqual(hubBefore, hubAfter)).To(gomega.BeTrue(), cmp.Diff(hubBefore, hubAfter)) + if !apiequality.Semantic.DeepEqual(hubBefore, hubAfter) { + diff := cmp.Diff(hubBefore, hubAfter) + g.Expect(false).To(gomega.BeTrue(), diff) + } } }) }