Skip to content

⚠️ Align KubeadmConfig to kubeadm v1beta4 types #12282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 172 additions & 4 deletions api/bootstrap/kubeadm/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,162 @@ limitations under the License.
package v1beta1

import (
"reflect"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apimachineryconversion "k8s.io/apimachinery/pkg/conversion"
"sigs.k8s.io/controller-runtime/pkg/conversion"

bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2"
clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1"
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
)

func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error {
dst := dstRaw.(*bootstrapv1.KubeadmConfig)
if err := Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(src, dst, nil); err != nil {
return err
}

return Convert_v1beta1_KubeadmConfig_To_v1beta2_KubeadmConfig(src, dst, nil)
// Manually restore data.
restored := &bootstrapv1.KubeadmConfig{}
ok, err := utilconversion.UnmarshalData(src, restored)
if err != nil {
return err
}
if ok {
RestoreKubeadmConfigSpec(&restored.Spec, &dst.Spec)
}
// Override restored data with timeouts values already existing in v1beta1 but in other structs.
src.Spec.ConvertTo(&dst.Spec)
return nil
}

func RestoreKubeadmConfigSpec(restored *bootstrapv1.KubeadmConfigSpec, dst *bootstrapv1.KubeadmConfigSpec) {
// Restore fields added in v1beta2
if restored.InitConfiguration != nil && restored.InitConfiguration.Timeouts != nil {
if dst.InitConfiguration == nil {
dst.InitConfiguration = &bootstrapv1.InitConfiguration{}
}
dst.InitConfiguration.Timeouts = restored.InitConfiguration.Timeouts
}
if restored.JoinConfiguration != nil && restored.JoinConfiguration.Timeouts != nil {
if dst.JoinConfiguration == nil {
dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
}
dst.JoinConfiguration.Timeouts = restored.JoinConfiguration.Timeouts
}
}

func (src *KubeadmConfigSpec) ConvertTo(dst *bootstrapv1.KubeadmConfigSpec) {
// Override with timeouts values already existing in v1beta1.
var initControlPlaneComponentHealthCheckSeconds *int32
if src.ClusterConfiguration != nil && src.ClusterConfiguration.APIServer.TimeoutForControlPlane != nil {
if dst.InitConfiguration == nil {
dst.InitConfiguration = &bootstrapv1.InitConfiguration{}
}
if dst.InitConfiguration.Timeouts == nil {
dst.InitConfiguration.Timeouts = &bootstrapv1.Timeouts{}
}
dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = bootstrapv1.ConvertToSeconds(src.ClusterConfiguration.APIServer.TimeoutForControlPlane)
initControlPlaneComponentHealthCheckSeconds = dst.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds
}
if (src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil) || initControlPlaneComponentHealthCheckSeconds != nil {
if dst.JoinConfiguration == nil {
dst.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
}
if dst.JoinConfiguration.Timeouts == nil {
dst.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{}
}
dst.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds
if src.JoinConfiguration != nil && src.JoinConfiguration.Discovery.Timeout != nil {
dst.JoinConfiguration.Timeouts.TLSBootstrapSeconds = bootstrapv1.ConvertToSeconds(src.JoinConfiguration.Discovery.Timeout)
}
}

if reflect.DeepEqual(dst.ClusterConfiguration, &bootstrapv1.ClusterConfiguration{}) {
dst.ClusterConfiguration = nil
}
}

func (dst *KubeadmConfig) ConvertFrom(srcRaw conversion.Hub) error {
src := srcRaw.(*bootstrapv1.KubeadmConfig)
if err := Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(src, dst, nil); err != nil {
return err
}

// Convert timeouts moved from one struct to another.
dst.Spec.ConvertFrom(&src.Spec)

return Convert_v1beta2_KubeadmConfig_To_v1beta1_KubeadmConfig(src, dst, nil)
// Preserve Hub data on down-conversion except for metadata.
return utilconversion.MarshalData(src, dst)
}

func (dst *KubeadmConfigSpec) ConvertFrom(src *bootstrapv1.KubeadmConfigSpec) {
// Convert timeouts moved from one struct to another.
if src.InitConfiguration != nil && src.InitConfiguration.Timeouts != nil && src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds != nil {
if dst.ClusterConfiguration == nil {
dst.ClusterConfiguration = &ClusterConfiguration{}
}
dst.ClusterConfiguration.APIServer.TimeoutForControlPlane = bootstrapv1.ConvertFromSeconds(src.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds)
}
if reflect.DeepEqual(dst.InitConfiguration, &InitConfiguration{}) {
dst.InitConfiguration = nil
}
if src.JoinConfiguration != nil && src.JoinConfiguration.Timeouts != nil && src.JoinConfiguration.Timeouts.TLSBootstrapSeconds != nil {
if dst.JoinConfiguration == nil {
dst.JoinConfiguration = &JoinConfiguration{}
}
dst.JoinConfiguration.Discovery.Timeout = bootstrapv1.ConvertFromSeconds(src.JoinConfiguration.Timeouts.TLSBootstrapSeconds)
}
if reflect.DeepEqual(dst.JoinConfiguration, &JoinConfiguration{}) {
dst.JoinConfiguration = nil
}
}

func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst := dstRaw.(*bootstrapv1.KubeadmConfigTemplate)
if err := Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(src, dst, nil); err != nil {
return err
}

// Manually restore data.
restored := &bootstrapv1.KubeadmConfigTemplate{}
ok, err := utilconversion.UnmarshalData(src, restored)
if err != nil {
return err
}
if ok {
RestoreKubeadmConfigSpec(&restored.Spec.Template.Spec, &dst.Spec.Template.Spec)
}

return Convert_v1beta1_KubeadmConfigTemplate_To_v1beta2_KubeadmConfigTemplate(src, dst, nil)
// Override restored data with timeouts values already existing in v1beta1 but in other structs.
src.Spec.Template.Spec.ConvertTo(&dst.Spec.Template.Spec)
return nil
}

func (dst *KubeadmConfigTemplate) ConvertFrom(srcRaw conversion.Hub) error {
src := srcRaw.(*bootstrapv1.KubeadmConfigTemplate)
if err := Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(src, dst, nil); err != nil {
return err
}

// Convert timeouts moved from one struct to another.
dst.Spec.Template.Spec.ConvertFrom(&src.Spec.Template.Spec)

// Preserve Hub data on down-conversion except for metadata.
return utilconversion.MarshalData(src, dst)
}

func Convert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in *bootstrapv1.InitConfiguration, out *InitConfiguration, s apimachineryconversion.Scope) error {
// Timeouts requires conversion at an upper level
return autoConvert_v1beta2_InitConfiguration_To_v1beta1_InitConfiguration(in, out, s)
}

return Convert_v1beta2_KubeadmConfigTemplate_To_v1beta1_KubeadmConfigTemplate(src, dst, nil)
func Convert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in *bootstrapv1.JoinConfiguration, out *JoinConfiguration, s apimachineryconversion.Scope) error {
// Timeouts requires conversion at an upper level
return autoConvert_v1beta2_JoinConfiguration_To_v1beta1_JoinConfiguration(in, out, s)
}

func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, out *KubeadmConfigStatus, s apimachineryconversion.Scope) error {
Expand Down Expand Up @@ -82,11 +207,54 @@ func Convert_v1beta2_KubeadmConfigStatus_To_v1beta1_KubeadmConfigStatus(in *boot
return nil
}

func Convert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in *bootstrapv1.ControlPlaneComponent, out *ControlPlaneComponent, s apimachineryconversion.Scope) error {
// Following fields require a custom conversions.
out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs)
return autoConvert_v1beta2_ControlPlaneComponent_To_v1beta1_ControlPlaneComponent(in, out, s)
}

func Convert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in *bootstrapv1.LocalEtcd, out *LocalEtcd, s apimachineryconversion.Scope) error {
// Following fields require a custom conversions.
out.ExtraArgs = bootstrapv1.ConvertFromArgs(in.ExtraArgs)
return autoConvert_v1beta2_LocalEtcd_To_v1beta1_LocalEtcd(in, out, s)
}

func Convert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *bootstrapv1.NodeRegistrationOptions, out *NodeRegistrationOptions, s apimachineryconversion.Scope) error {
// Following fields require a custom conversions.
out.KubeletExtraArgs = bootstrapv1.ConvertFromArgs(in.KubeletExtraArgs)
return autoConvert_v1beta2_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s)
}

func Convert_v1beta1_APIServer_To_v1beta2_APIServer(in *APIServer, out *bootstrapv1.APIServer, s apimachineryconversion.Scope) error {
// TimeoutForControlPlane has been removed in v1beta2
return autoConvert_v1beta1_APIServer_To_v1beta2_APIServer(in, out, s)
}

func Convert_v1beta1_Discovery_To_v1beta2_Discovery(in *Discovery, out *bootstrapv1.Discovery, s apimachineryconversion.Scope) error {
// Timeout has been removed in v1beta2
return autoConvert_v1beta1_Discovery_To_v1beta2_Discovery(in, out, s)
}

func Convert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in *KubeadmConfigSpec, out *bootstrapv1.KubeadmConfigSpec, s apimachineryconversion.Scope) error {
// NOTE: v1beta2 KubeadmConfigSpec does not have UseExperimentalRetryJoin anymore, so it's fine to just lose this field.
return autoConvert_v1beta1_KubeadmConfigSpec_To_v1beta2_KubeadmConfigSpec(in, out, s)
}

func Convert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in *ControlPlaneComponent, out *bootstrapv1.ControlPlaneComponent, s apimachineryconversion.Scope) error {
out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs)
return autoConvert_v1beta1_ControlPlaneComponent_To_v1beta2_ControlPlaneComponent(in, out, s)
}

