Skip to content

⚠️ Implement v1beta2 contract in CAPD #12409

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
merged 12 commits into from
Jun 30, 2025
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,11 @@ test-cover: ## Run unit and integration tests and generate a coverage report
.PHONY: test-docker-infrastructure
test-docker-infrastructure: $(SETUP_ENVTEST) ## Run unit and integration tests for docker infrastructure provider
cd $(CAPD_DIR); KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -race ./... $(TEST_ARGS)
$(MAKE) test-docker-infrastructure-conversions TEST_ARGS="$(TEST_ARGS)"

.PHONY: test-docker-infrastructure-conversions
test-docker-infrastructure-conversions: $(SETUP_ENVTEST) ## Run conversions test for docker infrastructure provider
cd $(CAPD_DIR); KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test -run "^TestFuzzyConversion$$" ./... $(TEST_ARGS)

.PHONY: test-docker-infrastructure-verbose
test-docker-infrastructure-verbose: ## Run unit and integration tests for docker infrastructure provider with verbose flag
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/config/docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ providers:
- sourcePath: "../data/infrastructure-docker/main/clusterclass-quick-start-kcp-only.yaml"
- sourcePath: "../data/infrastructure-docker/main/clusterclass-quick-start-runtimesdk.yaml"
- sourcePath: "../data/infrastructure-docker/main/clusterclass-in-memory.yaml"
- sourcePath: "../data/shared/main/docker/metadata.yaml"
- sourcePath: "../data/shared/main/metadata.yaml"

- name: test-extension
type: RuntimeExtensionProvider
Expand Down
45 changes: 0 additions & 45 deletions test/e2e/data/shared/main/docker/metadata.yaml

This file was deleted.

193 changes: 157 additions & 36 deletions test/infrastructure/docker/api/v1alpha3/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ limitations under the License.
package v1alpha3

