diff --git a/Makefile b/Makefile index 750c804b6fe5..7bc1a6e1260b 100644 --- a/Makefile +++ b/Makefile @@ -501,10 +501,11 @@ generate-go-conversions-kubeadm-bootstrap: $(CONVERSION_GEN) ## Generate convers --extra-peer-dirs=sigs.k8s.io/cluster-api/internal/apis/core/v1alpha4 \ --output-file-base=zz_generated.conversion $(CONVERSION_GEN_OUTPUT_BASE) \ --go-header-file=./hack/boilerplate/boilerplate.generatego.txt - $(MAKE) clean-generated-conversions SRC_DIRS="./bootstrap/kubeadm/types/upstreamv1beta2,./bootstrap/kubeadm/types/upstreamv1beta3" + $(MAKE) clean-generated-conversions SRC_DIRS="./bootstrap/kubeadm/types/upstreamv1beta2,./bootstrap/kubeadm/types/upstreamv1beta3,./bootstrap/kubeadm/types/upstreamv1beta4" $(CONVERSION_GEN) \ --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta2 \ --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta3 \ + --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta4 \ --build-tag=ignore_autogenerated_kubeadm_types \ --output-file-base=zz_generated.conversion $(CONVERSION_GEN_OUTPUT_BASE) \ --go-header-file=./hack/boilerplate/boilerplate.generatego.txt diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go index 183f99c1e288..a432e8bbe82e 100644 --- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go @@ -445,36 +445,38 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", kubernetesVersion) } - if scope.Config.Spec.InitConfiguration == nil { - scope.Config.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{ + if scope.Config.Spec.ClusterConfiguration == nil { + scope.Config.Spec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{ TypeMeta: metav1.TypeMeta{ - APIVersion: "kubeadm.k8s.io/v1beta3", - Kind: "InitConfiguration", + APIVersion: "kubeadm.k8s.io/v1beta4", + Kind: "ClusterConfiguration", }, } } - initdata, err := kubeadmtypes.MarshalInitConfigurationForVersion(scope.Config.Spec.InitConfiguration, parsedVersion) + // 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 init configuration") + scope.Error(err, "Failed to marshal cluster configuration") return ctrl.Result{}, err } - if scope.Config.Spec.ClusterConfiguration == nil { - scope.Config.Spec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{ + if scope.Config.Spec.InitConfiguration == nil { + scope.Config.Spec.InitConfiguration = &bootstrapv1.InitConfiguration{ TypeMeta: metav1.TypeMeta{ - APIVersion: "kubeadm.k8s.io/v1beta3", - Kind: "ClusterConfiguration", + APIVersion: "kubeadm.k8s.io/v1beta4", + Kind: "InitConfiguration", }, } } - // 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) + // 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) if err != nil { - scope.Error(err, "Failed to marshal cluster configuration") + scope.Error(err, "Failed to marshal init configuration") return ctrl.Result{}, err } @@ -602,7 +604,9 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) joinConfiguration.NodeRegistration.Taints = append(joinConfiguration.NodeRegistration.Taints, clusterv1.NodeUninitializedTaint) } - joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(joinConfiguration, parsedVersion) + // 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) if err != nil { scope.Error(err, "Failed to marshal join configuration") return ctrl.Result{}, err @@ -711,7 +715,9 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", kubernetesVersion) } - joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(scope.Config.Spec.JoinConfiguration, parsedVersion) + // 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) if err != nil { scope.Error(err, "Failed to marshal join configuration") return ctrl.Result{}, err diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go index e893686d52e1..eb3f43d4532c 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go @@ -23,75 +23,71 @@ import ( bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" ) -// ConvertTo converts this ClusterConfiguration to the Hub version (v1alpha4). func (src *ClusterConfiguration) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.ClusterConfiguration) return Convert_upstreamv1beta1_ClusterConfiguration_To_v1beta1_ClusterConfiguration(src, dst, nil) } -// ConvertFrom converts from the ClusterConfiguration Hub version (v1alpha4) to this version. func (dst *ClusterConfiguration) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.ClusterConfiguration) return Convert_v1beta1_ClusterConfiguration_To_upstreamv1beta1_ClusterConfiguration(src, dst, nil) } -// ConvertTo converts this ClusterStatus to the Hub version (v1alpha4). func (src *ClusterStatus) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.ClusterStatus) return Convert_upstreamv1beta1_ClusterStatus_To_v1beta1_ClusterStatus(src, dst, nil) } -// ConvertFrom converts from the ClusterStatus Hub version (v1alpha4) to this version. func (dst *ClusterStatus) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.ClusterStatus) return Convert_v1beta1_ClusterStatus_To_upstreamv1beta1_ClusterStatus(src, dst, nil) } -// ConvertTo converts this InitConfiguration to the Hub version (v1alpha4). func (src *InitConfiguration) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.InitConfiguration) return Convert_upstreamv1beta1_InitConfiguration_To_v1beta1_InitConfiguration(src, dst, nil) } -// ConvertFrom converts from the InitConfiguration Hub version (v1alpha4) to this version. func (dst *InitConfiguration) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.InitConfiguration) return Convert_v1beta1_InitConfiguration_To_upstreamv1beta1_InitConfiguration(src, dst, nil) } -// ConvertTo converts this JoinConfiguration to the Hub version (v1alpha4). func (src *JoinConfiguration) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.JoinConfiguration) return Convert_upstreamv1beta1_JoinConfiguration_To_v1beta1_JoinConfiguration(src, dst, nil) } -// ConvertFrom converts from the JoinConfiguration Hub version (v1alpha4) to this version. func (dst *JoinConfiguration) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.JoinConfiguration) return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(src, dst, nil) } -func Convert_upstreamv1beta1_DNS_To_v1beta1_DNS(in *DNS, out *bootstrapv1.DNS, s apimachineryconversion.Scope) error { - // DNS.Type was removed in v1alpha4 because only CoreDNS is supported, dropping this info. - return autoConvert_upstreamv1beta1_DNS_To_v1beta1_DNS(in, out, s) -} +// Custom conversion from this API, kubeadm v1beta1, to the hub version, CABPK v1beta1. func Convert_upstreamv1beta1_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *ClusterConfiguration, out *bootstrapv1.ClusterConfiguration, s apimachineryconversion.Scope) error { - // ClusterConfiguration.UseHyperKubeImage was removed in kubeadm v1alpha4 API + // ClusterConfiguration.UseHyperKubeImage was removed in CABPK v1alpha4 API version, dropping this info (no issue, it was not used). return autoConvert_upstreamv1beta1_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in, out, s) } -func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { - // NodeRegistrationOptions.IgnorePreflightErrors does not exist in kubeadm v1beta1 API - return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in, out, s) +func Convert_upstreamv1beta1_DNS_To_v1beta1_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_v1beta1_DNS(in, out, s) } +// Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta1. + func Convert_v1beta1_InitConfiguration_To_upstreamv1beta1_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.Patches does not exist in kubeadm v1beta1 API + // InitConfiguration.SkipPhases and Patches does not exist in kubeadm v1beta1, dropping those info. return autoConvert_v1beta1_InitConfiguration_To_upstreamv1beta1_InitConfiguration(in, out, s) } func Convert_v1beta1_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { - // JoinConfiguration.Patches does not exist in kubeadm v1beta1 API + // JoinConfiguration.SkipPhases and Patches does not exist in kubeadm v1beta1, dropping those info. return autoConvert_v1beta1_JoinConfiguration_To_upstreamv1beta1_JoinConfiguration(in, out, s) } + +func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { + // NodeRegistrationOptions.IgnorePreflightErrors and ImagePullPolicy does not exist in kubeadm v1beta1, dropping those info. + return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in, out, s) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go index 13d1f18319ac..b170100c3615 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go @@ -60,61 +60,57 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - dnsFuzzer, clusterConfigurationFuzzer, - kubeadmNodeRegistrationOptionsFuzzer, - kubeadmInitConfigurationFuzzer, - kubeadmJoinConfigurationFuzzer, + dnsFuzzer, + bootstrapv1InitConfigurationFuzzer, + bootstrapv1JoinConfigurationFuzzer, + bootstrapv1NodeRegistrationOptionsFuzzer, } } -func dnsFuzzer(obj *DNS, c fuzz.Continue) { - c.FuzzNoCustom(obj) - - // DNS.Type does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta1 --> v1alpha4 --> v1beta1 round trip errors. - obj.Type = "" -} +// Custom fuzzers for kubeadm v1beta1 types. +// 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 fuzz.Continue) { c.FuzzNoCustom(obj) - // ClusterConfiguration.UseHyperKubeImage has been removed in v1alpha4, so setting it to false in order to avoid v1beta1 --> v1alpha4 --> v1beta1 round trip errors. obj.UseHyperKubeImage = false } -func kubeadmNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) { +func dnsFuzzer(obj *DNS, c fuzz.Continue) { c.FuzzNoCustom(obj) - // NodeRegistrationOptions.IgnorePreflightErrors does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1alpha4 --> v1beta1 -> v1alpha4 round trip errors. - obj.IgnorePreflightErrors = nil - - // NodeRegistrationOptions.ImagePullPolicy does not exist in - // kubeadm v1beta1 API, so setting it to empty in order to - // avoid round trip errors. - obj.ImagePullPolicy = "" + obj.Type = "" } -func kubeadmInitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c fuzz.Continue) { +// 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 bootstrapv1InitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c fuzz.Continue) { c.FuzzNoCustom(obj) - // InitConfiguration.Patches does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta1 -> v1beta1 round trip errors. obj.Patches = nil - - // InitConfiguration.SkipPhases does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta1 -> v1beta1 round trip errors. obj.SkipPhases = nil } -func kubeadmJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.Continue) { +func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.Continue) { c.FuzzNoCustom(obj) - // JoinConfiguration.Patches does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta1 -> v1beta1 round trip errors. + // JoinConfiguration.Patches does not exist in kubeadm v1beta1 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta1 --> cabpk v1beta1 round trip errors. obj.Patches = nil - // JoinConfiguration.SkipPhases does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta1 -> v1beta1 round trip errors. + // JoinConfiguration.SkipPhases does not exist in kubeadm v1beta1 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta1 --> cabpk v1beta1 round trip errors. obj.SkipPhases = nil } + +func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + // NodeRegistrationOptions.IgnorePreflightErrors does not exist in kubeadm v1beta1 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta1 --> cabpk v1beta1 round trip errors. + obj.IgnorePreflightErrors = nil + + // NodeRegistrationOptions.ImagePullPolicy does not exist in kubeadm v1beta1 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta1 --> cabpk v1beta1 round trip errors. + obj.ImagePullPolicy = "" +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go index fc95ca21d35e..486e8962528a 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go @@ -63,38 +63,41 @@ func (dst *JoinConfiguration) ConvertFrom(srcRaw conversion.Hub) error { return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(src, dst, nil) } -func Convert_upstreamv1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *InitConfiguration, out *bootstrapv1.InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.CertificateKey exists in v1beta2 types but not in bootstrapv1.InitConfiguration (Cluster API does not uses automatic copy certs). Ignoring when converting. - return autoConvert_upstreamv1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s) -} +// Custom conversion from this API, kubeadm v1beta2, to the hub version, CABPK v1beta1. -func Convert_upstreamv1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(in *JoinControlPlane, out *bootstrapv1.JoinControlPlane, s apimachineryconversion.Scope) error { - // JoinControlPlane.CertificateKey exists in v1beta2 types but not in bootstrapv1.JoinControlPlane (Cluster API does not uses automatic copy certs). Ignoring when converting. - return autoConvert_upstreamv1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(in, out, s) +func Convert_upstreamv1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *ClusterConfiguration, out *bootstrapv1.ClusterConfiguration, s apimachineryconversion.Scope) error { + // ClusterConfiguration.UseHyperKubeImage was removed in CABPK v1alpha4 API version, dropping this info (no issue, it was not used). + return autoConvert_upstreamv1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in, out, s) } func Convert_upstreamv1beta2_DNS_To_v1beta1_DNS(in *DNS, out *bootstrapv1.DNS, s apimachineryconversion.Scope) error { - // DNS.Type was removed in v1alpha4 because only CoreDNS is supported, dropping this info. + // DNS.Type does not exist in CABPK v1beta1 version, because it always was CoreDNS. return autoConvert_upstreamv1beta2_DNS_To_v1beta1_DNS(in, out, s) } -func Convert_upstreamv1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *ClusterConfiguration, out *bootstrapv1.ClusterConfiguration, s apimachineryconversion.Scope) error { - // ClusterConfiguration.UseHyperKubeImage was removed in kubeadm v1alpha4 API - return autoConvert_upstreamv1beta2_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in, out, s) +func Convert_upstreamv1beta2_InitConfiguration_To_v1beta1_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. + return autoConvert_upstreamv1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s) +} + +func Convert_upstreamv1beta2_JoinControlPlane_To_v1beta1_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. + return autoConvert_upstreamv1beta2_JoinControlPlane_To_v1beta1_JoinControlPlane(in, out, s) } +// Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta2. + func Convert_v1beta1_InitConfiguration_To_upstreamv1beta2_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.Patches does not exist in kubeadm v1beta2 API + // InitConfiguration.SkipPhases and Patches does not exist in kubeadm v1beta2, dropping those info. return autoConvert_v1beta1_InitConfiguration_To_upstreamv1beta2_InitConfiguration(in, out, s) } func Convert_v1beta1_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { - // JoinConfiguration.Patches does not exist in kubeadm v1beta2 API + // JoinConfiguration.SkipPhases and Patches does not exist in kubeadm v1beta2, dropping those info. return autoConvert_v1beta1_JoinConfiguration_To_upstreamv1beta2_JoinConfiguration(in, out, s) } func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error { - // NodeRegistrationOptions.ImagePullPolicy does not exit in - // kubeadm v1beta2 API. + // NodeRegistrationOptions.IgnorePreflightErrors and ImagePullPolicy does not exist in kubeadm v1beta2, dropping those info. return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in, out, s) } diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go index 9ad0feaa7f78..7066cb264892 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go @@ -60,73 +60,64 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ + clusterConfigurationFuzzer, + dnsFuzzer, initConfigurationFuzzer, joinControlPlanesFuzzer, - dnsFuzzer, - clusterConfigurationFuzzer, - kubeadmInitConfigurationFuzzer, - kubeadmJoinConfigurationFuzzer, - kubeadmNodeRegistrationOptionsFuzzer, + bootstrapv1InitConfigurationFuzzer, + bootstrapv1JoinConfigurationFuzzer, + bootstrapv1NodeRegistrationOptionsFuzzer, } } -func joinControlPlanesFuzzer(obj *JoinControlPlane, c fuzz.Continue) { +// Custom fuzzers for kubeadm v1beta2 types. +// 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 fuzz.Continue) { c.FuzzNoCustom(obj) - // JoinControlPlane.CertificateKey does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta2 --> v1alpha4 --> v1beta2 round trip errors. - obj.CertificateKey = "" + obj.UseHyperKubeImage = false +} + +func dnsFuzzer(obj *DNS, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.Type = "" } func initConfigurationFuzzer(obj *InitConfiguration, c fuzz.Continue) { c.Fuzz(obj) - // InitConfiguration.CertificateKey does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta2 --> v1alpha4 --> v1beta2 round trip errors. obj.CertificateKey = "" } -func dnsFuzzer(obj *DNS, c fuzz.Continue) { +func joinControlPlanesFuzzer(obj *JoinControlPlane, c fuzz.Continue) { c.FuzzNoCustom(obj) - // DNS.Type does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta2 --> v1alpha4 --> v1beta2 round trip errors. - obj.Type = "" + obj.CertificateKey = "" } -func clusterConfigurationFuzzer(obj *ClusterConfiguration, c fuzz.Continue) { - c.FuzzNoCustom(obj) - - // ClusterConfiguration.UseHyperKubeImage has been removed in v1alpha4, so setting it to false in order to avoid v1beta2 --> v1alpha4 --> v1beta2 round trip errors. - obj.UseHyperKubeImage = false -} +// 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 kubeadmInitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c fuzz.Continue) { +func bootstrapv1InitConfigurationFuzzer(obj *bootstrapv1.InitConfiguration, c fuzz.Continue) { c.FuzzNoCustom(obj) - // InitConfiguration.Patches does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta2 -> v1beta1 round trip errors. obj.Patches = nil - - // InitConfiguration.SkipPhases does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta2 -> v1beta1 round trip errors. obj.SkipPhases = nil } -func kubeadmJoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.Continue) { +func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.Continue) { c.FuzzNoCustom(obj) - // JoinConfiguration.Patches does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta2 -> v1beta1 round trip errors. obj.Patches = nil - - // JoinConfiguration.SkipPhases does not exist in kubeadm v1beta1 API, so setting it to nil in order to avoid - // v1beta1 --> upstream v1beta2 -> v1beta1 round trip errors. obj.SkipPhases = nil } -func kubeadmNodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) { +func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) { c.FuzzNoCustom(obj) - // NodeRegistrationOptions.ImagePullPolicy does not exist in - // kubeadm v1beta2 API, so setting it to empty in order to - // avoid round trip errors. obj.ImagePullPolicy = "" } diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go index bcf9e1ef1987..feef95647790 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go @@ -23,58 +23,44 @@ import ( bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" ) -// ConvertTo converts this ClusterConfiguration to the Hub version (v1alpha4). func (src *ClusterConfiguration) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.ClusterConfiguration) return Convert_upstreamv1beta3_ClusterConfiguration_To_v1beta1_ClusterConfiguration(src, dst, nil) } -// ConvertFrom converts from the ClusterConfiguration Hub version (v1alpha4) to this version. func (dst *ClusterConfiguration) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.ClusterConfiguration) return Convert_v1beta1_ClusterConfiguration_To_upstreamv1beta3_ClusterConfiguration(src, dst, nil) } -// ConvertTo converts this InitConfiguration to the Hub version (v1alpha4). func (src *InitConfiguration) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.InitConfiguration) return Convert_upstreamv1beta3_InitConfiguration_To_v1beta1_InitConfiguration(src, dst, nil) } -// ConvertFrom converts from the InitConfiguration Hub version (v1alpha4) to this version. func (dst *InitConfiguration) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.InitConfiguration) return Convert_v1beta1_InitConfiguration_To_upstreamv1beta3_InitConfiguration(src, dst, nil) } -// ConvertTo converts this JoinConfiguration to the Hub version (v1alpha4). func (src *JoinConfiguration) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*bootstrapv1.JoinConfiguration) return Convert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(src, dst, nil) } -// ConvertFrom converts from the JoinConfiguration Hub version (v1alpha4) to this version. func (dst *JoinConfiguration) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*bootstrapv1.JoinConfiguration) return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(src, dst, nil) } +// Custom conversion from this API, kubeadm v1beta3, to the hub version, CABPK v1beta1. + func Convert_upstreamv1beta3_InitConfiguration_To_v1beta1_InitConfiguration(in *InitConfiguration, out *bootstrapv1.InitConfiguration, s apimachineryconversion.Scope) error { - // InitConfiguration.CertificateKey and SkipPhases exists in v1beta3 types but not in bootstrapv1.InitConfiguration (Cluster API does not uses automatic copy certs or does not support SkipPhases for now)). Ignoring when converting. + // InitConfiguration.CertificateKey does not exist in CABPK v1beta1 version, because Cluster API does not use automatic copy certs. return autoConvert_upstreamv1beta3_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s) } -func Convert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(in *JoinConfiguration, out *bootstrapv1.JoinConfiguration, s apimachineryconversion.Scope) error { - // JoinConfiguration.SkipPhases exists in v1beta3 types but not in bootstrapv1.JoinConfiguration (Cluster API does not support SkipPhases for now). Ignoring when converting. - return autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(in, out, s) -} - -func Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { - // NodeRegistrationOptions.IgnorePreflightErrors exists in v1beta3 types but not in bootstrapv1.NodeRegistrationOptions (Cluster API does not support it for now). Ignoring when converting. - return autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s) -} - func Convert_upstreamv1beta3_JoinControlPlane_To_v1beta1_JoinControlPlane(in *JoinControlPlane, out *bootstrapv1.JoinControlPlane, s apimachineryconversion.Scope) error { - // JoinControlPlane.CertificateKey exists in v1beta3 types but not in bootstrapv1.JoinControlPlane (Cluster API does not uses automatic copy certs). Ignoring when converting. + // JoinControlPlane.CertificateKey does not exist in CABPK v1beta1 version, because Cluster API does not use automatic copy certs. return autoConvert_upstreamv1beta3_JoinControlPlane_To_v1beta1_JoinControlPlane(in, out, s) } diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go index 92afffae5a24..7c5c7f5082cf 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go @@ -63,40 +63,38 @@ func TestFuzzyConversion(t *testing.T) { func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - nodeRegistrationOptionsFuzzer, initConfigurationFuzzer, joinConfigurationFuzzer, + nodeRegistrationOptionsFuzzer, joinControlPlanesFuzzer, } } -func nodeRegistrationOptionsFuzzer(obj *NodeRegistrationOptions, c fuzz.Continue) { - c.FuzzNoCustom(obj) - - // NodeRegistrationOptions.IgnorePreflightErrors does not exists in v1alpha4, so setting it to nil in order to avoid v1beta3 --> v1alpha4 --> v1beta3 round trip errors. - obj.IgnorePreflightErrors = nil -} - -func joinControlPlanesFuzzer(obj *JoinControlPlane, c fuzz.Continue) { - c.FuzzNoCustom(obj) - - // JoinControlPlane.CertificateKey does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta3 --> v1alpha4 --> v1beta3 round trip errors. - obj.CertificateKey = "" -} +// Custom fuzzers for kubeadm v1beta3 types. +// 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 fuzz.Continue) { c.Fuzz(obj) - // InitConfiguration.CertificateKey does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta3 --> v1alpha4 --> v1beta3 round trip errors. obj.CertificateKey = "" - - // InitConfiguration.SkipPhases does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta3 --> v1alpha4 --> v1beta3 round trip errors. obj.SkipPhases = nil } func joinConfigurationFuzzer(obj *JoinConfiguration, c fuzz.Continue) { c.Fuzz(obj) - // JoinConfiguration.SkipPhases does not exists in v1alpha4, so setting it to empty string in order to avoid v1beta3 --> v1alpha4 --> v1beta3 round trip errors. obj.SkipPhases = nil } + +func nodeRegistrationOptionsFuzzer(obj *NodeRegistrationOptions, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.IgnorePreflightErrors = nil +} + +func joinControlPlanesFuzzer(obj *JoinControlPlane, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.CertificateKey = "" +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go index d849616cb3de..d8a0dafc56e6 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go @@ -183,6 +183,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*JoinConfiguration)(nil), (*v1beta1.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta1.JoinConfiguration), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta1.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(a.(*v1beta1.JoinConfiguration), b.(*JoinConfiguration), scope) }); err != nil { @@ -213,6 +218,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta1.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta1.NodeRegistrationOptions), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta1.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(a.(*v1beta1.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) }); err != nil { @@ -233,21 +243,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*v1beta1.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta1.JoinConfiguration), scope) - }); err != nil { - return err - } if err := s.AddConversionFunc((*JoinControlPlane)(nil), (*v1beta1.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta3_JoinControlPlane_To_v1beta1_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta1.JoinControlPlane), scope) }); err != nil { return err } - if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta1.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta1.NodeRegistrationOptions), scope) - }); err != nil { - return err - } return nil } @@ -689,6 +689,11 @@ func autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration( return nil } +// Convert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration is an autogenerated conversion function. +func Convert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(in *JoinConfiguration, out *v1beta1.JoinConfiguration, s conversion.Scope) error { + return autoConvert_upstreamv1beta3_JoinConfiguration_To_v1beta1_JoinConfiguration(in, out, s) +} + func autoConvert_v1beta1_JoinConfiguration_To_upstreamv1beta3_JoinConfiguration(in *v1beta1.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { if err := Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { return err @@ -802,6 +807,11 @@ func autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistra return nil } +// Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions is an autogenerated conversion function. +func Convert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta1.NodeRegistrationOptions, s conversion.Scope) error { + return autoConvert_upstreamv1beta3_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s) +} + func autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta3_NodeRegistrationOptions(in *v1beta1.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { out.Name = in.Name out.CRISocket = in.CRISocket diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/bootstraptokenstring.go b/bootstrap/kubeadm/types/upstreamv1beta4/bootstraptokenstring.go new file mode 100644 index 000000000000..584da3bdf6bd --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/bootstraptokenstring.go @@ -0,0 +1,98 @@ +/* +Copyright 2024 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 ( + "fmt" + "strings" + + "github.com/pkg/errors" + bootstrapapi "k8s.io/cluster-bootstrap/token/api" + bootstraputil "k8s.io/cluster-bootstrap/token/util" +) + +// BootstrapTokenString is a token of the format abcdef.abcdef0123456789 that is used +// for both validation of the practically of the API server from a joining node's point +// of view and as an authentication method for the node in the bootstrap phase of +// "kubeadm join". This token is and should be short-lived. +type BootstrapTokenString struct { + ID string `json:"-"` + Secret string `json:"-" datapolicy:"token"` +} + +const ( + // When a token is matched with 'BootstrapTokenPattern', the size of validated substrings returned by + // regexp functions which contains 'Submatch' in their names will be 3. + // Submatch 0 is the match of the entire expression, submatch 1 is + // the match of the first parenthesized subexpression, and so on. + // e.g.: + // result := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch("abcdef.1234567890123456") + // result == []string{"abcdef.1234567890123456","abcdef","1234567890123456"} + // len(result) == 3. + validatedSubstringsSize = 3 +) + +// MarshalJSON implements the json.Marshaler interface. +func (bts BootstrapTokenString) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", bts.String())), nil +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (bts *BootstrapTokenString) UnmarshalJSON(b []byte) error { + // If the token is represented as "", just return quickly without an error + if len(b) == 0 { + return nil + } + + // Remove unnecessary " characters coming from the JSON parser + token := strings.Replace(string(b), `"`, ``, -1) + // Convert the string Token to a BootstrapTokenString object + newbts, err := NewBootstrapTokenString(token) + if err != nil { + return err + } + bts.ID = newbts.ID + bts.Secret = newbts.Secret + return nil +} + +// String returns the string representation of the BootstrapTokenString. +func (bts BootstrapTokenString) String() string { + if bts.ID != "" && bts.Secret != "" { + return bootstraputil.TokenFromIDAndSecret(bts.ID, bts.Secret) + } + return "" +} + +// NewBootstrapTokenString converts the given Bootstrap Token as a string +// to the BootstrapTokenString object used for serialization/deserialization +// and internal usage. It also automatically validates that the given token +// is of the right format. +func NewBootstrapTokenString(token string) (*BootstrapTokenString, error) { + substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token) + if len(substrs) != validatedSubstringsSize { + return nil, errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern) + } + + return &BootstrapTokenString{ID: substrs[1], Secret: substrs[2]}, nil +} + +// NewBootstrapTokenStringFromIDAndSecret is a wrapper around NewBootstrapTokenString +// that allows the caller to specify the ID and Secret separately. +func NewBootstrapTokenStringFromIDAndSecret(id, secret string) (*BootstrapTokenString, error) { + return NewBootstrapTokenString(bootstraputil.TokenFromIDAndSecret(id, secret)) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/bootstraptokenstring_test.go b/bootstrap/kubeadm/types/upstreamv1beta4/bootstraptokenstring_test.go new file mode 100644 index 000000000000..f5d9616aa2c0 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/bootstraptokenstring_test.go @@ -0,0 +1,253 @@ +/* +Copyright 2024 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 ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/pkg/errors" +) + +func TestMarshalJSON(t *testing.T) { + var tests = []struct { + bts BootstrapTokenString + expected string + }{ + {BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, `"abcdef.abcdef0123456789"`}, + {BootstrapTokenString{ID: "foo", Secret: "bar"}, `"foo.bar"`}, + {BootstrapTokenString{ID: "h", Secret: "b"}, `"h.b"`}, + } + for _, rt := range tests { + t.Run(rt.bts.ID, func(t *testing.T) { + b, err := json.Marshal(rt.bts) + if err != nil { + t.Fatalf("json.Marshal returned an unexpected error: %v", err) + } + if string(b) != rt.expected { + t.Errorf( + "failed BootstrapTokenString.MarshalJSON:\n\texpected: %s\n\t actual: %s", + rt.expected, + string(b), + ) + } + }) + } +} + +func TestUnmarshalJSON(t *testing.T) { + var tests = []struct { + input string + bts *BootstrapTokenString + expectedError bool + }{ + {`"f.s"`, &BootstrapTokenString{}, true}, + {`"abcdef."`, &BootstrapTokenString{}, true}, + {`"abcdef:abcdef0123456789"`, &BootstrapTokenString{}, true}, + {`abcdef.abcdef0123456789`, &BootstrapTokenString{}, true}, + {`"abcdef.abcdef0123456789`, &BootstrapTokenString{}, true}, + {`"abcdef.ABCDEF0123456789"`, &BootstrapTokenString{}, true}, + {`"abcdef.abcdef0123456789"`, &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, false}, + {`"123456.aabbccddeeffgghh"`, &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}, false}, + } + for _, rt := range tests { + t.Run(rt.input, func(t *testing.T) { + newbts := &BootstrapTokenString{} + err := json.Unmarshal([]byte(rt.input), newbts) + if (err != nil) != rt.expectedError { + t.Errorf("failed BootstrapTokenString.UnmarshalJSON:\n\texpected error: %t\n\t actual error: %v", rt.expectedError, err) + } else if diff := cmp.Diff(rt.bts, newbts); diff != "" { + t.Errorf( + "failed BootstrapTokenString.UnmarshalJSON:\n\texpected: %v\n\t actual: %v\n\t diff: %v", + rt.bts, + newbts, + diff, + ) + } + }) + } +} + +func TestJSONRoundtrip(t *testing.T) { + var tests = []struct { + input string + bts *BootstrapTokenString + }{ + {`"abcdef.abcdef0123456789"`, nil}, + {"", &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + } + for _, rt := range tests { + t.Run(rt.input, func(t *testing.T) { + if err := roundtrip(rt.input, rt.bts); err != nil { + t.Errorf("failed BootstrapTokenString JSON roundtrip with error: %v", err) + } + }) + } +} + +func roundtrip(input string, bts *BootstrapTokenString) error { + var b []byte + var err error + newbts := &BootstrapTokenString{} + // If string input was specified, roundtrip like this: string -> (unmarshal) -> object -> (marshal) -> string + if input != "" { + if err := json.Unmarshal([]byte(input), newbts); err != nil { + return errors.Wrap(err, "expected no unmarshal error, got error") + } + if b, err = json.Marshal(newbts); err != nil { + return errors.Wrap(err, "expected no marshal error, got error") + } + if input != string(b) { + return errors.Errorf( + "expected token: %s\n\t actual: %s", + input, + string(b), + ) + } + } else { // Otherwise, roundtrip like this: object -> (marshal) -> string -> (unmarshal) -> object + if b, err = json.Marshal(bts); err != nil { + return errors.Wrap(err, "expected no marshal error, got error") + } + if err := json.Unmarshal(b, newbts); err != nil { + return errors.Wrap(err, "expected no unmarshal error, got error") + } + if diff := cmp.Diff(bts, newbts); diff != "" { + return errors.Errorf( + "expected object: %v\n\t actual: %v\n\t got diff: %v", + bts, + newbts, + diff, + ) + } + } + return nil +} + +func TestTokenFromIDAndSecret(t *testing.T) { + var tests = []struct { + bts BootstrapTokenString + expected string + }{ + {BootstrapTokenString{ID: "foo", Secret: "bar"}, "foo.bar"}, + {BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, "abcdef.abcdef0123456789"}, + {BootstrapTokenString{ID: "h", Secret: "b"}, "h.b"}, + } + for _, rt := range tests { + t.Run(rt.bts.ID, func(t *testing.T) { + actual := rt.bts.String() + if actual != rt.expected { + t.Errorf( + "failed BootstrapTokenString.String():\n\texpected: %s\n\t actual: %s", + rt.expected, + actual, + ) + } + }) + } +} + +func TestNewBootstrapTokenString(t *testing.T) { + var tests = []struct { + token string + expectedError bool + bts *BootstrapTokenString + }{ + {token: "", expectedError: true, bts: nil}, + {token: ".", expectedError: true, bts: nil}, + {token: "1234567890123456789012", expectedError: true, bts: nil}, // invalid parcel size + {token: "12345.1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {token: ".1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {token: "123456.", expectedError: true, bts: nil}, // invalid parcel size + {token: "123456:1234567890.123456", expectedError: true, bts: nil}, // invalid separation + {token: "abcdef:1234567890123456", expectedError: true, bts: nil}, // invalid separation + {token: "Abcdef.1234567890123456", expectedError: true, bts: nil}, // invalid token id + {token: "123456.AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret + {token: "123456.AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character + {token: "abc*ef.1234567890123456", expectedError: true, bts: nil}, // invalid character + {token: "abcdef.1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}}, + {token: "123456.aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}}, + {token: "abcdef.abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + {token: "123456.1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}}, + } + for _, rt := range tests { + t.Run(rt.token, func(t *testing.T) { + actual, err := NewBootstrapTokenString(rt.token) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed NewBootstrapTokenString for the token %q\n\texpected error: %t\n\t actual error: %v", + rt.token, + rt.expectedError, + err, + ) + } else if diff := cmp.Diff(actual, rt.bts); diff != "" { + t.Errorf( + "failed NewBootstrapTokenString for the token %q\n\texpected: %v\n\t actual: %v\n\t diff: %v", + rt.token, + rt.bts, + actual, + diff, + ) + } + }) + } +} + +func TestNewBootstrapTokenStringFromIDAndSecret(t *testing.T) { + var tests = []struct { + id, secret string + expectedError bool + bts *BootstrapTokenString + }{ + {id: "", secret: "", expectedError: true, bts: nil}, + {id: "1234567890123456789012", secret: "", expectedError: true, bts: nil}, // invalid parcel size + {id: "12345", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {id: "", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {id: "123456", secret: "", expectedError: true, bts: nil}, // invalid parcel size + {id: "Abcdef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid token id + {id: "123456", secret: "AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret + {id: "123456", secret: "AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character + {id: "abc*ef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid character + {id: "abcdef", secret: "1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}}, + {id: "123456", secret: "aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}}, + {id: "abcdef", secret: "abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + {id: "123456", secret: "1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}}, + } + for _, rt := range tests { + t.Run(rt.id, func(t *testing.T) { + actual, err := NewBootstrapTokenStringFromIDAndSecret(rt.id, rt.secret) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected error: %t\n\t actual error: %v", + rt.id, + rt.secret, + rt.expectedError, + err, + ) + } else if diff := cmp.Diff(actual, rt.bts); diff != "" { + t.Errorf( + "failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected: %v\n\t actual: %v\n\t diff: %v", + rt.id, + rt.secret, + rt.bts, + actual, + diff, + ) + } + }) + } +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go new file mode 100644 index 000000000000..5a34620fefa4 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go @@ -0,0 +1,259 @@ +/* +Copyright 2024 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 ( + "sort" + + "github.com/pkg/errors" + apimachineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" +) + +func (src *ClusterConfiguration) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*bootstrapv1.ClusterConfiguration) + return Convert_upstreamv1beta4_ClusterConfiguration_To_v1beta1_ClusterConfiguration(src, dst, nil) +} + +func (dst *ClusterConfiguration) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*bootstrapv1.ClusterConfiguration) + return Convert_v1beta1_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguration(src, dst, nil) +} + +func (src *InitConfiguration) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*bootstrapv1.InitConfiguration) + return Convert_upstreamv1beta4_InitConfiguration_To_v1beta1_InitConfiguration(src, dst, nil) +} + +func (dst *InitConfiguration) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*bootstrapv1.InitConfiguration) + return Convert_v1beta1_InitConfiguration_To_upstreamv1beta4_InitConfiguration(src, dst, nil) +} + +func (src *JoinConfiguration) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*bootstrapv1.JoinConfiguration) + return Convert_upstreamv1beta4_JoinConfiguration_To_v1beta1_JoinConfiguration(src, dst, nil) +} + +func (dst *JoinConfiguration) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*bootstrapv1.JoinConfiguration) + return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(src, dst, nil) +} + +// Custom conversion from this API, kubeadm v1beta4, to the hub version, CABPK v1beta1. + +func Convert_upstreamv1beta4_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *ClusterConfiguration, out *bootstrapv1.ClusterConfiguration, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - Proxy (Not supported yet) + // - EncryptionAlgorithm (Not supported yet) + // - CertificateValidityPeriod (Not supported yet) + // - CACertificateValidityPeriod (Not supported yet) + return autoConvert_upstreamv1beta4_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in, out, s) +} + +func Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - ExtraEnvs (Not supported yet) + + // 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_v1beta1_ControlPlaneComponent(in, out, s) +} + +func Convert_upstreamv1beta4_LocalEtcd_To_v1beta1_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - ExtraEnvs (Not supported yet) + + // 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_v1beta1_LocalEtcd(in, out, s) +} + +func Convert_upstreamv1beta4_DNS_To_v1beta1_DNS(in *DNS, out *bootstrapv1.DNS, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - Disabled (Not supported yet) + return autoConvert_upstreamv1beta4_DNS_To_v1beta1_DNS(in, out, s) +} + +func Convert_upstreamv1beta4_InitConfiguration_To_v1beta1_InitConfiguration(in *InitConfiguration, out *bootstrapv1.InitConfiguration, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - DryRun (Does not make sense for CAPBK) + // - CertificateKey (CABPK does not use automatic copy certs) + // - Timeouts (Not supported yet) + return autoConvert_upstreamv1beta4_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s) +} + +func Convert_upstreamv1beta4_JoinConfiguration_To_v1beta1_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_v1beta1_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_v1beta1_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - ImagePullSerial (Not supported yet) + + // 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_v1beta1_NodeRegistrationOptions(in, out, s) +} + +func Convert_upstreamv1beta4_JoinControlPlane_To_v1beta1_JoinControlPlane(in *JoinControlPlane, out *bootstrapv1.JoinControlPlane, s apimachineryconversion.Scope) error { + // Following fields do not exist in CABPK v1beta1 version: + // - CertificateKey (CABPK does not use automatic copy certs) + return autoConvert_upstreamv1beta4_JoinControlPlane_To_v1beta1_JoinControlPlane(in, out, s) +} + +// Custom conversion from the hub version, CABPK v1beta1, to this API, kubeadm v1beta4. + +func Convert_v1beta1_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_v1beta1_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in, out, s) +} + +func Convert_v1beta1_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_v1beta1_APIServer_To_upstreamv1beta4_APIServer(in, out, s) +} + +func Convert_v1beta1_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_v1beta1_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in, out, s) +} + +func Convert_v1beta1_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error { + err := autoConvert_v1beta1_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_v1beta1_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_v1beta1_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in, out, s) +} + +func Convert_v1beta1_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_v1beta1_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration) + return autoConvert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(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 + } + + 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 + return nil +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go new file mode 100644 index 000000000000..2bacf29088dd --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go @@ -0,0 +1,196 @@ +/* +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" + + fuzz "github.com/google/gofuzz" + . "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" + + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" +) + +func TestFuzzyConversion(t *testing.T) { + g := NewWithT(t) + scheme := runtime.NewScheme() + g.Expect(AddToScheme(scheme)).To(Succeed()) + g.Expect(bootstrapv1.AddToScheme(scheme)).To(Succeed()) + + t.Run("for ClusterConfiguration", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ + Scheme: scheme, + Hub: &bootstrapv1.ClusterConfiguration{}, + Spoke: &ClusterConfiguration{}, + // NOTE: Kubeadm types does not have ObjectMeta, so we are required to skip data annotation cleanup in the spoke-hub-spoke round trip test. + SkipSpokeAnnotationCleanup: true, + FuzzerFuncs: []fuzzer.FuzzerFuncs{fuzzFuncs}, + })) + t.Run("for InitConfiguration", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ + Scheme: scheme, + Hub: &bootstrapv1.InitConfiguration{}, + Spoke: &InitConfiguration{}, + // NOTE: Kubeadm types does not have ObjectMeta, so we are required to skip data annotation cleanup in the spoke-hub-spoke round trip test. + SkipSpokeAnnotationCleanup: true, + FuzzerFuncs: []fuzzer.FuzzerFuncs{fuzzFuncs}, + })) + t.Run("for JoinConfiguration", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ + Scheme: scheme, + Hub: &bootstrapv1.JoinConfiguration{}, + Spoke: &JoinConfiguration{}, + // NOTE: Kubeadm types does not have ObjectMeta, so we are required to skip data annotation cleanup in the spoke-hub-spoke round trip test. + SkipSpokeAnnotationCleanup: true, + FuzzerFuncs: []fuzzer.FuzzerFuncs{fuzzFuncs}, + })) +} + +func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + clusterConfigurationFuzzer, + controlPlaneComponentFuzzer, + dnsFuzzer, + localEtcdFuzzer, + initConfigurationFuzzer, + joinConfigurationFuzzer, + nodeRegistrationOptionsFuzzer, + joinControlPlaneFuzzer, + bootstrapv1APIServerFuzzer, + } +} + +// Custom fuzzers for kubeadm v1beta4 types. +// 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 fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.Proxy = Proxy{} + obj.EncryptionAlgorithm = "" + obj.CACertificateValidityPeriod = nil + obj.CertificateValidityPeriod = nil +} + +func controlPlaneComponentFuzzer(obj *ControlPlaneComponent, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.ExtraEnvs = nil +} + +func dnsFuzzer(obj *DNS, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.Disabled = false +} + +func localEtcdFuzzer(obj *LocalEtcd, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.ExtraEnvs = nil +} + +func initConfigurationFuzzer(obj *InitConfiguration, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.DryRun = false + obj.CertificateKey = "" + obj.Timeouts = nil +} + +func joinConfigurationFuzzer(obj *JoinConfiguration, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.DryRun = false + + // If timeouts have been set, unset unsupported timeouts (only TLSBootstrap is supported - corresponds to JoinConfiguration.Discovery.Timeout in cabpk v1beta1 API + if obj.Timeouts == nil { + return + } + + var supportedTimeouts *Timeouts + if obj.Timeouts.TLSBootstrap != nil { + supportedTimeouts = &Timeouts{ + TLSBootstrap: obj.Timeouts.TLSBootstrap, + } + } + obj.Timeouts = supportedTimeouts +} + +func nodeRegistrationOptionsFuzzer(obj *NodeRegistrationOptions, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.ImagePullSerial = nil +} + +func joinControlPlaneFuzzer(obj *JoinControlPlane, c fuzz.Continue) { + c.FuzzNoCustom(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 fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.TimeoutForControlPlane = nil +} + +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/doc.go b/bootstrap/kubeadm/types/upstreamv1beta4/doc.go new file mode 100644 index 000000000000..cf7579db8043 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/doc.go @@ -0,0 +1,24 @@ +/* +Copyright 2024 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 contains a mirror of kubeadm API v1beta4 API, required because it is not possible to import k/K. +// +// IMPORTANT: Do not change these files! +// IMPORTANT: only for KubeadmConfig serialization/deserialization, and should not be used for other purposes. +// +// +k8s:conversion-gen=sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1 +// +k8s:deepcopy-gen=package +package upstreamv1beta4 // import "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4" diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/groupversion_info.go b/bootstrap/kubeadm/types/upstreamv1beta4/groupversion_info.go new file mode 100644 index 000000000000..f7f5df3ec1d3 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/groupversion_info.go @@ -0,0 +1,35 @@ +/* +Copyright 2024 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 ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "kubeadm.k8s.io", Version: "v1beta4"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme + + localSchemeBuilder = SchemeBuilder.SchemeBuilder +) diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/types.go b/bootstrap/kubeadm/types/upstreamv1beta4/types.go new file mode 100644 index 000000000000..19f937b897d4 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/types.go @@ -0,0 +1,591 @@ +/* +Copyright 2024 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 ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// InitConfiguration contains a list of elements that is specific "kubeadm init"-only runtime +// information. +type InitConfiguration struct { + metav1.TypeMeta `json:",inline"` + + // `kubeadm init`-only information. These fields are solely used the first time `kubeadm init` runs. + // After that, the information in the fields IS NOT uploaded to the `kubeadm-config` ConfigMap + // that is used by `kubeadm upgrade` for instance. These fields must be omitempty. + + // BootstrapTokens is respected at `kubeadm init` time and describes a set of Bootstrap Tokens to create. + // This information IS NOT uploaded to the kubeadm cluster configmap, partly because of its sensitive nature + // +optional + BootstrapTokens []BootstrapToken `json:"bootstrapTokens,omitempty"` + + // DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done. + // +optional + DryRun bool `json:"dryRun,omitempty"` + + // NodeRegistration holds fields that relate to registering the new control-plane node to the cluster + // +optional + NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"` + + // LocalAPIEndpoint represents the endpoint of the API server instance that's deployed on this control plane node + // In HA setups, this differs from ClusterConfiguration.ControlPlaneEndpoint in the sense that ControlPlaneEndpoint + // is the global endpoint for the cluster, which then loadbalances the requests to each individual API server. This + // configuration object lets you customize what IP/DNS name and port the local API server advertises it's accessible + // on. By default, kubeadm tries to auto-detect the IP of the default interface and use that, but in case that process + // fails you may set the desired value here. + // +optional + LocalAPIEndpoint APIEndpoint `json:"localAPIEndpoint,omitempty"` + + // CertificateKey sets the key with which certificates and keys are encrypted prior to being uploaded in + // a secret in the cluster during the uploadcerts init phase. + // The certificate key is a hex encoded string that is an AES key of size 32 bytes. + // +optional + CertificateKey string `json:"certificateKey,omitempty"` + + // SkipPhases is a list of phases to skip during command execution. + // The list of phases can be obtained with the "kubeadm init --help" command. + // The flag "--skip-phases" takes precedence over this field. + // +optional + SkipPhases []string `json:"skipPhases,omitempty"` + + // Patches contains options related to applying patches to components deployed by kubeadm during + // "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 + +// ClusterConfiguration contains cluster-wide configuration for a kubeadm cluster. +type ClusterConfiguration struct { + metav1.TypeMeta `json:",inline"` + + // Etcd holds configuration for etcd. + // +optional + Etcd Etcd `json:"etcd,omitempty"` + + // Networking holds configuration for the networking topology of the cluster. + // +optional + Networking Networking `json:"networking,omitempty"` + + // KubernetesVersion is the target version of the control plane. + // +optional + KubernetesVersion string `json:"kubernetesVersion,omitempty"` + + // ControlPlaneEndpoint sets a stable IP address or DNS name for the control plane; it + // can be a valid IP address or a RFC-1123 DNS subdomain, both with optional TCP port. + // In case the ControlPlaneEndpoint is not specified, the AdvertiseAddress + BindPort + // are used; in case the ControlPlaneEndpoint is specified but without a TCP port, + // the BindPort is used. + // Possible usages are: + // e.g. In a cluster with more than one control plane instances, this field should be + // assigned the address of the external load balancer in front of the + // control plane instances. + // e.g. in environments with enforced node recycling, the ControlPlaneEndpoint + // could be used for assigning a stable DNS to the control plane. + // +optional + ControlPlaneEndpoint string `json:"controlPlaneEndpoint,omitempty"` + + // APIServer contains extra settings for the API server control plane component + // +optional + APIServer APIServer `json:"apiServer,omitempty"` + + // ControllerManager contains extra settings for the controller manager control plane component + // +optional + ControllerManager ControlPlaneComponent `json:"controllerManager,omitempty"` + + // Scheduler contains extra settings for the scheduler control plane component + // +optional + Scheduler ControlPlaneComponent `json:"scheduler,omitempty"` + + // DNS defines the options for the DNS add-on installed in the cluster. + // +optional + DNS DNS `json:"dns,omitempty"` + + // Proxy defines the options for the proxy add-on installed in the cluster. + Proxy Proxy `json:"proxy,omitempty"` + + // CertificatesDir specifies where to store or look for all required certificates. + // +optional + CertificatesDir string `json:"certificatesDir,omitempty"` + + // ImageRepository sets the container registry to pull images from. + // If empty, `registry.k8s.io` will be used by default; in case of kubernetes version is a CI build (kubernetes version starts with `ci/`) + // `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components and for kube-proxy, while `registry.k8s.io` + // will be used for all the other images. + // +optional + ImageRepository string `json:"imageRepository,omitempty"` + + // FeatureGates enabled by the user. + // +optional + FeatureGates map[string]bool `json:"featureGates,omitempty"` + + // The cluster name + // +optional + 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". + // +optional + EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"` + + // CertificateValidityPeriod specifies the validity period for a non-CA certificate generated by kubeadm. + // Default value: 8760h (365 days * 24 hours = 1 year) + // +optional + CertificateValidityPeriod *metav1.Duration `json:"certificateValidityPeriod,omitempty"` + + // CACertificateValidityPeriod specifies the validity period for a CA certificate generated by kubeadm. + // Default value: 87600h (365 days * 24 hours * 10 = 10 years) + // +optional + CACertificateValidityPeriod *metav1.Duration `json:"caCertificateValidityPeriod,omitempty"` +} + +// 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. + // An argument name in this list is the flag name as it appears on the + // command line except without leading dash(es). Extra arguments will override existing + // default arguments. Duplicate extra arguments are allowed. + // +optional + ExtraArgs []Arg `json:"extraArgs,omitempty"` + + // ExtraVolumes is an extra set of host volumes, mounted to the control plane component. + // +optional + ExtraVolumes []HostPathMount `json:"extraVolumes,omitempty"` + + // ExtraEnvs is an extra set of environment variables to pass to the control plane component. + // Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. + // +optional + ExtraEnvs []EnvVar `json:"extraEnvs,omitempty"` +} + +// APIServer holds settings necessary for API server deployments in the cluster. +type APIServer struct { + ControlPlaneComponent `json:",inline"` + + // CertSANs sets extra Subject Alternative Names for the API Server signing cert. + // +optional + CertSANs []string `json:"certSANs,omitempty"` +} + +// DNS defines the DNS addon that should be used in the cluster. +type DNS struct { + // ImageMeta allows to customize the image used for the DNS addon + ImageMeta `json:",inline"` + + // Disabled specifies whether to disable this addon in the cluster + // +optional + Disabled bool `json:"disabled,omitempty"` +} + +// Proxy defines the proxy addon that should be used in the cluster. +type Proxy struct { + // Disabled specifies whether to disable this addon in the cluster + // +optional + Disabled bool `json:"disabled,omitempty"` +} + +// ImageMeta allows to customize the image used for components that are not +// originated from the Kubernetes/Kubernetes release process. +type ImageMeta struct { + // ImageRepository sets the container registry to pull images from. + // if not set, the ImageRepository defined in ClusterConfiguration will be used instead. + // +optional + ImageRepository string `json:"imageRepository,omitempty"` + + // ImageTag allows to specify a tag for the image. + // In case this value is set, kubeadm does not change automatically the version of the above components during upgrades. + // +optional + ImageTag string `json:"imageTag,omitempty"` + + // TODO: evaluate if we need also a ImageName based on user feedbacks +} + +// APIEndpoint struct contains elements of API server instance deployed on a node. +type APIEndpoint struct { + // AdvertiseAddress sets the IP address for the API server to advertise. + // +optional + AdvertiseAddress string `json:"advertiseAddress,omitempty"` + + // BindPort sets the secure port for the API Server to bind to. + // Defaults to 6443. + // +optional + BindPort int32 `json:"bindPort,omitempty"` +} + +// NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join". +type NodeRegistrationOptions struct { + + // Name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation. + // This field is also used in the CommonName field of the kubelet's client certificate to the API server. + // Defaults to the hostname of the node if not provided. + // +optional + Name string `json:"name,omitempty"` + + // CRISocket is used to retrieve container runtime info. This information will be annotated to the Node API object, for later re-use + // +optional + CRISocket string `json:"criSocket,omitempty"` + + // Taints specifies the taints the Node API object should be registered with. If this field is unset, i.e. nil, + // it will be defaulted with a control-plane taint for control-plane nodes. If you don't want to taint your control-plane + // node, set this field to an empty slice, i.e. `taints: []` in the YAML file. This field is solely used for Node registration. + Taints []corev1.Taint `json:"taints"` + + // 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 ConfigMap + // Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. + // An argument name in this list is the flag name as it appears on the command line except without leading dash(es). + // Extra arguments will override existing default arguments. Duplicate extra arguments are allowed. + // +optional + KubeletExtraArgs []Arg `json:"kubeletExtraArgs,omitempty"` + + // 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 + IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"` + + // ImagePullPolicy specifies the policy for image pulling during kubeadm "init" and "join" operations. + // The value of this field must be one of "Always", "IfNotPresent" or "Never". + // If this field is unset kubeadm will default it to "IfNotPresent", or pull the required images if not present on the host. + // +optional + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + + // ImagePullSerial specifies if image pulling performed by kubeadm must be done serially or in parallel. + // Default: true + // +optional + ImagePullSerial *bool `json:"imagePullSerial,omitempty"` +} + +// Networking contains elements describing cluster's networking configuration. +type Networking struct { + // ServiceSubnet is the subnet used by k8s services. Defaults to "10.96.0.0/12". + // +optional + ServiceSubnet string `json:"serviceSubnet,omitempty"` + // PodSubnet is the subnet used by pods. + // +optional + PodSubnet string `json:"podSubnet,omitempty"` + // DNSDomain is the dns domain used by k8s services. Defaults to "cluster.local". + // +optional + DNSDomain string `json:"dnsDomain,omitempty"` +} + +// BootstrapToken describes one bootstrap token, stored as a Secret in the cluster. +// +k8s:deepcopy-gen=true +type BootstrapToken struct { + // Token is used for establishing bidirectional trust between nodes and control-planes. + // Used for joining nodes in the cluster. + Token *BootstrapTokenString `json:"token" datapolicy:"token"` + // Description sets a human-friendly message why this token exists and what it's used + // for, so other administrators can know its purpose. + // +optional + Description string `json:"description,omitempty"` + // TTL defines the time to live for this token. Defaults to 24h. + // Expires and TTL are mutually exclusive. + // +optional + TTL *metav1.Duration `json:"ttl,omitempty"` + // Expires specifies the timestamp when this token expires. Defaults to being set + // dynamically at runtime based on the TTL. Expires and TTL are mutually exclusive. + // +optional + Expires *metav1.Time `json:"expires,omitempty"` + // Usages describes the ways in which this token can be used. Can by default be used + // for establishing bidirectional trust, but that can be changed here. + // +optional + Usages []string `json:"usages,omitempty"` + // Groups specifies the extra groups that this token will authenticate as when/if + // used for authentication + // +optional + Groups []string `json:"groups,omitempty"` +} + +// Etcd contains elements describing Etcd configuration. +type Etcd struct { + + // Local provides configuration knobs for configuring the local etcd instance + // Local and External are mutually exclusive + // +optional + Local *LocalEtcd `json:"local,omitempty"` + + // External describes how to connect to an external etcd cluster + // Local and External are mutually exclusive + // +optional + External *ExternalEtcd `json:"external,omitempty"` +} + +// LocalEtcd describes that kubeadm should run an etcd cluster locally. +type LocalEtcd struct { + // ImageMeta allows to customize the container used for etcd + ImageMeta `json:",inline"` + + // DataDir is the directory etcd will place its data. + // Defaults to "/var/lib/etcd". + DataDir string `json:"dataDir"` + + // ExtraArgs are extra arguments provided to the etcd binary + // when run inside a static pod. + // An argument name in this list is the flag name as it appears on the + // command line except without leading dash(es). Extra arguments will override existing + // default arguments. Duplicate extra arguments are allowed. + // +optional + ExtraArgs []Arg `json:"extraArgs,omitempty"` + + // ExtraEnvs is an extra set of environment variables to pass to the control plane component. + // Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default. + // +optional + ExtraEnvs []EnvVar `json:"extraEnvs,omitempty"` + + // ServerCertSANs sets extra Subject Alternative Names for the etcd server signing cert. + // +optional + ServerCertSANs []string `json:"serverCertSANs,omitempty"` + // PeerCertSANs sets extra Subject Alternative Names for the etcd peer signing cert. + // +optional + PeerCertSANs []string `json:"peerCertSANs,omitempty"` +} + +// ExternalEtcd describes an external etcd cluster. +// Kubeadm has no knowledge of where certificate files live and they must be supplied. +type ExternalEtcd struct { + // Endpoints of etcd members. Required for ExternalEtcd. + Endpoints []string `json:"endpoints"` + + // CAFile is an SSL Certificate Authority file used to secure etcd communication. + // Required if using a TLS connection. + CAFile string `json:"caFile"` + + // CertFile is an SSL certification file used to secure etcd communication. + // Required if using a TLS connection. + CertFile string `json:"certFile"` + + // KeyFile is an SSL key file used to secure etcd communication. + // Required if using a TLS connection. + KeyFile string `json:"keyFile"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// JoinConfiguration contains elements describing a particular node. +type JoinConfiguration struct { + metav1.TypeMeta `json:",inline"` + + // DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done. + // +optional + DryRun bool `json:"dryRun,omitempty"` + + // NodeRegistration holds fields that relate to registering the new control-plane node to the cluster + // +optional + NodeRegistration NodeRegistrationOptions `json:"nodeRegistration,omitempty"` + + // CACertPath is the path to the SSL certificate authority used to + // secure comunications between node and control-plane. + // Defaults to "/etc/kubernetes/pki/ca.crt". + // +optional + CACertPath string `json:"caCertPath,omitempty"` + + // Discovery specifies the options for the kubelet to use during the TLS Bootstrap process + Discovery Discovery `json:"discovery"` + + // ControlPlane defines the additional control plane instance to be deployed on the joining node. + // If nil, no additional control plane instance will be deployed. + // +optional + ControlPlane *JoinControlPlane `json:"controlPlane,omitempty"` + + // SkipPhases is a list of phases to skip during command execution. + // The list of phases can be obtained with the "kubeadm join --help" command. + // The flag "--skip-phases" takes precedence over this field. + // +optional + SkipPhases []string `json:"skipPhases,omitempty"` + + // Patches contains options related to applying patches to components deployed by kubeadm during + // "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. +type JoinControlPlane struct { + // LocalAPIEndpoint represents the endpoint of the API server instance to be deployed on this node. + // +optional + LocalAPIEndpoint APIEndpoint `json:"localAPIEndpoint,omitempty"` + + // CertificateKey is the key that is used for decryption of certificates after they are downloaded from the secret + // upon joining a new control plane node. The corresponding encryption key is in the InitConfiguration. + // The certificate key is a hex encoded string that is an AES key of size 32 bytes. + // +optional + CertificateKey string `json:"certificateKey,omitempty"` +} + +// Discovery specifies the options for the kubelet to use during the TLS Bootstrap process. +type Discovery struct { + // BootstrapToken is used to set the options for bootstrap token based discovery + // BootstrapToken and File are mutually exclusive + // +optional + BootstrapToken *BootstrapTokenDiscovery `json:"bootstrapToken,omitempty"` + + // File is used to specify a file or URL to a kubeconfig file from which to load cluster information + // BootstrapToken and File are mutually exclusive + // +optional + File *FileDiscovery `json:"file,omitempty"` + + // TLSBootstrapToken is a token used for TLS bootstrapping. + // If .BootstrapToken is set, this field is defaulted to .BootstrapToken.Token, but can be overridden. + // If .File is set, this field **must be set** in case the KubeConfigFile does not contain any other authentication information + // +optional + TLSBootstrapToken string `json:"tlsBootstrapToken,omitempty" datapolicy:"token"` +} + +// BootstrapTokenDiscovery is used to set the options for bootstrap token based discovery. +type BootstrapTokenDiscovery struct { + // Token is a token used to validate cluster information + // fetched from the control-plane. + Token string `json:"token" datapolicy:"token"` + + // APIServerEndpoint is an IP or domain name to the API server from which info will be fetched. + // +optional + APIServerEndpoint string `json:"apiServerEndpoint,omitempty"` + + // CACertHashes specifies a set of public key pins to verify + // when token-based discovery is used. The root CA found during discovery + // must match one of these values. Specifying an empty set disables root CA + // pinning, which can be unsafe. Each hash is specified as ":", + // where the only currently supported type is "sha256". This is a hex-encoded + // SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded + // ASN.1. These hashes can be calculated using, for example, OpenSSL. + // +optional + CACertHashes []string `json:"caCertHashes,omitempty" datapolicy:"security-key"` + + // UnsafeSkipCAVerification allows token-based discovery + // without CA verification via CACertHashes. This can weaken + // the security of kubeadm since other nodes can impersonate the control-plane. + // +optional + UnsafeSkipCAVerification bool `json:"unsafeSkipCAVerification,omitempty"` +} + +// FileDiscovery is used to specify a file or URL to a kubeconfig file from which to load cluster information. +type FileDiscovery struct { + // KubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from which to load cluster information + KubeConfigPath string `json:"kubeConfigPath"` +} + +// HostPathMount contains elements describing volumes that are mounted from the +// host. +type HostPathMount struct { + // Name of the volume inside the pod template. + Name string `json:"name"` + // HostPath is the path in the host that will be mounted inside + // the pod. + HostPath string `json:"hostPath"` + // MountPath is the path inside the pod where hostPath will be mounted. + MountPath string `json:"mountPath"` + // ReadOnly controls write access to the volume + // +optional + ReadOnly bool `json:"readOnly,omitempty"` + // PathType is the type of the HostPath. + // +optional + PathType corev1.HostPathType `json:"pathType,omitempty"` +} + +// Patches contains options related to applying patches to components deployed by kubeadm. +type Patches struct { + // Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". + // For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". + // "patchtype" can be one of "strategic" "merge" or "json" and they match the patch formats supported by kubectl. + // The default "patchtype" is "strategic". "extension" must be either "json" or "yaml". + // "suffix" is an optional string that can be used to determine which patches are applied + // first alpha-numerically. + // +optional + Directory string `json:"directory,omitempty"` +} + +// Arg represents an argument with a name and a value. +type Arg struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// EnvVar represents an environment variable present in a Container. +type EnvVar struct { + corev1.EnvVar `json:",inline"` +} + +// EncryptionAlgorithmType can define an asymmetric encryption algorithm type. +type EncryptionAlgorithmType string + +const ( + // EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256. + EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256" + // 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. + EncryptionAlgorithmRSA3072 EncryptionAlgorithmType = "RSA-3072" + // EncryptionAlgorithmRSA4096 defines the RSA encryption algorithm type with key size 4096 bits. + EncryptionAlgorithmRSA4096 EncryptionAlgorithmType = "RSA-4096" +) + +// Timeouts holds various timeouts that apply to kubeadm commands. +type Timeouts struct { + // ControlPlaneComponentHealthCheck 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". + // Default: 4m + // +optional + ControlPlaneComponentHealthCheck *metav1.Duration `json:"controlPlaneComponentHealthCheck,omitempty"` + + // KubeletHealthCheck is the amount of time to wait for the kubelet to be healthy + // during "kubeadm init" and "kubeadm join". + // Default: 4m + // +optional + KubeletHealthCheck *metav1.Duration `json:"kubeletHealthCheck,omitempty"` + + // KubernetesAPICall 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). + // Default: 1m + // +optional + KubernetesAPICall *metav1.Duration `json:"kubernetesAPICall,omitempty"` + + // EtcdAPICall is the amount of time to wait for the kubeadm etcd client to complete a request to + // the etcd cluster. + // Default: 2m + // +optional + EtcdAPICall *metav1.Duration `json:"etcdAPICall,omitempty"` + + // TLSBootstrap is the amount of time to wait for the kubelet to complete TLS bootstrap + // for a joining node. + // Default: 5m + // +optional + TLSBootstrap *metav1.Duration `json:"tlsBootstrap,omitempty"` + + // Discovery is the amount of time to wait for kubeadm to validate the API server identity + // for a joining node. + // Default: 5m + // +optional + Discovery *metav1.Duration `json:"discovery,omitempty"` + + // UpgradeManifests is the timeout for upgrading static Pod manifests + // Default: 5m + UpgradeManifests *metav1.Duration `json:"upgradeManifests,omitempty"` +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go new file mode 100644 index 000000000000..b2cde96018a9 --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go @@ -0,0 +1,814 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package upstreamv1beta4 + +import ( + unsafe "unsafe" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + v1beta1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*APIEndpoint)(nil), (*v1beta1.APIEndpoint)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint(a.(*APIEndpoint), b.(*v1beta1.APIEndpoint), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.APIEndpoint)(nil), (*APIEndpoint)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint(a.(*v1beta1.APIEndpoint), b.(*APIEndpoint), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*APIServer)(nil), (*v1beta1.APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_APIServer_To_v1beta1_APIServer(a.(*APIServer), b.(*v1beta1.APIServer), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*BootstrapToken)(nil), (*v1beta1.BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_BootstrapToken_To_v1beta1_BootstrapToken(a.(*BootstrapToken), b.(*v1beta1.BootstrapToken), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.BootstrapToken)(nil), (*BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_BootstrapToken_To_upstreamv1beta4_BootstrapToken(a.(*v1beta1.BootstrapToken), b.(*BootstrapToken), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*BootstrapTokenDiscovery)(nil), (*v1beta1.BootstrapTokenDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(a.(*BootstrapTokenDiscovery), b.(*v1beta1.BootstrapTokenDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.BootstrapTokenDiscovery)(nil), (*BootstrapTokenDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_BootstrapTokenDiscovery_To_upstreamv1beta4_BootstrapTokenDiscovery(a.(*v1beta1.BootstrapTokenDiscovery), b.(*BootstrapTokenDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*BootstrapTokenString)(nil), (*v1beta1.BootstrapTokenString)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_BootstrapTokenString_To_v1beta1_BootstrapTokenString(a.(*BootstrapTokenString), b.(*v1beta1.BootstrapTokenString), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.BootstrapTokenString)(nil), (*BootstrapTokenString)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_BootstrapTokenString_To_upstreamv1beta4_BootstrapTokenString(a.(*v1beta1.BootstrapTokenString), b.(*BootstrapTokenString), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.ClusterConfiguration)(nil), (*ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguration(a.(*v1beta1.ClusterConfiguration), b.(*ClusterConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.DNS)(nil), (*DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_DNS_To_upstreamv1beta4_DNS(a.(*v1beta1.DNS), b.(*DNS), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Discovery)(nil), (*v1beta1.Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(a.(*Discovery), b.(*v1beta1.Discovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Etcd)(nil), (*v1beta1.Etcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_Etcd_To_v1beta1_Etcd(a.(*Etcd), b.(*v1beta1.Etcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.Etcd)(nil), (*Etcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Etcd_To_upstreamv1beta4_Etcd(a.(*v1beta1.Etcd), b.(*Etcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ExternalEtcd)(nil), (*v1beta1.ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_ExternalEtcd_To_v1beta1_ExternalEtcd(a.(*ExternalEtcd), b.(*v1beta1.ExternalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.ExternalEtcd)(nil), (*ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ExternalEtcd_To_upstreamv1beta4_ExternalEtcd(a.(*v1beta1.ExternalEtcd), b.(*ExternalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*FileDiscovery)(nil), (*v1beta1.FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery(a.(*FileDiscovery), b.(*v1beta1.FileDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*HostPathMount)(nil), (*v1beta1.HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount(a.(*HostPathMount), b.(*v1beta1.HostPathMount), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.HostPathMount)(nil), (*HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_HostPathMount_To_upstreamv1beta4_HostPathMount(a.(*v1beta1.HostPathMount), b.(*HostPathMount), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ImageMeta)(nil), (*v1beta1.ImageMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta(a.(*ImageMeta), b.(*v1beta1.ImageMeta), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.ImageMeta)(nil), (*ImageMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ImageMeta_To_upstreamv1beta4_ImageMeta(a.(*v1beta1.ImageMeta), b.(*ImageMeta), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_InitConfiguration_To_upstreamv1beta4_InitConfiguration(a.(*v1beta1.InitConfiguration), b.(*InitConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.JoinControlPlane)(nil), (*JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane(a.(*v1beta1.JoinControlPlane), b.(*JoinControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Networking)(nil), (*v1beta1.Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_Networking_To_v1beta1_Networking(a.(*Networking), b.(*v1beta1.Networking), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.Networking)(nil), (*Networking)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Networking_To_upstreamv1beta4_Networking(a.(*v1beta1.Networking), b.(*Networking), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Patches)(nil), (*v1beta1.Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_Patches_To_v1beta1_Patches(a.(*Patches), b.(*v1beta1.Patches), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.Patches)(nil), (*Patches)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Patches_To_upstreamv1beta4_Patches(a.(*v1beta1.Patches), b.(*Patches), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ClusterConfiguration)(nil), (*v1beta1.ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_ClusterConfiguration_To_v1beta1_ClusterConfiguration(a.(*ClusterConfiguration), b.(*v1beta1.ClusterConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*v1beta1.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*v1beta1.ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*DNS)(nil), (*v1beta1.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_DNS_To_v1beta1_DNS(a.(*DNS), b.(*v1beta1.DNS), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*InitConfiguration)(nil), (*v1beta1.InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_InitConfiguration_To_v1beta1_InitConfiguration(a.(*InitConfiguration), b.(*v1beta1.InitConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*v1beta1.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_JoinConfiguration_To_v1beta1_JoinConfiguration(a.(*JoinConfiguration), b.(*v1beta1.JoinConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*JoinControlPlane)(nil), (*v1beta1.JoinControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_JoinControlPlane_To_v1beta1_JoinControlPlane(a.(*JoinControlPlane), b.(*v1beta1.JoinControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*LocalEtcd)(nil), (*v1beta1.LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_LocalEtcd_To_v1beta1_LocalEtcd(a.(*LocalEtcd), b.(*v1beta1.LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*NodeRegistrationOptions)(nil), (*v1beta1.NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*NodeRegistrationOptions), b.(*v1beta1.NodeRegistrationOptions), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_APIServer_To_upstreamv1beta4_APIServer(a.(*v1beta1.APIServer), b.(*APIServer), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(a.(*v1beta1.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.Discovery)(nil), (*Discovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(a.(*v1beta1.Discovery), b.(*Discovery), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(a.(*v1beta1.JoinConfiguration), b.(*JoinConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.LocalEtcd)(nil), (*LocalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_LocalEtcd_To_upstreamv1beta4_LocalEtcd(a.(*v1beta1.LocalEtcd), b.(*LocalEtcd), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(a.(*v1beta1.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint(in *APIEndpoint, out *v1beta1.APIEndpoint, s conversion.Scope) error { + out.AdvertiseAddress = in.AdvertiseAddress + out.BindPort = in.BindPort + return nil +} + +// Convert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint is an autogenerated conversion function. +func Convert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint(in *APIEndpoint, out *v1beta1.APIEndpoint, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint(in, out, s) +} + +func autoConvert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint(in *v1beta1.APIEndpoint, out *APIEndpoint, s conversion.Scope) error { + out.AdvertiseAddress = in.AdvertiseAddress + out.BindPort = in.BindPort + return nil +} + +// Convert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint is an autogenerated conversion function. +func Convert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint(in *v1beta1.APIEndpoint, out *APIEndpoint, s conversion.Scope) error { + return autoConvert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint(in, out, s) +} + +func autoConvert_upstreamv1beta4_APIServer_To_v1beta1_APIServer(in *APIServer, out *v1beta1.APIServer, s conversion.Scope) error { + if err := Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { + return err + } + out.CertSANs = *(*[]string)(unsafe.Pointer(&in.CertSANs)) + return nil +} + +// Convert_upstreamv1beta4_APIServer_To_v1beta1_APIServer is an autogenerated conversion function. +func Convert_upstreamv1beta4_APIServer_To_v1beta1_APIServer(in *APIServer, out *v1beta1.APIServer, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_APIServer_To_v1beta1_APIServer(in, out, s) +} + +func autoConvert_v1beta1_APIServer_To_upstreamv1beta4_APIServer(in *v1beta1.APIServer, out *APIServer, s conversion.Scope) error { + if err := Convert_v1beta1_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(&in.ControlPlaneComponent, &out.ControlPlaneComponent, s); err != nil { + 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_BootstrapToken_To_v1beta1_BootstrapToken(in *BootstrapToken, out *v1beta1.BootstrapToken, s conversion.Scope) error { + out.Token = (*v1beta1.BootstrapTokenString)(unsafe.Pointer(in.Token)) + out.Description = in.Description + out.TTL = (*v1.Duration)(unsafe.Pointer(in.TTL)) + out.Expires = (*v1.Time)(unsafe.Pointer(in.Expires)) + out.Usages = *(*[]string)(unsafe.Pointer(&in.Usages)) + out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups)) + return nil +} + +// Convert_upstreamv1beta4_BootstrapToken_To_v1beta1_BootstrapToken is an autogenerated conversion function. +func Convert_upstreamv1beta4_BootstrapToken_To_v1beta1_BootstrapToken(in *BootstrapToken, out *v1beta1.BootstrapToken, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_BootstrapToken_To_v1beta1_BootstrapToken(in, out, s) +} + +func autoConvert_v1beta1_BootstrapToken_To_upstreamv1beta4_BootstrapToken(in *v1beta1.BootstrapToken, out *BootstrapToken, s conversion.Scope) error { + out.Token = (*BootstrapTokenString)(unsafe.Pointer(in.Token)) + out.Description = in.Description + out.TTL = (*v1.Duration)(unsafe.Pointer(in.TTL)) + out.Expires = (*v1.Time)(unsafe.Pointer(in.Expires)) + out.Usages = *(*[]string)(unsafe.Pointer(&in.Usages)) + out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups)) + return nil +} + +// Convert_v1beta1_BootstrapToken_To_upstreamv1beta4_BootstrapToken is an autogenerated conversion function. +func Convert_v1beta1_BootstrapToken_To_upstreamv1beta4_BootstrapToken(in *v1beta1.BootstrapToken, out *BootstrapToken, s conversion.Scope) error { + return autoConvert_v1beta1_BootstrapToken_To_upstreamv1beta4_BootstrapToken(in, out, s) +} + +func autoConvert_upstreamv1beta4_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(in *BootstrapTokenDiscovery, out *v1beta1.BootstrapTokenDiscovery, s conversion.Scope) error { + out.Token = in.Token + out.APIServerEndpoint = in.APIServerEndpoint + out.CACertHashes = *(*[]string)(unsafe.Pointer(&in.CACertHashes)) + out.UnsafeSkipCAVerification = in.UnsafeSkipCAVerification + return nil +} + +// Convert_upstreamv1beta4_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery is an autogenerated conversion function. +func Convert_upstreamv1beta4_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(in *BootstrapTokenDiscovery, out *v1beta1.BootstrapTokenDiscovery, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_BootstrapTokenDiscovery_To_v1beta1_BootstrapTokenDiscovery(in, out, s) +} + +func autoConvert_v1beta1_BootstrapTokenDiscovery_To_upstreamv1beta4_BootstrapTokenDiscovery(in *v1beta1.BootstrapTokenDiscovery, out *BootstrapTokenDiscovery, s conversion.Scope) error { + out.Token = in.Token + out.APIServerEndpoint = in.APIServerEndpoint + out.CACertHashes = *(*[]string)(unsafe.Pointer(&in.CACertHashes)) + out.UnsafeSkipCAVerification = in.UnsafeSkipCAVerification + return nil +} + +// Convert_v1beta1_BootstrapTokenDiscovery_To_upstreamv1beta4_BootstrapTokenDiscovery is an autogenerated conversion function. +func Convert_v1beta1_BootstrapTokenDiscovery_To_upstreamv1beta4_BootstrapTokenDiscovery(in *v1beta1.BootstrapTokenDiscovery, out *BootstrapTokenDiscovery, s conversion.Scope) error { + return autoConvert_v1beta1_BootstrapTokenDiscovery_To_upstreamv1beta4_BootstrapTokenDiscovery(in, out, s) +} + +func autoConvert_upstreamv1beta4_BootstrapTokenString_To_v1beta1_BootstrapTokenString(in *BootstrapTokenString, out *v1beta1.BootstrapTokenString, s conversion.Scope) error { + out.ID = in.ID + out.Secret = in.Secret + return nil +} + +// Convert_upstreamv1beta4_BootstrapTokenString_To_v1beta1_BootstrapTokenString is an autogenerated conversion function. +func Convert_upstreamv1beta4_BootstrapTokenString_To_v1beta1_BootstrapTokenString(in *BootstrapTokenString, out *v1beta1.BootstrapTokenString, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_BootstrapTokenString_To_v1beta1_BootstrapTokenString(in, out, s) +} + +func autoConvert_v1beta1_BootstrapTokenString_To_upstreamv1beta4_BootstrapTokenString(in *v1beta1.BootstrapTokenString, out *BootstrapTokenString, s conversion.Scope) error { + out.ID = in.ID + out.Secret = in.Secret + return nil +} + +// Convert_v1beta1_BootstrapTokenString_To_upstreamv1beta4_BootstrapTokenString is an autogenerated conversion function. +func Convert_v1beta1_BootstrapTokenString_To_upstreamv1beta4_BootstrapTokenString(in *v1beta1.BootstrapTokenString, out *BootstrapTokenString, s conversion.Scope) error { + return autoConvert_v1beta1_BootstrapTokenString_To_upstreamv1beta4_BootstrapTokenString(in, out, s) +} + +func autoConvert_upstreamv1beta4_ClusterConfiguration_To_v1beta1_ClusterConfiguration(in *ClusterConfiguration, out *v1beta1.ClusterConfiguration, s conversion.Scope) error { + if err := Convert_upstreamv1beta4_Etcd_To_v1beta1_Etcd(&in.Etcd, &out.Etcd, s); err != nil { + return err + } + if err := Convert_upstreamv1beta4_Networking_To_v1beta1_Networking(&in.Networking, &out.Networking, s); err != nil { + return err + } + out.KubernetesVersion = in.KubernetesVersion + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if err := Convert_upstreamv1beta4_APIServer_To_v1beta1_APIServer(&in.APIServer, &out.APIServer, s); err != nil { + return err + } + if err := Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(&in.ControllerManager, &out.ControllerManager, s); err != nil { + return err + } + if err := Convert_upstreamv1beta4_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(&in.Scheduler, &out.Scheduler, s); err != nil { + return err + } + if err := Convert_upstreamv1beta4_DNS_To_v1beta1_DNS(&in.DNS, &out.DNS, s); err != nil { + return err + } + // WARNING: in.Proxy requires manual conversion: does not exist in peer-type + out.CertificatesDir = in.CertificatesDir + out.ImageRepository = in.ImageRepository + out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) + out.ClusterName = in.ClusterName + // WARNING: in.EncryptionAlgorithm requires manual conversion: does not exist in peer-type + // WARNING: in.CertificateValidityPeriod requires manual conversion: does not exist in peer-type + // WARNING: in.CACertificateValidityPeriod requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguration(in *v1beta1.ClusterConfiguration, out *ClusterConfiguration, s conversion.Scope) error { + if err := Convert_v1beta1_Etcd_To_upstreamv1beta4_Etcd(&in.Etcd, &out.Etcd, s); err != nil { + return err + } + if err := Convert_v1beta1_Networking_To_upstreamv1beta4_Networking(&in.Networking, &out.Networking, s); err != nil { + return err + } + out.KubernetesVersion = in.KubernetesVersion + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if err := Convert_v1beta1_APIServer_To_upstreamv1beta4_APIServer(&in.APIServer, &out.APIServer, s); err != nil { + return err + } + if err := Convert_v1beta1_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(&in.ControllerManager, &out.ControllerManager, s); err != nil { + return err + } + if err := Convert_v1beta1_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(&in.Scheduler, &out.Scheduler, s); err != nil { + return err + } + if err := Convert_v1beta1_DNS_To_upstreamv1beta4_DNS(&in.DNS, &out.DNS, s); err != nil { + return err + } + out.CertificatesDir = in.CertificatesDir + out.ImageRepository = in.ImageRepository + out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) + out.ClusterName = in.ClusterName + return nil +} + +// Convert_v1beta1_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguration is an autogenerated conversion function. +func Convert_v1beta1_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguration(in *v1beta1.ClusterConfiguration, out *ClusterConfiguration, s conversion.Scope) error { + return autoConvert_v1beta1_ClusterConfiguration_To_upstreamv1beta4_ClusterConfiguration(in, out, s) +} + +func autoConvert_upstreamv1beta4_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in *ControlPlaneComponent, out *v1beta1.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.ExtraVolumes = *(*[]v1beta1.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) + // WARNING: in.ExtraEnvs requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_ControlPlaneComponent_To_upstreamv1beta4_ControlPlaneComponent(in *v1beta1.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.ExtraVolumes = *(*[]HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) + return nil +} + +func autoConvert_upstreamv1beta4_DNS_To_v1beta1_DNS(in *DNS, out *v1beta1.DNS, s conversion.Scope) error { + if err := Convert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { + return err + } + // WARNING: in.Disabled requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_DNS_To_upstreamv1beta4_DNS(in *v1beta1.DNS, out *DNS, s conversion.Scope) error { + if err := Convert_v1beta1_ImageMeta_To_upstreamv1beta4_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_DNS_To_upstreamv1beta4_DNS is an autogenerated conversion function. +func Convert_v1beta1_DNS_To_upstreamv1beta4_DNS(in *v1beta1.DNS, out *DNS, s conversion.Scope) error { + return autoConvert_v1beta1_DNS_To_upstreamv1beta4_DNS(in, out, s) +} + +func autoConvert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { + out.BootstrapToken = (*v1beta1.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) + out.File = (*v1beta1.FileDiscovery)(unsafe.Pointer(in.File)) + out.TLSBootstrapToken = in.TLSBootstrapToken + return nil +} + +// Convert_upstreamv1beta4_Discovery_To_v1beta1_Discovery is an autogenerated conversion function. +func Convert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(in, out, s) +} + +func autoConvert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(in *v1beta1.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 + // WARNING: in.Timeout requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_upstreamv1beta4_Etcd_To_v1beta1_Etcd(in *Etcd, out *v1beta1.Etcd, s conversion.Scope) error { + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(v1beta1.LocalEtcd) + if err := Convert_upstreamv1beta4_LocalEtcd_To_v1beta1_LocalEtcd(*in, *out, s); err != nil { + return err + } + } else { + out.Local = nil + } + out.External = (*v1beta1.ExternalEtcd)(unsafe.Pointer(in.External)) + return nil +} + +// Convert_upstreamv1beta4_Etcd_To_v1beta1_Etcd is an autogenerated conversion function. +func Convert_upstreamv1beta4_Etcd_To_v1beta1_Etcd(in *Etcd, out *v1beta1.Etcd, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_Etcd_To_v1beta1_Etcd(in, out, s) +} + +func autoConvert_v1beta1_Etcd_To_upstreamv1beta4_Etcd(in *v1beta1.Etcd, out *Etcd, s conversion.Scope) error { + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(LocalEtcd) + if err := Convert_v1beta1_LocalEtcd_To_upstreamv1beta4_LocalEtcd(*in, *out, s); err != nil { + return err + } + } else { + out.Local = nil + } + out.External = (*ExternalEtcd)(unsafe.Pointer(in.External)) + return nil +} + +// Convert_v1beta1_Etcd_To_upstreamv1beta4_Etcd is an autogenerated conversion function. +func Convert_v1beta1_Etcd_To_upstreamv1beta4_Etcd(in *v1beta1.Etcd, out *Etcd, s conversion.Scope) error { + return autoConvert_v1beta1_Etcd_To_upstreamv1beta4_Etcd(in, out, s) +} + +func autoConvert_upstreamv1beta4_ExternalEtcd_To_v1beta1_ExternalEtcd(in *ExternalEtcd, out *v1beta1.ExternalEtcd, s conversion.Scope) error { + out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + out.CAFile = in.CAFile + out.CertFile = in.CertFile + out.KeyFile = in.KeyFile + return nil +} + +// Convert_upstreamv1beta4_ExternalEtcd_To_v1beta1_ExternalEtcd is an autogenerated conversion function. +func Convert_upstreamv1beta4_ExternalEtcd_To_v1beta1_ExternalEtcd(in *ExternalEtcd, out *v1beta1.ExternalEtcd, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_ExternalEtcd_To_v1beta1_ExternalEtcd(in, out, s) +} + +func autoConvert_v1beta1_ExternalEtcd_To_upstreamv1beta4_ExternalEtcd(in *v1beta1.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { + out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + out.CAFile = in.CAFile + out.CertFile = in.CertFile + out.KeyFile = in.KeyFile + return nil +} + +// Convert_v1beta1_ExternalEtcd_To_upstreamv1beta4_ExternalEtcd is an autogenerated conversion function. +func Convert_v1beta1_ExternalEtcd_To_upstreamv1beta4_ExternalEtcd(in *v1beta1.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { + return autoConvert_v1beta1_ExternalEtcd_To_upstreamv1beta4_ExternalEtcd(in, out, s) +} + +func autoConvert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDiscovery, out *v1beta1.FileDiscovery, s conversion.Scope) error { + out.KubeConfigPath = in.KubeConfigPath + return nil +} + +// Convert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery is an autogenerated conversion function. +func Convert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDiscovery, out *v1beta1.FileDiscovery, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery(in, out, s) +} + +func autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { + out.KubeConfigPath = in.KubeConfigPath + return nil +} + +// Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery is an autogenerated conversion function. +func Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { + return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in, out, s) +} + +func autoConvert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount(in *HostPathMount, out *v1beta1.HostPathMount, s conversion.Scope) error { + out.Name = in.Name + out.HostPath = in.HostPath + out.MountPath = in.MountPath + out.ReadOnly = in.ReadOnly + out.PathType = corev1.HostPathType(in.PathType) + return nil +} + +// Convert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount is an autogenerated conversion function. +func Convert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount(in *HostPathMount, out *v1beta1.HostPathMount, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount(in, out, s) +} + +func autoConvert_v1beta1_HostPathMount_To_upstreamv1beta4_HostPathMount(in *v1beta1.HostPathMount, out *HostPathMount, s conversion.Scope) error { + out.Name = in.Name + out.HostPath = in.HostPath + out.MountPath = in.MountPath + out.ReadOnly = in.ReadOnly + out.PathType = corev1.HostPathType(in.PathType) + return nil +} + +// Convert_v1beta1_HostPathMount_To_upstreamv1beta4_HostPathMount is an autogenerated conversion function. +func Convert_v1beta1_HostPathMount_To_upstreamv1beta4_HostPathMount(in *v1beta1.HostPathMount, out *HostPathMount, s conversion.Scope) error { + return autoConvert_v1beta1_HostPathMount_To_upstreamv1beta4_HostPathMount(in, out, s) +} + +func autoConvert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta(in *ImageMeta, out *v1beta1.ImageMeta, s conversion.Scope) error { + out.ImageRepository = in.ImageRepository + out.ImageTag = in.ImageTag + return nil +} + +// Convert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta is an autogenerated conversion function. +func Convert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta(in *ImageMeta, out *v1beta1.ImageMeta, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta(in, out, s) +} + +func autoConvert_v1beta1_ImageMeta_To_upstreamv1beta4_ImageMeta(in *v1beta1.ImageMeta, out *ImageMeta, s conversion.Scope) error { + out.ImageRepository = in.ImageRepository + out.ImageTag = in.ImageTag + return nil +} + +// Convert_v1beta1_ImageMeta_To_upstreamv1beta4_ImageMeta is an autogenerated conversion function. +func Convert_v1beta1_ImageMeta_To_upstreamv1beta4_ImageMeta(in *v1beta1.ImageMeta, out *ImageMeta, s conversion.Scope) error { + return autoConvert_v1beta1_ImageMeta_To_upstreamv1beta4_ImageMeta(in, out, s) +} + +func autoConvert_upstreamv1beta4_InitConfiguration_To_v1beta1_InitConfiguration(in *InitConfiguration, out *v1beta1.InitConfiguration, s conversion.Scope) error { + out.BootstrapTokens = *(*[]v1beta1.BootstrapToken)(unsafe.Pointer(&in.BootstrapTokens)) + // WARNING: in.DryRun requires manual conversion: does not exist in peer-type + if err := Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + if err := Convert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + // WARNING: in.CertificateKey requires manual conversion: does not exist in peer-type + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + out.Patches = (*v1beta1.Patches)(unsafe.Pointer(in.Patches)) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_InitConfiguration_To_upstreamv1beta4_InitConfiguration(in *v1beta1.InitConfiguration, out *InitConfiguration, s conversion.Scope) error { + out.BootstrapTokens = *(*[]BootstrapToken)(unsafe.Pointer(&in.BootstrapTokens)) + if err := Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + if err := Convert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + return nil +} + +// Convert_v1beta1_InitConfiguration_To_upstreamv1beta4_InitConfiguration is an autogenerated conversion function. +func Convert_v1beta1_InitConfiguration_To_upstreamv1beta4_InitConfiguration(in *v1beta1.InitConfiguration, out *InitConfiguration, s conversion.Scope) error { + return autoConvert_v1beta1_InitConfiguration_To_upstreamv1beta4_InitConfiguration(in, out, s) +} + +func autoConvert_upstreamv1beta4_JoinConfiguration_To_v1beta1_JoinConfiguration(in *JoinConfiguration, out *v1beta1.JoinConfiguration, s conversion.Scope) error { + // WARNING: in.DryRun requires manual conversion: does not exist in peer-type + if err := Convert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + out.CACertPath = in.CACertPath + if err := Convert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(&in.Discovery, &out.Discovery, s); err != nil { + return err + } + if in.ControlPlane != nil { + in, out := &in.ControlPlane, &out.ControlPlane + *out = new(v1beta1.JoinControlPlane) + if err := Convert_upstreamv1beta4_JoinControlPlane_To_v1beta1_JoinControlPlane(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlane = nil + } + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + out.Patches = (*v1beta1.Patches)(unsafe.Pointer(in.Patches)) + // WARNING: in.Timeouts requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(in *v1beta1.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { + if err := Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } + out.CACertPath = in.CACertPath + if err := Convert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(&in.Discovery, &out.Discovery, s); err != nil { + return err + } + if in.ControlPlane != nil { + in, out := &in.ControlPlane, &out.ControlPlane + *out = new(JoinControlPlane) + if err := Convert_v1beta1_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane(*in, *out, s); err != nil { + return err + } + } else { + out.ControlPlane = nil + } + out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases)) + out.Patches = (*Patches)(unsafe.Pointer(in.Patches)) + return nil +} + +func autoConvert_upstreamv1beta4_JoinControlPlane_To_v1beta1_JoinControlPlane(in *JoinControlPlane, out *v1beta1.JoinControlPlane, s conversion.Scope) error { + if err := Convert_upstreamv1beta4_APIEndpoint_To_v1beta1_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + // WARNING: in.CertificateKey requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane(in *v1beta1.JoinControlPlane, out *JoinControlPlane, s conversion.Scope) error { + if err := Convert_v1beta1_APIEndpoint_To_upstreamv1beta4_APIEndpoint(&in.LocalAPIEndpoint, &out.LocalAPIEndpoint, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane is an autogenerated conversion function. +func Convert_v1beta1_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane(in *v1beta1.JoinControlPlane, out *JoinControlPlane, s conversion.Scope) error { + return autoConvert_v1beta1_JoinControlPlane_To_upstreamv1beta4_JoinControlPlane(in, out, s) +} + +func autoConvert_upstreamv1beta4_LocalEtcd_To_v1beta1_LocalEtcd(in *LocalEtcd, out *v1beta1.LocalEtcd, s conversion.Scope) error { + if err := Convert_upstreamv1beta4_ImageMeta_To_v1beta1_ImageMeta(&in.ImageMeta, &out.ImageMeta, s); err != nil { + 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) + // 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)) + return nil +} + +func autoConvert_v1beta1_LocalEtcd_To_upstreamv1beta4_LocalEtcd(in *v1beta1.LocalEtcd, out *LocalEtcd, s conversion.Scope) error { + if err := Convert_v1beta1_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.ServerCertSANs = *(*[]string)(unsafe.Pointer(&in.ServerCertSANs)) + out.PeerCertSANs = *(*[]string)(unsafe.Pointer(&in.PeerCertSANs)) + return nil +} + +func autoConvert_upstreamv1beta4_Networking_To_v1beta1_Networking(in *Networking, out *v1beta1.Networking, s conversion.Scope) error { + out.ServiceSubnet = in.ServiceSubnet + out.PodSubnet = in.PodSubnet + out.DNSDomain = in.DNSDomain + return nil +} + +// Convert_upstreamv1beta4_Networking_To_v1beta1_Networking is an autogenerated conversion function. +func Convert_upstreamv1beta4_Networking_To_v1beta1_Networking(in *Networking, out *v1beta1.Networking, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_Networking_To_v1beta1_Networking(in, out, s) +} + +func autoConvert_v1beta1_Networking_To_upstreamv1beta4_Networking(in *v1beta1.Networking, out *Networking, s conversion.Scope) error { + out.ServiceSubnet = in.ServiceSubnet + out.PodSubnet = in.PodSubnet + out.DNSDomain = in.DNSDomain + return nil +} + +// Convert_v1beta1_Networking_To_upstreamv1beta4_Networking is an autogenerated conversion function. +func Convert_v1beta1_Networking_To_upstreamv1beta4_Networking(in *v1beta1.Networking, out *Networking, s conversion.Scope) error { + return autoConvert_v1beta1_Networking_To_upstreamv1beta4_Networking(in, out, s) +} + +func autoConvert_upstreamv1beta4_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *NodeRegistrationOptions, out *v1beta1.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 ([]sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4.Arg vs map[string]string) + out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) + out.ImagePullPolicy = string(in.ImagePullPolicy) + // WARNING: in.ImagePullSerial requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta4_NodeRegistrationOptions(in *v1beta1.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.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) + out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) + return nil +} + +func autoConvert_upstreamv1beta4_Patches_To_v1beta1_Patches(in *Patches, out *v1beta1.Patches, s conversion.Scope) error { + out.Directory = in.Directory + return nil +} + +// Convert_upstreamv1beta4_Patches_To_v1beta1_Patches is an autogenerated conversion function. +func Convert_upstreamv1beta4_Patches_To_v1beta1_Patches(in *Patches, out *v1beta1.Patches, s conversion.Scope) error { + return autoConvert_upstreamv1beta4_Patches_To_v1beta1_Patches(in, out, s) +} + +func autoConvert_v1beta1_Patches_To_upstreamv1beta4_Patches(in *v1beta1.Patches, out *Patches, s conversion.Scope) error { + out.Directory = in.Directory + return nil +} + +// Convert_v1beta1_Patches_To_upstreamv1beta4_Patches is an autogenerated conversion function. +func Convert_v1beta1_Patches_To_upstreamv1beta4_Patches(in *v1beta1.Patches, out *Patches, s conversion.Scope) error { + return autoConvert_v1beta1_Patches_To_upstreamv1beta4_Patches(in, out, s) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.deepcopy.go b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.deepcopy.go new file mode 100644 index 000000000000..c2a9da2058ef --- /dev/null +++ b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.deepcopy.go @@ -0,0 +1,659 @@ +//go:build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package upstreamv1beta4 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIEndpoint) DeepCopyInto(out *APIEndpoint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIEndpoint. +func (in *APIEndpoint) DeepCopy() *APIEndpoint { + if in == nil { + return nil + } + out := new(APIEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIServer) DeepCopyInto(out *APIServer) { + *out = *in + in.ControlPlaneComponent.DeepCopyInto(&out.ControlPlaneComponent) + if in.CertSANs != nil { + in, out := &in.CertSANs, &out.CertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIServer. +func (in *APIServer) DeepCopy() *APIServer { + if in == nil { + return nil + } + out := new(APIServer) + in.DeepCopyInto(out) + 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 + if in.Token != nil { + in, out := &in.Token, &out.Token + *out = new(BootstrapTokenString) + **out = **in + } + if in.TTL != nil { + in, out := &in.TTL, &out.TTL + *out = new(v1.Duration) + **out = **in + } + if in.Expires != nil { + in, out := &in.Expires, &out.Expires + *out = (*in).DeepCopy() + } + if in.Usages != nil { + in, out := &in.Usages, &out.Usages + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapToken. +func (in *BootstrapToken) DeepCopy() *BootstrapToken { + if in == nil { + return nil + } + out := new(BootstrapToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapTokenDiscovery) DeepCopyInto(out *BootstrapTokenDiscovery) { + *out = *in + if in.CACertHashes != nil { + in, out := &in.CACertHashes, &out.CACertHashes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenDiscovery. +func (in *BootstrapTokenDiscovery) DeepCopy() *BootstrapTokenDiscovery { + if in == nil { + return nil + } + out := new(BootstrapTokenDiscovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenString. +func (in *BootstrapTokenString) DeepCopy() *BootstrapTokenString { + if in == nil { + return nil + } + out := new(BootstrapTokenString) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterConfiguration) DeepCopyInto(out *ClusterConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.Etcd.DeepCopyInto(&out.Etcd) + out.Networking = in.Networking + in.APIServer.DeepCopyInto(&out.APIServer) + in.ControllerManager.DeepCopyInto(&out.ControllerManager) + in.Scheduler.DeepCopyInto(&out.Scheduler) + out.DNS = in.DNS + out.Proxy = in.Proxy + if in.FeatureGates != nil { + in, out := &in.FeatureGates, &out.FeatureGates + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.CertificateValidityPeriod != nil { + in, out := &in.CertificateValidityPeriod, &out.CertificateValidityPeriod + *out = new(v1.Duration) + **out = **in + } + if in.CACertificateValidityPeriod != nil { + in, out := &in.CACertificateValidityPeriod, &out.CACertificateValidityPeriod + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterConfiguration. +func (in *ClusterConfiguration) DeepCopy() *ClusterConfiguration { + if in == nil { + return nil + } + out := new(ClusterConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneComponent) DeepCopyInto(out *ControlPlaneComponent) { + *out = *in + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make([]Arg, len(*in)) + copy(*out, *in) + } + if in.ExtraVolumes != nil { + in, out := &in.ExtraVolumes, &out.ExtraVolumes + *out = make([]HostPathMount, len(*in)) + copy(*out, *in) + } + if in.ExtraEnvs != nil { + in, out := &in.ExtraEnvs, &out.ExtraEnvs + *out = make([]EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneComponent. +func (in *ControlPlaneComponent) DeepCopy() *ControlPlaneComponent { + if in == nil { + return nil + } + out := new(ControlPlaneComponent) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DNS) DeepCopyInto(out *DNS) { + *out = *in + out.ImageMeta = in.ImageMeta +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNS. +func (in *DNS) DeepCopy() *DNS { + if in == nil { + return nil + } + out := new(DNS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Discovery) DeepCopyInto(out *Discovery) { + *out = *in + if in.BootstrapToken != nil { + in, out := &in.BootstrapToken, &out.BootstrapToken + *out = new(BootstrapTokenDiscovery) + (*in).DeepCopyInto(*out) + } + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Discovery. +func (in *Discovery) DeepCopy() *Discovery { + if in == nil { + return nil + } + out := new(Discovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvVar) DeepCopyInto(out *EnvVar) { + *out = *in + in.EnvVar.DeepCopyInto(&out.EnvVar) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvVar. +func (in *EnvVar) DeepCopy() *EnvVar { + if in == nil { + return nil + } + out := new(EnvVar) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Etcd) DeepCopyInto(out *Etcd) { + *out = *in + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(LocalEtcd) + (*in).DeepCopyInto(*out) + } + if in.External != nil { + in, out := &in.External, &out.External + *out = new(ExternalEtcd) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Etcd. +func (in *Etcd) DeepCopy() *Etcd { + if in == nil { + return nil + } + out := new(Etcd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalEtcd) DeepCopyInto(out *ExternalEtcd) { + *out = *in + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalEtcd. +func (in *ExternalEtcd) DeepCopy() *ExternalEtcd { + if in == nil { + return nil + } + out := new(ExternalEtcd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileDiscovery) DeepCopyInto(out *FileDiscovery) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscovery. +func (in *FileDiscovery) DeepCopy() *FileDiscovery { + if in == nil { + return nil + } + out := new(FileDiscovery) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostPathMount) DeepCopyInto(out *HostPathMount) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPathMount. +func (in *HostPathMount) DeepCopy() *HostPathMount { + if in == nil { + return nil + } + out := new(HostPathMount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageMeta) DeepCopyInto(out *ImageMeta) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageMeta. +func (in *ImageMeta) DeepCopy() *ImageMeta { + if in == nil { + return nil + } + out := new(ImageMeta) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InitConfiguration) DeepCopyInto(out *InitConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BootstrapTokens != nil { + in, out := &in.BootstrapTokens, &out.BootstrapTokens + *out = make([]BootstrapToken, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) + out.LocalAPIEndpoint = in.LocalAPIEndpoint + if in.SkipPhases != nil { + in, out := &in.SkipPhases, &out.SkipPhases + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *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. +func (in *InitConfiguration) DeepCopy() *InitConfiguration { + if in == nil { + return nil + } + out := new(InitConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InitConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) + in.Discovery.DeepCopyInto(&out.Discovery) + if in.ControlPlane != nil { + in, out := &in.ControlPlane, &out.ControlPlane + *out = new(JoinControlPlane) + **out = **in + } + if in.SkipPhases != nil { + in, out := &in.SkipPhases, &out.SkipPhases + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Patches != nil { + in, out := &in.Patches, &out.Patches + *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. +func (in *JoinConfiguration) DeepCopy() *JoinConfiguration { + if in == nil { + return nil + } + out := new(JoinConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JoinConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JoinControlPlane) DeepCopyInto(out *JoinControlPlane) { + *out = *in + out.LocalAPIEndpoint = in.LocalAPIEndpoint +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JoinControlPlane. +func (in *JoinControlPlane) DeepCopy() *JoinControlPlane { + if in == nil { + return nil + } + out := new(JoinControlPlane) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalEtcd) DeepCopyInto(out *LocalEtcd) { + *out = *in + out.ImageMeta = in.ImageMeta + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make([]Arg, len(*in)) + copy(*out, *in) + } + if in.ExtraEnvs != nil { + in, out := &in.ExtraEnvs, &out.ExtraEnvs + *out = make([]EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ServerCertSANs != nil { + in, out := &in.ServerCertSANs, &out.ServerCertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PeerCertSANs != nil { + in, out := &in.PeerCertSANs, &out.PeerCertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalEtcd. +func (in *LocalEtcd) DeepCopy() *LocalEtcd { + if in == nil { + return nil + } + out := new(LocalEtcd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Networking) DeepCopyInto(out *Networking) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Networking. +func (in *Networking) DeepCopy() *Networking { + if in == nil { + return nil + } + out := new(Networking) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { + *out = *in + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]corev1.Taint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.KubeletExtraArgs != nil { + in, out := &in.KubeletExtraArgs, &out.KubeletExtraArgs + *out = make([]Arg, len(*in)) + copy(*out, *in) + } + if in.IgnorePreflightErrors != nil { + in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ImagePullSerial != nil { + in, out := &in.ImagePullSerial, &out.ImagePullSerial + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeRegistrationOptions. +func (in *NodeRegistrationOptions) DeepCopy() *NodeRegistrationOptions { + if in == nil { + return nil + } + out := new(NodeRegistrationOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Patches) DeepCopyInto(out *Patches) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Patches. +func (in *Patches) DeepCopy() *Patches { + if in == nil { + return nil + } + out := new(Patches) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Proxy) DeepCopyInto(out *Proxy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Proxy. +func (in *Proxy) DeepCopy() *Proxy { + if in == nil { + return nil + } + out := new(Proxy) + in.DeepCopyInto(out) + 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.ControlPlaneComponentHealthCheck != nil { + in, out := &in.ControlPlaneComponentHealthCheck, &out.ControlPlaneComponentHealthCheck + *out = new(v1.Duration) + **out = **in + } + if in.KubeletHealthCheck != nil { + in, out := &in.KubeletHealthCheck, &out.KubeletHealthCheck + *out = new(v1.Duration) + **out = **in + } + if in.KubernetesAPICall != nil { + in, out := &in.KubernetesAPICall, &out.KubernetesAPICall + *out = new(v1.Duration) + **out = **in + } + if in.EtcdAPICall != nil { + in, out := &in.EtcdAPICall, &out.EtcdAPICall + *out = new(v1.Duration) + **out = **in + } + if in.TLSBootstrap != nil { + in, out := &in.TLSBootstrap, &out.TLSBootstrap + *out = new(v1.Duration) + **out = **in + } + if in.Discovery != nil { + in, out := &in.Discovery, &out.Discovery + *out = new(v1.Duration) + **out = **in + } + if in.UpgradeManifests != nil { + in, out := &in.UpgradeManifests, &out.UpgradeManifests + *out = new(v1.Duration) + **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 +} diff --git a/bootstrap/kubeadm/types/utils.go b/bootstrap/kubeadm/types/utils.go index 57505e7ec819..81161faa0e18 100644 --- a/bootstrap/kubeadm/types/utils.go +++ b/bootstrap/kubeadm/types/utils.go @@ -29,34 +29,51 @@ import ( bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta2" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta3" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4" "sigs.k8s.io/cluster-api/util/version" ) var ( v1beta2KubeadmVersion = semver.MustParse("1.15.0") v1beta3KubeadmVersion = semver.MustParse("1.22.0") + v1beta4KubeadmVersion = semver.MustParse("1.31.0") clusterConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ + upstreamv1beta4.GroupVersion: &upstreamv1beta4.ClusterConfiguration{}, upstreamv1beta3.GroupVersion: &upstreamv1beta3.ClusterConfiguration{}, upstreamv1beta2.GroupVersion: &upstreamv1beta2.ClusterConfiguration{}, } clusterStatusVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ - // ClusterStatus has been removed in v1beta3, so we don't need an entry for v1beta3 + // ClusterStatus has been removed in v1beta3, so we don't need an entry for v1beta3 & v1beta4 upstreamv1beta2.GroupVersion: &upstreamv1beta2.ClusterStatus{}, } initConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ + upstreamv1beta4.GroupVersion: &upstreamv1beta4.InitConfiguration{}, upstreamv1beta3.GroupVersion: &upstreamv1beta3.InitConfiguration{}, upstreamv1beta2.GroupVersion: &upstreamv1beta2.InitConfiguration{}, } joinConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ + upstreamv1beta4.GroupVersion: &upstreamv1beta4.JoinConfiguration{}, upstreamv1beta3.GroupVersion: &upstreamv1beta3.JoinConfiguration{}, upstreamv1beta2.GroupVersion: &upstreamv1beta2.JoinConfiguration{}, } ) +// 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 +} + +// 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 +} + // KubeVersionToKubeadmAPIGroupVersion maps a Kubernetes version to the correct Kubeadm API Group supported. func KubeVersionToKubeadmAPIGroupVersion(v semver.Version) (schema.GroupVersion, error) { switch { @@ -65,46 +82,46 @@ func KubeVersionToKubeadmAPIGroupVersion(v semver.Version) (schema.GroupVersion, case version.Compare(v, v1beta3KubeadmVersion, version.WithoutPreReleases()) < 0: // NOTE: All the Kubernetes version >= v1.15 and < v1.22 should use the kubeadm API version v1beta2 return upstreamv1beta2.GroupVersion, nil - default: - // NOTE: All the Kubernetes version greater or equal to v1.22 should use the kubeadm API version v1beta3. - // Also future Kubernetes versions (not yet released at the time of writing this code) are going to use v1beta3, - // no matter if kubeadm API versions newer than v1beta3 could be introduced by those release. - // This is acceptable because v1beta3 will be supported by kubeadm until the deprecation cycle completes - // (9 months minimum after the deprecation date, not yet announced now); this gives Cluster API project time to - // introduce support for newer releases without blocking users to deploy newer version of Kubernetes. + case version.Compare(v, v1beta4KubeadmVersion, version.WithoutPreReleases()) < 0: + // 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) + // 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 } } // 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(obj *bootstrapv1.ClusterConfiguration, version semver.Version) (string, error) { - return marshalForVersion(obj, version, clusterConfigurationVersionTypeMap) +func MarshalClusterConfigurationForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, version semver.Version) (string, error) { + return marshalForVersion(nil, clusterConfiguration, version, clusterConfigurationVersionTypeMap) } // MarshalClusterStatusForVersion converts a Cluster API ClusterStatus type to the kubeadm API type // for the given Kubernetes Version. // NOTE: This assumes Kubernetes Version equals to kubeadm version. -func MarshalClusterStatusForVersion(obj *bootstrapv1.ClusterStatus, version semver.Version) (string, error) { - return marshalForVersion(obj, version, clusterStatusVersionTypeMap) +func MarshalClusterStatusForVersion(clusterStatus *bootstrapv1.ClusterStatus, version semver.Version) (string, error) { + return marshalForVersion(nil, clusterStatus, version, clusterStatusVersionTypeMap) } // 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(obj *bootstrapv1.InitConfiguration, version semver.Version) (string, error) { - return marshalForVersion(obj, version, initConfigurationVersionTypeMap) +func MarshalInitConfigurationForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, initConfiguration *bootstrapv1.InitConfiguration, version semver.Version) (string, error) { + return marshalForVersion(clusterConfiguration, 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(obj *bootstrapv1.JoinConfiguration, version semver.Version) (string, error) { - return marshalForVersion(obj, version, joinConfigurationVersionTypeMap) +func MarshalJoinConfigurationForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, joinConfiguration *bootstrapv1.JoinConfiguration, version semver.Version) (string, error) { + return marshalForVersion(clusterConfiguration, joinConfiguration, version, joinConfigurationVersionTypeMap) } -func marshalForVersion(obj conversion.Hub, version semver.Version, kubeadmObjVersionTypeMap map[schema.GroupVersion]conversion.Convertible) (string, error) { +func marshalForVersion(clusterConfiguration *bootstrapv1.ClusterConfiguration, obj conversion.Hub, version semver.Version, kubeadmObjVersionTypeMap map[schema.GroupVersion]conversion.Convertible) (string, error) { kubeadmAPIGroupVersion, err := KubeVersionToKubeadmAPIGroupVersion(version) if err != nil { return "", err @@ -120,6 +137,12 @@ func marshalForVersion(obj conversion.Hub, version semver.Version, kubeadmObjVer 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) + } + } + codecs, err := getCodecsFor(kubeadmAPIGroupVersion, targetKubeadmObj) if err != nil { return "", err @@ -156,7 +179,7 @@ func toYaml(obj runtime.Object, gv runtime.GroupVersioner, codecs serializer.Cod // NOTE: The yaml could be any of the known formats for the kubeadm ClusterConfiguration type. func UnmarshalClusterConfiguration(yaml string) (*bootstrapv1.ClusterConfiguration, error) { obj := &bootstrapv1.ClusterConfiguration{} - if err := unmarshalFromVersions(yaml, clusterConfigurationVersionTypeMap, obj); err != nil { + if err := unmarshalFromVersions(yaml, clusterConfigurationVersionTypeMap, nil, obj); err != nil { return nil, err } return obj, nil @@ -166,7 +189,7 @@ func UnmarshalClusterConfiguration(yaml string) (*bootstrapv1.ClusterConfigurati // NOTE: The yaml could be any of the known formats for the kubeadm ClusterStatus type. func UnmarshalClusterStatus(yaml string) (*bootstrapv1.ClusterStatus, error) { obj := &bootstrapv1.ClusterStatus{} - if err := unmarshalFromVersions(yaml, clusterStatusVersionTypeMap, obj); err != nil { + if err := unmarshalFromVersions(yaml, clusterStatusVersionTypeMap, nil, obj); err != nil { return nil, err } return obj, nil @@ -174,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) (*bootstrapv1.InitConfiguration, error) { +func UnmarshalInitConfiguration(yaml string, clusterConfiguration *bootstrapv1.ClusterConfiguration) (*bootstrapv1.InitConfiguration, error) { obj := &bootstrapv1.InitConfiguration{} - if err := unmarshalFromVersions(yaml, initConfigurationVersionTypeMap, obj); err != nil { + if err := unmarshalFromVersions(yaml, initConfigurationVersionTypeMap, clusterConfiguration, obj); err != nil { return nil, err } return obj, nil @@ -184,15 +207,15 @@ func UnmarshalInitConfiguration(yaml string) (*bootstrapv1.InitConfiguration, er // 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) (*bootstrapv1.JoinConfiguration, error) { +func UnmarshalJoinConfiguration(yaml string, clusterConfiguration *bootstrapv1.ClusterConfiguration) (*bootstrapv1.JoinConfiguration, error) { obj := &bootstrapv1.JoinConfiguration{} - if err := unmarshalFromVersions(yaml, joinConfigurationVersionTypeMap, obj); err != nil { + if err := unmarshalFromVersions(yaml, joinConfigurationVersionTypeMap, clusterConfiguration, obj); err != nil { return nil, err } return obj, nil } -func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersion]conversion.Convertible, capiObj conversion.Hub) error { +func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersion]conversion.Convertible, clusterConfiguration *bootstrapv1.ClusterConfiguration, capiObj conversion.Hub) error { // For each know kubeadm API version for gv, obj := range kubeadmAPIVersions { // Tries conversion from yaml to the corresponding kubeadmObj @@ -205,6 +228,12 @@ 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 conversion worked, then converts the kubeadmObj (spoke) back to the Cluster API ClusterConfiguration 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 49c9450b953c..92b7fbcdc0e4 100644 --- a/bootstrap/kubeadm/types/utils_test.go +++ b/bootstrap/kubeadm/types/utils_test.go @@ -18,15 +18,18 @@ 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" bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta2" "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta3" + "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta4" ) func TestKubeVersionToKubeadmAPIGroupVersion(t *testing.T) { @@ -102,12 +105,28 @@ func TestKubeVersionToKubeadmAPIGroupVersion(t *testing.T) { want: upstreamv1beta3.GroupVersion, wantErr: false, }, + { + name: "pass with minimum kubernetes version for kubeadm API v1beta4", + args: args{ + version: semver.MustParse("1.31.0"), + }, + want: upstreamv1beta4.GroupVersion, + wantErr: false, + }, + { + name: "pass with kubernetes version for kubeadm API v1beta4", + args: args{ + version: semver.MustParse("1.32.99"), + }, + want: upstreamv1beta4.GroupVersion, + wantErr: false, + }, { name: "pass with future kubernetes version", args: args{ version: semver.MustParse("99.99.99"), }, - want: upstreamv1beta3.GroupVersion, + want: upstreamv1beta4.GroupVersion, wantErr: false, }, } @@ -144,7 +163,7 @@ func TestMarshalClusterConfigurationForVersion(t *testing.T) { version: semver.MustParse("1.15.0"), }, want: "apiServer: {}\n" + - "apiVersion: kubeadm.k8s.io/v1beta2\n" + "" + + "apiVersion: kubeadm.k8s.io/v1beta2\n" + "controllerManager: {}\n" + "dns: {}\n" + "etcd: {}\n" + @@ -160,7 +179,7 @@ func TestMarshalClusterConfigurationForVersion(t *testing.T) { version: semver.MustParse("1.22.0"), }, want: "apiServer: {}\n" + - "apiVersion: kubeadm.k8s.io/v1beta3\n" + "" + + "apiVersion: kubeadm.k8s.io/v1beta3\n" + "controllerManager: {}\n" + "dns: {}\n" + "etcd: {}\n" + @@ -169,6 +188,23 @@ func TestMarshalClusterConfigurationForVersion(t *testing.T) { "scheduler: {}\n", wantErr: false, }, + { + name: "Generates a v1beta4 kubeadm configuration", + args: args{ + capiObj: &bootstrapv1.ClusterConfiguration{}, + version: semver.MustParse("1.31.0"), + }, + want: "apiServer: {}\n" + + "apiVersion: kubeadm.k8s.io/v1beta4\n" + + "controllerManager: {}\n" + + "dns: {}\n" + + "etcd: {}\n" + + "kind: ClusterConfiguration\n" + + "networking: {}\n" + + "proxy: {}\n" + + "scheduler: {}\n", + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -232,9 +268,11 @@ func TestMarshalClusterStatusForVersion(t *testing.T) { } func TestMarshalInitConfigurationForVersion(t *testing.T) { + timeout := metav1.Duration{Duration: 10 * time.Second} type args struct { - capiObj *bootstrapv1.InitConfiguration - version semver.Version + clusterConfiguration *bootstrapv1.ClusterConfiguration + initConfiguration *bootstrapv1.InitConfiguration + version semver.Version } tests := []struct { name string @@ -243,9 +281,9 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { wantErr bool }{ { - name: "Generates a v1beta2 kubeadm configuration", + name: "Generates a v1beta2 kubeadm init configuration", args: args{ - capiObj: &bootstrapv1.InitConfiguration{ + initConfiguration: &bootstrapv1.InitConfiguration{ NodeRegistration: bootstrapv1.NodeRegistrationOptions{ IgnorePreflightErrors: []string{"some-preflight-check"}, }, @@ -261,9 +299,9 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { wantErr: false, }, { - name: "Generates a v1beta3 kubeadm configuration", + name: "Generates a v1beta3 kubeadm init configuration", args: args{ - capiObj: &bootstrapv1.InitConfiguration{ + initConfiguration: &bootstrapv1.InitConfiguration{ NodeRegistration: bootstrapv1.NodeRegistrationOptions{ IgnorePreflightErrors: []string{"some-preflight-check"}, }, @@ -279,12 +317,55 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { " taints: null\n", wantErr: false, }, + { + name: "Generates a v1beta4 kubeadm init configuration", + args: args{ + initConfiguration: &bootstrapv1.InitConfiguration{ + 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"}, + }, + }, + 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" + + "timeouts:\n" + + " controlPlaneComponentHealthCheck: 10s\n", + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := MarshalInitConfigurationForVersion(tt.args.capiObj, tt.args.version) + got, err := MarshalInitConfigurationForVersion(tt.args.clusterConfiguration, tt.args.initConfiguration, tt.args.version) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return @@ -296,9 +377,11 @@ func TestMarshalInitConfigurationForVersion(t *testing.T) { } func TestMarshalJoinConfigurationForVersion(t *testing.T) { + timeout := metav1.Duration{Duration: 10 * time.Second} type args struct { - capiObj *bootstrapv1.JoinConfiguration - version semver.Version + clusterConfiguration *bootstrapv1.ClusterConfiguration + joinConfiguration *bootstrapv1.JoinConfiguration + version semver.Version } tests := []struct { name string @@ -307,9 +390,9 @@ func TestMarshalJoinConfigurationForVersion(t *testing.T) { wantErr bool }{ { - name: "Generates a v1beta2 kubeadm configuration", + name: "Generates a v1beta2 kubeadm join configuration", args: args{ - capiObj: &bootstrapv1.JoinConfiguration{ + joinConfiguration: &bootstrapv1.JoinConfiguration{ NodeRegistration: bootstrapv1.NodeRegistrationOptions{ IgnorePreflightErrors: []string{"some-preflight-check"}, }, @@ -325,9 +408,9 @@ func TestMarshalJoinConfigurationForVersion(t *testing.T) { wantErr: false, }, { - name: "Generates a v1beta3 kubeadm configuration", + name: "Generates a v1beta3 kubeadm join configuration", args: args{ - capiObj: &bootstrapv1.JoinConfiguration{ + joinConfiguration: &bootstrapv1.JoinConfiguration{ NodeRegistration: bootstrapv1.NodeRegistrationOptions{ IgnorePreflightErrors: []string{"some-preflight-check"}, }, @@ -343,12 +426,55 @@ func TestMarshalJoinConfigurationForVersion(t *testing.T) { " taints: null\n", wantErr: false, }, + { + name: "Generates a v1beta4 kubeadm join configuration", + args: args{ + joinConfiguration: &bootstrapv1.JoinConfiguration{ + 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"}, + }, + }, + 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" + + "timeouts:\n" + + " controlPlaneComponentHealthCheck: 10s\n", + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := MarshalJoinConfigurationForVersion(tt.args.capiObj, tt.args.version) + got, err := MarshalJoinConfigurationForVersion(tt.args.clusterConfiguration, tt.args.joinConfiguration, tt.args.version) if tt.wantErr { g.Expect(err).To(HaveOccurred()) return @@ -399,6 +525,22 @@ func TestUnmarshalClusterConfiguration(t *testing.T) { want: &bootstrapv1.ClusterConfiguration{}, wantErr: false, }, + { + name: "Parses a v1beta4 kubeadm configuration", + args: args{ + yaml: "apiServer: {}\n" + + "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + + "controllerManager: {}\n" + + "dns: {}\n" + + "etcd: {}\n" + + "kind: ClusterConfiguration\n" + + "networking: {}\n" + + "proxy: {}\n" + + "scheduler: {}\n", + }, + want: &bootstrapv1.ClusterConfiguration{}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -444,6 +586,15 @@ func TestUnmarshalClusterStatus(t *testing.T) { }, wantErr: true, }, + { + name: "Fails parsing a v1beta4 kubeadm configuration", + args: args{ + yaml: "apiEndpoints: null\n" + + "apiVersion: kubeadm.k8s.io/v1beta4\n" + "" + + "kind: ClusterStatus\n", + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -461,14 +612,16 @@ 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 - wantErr bool + name string + args args + want *bootstrapv1.InitConfiguration + wantClusterConfiguration *bootstrapv1.ClusterConfiguration + wantErr bool }{ { name: "Parses a v1beta2 kubeadm configuration", @@ -478,8 +631,9 @@ func TestUnmarshalInitConfiguration(t *testing.T) { "localAPIEndpoint: {}\n" + "nodeRegistration: {}\n", }, - want: &bootstrapv1.InitConfiguration{}, - wantErr: false, + want: &bootstrapv1.InitConfiguration{}, + wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, + wantErr: false, }, { name: "Parses a v1beta3 kubeadm configuration", @@ -489,7 +643,36 @@ func TestUnmarshalInitConfiguration(t *testing.T) { "localAPIEndpoint: {}\n" + "nodeRegistration: {}\n", }, - want: &bootstrapv1.InitConfiguration{}, + want: &bootstrapv1.InitConfiguration{}, + wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, + 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", + }, + want: &bootstrapv1.InitConfiguration{}, + wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{ + APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, + }, wantErr: false, }, } @@ -497,26 +680,30 @@ func TestUnmarshalInitConfiguration(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := UnmarshalInitConfiguration(tt.args.yaml) + gotClusterConfiguration := &bootstrapv1.ClusterConfiguration{} + got, err := UnmarshalInitConfiguration(tt.args.yaml, gotClusterConfiguration) 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 - wantErr bool + name string + args args + want *bootstrapv1.JoinConfiguration + wantClusterConfiguration *bootstrapv1.ClusterConfiguration + wantErr bool }{ { name: "Parses a v1beta2 kubeadm configuration", @@ -526,8 +713,9 @@ func TestUnmarshalJoinConfiguration(t *testing.T) { "discovery: {}\n" + "kind: JoinConfiguration\n", }, - want: &bootstrapv1.JoinConfiguration{}, - wantErr: false, + want: &bootstrapv1.JoinConfiguration{}, + wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, + wantErr: false, }, { name: "Parses a v1beta3 kubeadm configuration", @@ -537,7 +725,36 @@ func TestUnmarshalJoinConfiguration(t *testing.T) { "discovery: {}\n" + "kind: JoinConfiguration\n", }, - want: &bootstrapv1.JoinConfiguration{}, + want: &bootstrapv1.JoinConfiguration{}, + wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{}, + 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", + }, + want: &bootstrapv1.JoinConfiguration{}, + wantClusterConfiguration: &bootstrapv1.ClusterConfiguration{ + APIServer: bootstrapv1.APIServer{TimeoutForControlPlane: &timeout}, + }, wantErr: false, }, } @@ -545,13 +762,15 @@ func TestUnmarshalJoinConfiguration(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - got, err := UnmarshalJoinConfiguration(tt.args.yaml) + gotClusterConfiguration := &bootstrapv1.ClusterConfiguration{} + got, err := UnmarshalJoinConfiguration(tt.args.yaml, gotClusterConfiguration) 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/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go b/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go index aa47d0db2432..ffd7895240e8 100644 --- a/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go +++ b/test/infrastructure/docker/internal/provisioning/cloudinit/writefiles.go @@ -79,20 +79,21 @@ func (a *writeFilesAction) Unmarshal(userData []byte, kindMapping kind.Mapping) } for i, f := range a.Files { if f.Path == kubeadmInitPath { - // NOTE: in case of join the kubeadmConfigFile contains both the ClusterConfiguration and the InitConfiguration + // NOTE: in case of init the kubeadmConfigFile contains both the ClusterConfiguration and the InitConfiguration contentSplit := strings.Split(f.Content, "---\n") if len(contentSplit) != 3 { return errors.Errorf("invalid kubeadm config file, unable to parse it") } - initConfiguration, err := kubeadmtypes.UnmarshalInitConfiguration(contentSplit[2]) + clusterConfiguration := &bootstrapv1.ClusterConfiguration{} + initConfiguration, err := kubeadmtypes.UnmarshalInitConfiguration(contentSplit[2], clusterConfiguration) if err != nil { return errors.Wrapf(err, "failed to parse init configuration") } fixNodeRegistration(&initConfiguration.NodeRegistration, kindMapping) - contentSplit[2], err = kubeadmtypes.MarshalInitConfigurationForVersion(initConfiguration, kindMapping.KubernetesVersion) + contentSplit[2], err = kubeadmtypes.MarshalInitConfigurationForVersion(clusterConfiguration, initConfiguration, kindMapping.KubernetesVersion) if err != nil { return errors.Wrapf(err, "failed to marshal init configuration") } @@ -100,14 +101,15 @@ 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 - joinConfiguration, err := kubeadmtypes.UnmarshalJoinConfiguration(f.Content) + clusterConfiguration := &bootstrapv1.ClusterConfiguration{} + joinConfiguration, err := kubeadmtypes.UnmarshalJoinConfiguration(f.Content, clusterConfiguration) if err != nil { return errors.Wrapf(err, "failed to parse join configuration") } fixNodeRegistration(&joinConfiguration.NodeRegistration, kindMapping) - a.Files[i].Content, err = kubeadmtypes.MarshalJoinConfigurationForVersion(joinConfiguration, kindMapping.KubernetesVersion) + a.Files[i].Content, err = kubeadmtypes.MarshalJoinConfigurationForVersion(clusterConfiguration, joinConfiguration, kindMapping.KubernetesVersion) if err != nil { return errors.Wrapf(err, "failed to marshal join configuration") }