Skip to content

Commit

Permalink
Simplify clusterctl upgrade syntax. Don't require namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
oscr committed Nov 24, 2022
1 parent 6c65434 commit 035a5ae
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 32 deletions.
2 changes: 1 addition & 1 deletion cmd/clusterctl/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ type RepositoryClientFactoryInput struct {
// RepositoryClientFactory is a factory of repository.Client from a given input.
type RepositoryClientFactory func(RepositoryClientFactoryInput) (repository.Client, error)

// ClusterClientFactoryInput reporesents the inputs required by the factory.
// ClusterClientFactoryInput represents the inputs required by the factory.
type ClusterClientFactoryInput struct {
Kubeconfig Kubeconfig
Processor Processor
Expand Down
81 changes: 65 additions & 16 deletions cmd/clusterctl/client/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
)

const upgradeItemProviderNameError = "invalid provider name %q. Provider name should be in the form namespace/provider:version or provider:version"

// PlanUpgradeOptions carries the options supported by upgrade plan.
type PlanUpgradeOptions struct {
// Kubeconfig defines the kubeconfig to use for accessing the management cluster. If empty, default discovery rules apply.
Expand Down Expand Up @@ -97,16 +99,20 @@ type ApplyUpgradeOptions struct {
// a more granular control on upgrade, use CoreProvider, BootstrapProviders, ControlPlaneProviders, InfrastructureProviders.
Contract string

// CoreProvider instance and version (e.g. capi-system/cluster-api:v1.1.5) to upgrade to. This field can be used as alternative to Contract.
// CoreProvider instance and version (e.g. [capi-system/]cluster-api:v1.1.5) to upgrade to. This field can be used as alternative to Contract.
// Specifying a namespace is now optional and in the future it will be deprecated.
CoreProvider string

// BootstrapProviders instance and versions (e.g. capi-kubeadm-bootstrap-system/kubeadm:v1.1.5) to upgrade to. This field can be used as alternative to Contract.
// BootstrapProviders instance and versions (e.g. [capi-kubeadm-bootstrap-system/]kubeadm:v1.1.5) to upgrade to. This field can be used as alternative to Contract.
// Specifying a namespace is now optional and in the future it will be deprecated.
BootstrapProviders []string

// ControlPlaneProviders instance and versions (e.g. capi-kubeadm-control-plane-system/kubeadm:v1.1.5) to upgrade to. This field can be used as alternative to Contract.
// ControlPlaneProviders instance and versions (e.g. [capi-kubeadm-control-plane-system/]kubeadm:v1.1.5) to upgrade to. This field can be used as alternative to Contract.
// Specifying a namespace is now optional and in the future it will be deprecated.
ControlPlaneProviders []string

// InfrastructureProviders instance and versions (e.g. capa-system/aws:v0.5.0) to upgrade to. This field can be used as alternative to Contract.
// InfrastructureProviders instance and versions (e.g. [capa-system/]aws:v0.5.0) to upgrade to. This field can be used as alternative to Contract.
// Specifying a namespace is now optional and in the future it will be deprecated.
InfrastructureProviders []string

// IPAMProviders instance and versions (e.g. ipam-system/infoblox:v0.0.1) to upgrade to. This field can be used as alternative to Contract.
Expand Down Expand Up @@ -176,28 +182,28 @@ func (c *clusterctlClient) ApplyUpgrade(options ApplyUpgradeOptions) error {
upgradeItems := []cluster.UpgradeItem{}

if options.CoreProvider != "" {
upgradeItems, err = addUpgradeItems(upgradeItems, clusterctlv1.CoreProviderType, options.CoreProvider)
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.CoreProviderType, options.CoreProvider)
if err != nil {
return err
}
}
upgradeItems, err = addUpgradeItems(upgradeItems, clusterctlv1.BootstrapProviderType, options.BootstrapProviders...)
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.BootstrapProviderType, options.BootstrapProviders...)
if err != nil {
return err
}
upgradeItems, err = addUpgradeItems(upgradeItems, clusterctlv1.ControlPlaneProviderType, options.ControlPlaneProviders...)
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.ControlPlaneProviderType, options.ControlPlaneProviders...)
if err != nil {
return err
}
upgradeItems, err = addUpgradeItems(upgradeItems, clusterctlv1.InfrastructureProviderType, options.InfrastructureProviders...)
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.InfrastructureProviderType, options.InfrastructureProviders...)
if err != nil {
return err
}
upgradeItems, err = addUpgradeItems(upgradeItems, clusterctlv1.IPAMProviderType, options.IPAMProviders...)
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.IPAMProviderType, options.IPAMProviders...)
if err != nil {
return err
}
upgradeItems, err = addUpgradeItems(upgradeItems, clusterctlv1.RuntimeExtensionProviderType, options.RuntimeExtensionProviders...)
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.RuntimeExtensionProviderType, options.RuntimeExtensionProviders...)
if err != nil {
return err
}
Expand All @@ -210,9 +216,9 @@ func (c *clusterctlClient) ApplyUpgrade(options ApplyUpgradeOptions) error {
return clusterClient.ProviderUpgrader().ApplyPlan(opts, options.Contract)
}

func addUpgradeItems(upgradeItems []cluster.UpgradeItem, providerType clusterctlv1.ProviderType, providers ...string) ([]cluster.UpgradeItem, error) {
func addUpgradeItems(clusterClient cluster.Client, upgradeItems []cluster.UpgradeItem, providerType clusterctlv1.ProviderType, providers ...string) ([]cluster.UpgradeItem, error) {
for _, upgradeReference := range providers {
providerUpgradeItem, err := parseUpgradeItem(upgradeReference, providerType)
providerUpgradeItem, err := parseUpgradeItem(clusterClient, upgradeReference, providerType)
if err != nil {
return nil, err
}
Expand All @@ -224,20 +230,63 @@ func addUpgradeItems(upgradeItems []cluster.UpgradeItem, providerType clusterctl
return upgradeItems, nil
}

func parseUpgradeItem(ref string, providerType clusterctlv1.ProviderType) (*cluster.UpgradeItem, error) {
func parseUpgradeItem(clusterClient cluster.Client, ref string, providerType clusterctlv1.ProviderType) (*cluster.UpgradeItem, error) {
// TODO(oscr) Remove when explicit namespaces for providers is removed
// ref format is old format: namespace/provider:version
if strings.Contains(ref, "/") {
return parseUpgradeItemWithNamespace(ref, providerType)
}

// ref format is: provider:version
return parseUpgradeItemWithoutNamespace(clusterClient, ref, providerType)
}

func parseUpgradeItemWithNamespace(ref string, providerType clusterctlv1.ProviderType) (*cluster.UpgradeItem, error) {
refSplit := strings.Split(strings.ToLower(ref), "/")

if len(refSplit) != 2 {
return nil, errors.Errorf("invalid provider name %q. Provider name should be in the form namespace/provider[:version]", ref)
return nil, errors.Errorf(upgradeItemProviderNameError, ref)
}

if refSplit[0] == "" {
return nil, errors.Errorf("invalid provider name %q. Provider name should be in the form namespace/name[:version] and namespace cannot be empty", ref)
return nil, errors.Errorf(upgradeItemProviderNameError, ref)
}
namespace := refSplit[0]

name, version, err := parseProviderName(refSplit[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid provider name %q. Provider name should be in the form namespace/name[:version] and the namespace should be valid", ref)
return nil, errors.Wrapf(err, upgradeItemProviderNameError, ref)
}

return &cluster.UpgradeItem{
Provider: clusterctlv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: clusterctlv1.ManifestLabel(name, providerType),
},
ProviderName: name,
Type: string(providerType),
// The value for the following fields will be retrieved while
// creating the custom upgrade plan.
WatchedNamespace: "",
},
NextVersion: version,
}, nil
}

func parseUpgradeItemWithoutNamespace(clusterClient cluster.Client, ref string, providerType clusterctlv1.ProviderType) (*cluster.UpgradeItem, error) {
if !strings.Contains(ref, ":") {
return nil, errors.Errorf(upgradeItemProviderNameError, ref)
}

name, version, err := parseProviderName(ref)
if err != nil {
return nil, errors.Wrapf(err, upgradeItemProviderNameError, ref)
}

namespace, err := clusterClient.ProviderInventory().GetProviderNamespace(name, providerType)
if err != nil {
return nil, errors.Errorf("unable to find default namespace for provider %q", ref)
}

return &cluster.UpgradeItem{
Expand Down
37 changes: 34 additions & 3 deletions cmd/clusterctl/client/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@ func Test_parseUpgradeItem(t *testing.T) {
type args struct {
provider string
}

configClient := newFakeConfig()
clusterClient := newFakeCluster(cluster.Kubeconfig{Path: "cluster1"}, configClient)
clusterClient.WithProviderInventory("best-provider", clusterctlv1.CoreProviderType, "v1.0.0", "best-provider-system")

tests := []struct {
name string
args args
Expand Down Expand Up @@ -418,9 +423,35 @@ func Test_parseUpgradeItem(t *testing.T) {
wantErr: false,
},
{
name: "namespace missing",
name: "provider:version",
args: args{
provider: "best-provider:v1.0.0",
},
want: &cluster.UpgradeItem{
Provider: clusterctlv1.Provider{
ObjectMeta: metav1.ObjectMeta{
Namespace: "best-provider-system",
Name: clusterctlv1.ManifestLabel("best-provider", clusterctlv1.CoreProviderType),
},
ProviderName: "best-provider",
Type: string(clusterctlv1.CoreProviderType),
},
NextVersion: "v1.0.0",
},
wantErr: false,
},
{
name: "provider: with no version",
args: args{
provider: "provider:",
},
want: nil,
wantErr: true,
},
{
name: "provider with no version",
args: args{
provider: "provider:version",
provider: "provider",
},
want: nil,
wantErr: true,
Expand All @@ -438,7 +469,7 @@ func Test_parseUpgradeItem(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

got, err := parseUpgradeItem(tt.args.provider, clusterctlv1.CoreProviderType)
got, err := parseUpgradeItem(clusterClient, tt.args.provider, clusterctlv1.CoreProviderType)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
Expand Down
12 changes: 6 additions & 6 deletions cmd/clusterctl/cmd/upgrade_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ var upgradeApplyCmd = &cobra.Command{
# to the v1alpha4 API Version of Cluster API (contract).
clusterctl upgrade apply --contract v1alpha4
# Upgrades only the capa-system/aws provider to the v0.5.0 version.
clusterctl upgrade apply --infrastructure capa-system/aws:v0.5.0`),
# Upgrades only the aws provider to the v2.0.1 version.
clusterctl upgrade apply --infrastructure aws:v2.0.1`),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runUpgradeApply()
Expand All @@ -72,13 +72,13 @@ func init() {
"The API Version of Cluster API (contract, e.g. v1alpha4) the management cluster should upgrade to")

upgradeApplyCmd.Flags().StringVar(&ua.coreProvider, "core", "",
"Core provider instance version (e.g. capi-system/cluster-api:v1.1.5) to upgrade to. This flag can be used as alternative to --contract.")
"Core provider instance version (e.g. cluster-api:v1.1.5) to upgrade to. This flag can be used as alternative to --contract.")
upgradeApplyCmd.Flags().StringSliceVarP(&ua.infrastructureProviders, "infrastructure", "i", nil,
"Infrastructure providers instance and versions (e.g. capa-system/aws:v0.5.0) to upgrade to. This flag can be used as alternative to --contract.")
"Infrastructure providers instance and versions (e.g. aws:v2.0.1) to upgrade to. This flag can be used as alternative to --contract.")
upgradeApplyCmd.Flags().StringSliceVarP(&ua.bootstrapProviders, "bootstrap", "b", nil,
"Bootstrap providers instance and versions (e.g. capi-kubeadm-bootstrap-system/kubeadm:v1.1.5) to upgrade to. This flag can be used as alternative to --contract.")
"Bootstrap providers instance and versions (e.g. kubeadm:v1.1.5) to upgrade to. This flag can be used as alternative to --contract.")
upgradeApplyCmd.Flags().StringSliceVarP(&ua.controlPlaneProviders, "control-plane", "c", nil,
"ControlPlane providers instance and versions (e.g. capi-kubeadm-control-plane-system/kubeadm:v1.1.5) to upgrade to. This flag can be used as alternative to --contract.")
"ControlPlane providers instance and versions (e.g. kubeadm:v1.1.5) to upgrade to. This flag can be used as alternative to --contract.")
upgradeApplyCmd.Flags().StringSliceVar(&ua.ipamProviders, "ipam", nil,
"IPAM providers and versions (e.g. infoblox:v0.0.1) to upgrade to. This flag can be used as alternative to --contract.")
upgradeApplyCmd.Flags().StringSliceVar(&ua.runtimeExtensionProviders, "runtime-extension", nil,
Expand Down
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
- [v1.0 to v1.1](./developer/providers/v1.0-to-v1.1.md)
- [v1.1 to v1.2](./developer/providers/v1.1-to-v1.2.md)
- [v1.2 to v1.3](./developer/providers/v1.2-to-v1.3.md)
- [v1.3 to v1.4](./developer/providers/v1.3-to-v1.4.md)
- [Provider contracts](./developer/providers/contracts.md)
- [Cluster Infrastructure](./developer/providers/cluster-infrastructure.md)
- [Machine Infrastructure](./developer/providers/machine-infrastructure.md)
Expand Down
18 changes: 12 additions & 6 deletions docs/book/src/clusterctl/commands/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@ clusterctl upgrade apply --contract v1beta1
The upgrade process is composed by three steps:

* Check the cert-manager version, and if necessary, upgrade it.
* Delete the current version of the provider components, while preserving the namespace where the provider components
are hosted and the provider's CRDs.
* Delete the current version of the provider components except the CRDs.
* Install the new version of the provider components.

Please note that clusterctl does not upgrade Cluster API objects (Clusters, MachineDeployments, Machine etc.); upgrading
such objects are the responsibility of the provider's controllers.

It is also possible to explicitly upgrade one or more components to specific versions.
```bash
clusterctl upgrade apply \
--core cluster-api:v1.2.4 \
--infrastructure docker:v1.2.4
```

<aside class="note warning">

<h1>Warning!</h1>
Expand All @@ -86,10 +92,10 @@ the following:

```bash
clusterctl upgrade apply \
--core capi-system/cluster-api:v1.0.0 \
--bootstrap capi-kubeadm-bootstrap-system/kubeadm:v1.0.0 \
--control-plane capi-kubeadm-control-plane-system/kubeadm:v1.0.0 \
--infrastructure capd-system/docker:v1.0.0-rc.0
--core cluster-api:v1.0.0 \
--bootstrap kubeadm:v1.0.0 \
--control-plane kubeadm:v1.0.0 \
--infrastructure docker:v1.0.0-rc.0
```

In this case, all the provider's versions must be explicitly stated.
Expand Down
28 changes: 28 additions & 0 deletions docs/book/src/developer/providers/v1.3-to-v1.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Cluster API v1.3 compared to v1.4

This document provides an overview over relevant changes between Cluster API v1.3 and v1.4 for
maintainers of providers and consumers of our Go API.

## Minimum Go version

* The Go version used by Cluster API is Go 1.19.x

## Dependencies

**Note**: Only the most relevant dependencies are listed, `k8s.io/` and `ginkgo`/`gomega` dependencies
in Cluster API are kept in sync with the versions used by `sigs.k8s.io/controller-runtime`.


## Changes by Kind

### Deprecation

### Removals

### API Changes

### Other

- `clusterctl upgrade apply` no longer requires a namespace when updating providers. It is now optional and in a future release it will be deprecated. The new syntax is `[namespace/]provider:version`.

### Suggested changes for providers

0 comments on commit 035a5ae

Please sign in to comment.