import (
"maps"
"slices"
"sort"

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

clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
clusterv1alpha3 "sigs.k8s.io/cluster-api/internal/api/core/v1alpha3"
infrav1 "sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta2"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
Expand All @@ -33,15 +39,6 @@ func (src *DockerCluster) ConvertTo(dstRaw conversion.Hub) error {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
dst.Status.Conditions = nil
if src.Status.Conditions != nil {
dst.Status.Deprecated = &infrav1.DockerClusterDeprecatedStatus{}
dst.Status.Deprecated.V1Beta1 = &infrav1.DockerClusterV1Beta1DeprecatedStatus{}
clusterv1alpha3.Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&src.Status.Conditions, &dst.Status.Deprecated.V1Beta1.Conditions)
}

// Manually restore data.
restored := &infrav1.DockerCluster{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
Expand All @@ -61,6 +58,7 @@ func (src *DockerCluster) ConvertTo(dstRaw conversion.Hub) error {
}

dst.Status.Conditions = restored.Status.Conditions
dst.Status.Initialization = restored.Status.Initialization

return nil
}
Expand All @@ -72,13 +70,6 @@ func (dst *DockerCluster) ConvertFrom(srcRaw conversion.Hub) error {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1beta2 conditions should not be automatically be converted into v1alpha3 conditions.
dst.Status.Conditions = nil
if src.Status.Deprecated != nil && src.Status.Deprecated.V1Beta1 != nil && src.Status.Deprecated.V1Beta1.Conditions != nil {
clusterv1alpha3.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&src.Status.Deprecated.V1Beta1.Conditions, &dst.Status.Conditions)
}

// Preserve Hub data on down-conversion except for metadata
if err := utilconversion.MarshalData(src, dst); err != nil {
return err
Expand All @@ -94,16 +85,6 @@ func (src *DockerMachine) ConvertTo(dstRaw conversion.Hub) error {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
dst.Status.Conditions = nil

if src.Status.Conditions != nil {
dst.Status.Deprecated = &infrav1.DockerMachineDeprecatedStatus{}
dst.Status.Deprecated.V1Beta1 = &infrav1.DockerMachineV1Beta1DeprecatedStatus{}
clusterv1alpha3.Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&src.Status.Conditions, &dst.Status.Deprecated.V1Beta1.Conditions)
}

// Manually restore data.
restored := &infrav1.DockerMachine{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
Expand All @@ -115,6 +96,7 @@ func (src *DockerMachine) ConvertTo(dstRaw conversion.Hub) error {
}

dst.Status.Conditions = restored.Status.Conditions
dst.Status.Initialization = restored.Status.Initialization

return nil
}
Expand All @@ -126,13 +108,6 @@ func (dst *DockerMachine) ConvertFrom(srcRaw conversion.Hub) error {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1beta2 conditions should not be automatically be converted into v1alpha3 conditions.
dst.Status.Conditions = nil
if src.Status.Deprecated != nil && src.Status.Deprecated.V1Beta1 != nil && src.Status.Deprecated.V1Beta1.Conditions != nil {
clusterv1alpha3.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&src.Status.Deprecated.V1Beta1.Conditions, &dst.Status.Conditions)
}

if err := utilconversion.MarshalData(src, dst); err != nil {
return err
}
Expand Down Expand Up @@ -177,7 +152,22 @@ func (dst *DockerMachineTemplate) ConvertFrom(srcRaw conversion.Hub) error {
// Convert_v1beta2_DockerClusterSpec_To_v1alpha3_DockerClusterSpec is an autogenerated conversion function.
func Convert_v1beta2_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in *infrav1.DockerClusterSpec, out *DockerClusterSpec, s apiconversion.Scope) error {
// DockerClusterSpec.LoadBalancer was added in v1alpha4, so automatic conversion is not possible
return autoConvert_v1beta2_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in, out, s)
if err := autoConvert_v1beta2_DockerClusterSpec_To_v1alpha3_DockerClusterSpec(in, out, s); err != nil {
return err
}

// Move FailureDomains
if in.FailureDomains != nil {
out.FailureDomains = clusterv1alpha3.FailureDomains{}
for _, fd := range in.FailureDomains {
out.FailureDomains[fd.Name] = clusterv1alpha3.FailureDomainSpec{
ControlPlane: fd.ControlPlane,
Attributes: fd.Attributes,
}
}
}

return nil
}

func Convert_v1beta2_DockerMachineTemplateResource_To_v1alpha3_DockerMachineTemplateResource(in *infrav1.DockerMachineTemplateResource, out *DockerMachineTemplateResource, s apiconversion.Scope) error {
Expand All @@ -192,11 +182,52 @@ func Convert_v1beta2_DockerMachineSpec_To_v1alpha3_DockerMachineSpec(in *infrav1
}

func Convert_v1beta2_DockerClusterStatus_To_v1alpha3_DockerClusterStatus(in *infrav1.DockerClusterStatus, out *DockerClusterStatus, s apiconversion.Scope) error {
return autoConvert_v1beta2_DockerClusterStatus_To_v1alpha3_DockerClusterStatus(in, out, s)
if err := autoConvert_v1beta2_DockerClusterStatus_To_v1alpha3_DockerClusterStatus(in, out, s); err != nil {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1beta2 conditions should not be automatically be converted into v1alpha3 conditions.
out.Conditions = nil
if in.Deprecated != nil && in.Deprecated.V1Beta1 != nil && in.Deprecated.V1Beta1.Conditions != nil {
clusterv1alpha3.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&in.Deprecated.V1Beta1.Conditions, &out.Conditions)
}

if in.Initialization != nil && in.Initialization.Provisioned != nil {
out.Ready = *in.Initialization.Provisioned
}

// Move FailureDomains
if in.FailureDomains != nil {
out.FailureDomains = clusterv1alpha3.FailureDomains{}
for _, fd := range in.FailureDomains {
out.FailureDomains[fd.Name] = clusterv1alpha3.FailureDomainSpec{
ControlPlane: fd.ControlPlane,
Attributes: fd.Attributes,
}
}
}

return nil
}

func Convert_v1beta2_DockerMachineStatus_To_v1alpha3_DockerMachineStatus(in *infrav1.DockerMachineStatus, out *DockerMachineStatus, s apiconversion.Scope) error {
return autoConvert_v1beta2_DockerMachineStatus_To_v1alpha3_DockerMachineStatus(in, out, s)
if err := autoConvert_v1beta2_DockerMachineStatus_To_v1alpha3_DockerMachineStatus(in, out, s); err != nil {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1beta2 conditions should not be automatically be converted into v1alpha3 conditions.
out.Conditions = nil
if in.Deprecated != nil && in.Deprecated.V1Beta1 != nil && in.Deprecated.V1Beta1.Conditions != nil {
clusterv1alpha3.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1alpha3_Conditions(&in.Deprecated.V1Beta1.Conditions, &out.Conditions)
}

if in.Initialization != nil && in.Initialization.Provisioned != nil {
out.Ready = *in.Initialization.Provisioned
}

return nil
}

// Implement local conversion func because conversion-gen is not aware of conversion func in other packages (see https://github.com/kubernetes/code-generator/issues/94)
Expand All @@ -208,3 +239,93 @@ func Convert_v1alpha3_Condition_To_v1_Condition(in *clusterv1alpha3.Condition, o
func Convert_v1_Condition_To_v1alpha3_Condition(in *metav1.Condition, out *clusterv1alpha3.Condition, s apiconversion.Scope) error {
return clusterv1alpha3.Convert_v1_Condition_To_v1alpha3_Condition(in, out, s)
}

func Convert_v1alpha3_DockerMachineStatus_To_v1beta2_DockerMachineStatus(in *DockerMachineStatus, out *infrav1.DockerMachineStatus, s apiconversion.Scope) error {
if err := autoConvert_v1alpha3_DockerMachineStatus_To_v1beta2_DockerMachineStatus(in, out, s); err != nil {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
out.Conditions = nil

if in.Conditions != nil {
out.Deprecated = &infrav1.DockerMachineDeprecatedStatus{}
out.Deprecated.V1Beta1 = &infrav1.DockerMachineV1Beta1DeprecatedStatus{}
clusterv1alpha3.Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&in.Conditions, &out.Deprecated.V1Beta1.Conditions)
}

if out.Initialization == nil {
out.Initialization = &infrav1.DockerMachineInitializationStatus{}
}

if in.Ready {
out.Initialization.Provisioned = ptr.To(in.Ready)
}

return nil
}

func Convert_v1alpha3_DockerClusterStatus_To_v1beta2_DockerClusterStatus(in *DockerClusterStatus, out *infrav1.DockerClusterStatus, s apiconversion.Scope) error {
// NOTE: custom conversion func is required because status.conditions has been added in v1beta2.
if err := autoConvert_v1alpha3_DockerClusterStatus_To_v1beta2_DockerClusterStatus(in, out, s); err != nil {
return err
}

// Reset conditions from autogenerated conversions
// NOTE: v1alpha3 conditions should not be automatically be converted into v1beta2 conditions.
out.Conditions = nil
if in.Conditions != nil {
out.Deprecated = &infrav1.DockerClusterDeprecatedStatus{}
out.Deprecated.V1Beta1 = &infrav1.DockerClusterV1Beta1DeprecatedStatus{}
clusterv1alpha3.Convert_v1alpha3_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&in.Conditions, &out.Deprecated.V1Beta1.Conditions)
}

if out.Initialization == nil {
out.Initialization = &infrav1.DockerClusterInitializationStatus{}
}

if in.Ready {
out.Initialization.Provisioned = ptr.To(in.Ready)
}

// Move FailureDomains
if in.FailureDomains != nil {
out.FailureDomains = []clusterv1.FailureDomain{}
domainNames := slices.Collect(maps.Keys(in.FailureDomains))
sort.Strings(domainNames)
for _, name := range domainNames {
domain := in.FailureDomains[name]
out.FailureDomains = append(out.FailureDomains, clusterv1.FailureDomain{
Name: name,
ControlPlane: domain.ControlPlane,
Attributes: domain.Attributes,
})
}
}

return nil
}

func Convert_v1alpha3_DockerClusterSpec_To_v1beta2_DockerClusterSpec(in *DockerClusterSpec, out *infrav1.DockerClusterSpec, s apiconversion.Scope) error {
if err := autoConvert_v1alpha3_DockerClusterSpec_To_v1beta2_DockerClusterSpec(in, out, s); err != nil {
return err
}

// Move FailureDomains
if in.FailureDomains != nil {
out.FailureDomains = []clusterv1.FailureDomain{}
domainNames := slices.Collect(maps.Keys(in.FailureDomains))
sort.Strings(domainNames)
for _, name := range domainNames {
domain := in.FailureDomains[name]
out.FailureDomains = append(out.FailureDomains, clusterv1.FailureDomain{
Name: name,
ControlPlane: domain.ControlPlane,
Attributes: domain.Attributes,
})
}
}

return nil
}
12 changes: 12 additions & 0 deletions test/infrastructure/docker/api/v1alpha3/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ func hubDockerClusterStatus(in *infrav1.DockerClusterStatus, c randfill.Continue
in.Deprecated = nil
}
}

if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &infrav1.DockerClusterInitializationStatus{}) {
in.Initialization = nil
}
}
}

func DockerMachineFuzzFunc(_ runtimeserializer.CodecFactory) []any {
Expand All @@ -81,4 +87,10 @@ func hubDockerMachineStatus(in *infrav1.DockerMachineStatus, c randfill.Continue
in.Deprecated = nil
}
}

if in.Initialization != nil {
if reflect.DeepEqual(in.Initialization, &infrav1.DockerMachineInitializationStatus{}) {
in.Initialization = nil
}
}
}
Loading