func Convert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in *LocalEtcd, out *bootstrapv1.LocalEtcd, s apimachineryconversion.Scope) error {
out.ExtraArgs = bootstrapv1.ConvertToArgs(in.ExtraArgs)
return autoConvert_v1beta1_LocalEtcd_To_v1beta2_LocalEtcd(in, out, s)
}

func Convert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in *NodeRegistrationOptions, out *bootstrapv1.NodeRegistrationOptions, s apimachineryconversion.Scope) error {
out.KubeletExtraArgs = bootstrapv1.ConvertToArgs(in.KubeletExtraArgs)
return autoConvert_v1beta1_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOptions(in, out, s)
}

func Convert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in *KubeadmConfigStatus, out *bootstrapv1.KubeadmConfigStatus, s apimachineryconversion.Scope) error {
if err := autoConvert_v1beta1_KubeadmConfigStatus_To_v1beta2_KubeadmConfigStatus(in, out, s); err != nil {
return err
Expand Down
75 changes: 69 additions & 6 deletions api/bootstrap/kubeadm/v1beta1/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@ package v1beta1
import (
"reflect"
"testing"
"time"

"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/utils/ptr"
"sigs.k8s.io/randfill"

bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
)

const (
fakeID = "abcdef"
fakeSecret = "abcdef0123456789"
)

// Test is disabled when the race detector is enabled (via "//go:build !race" above) because otherwise the fuzz tests would just time out.

func TestFuzzyConversion(t *testing.T) {
Expand All @@ -48,11 +56,37 @@ func TestFuzzyConversion(t *testing.T) {
func KubeadmConfigFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
hubKubeadmConfigStatus,
spokeAPIServer,
spokeDiscovery,
spokeKubeadmConfigSpec,
spokeKubeadmConfigStatus,
hubBootstrapTokenString,
spokeBootstrapTokenString,
hubKubeadmConfigSpec,
}
}

func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
spokeAPIServer,
spokeDiscovery,
spokeKubeadmConfigSpec,
spokeBootstrapTokenString,
hubBootstrapTokenString,
hubKubeadmConfigSpec,
}
}

func hubBootstrapTokenString(in *bootstrapv1.BootstrapTokenString, _ randfill.Continue) {
in.ID = fakeID
in.Secret = fakeSecret
}

func spokeBootstrapTokenString(in *BootstrapTokenString, _ randfill.Continue) {
in.ID = fakeID
in.Secret = fakeSecret
}

func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c randfill.Continue) {
c.FillNoCustom(in)
// Always create struct with at least one mandatory fields.
Expand All @@ -71,13 +105,48 @@ func hubKubeadmConfigStatus(in *bootstrapv1.KubeadmConfigStatus, c randfill.Cont
}
}

func hubKubeadmConfigSpec(in *bootstrapv1.KubeadmConfigSpec, c randfill.Continue) {
c.FillNoCustom(in)

// enforce ControlPlaneComponentHealthCheckSeconds to be equal on init and join configuration
var initControlPlaneComponentHealthCheckSeconds *int32
if in.InitConfiguration != nil && in.InitConfiguration.Timeouts != nil {
initControlPlaneComponentHealthCheckSeconds = in.InitConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds
}
if (in.JoinConfiguration != nil && in.JoinConfiguration.Timeouts != nil) || initControlPlaneComponentHealthCheckSeconds != nil {
if in.JoinConfiguration == nil {
in.JoinConfiguration = &bootstrapv1.JoinConfiguration{}
}
if in.JoinConfiguration.Timeouts == nil {
in.JoinConfiguration.Timeouts = &bootstrapv1.Timeouts{}
}
in.JoinConfiguration.Timeouts.ControlPlaneComponentHealthCheckSeconds = initControlPlaneComponentHealthCheckSeconds
}
}

func spokeKubeadmConfigSpec(in *KubeadmConfigSpec, c randfill.Continue) {
c.FillNoCustom(in)

// Drop UseExperimentalRetryJoin as we intentionally don't preserve it.
in.UseExperimentalRetryJoin = false
}

func spokeAPIServer(in *APIServer, c randfill.Continue) {
c.FillNoCustom(in)

if in.TimeoutForControlPlane != nil {
in.TimeoutForControlPlane = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second})
}
}

func spokeDiscovery(in *Discovery, c randfill.Continue) {
c.FillNoCustom(in)

if in.Timeout != nil {
in.Timeout = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31()) * time.Second})
}
}

func spokeKubeadmConfigStatus(in *KubeadmConfigStatus, c randfill.Continue) {
c.FillNoCustom(in)
// Drop empty structs with only omit empty fields.
Expand All @@ -87,9 +156,3 @@ func spokeKubeadmConfigStatus(in *KubeadmConfigStatus, c randfill.Continue) {
}
}
}

func KubeadmConfigTemplateFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
spokeKubeadmConfigSpec,
}
}
Loading
Loading