Skip to content
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

✨ Clusterctl: add support for v1alpha3 to v1beta1 upgrades #5271

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
13 changes: 9 additions & 4 deletions cmd/clusterctl/client/cluster/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ type CheckCAPIContractOptions struct {
// AllowCAPINotInstalled instructs CheckCAPIContract to tolerate management clusters without Cluster API installed yet.
AllowCAPINotInstalled bool

// AllowCAPIContract instructs CheckCAPIContract to tolerate management clusters with Cluster API with the given contract.
AllowCAPIContract string
// AllowCAPIContracts instructs CheckCAPIContract to tolerate management clusters with Cluster API with the given contract.
AllowCAPIContracts []string
}

// AllowCAPINotInstalled instructs CheckCAPIContract to tolerate management clusters without Cluster API installed yet.
Expand All @@ -72,7 +72,7 @@ type AllowCAPIContract struct {

// Apply applies this configuration to the given CheckCAPIContractOptions.
func (t AllowCAPIContract) Apply(in *CheckCAPIContractOptions) {
in.AllowCAPIContract = t.Contract
in.AllowCAPIContracts = append(in.AllowCAPIContracts, t.Contract)
}

// InventoryClient exposes methods to interface with a cluster's provider inventory.
Expand Down Expand Up @@ -397,9 +397,14 @@ func (p *inventoryClient) CheckCAPIContract(options ...CheckCAPIContractOption)

for _, version := range crd.Spec.Versions {
if version.Storage {
if version.Name == clusterv1.GroupVersion.Version || version.Name == opt.AllowCAPIContract {
if version.Name == clusterv1.GroupVersion.Version {
return nil
}
for _, allowedContract := range opt.AllowCAPIContracts {
if version.Name == allowedContract {
return nil
}
}
return errors.Errorf("this version of clusterctl could be used only with %q management clusters, %q detected", clusterv1.GroupVersion.Version, version.Name)
}
}
Expand Down
23 changes: 22 additions & 1 deletion cmd/clusterctl/client/cluster/inventory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
clusterv1v1alpha3 "sigs.k8s.io/cluster-api/api/v1alpha3"
fabriziopandini marked this conversation as resolved.
Show resolved Hide resolved
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -276,6 +277,26 @@ func Test_CheckCAPIContract(t *testing.T) {
args: args{},
wantErr: true,
},
{
name: "Pass when Cluster API with v1alpha3 contract is installed, but this is explicitly tolerated",
fields: fields{
proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"},
Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{
Name: clusterv1v1alpha3.GroupVersion.Version,
Storage: true,
},
},
},
}),
},
args: args{
options: []CheckCAPIContractOption{AllowCAPIContract{Contract: clusterv1v1alpha3.GroupVersion.Version}, AllowCAPIContract{Contract: test.PreviousCAPIContractNotSupported}},
},
wantErr: false,
},
{
name: "Pass when Cluster API with previous contract is installed, but this is explicitly tolerated",
fields: fields{
Expand All @@ -295,7 +316,7 @@ func Test_CheckCAPIContract(t *testing.T) {
}),
},
args: args{
options: []CheckCAPIContractOption{AllowCAPIContract{Contract: test.PreviousCAPIContractNotSupported}},
options: []CheckCAPIContractOption{AllowCAPIContract{Contract: clusterv1v1alpha3.GroupVersion.Version}, AllowCAPIContract{Contract: test.PreviousCAPIContractNotSupported}},
fabriziopandini marked this conversation as resolved.
Show resolved Hide resolved
},
wantErr: false,
},
Expand Down
83 changes: 80 additions & 3 deletions cmd/clusterctl/client/cluster/upgrader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ package cluster
import (
"testing"

"github.com/google/go-cmp/cmp"
. "github.com/onsi/gomega"

clusterv1v1alpha3 "sigs.k8s.io/cluster-api/api/v1alpha3"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
Expand Down Expand Up @@ -135,6 +137,81 @@ func Test_providerUpgrader_Plan(t *testing.T) {
},
wantErr: false,
},
{
name: "Upgrade for v1alpha3 (not supported), previous contract (not supported), current contract", // upgrade plan should report unsupported options
fabriziopandini marked this conversation as resolved.
Show resolved Hide resolved
fields: fields{
// config for two providers
reader: test.NewFakeReader().
WithProvider("cluster-api", clusterctlv1.CoreProviderType, "https://somewhere.com").
WithProvider("infra", clusterctlv1.InfrastructureProviderType, "https://somewhere.com"),
repository: map[string]repository.Repository{
"cluster-api": repository.NewMemoryRepository().
WithVersions("v1.0.0", "v1.0.1", "v2.0.0", "v3.0.0").
WithMetadata("v3.0.0", &clusterctlv1.Metadata{
ReleaseSeries: []clusterctlv1.ReleaseSeries{
{Major: 1, Minor: 0, Contract: clusterv1v1alpha3.GroupVersion.Version},
{Major: 2, Minor: 0, Contract: test.PreviousCAPIContractNotSupported},
{Major: 3, Minor: 0, Contract: test.CurrentCAPIContract},
},
}),
"infrastructure-infra": repository.NewMemoryRepository().
WithVersions("v1.0.0", "v2.0.0", "v2.0.1", "v3.0.0").
WithMetadata("v3.0.0", &clusterctlv1.Metadata{
ReleaseSeries: []clusterctlv1.ReleaseSeries{
{Major: 1, Minor: 0, Contract: clusterv1v1alpha3.GroupVersion.Version},
{Major: 2, Minor: 0, Contract: test.PreviousCAPIContractNotSupported},
{Major: 3, Minor: 0, Contract: test.CurrentCAPIContract},
},
}),
},
// two providers existing in the cluster
proxy: test.NewFakeProxy().
WithProviderInventory("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system").
WithProviderInventory("infra", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra-system"),
},
want: []UpgradePlan{
{ // one upgrade plan with the latest releases in the v1alpha3 contract (not supported, but upgrade plan should report these options)
Contract: clusterv1v1alpha3.GroupVersion.Version,
Providers: []UpgradeItem{
{
Provider: fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"),
NextVersion: "v1.0.1",
},
{
Provider: fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra-system"),
NextVersion: "",
},
},
},
{ // one upgrade plan with the latest releases in the previous contract (not supported, but upgrade plan should report these options)
Contract: test.PreviousCAPIContractNotSupported,
Providers: []UpgradeItem{
{
Provider: fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"),
NextVersion: "v2.0.0",
},
{
Provider: fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra-system"),
NextVersion: "v2.0.1",
},
},
},
{ // one upgrade plan with the latest releases in the current contract
Contract: test.CurrentCAPIContract,
Providers: []UpgradeItem{
{
Provider: fakeProvider("cluster-api", clusterctlv1.CoreProviderType, "v1.0.0", "cluster-api-system"),
NextVersion: "v3.0.0",
},
{
Provider: fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v1.0.0", "infra-system"),
NextVersion: "v3.0.0",
},
},
},
},
wantErr: false,
},
{
name: "Upgrade for previous contract (not supported), current contract", // upgrade plan should report unsupported options
fields: fields{
Expand Down Expand Up @@ -323,7 +400,7 @@ func Test_providerUpgrader_Plan(t *testing.T) {
}

g.Expect(err).NotTo(HaveOccurred())
g.Expect(got).To(Equal(tt.want))
g.Expect(got).To(Equal(tt.want), cmp.Diff(got, tt.want))
})
}
}
Expand Down Expand Up @@ -813,7 +890,7 @@ func Test_providerUpgrader_ApplyPlan(t *testing.T) {
errorMsg string
}{
{
name: "fails to upgrade to v1alpha4 when there are multiple instances of the core provider",
name: "fails to upgrade to current contract when there are multiple instances of the core provider",
fields: fields{
// config for two providers
reader: test.NewFakeReader().
Expand Down Expand Up @@ -849,7 +926,7 @@ func Test_providerUpgrader_ApplyPlan(t *testing.T) {
errorMsg: "detected multiple instances of the same provider",
},
{
name: "fails to upgrade to v1alpha4 when there are multiple instances of the infra provider",
name: "fails to upgrade to current contract when there are multiple instances of the infra provider",
fields: fields{
// config for two providers
reader: test.NewFakeReader().
Expand Down
17 changes: 14 additions & 3 deletions cmd/clusterctl/client/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import (

"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterv1old "sigs.k8s.io/cluster-api/api/v1alpha3"
clusterv1v1alpha3 "sigs.k8s.io/cluster-api/api/v1alpha3"
fabriziopandini marked this conversation as resolved.
Show resolved Hide resolved
clusterv1old "sigs.k8s.io/cluster-api/api/v1alpha4"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
Expand Down Expand Up @@ -53,7 +54,12 @@ func (c *clusterctlClient) PlanUpgrade(options PlanUpgradeOptions) ([]UpgradePla
}

// Ensure this command only runs against management clusters with the current Cluster API contract (default) or the previous one.
if err := clusterClient.ProviderInventory().CheckCAPIContract(cluster.AllowCAPIContract{Contract: clusterv1old.GroupVersion.Version}); err != nil {
// NOTE: given that v1beta1 (current) and v1alpha4 (previous) does not have breaking changes, we support also upgrades from v1alpha3 to v1beta1;
// this is an exception and support for skipping releases should be removed in future releases.
if err := clusterClient.ProviderInventory().CheckCAPIContract(
cluster.AllowCAPIContract{Contract: clusterv1v1alpha3.GroupVersion.Version},
cluster.AllowCAPIContract{Contract: clusterv1old.GroupVersion.Version},
); err != nil {
return nil, err
}

Expand Down Expand Up @@ -114,7 +120,12 @@ func (c *clusterctlClient) ApplyUpgrade(options ApplyUpgradeOptions) error {
}

// Ensure this command only runs against management clusters with the current Cluster API contract (default) or the previous one.
if err := clusterClient.ProviderInventory().CheckCAPIContract(cluster.AllowCAPIContract{Contract: clusterv1old.GroupVersion.Version}); err != nil {
// NOTE: given that v1beta1 (current) and v1alpha4 (previous) does not have breaking changes, we support also upgrades from v1alpha3 to v1beta1;
// this is an exception and support for skipping releases should be removed in future releases.
if err := clusterClient.ProviderInventory().CheckCAPIContract(
cluster.AllowCAPIContract{Contract: clusterv1v1alpha3.GroupVersion.Version},
cluster.AllowCAPIContract{Contract: clusterv1old.GroupVersion.Version},
); err != nil {
return err
}

Expand Down
18 changes: 14 additions & 4 deletions test/e2e/clusterctl_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (

const (
initWithBinaryVariableName = "INIT_WITH_BINARY"
initWithProvidersContract = "INIT_WITH_PROVIDERS_CONTRACT"
initWithKubernetesVersion = "INIT_WITH_KUBERNETES_VERSION"
)

Expand Down Expand Up @@ -155,14 +156,23 @@ func ClusterctlUpgradeSpec(ctx context.Context, inputGetter func() ClusterctlUpg
Expect(err).ToNot(HaveOccurred(), "failed to chmod temporary file")

By("Initializing the workload cluster with older versions of providers")

// NOTE: by default we are considering all the providers, no matter of the contract.
// However, given that we want to test both v1alpha3 --> v1beta1 and v1alpha4 --> v1beta1, the INIT_WITH_PROVIDERS_CONTRACT
// variable can be used to select versions with a specific contract.
contract := "*"
if input.E2EConfig.HasVariable(initWithProvidersContract) {
contract = input.E2EConfig.GetVariable(initWithProvidersContract)
}

clusterctl.InitManagementClusterAndWatchControllerLogs(ctx, clusterctl.InitManagementClusterAndWatchControllerLogsInput{
ClusterctlBinaryPath: clusterctlBinaryPath, // use older version of clusterctl to init the management cluster
ClusterProxy: managementClusterProxy,
ClusterctlConfigPath: input.ClusterctlConfigPath,
CoreProvider: input.E2EConfig.GetProvidersWithOldestVersion(config.ClusterAPIProviderName)[0],
BootstrapProviders: input.E2EConfig.GetProvidersWithOldestVersion(config.KubeadmBootstrapProviderName),
ControlPlaneProviders: input.E2EConfig.GetProvidersWithOldestVersion(config.KubeadmControlPlaneProviderName),
InfrastructureProviders: input.E2EConfig.GetProvidersWithOldestVersion(input.E2EConfig.InfrastructureProviders()...),
CoreProvider: input.E2EConfig.GetProviderLatestVersionsByContract(contract, config.ClusterAPIProviderName)[0],
BootstrapProviders: input.E2EConfig.GetProviderLatestVersionsByContract(contract, config.KubeadmBootstrapProviderName),
ControlPlaneProviders: input.E2EConfig.GetProviderLatestVersionsByContract(contract, config.KubeadmControlPlaneProviderName),
InfrastructureProviders: input.E2EConfig.GetProviderLatestVersionsByContract(contract, input.E2EConfig.InfrastructureProviders()...),
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", cluster.Name),
}, input.E2EConfig.GetIntervals(specName, "wait-controllers")...)

Expand Down
35 changes: 31 additions & 4 deletions test/e2e/config/docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,18 @@ providers:
- name: cluster-api
type: CoreProvider
versions:
- name: v0.3.23 # latest published release
- name: v0.3.23 # latest published release in the v1alpha3 series; this is used for v1alpha3 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.23/core-components.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
- name: v0.4.3 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.3/core-components.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
- name: v1.0.99 # next; use manifest from source files
value: ../../../config/default
replacements:
Expand All @@ -46,12 +52,18 @@ providers:
- name: kubeadm
type: BootstrapProvider
versions:
- name: v0.3.23 # latest published release
- name: v0.3.23 # latest published release in the v1alpha3 series; this is used for v1alpha3 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.23/bootstrap-components.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
- name: v0.4.3 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.3/bootstrap-components.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
- name: v1.0.99 # next; use manifest from source files
value: ../../../bootstrap/kubeadm/config/default
replacements:
Expand All @@ -63,12 +75,18 @@ providers:
- name: kubeadm
type: ControlPlaneProvider
versions:
- name: v0.3.23 # latest published release
- name: v0.3.23 # latest published release in the v1alpha3 series; this is used for v1alpha3 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.23/control-plane-components.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
- name: v0.4.3 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.3/control-plane-components.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
- name: v1.0.99 # next; use manifest from source files
value: ../../../controlplane/kubeadm/config/default
replacements:
Expand All @@ -80,7 +98,7 @@ providers:
- name: docker
type: InfrastructureProvider
versions:
- name: v0.3.23 # latest published release
- name: v0.3.23 # latest published release in the v1alpha3 series; this is used for v1alpha3 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.23/infrastructure-components-development.yaml"
type: "url"
replacements:
Expand All @@ -89,6 +107,15 @@ providers:
files:
# Add cluster templates
- sourcePath: "../data/infrastructure-docker/v1alpha3/cluster-template.yaml"
- name: v0.4.3 # latest published release in the v1alpha4 series; this is used for v1alpha4 --> v1beta1 clusterctl upgrades test only.
value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.4.3/infrastructure-components-development.yaml"
type: "url"
replacements:
- old: --metrics-addr=127.0.0.1:8080
new: --metrics-addr=:8080
files:
# Add cluster templates
- sourcePath: "../data/infrastructure-docker/v1alpha4/cluster-template.yaml"
- name: v1.0.99 # next; use manifest from source files
fabriziopandini marked this conversation as resolved.
Show resolved Hide resolved
value: ../../../test/infrastructure/docker/config/default
replacements:
Expand Down
Loading