Skip to content

Allow clusterctl move to work with cross-version upgrade #13415

@LuckyChen666

Description

@LuckyChen666

What would you like to be added (User Story)?

As a Cluster API operator, I would like to use a newer clusterctl binary to perform a cross-version management cluster upgrade from CAPI v1.10 (contract v1beta1) to v1.11 (contract v1beta2), so that upgrade workflows are smoother and less brittle across contract boundaries.

Detailed Description

I am trying to upgrade a management cluster from CAPI v1.10 to v1.11.

During this process, clusterctl rejects the source management cluster because it enforces only v1beta2 contracts:

Error: this version of clusterctl could be used only with "v1beta2" management clusters, "v1beta1" detected

Concrete example from my local experiment (explicitly allowing only v1beta2 during contract validation):

diff --git a/cmd/clusterctl/client/move.go b/cmd/clusterctl/client/move.go
index 6d5299cc9..bebadea6e 100644
--- a/cmd/clusterctl/client/move.go
+++ b/cmd/clusterctl/client/move.go
@@ -138,19 +138,23 @@ func (c *clusterctlClient) toDirectory(ctx context.Context, options MoveOptions)
 }

 func (c *clusterctlClient) getClusterClient(ctx context.Context, kubeconfig Kubeconfig) (cluster.Client, error) {
-       cluster, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: kubeconfig})
+       clusterClient, err := c.clusterClientFactory(ClusterClientFactoryInput{Kubeconfig: kubeconfig})
	if err != nil {
		return nil, err
	}

+       contractOptions := []cluster.CheckCAPIContractOption{
+               cluster.AllowCAPIContract{Contract: "v1beta2"},
+       }
+
	// Ensure this command only runs against management clusters with the current Cluster API contract.
-       if err := cluster.ProviderInventory().CheckCAPIContract(ctx); err != nil {
+       if err := clusterClient.ProviderInventory().CheckCAPIContract(ctx, contractOptions...); err != nil {
		return nil, err
	}

	// Ensures the custom resource definitions required by clusterctl are in place.
-       if err := cluster.ProviderInventory().EnsureCustomResourceDefinitions(ctx); err != nil {
+       if err := clusterClient.ProviderInventory().EnsureCustomResourceDefinitions(ctx); err != nil {
		return nil, err
	}
-       return cluster, nil
+       return clusterClient, nil
 }

From code inspection, this appears to come from strict contract checks in cmd/clusterctl/client/move.go where CheckCAPIContract(ctx) is called without options, allowing only the current contract version (v1beta1).

My local experiment shows that adding support for multiple contracts via CheckCAPIContractOption (e.g., AllowCAPIContract{Contract: "v1beta2"}) can allow the tool to work with previous contract versions.

Note: the diff above is only to illustrate the workaround I observed; it is not intended as the proposed final implementation.

This blocks cross-version upgrade scenarios where the source cluster is still on v1beta1, but the operator needs to move/upgrade toward v1beta2.

Anything else you would like to add?

No response

Label(s) to be applied

/kind feature
One or more /area label. See https://github.com/kubernetes-sigs/cluster-api/labels?q=area for the list of labels.

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/featureCategorizes issue or PR as related to a new feature.needs-priorityIndicates an issue lacks a `priority/foo` label and requires one.needs-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions