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

✨ Add support for add-on providers in clusterctl #8472

Merged
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
2 changes: 2 additions & 0 deletions cmd/clusterctl/api/v1alpha3/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ func ManifestLabel(name string, providerType ProviderType) string {
return fmt.Sprintf("ipam-%s", name)
case RuntimeExtensionProviderType:
return fmt.Sprintf("runtime-extension-%s", name)
case AddonProviderType:
return fmt.Sprintf("addon-%s", name)
default:
return name
}
Expand Down
9 changes: 8 additions & 1 deletion cmd/clusterctl/api/v1alpha3/provider_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ func (p *Provider) GetProviderType() ProviderType {
InfrastructureProviderType,
ControlPlaneProviderType,
IPAMProviderType,
RuntimeExtensionProviderType:
RuntimeExtensionProviderType,
AddonProviderType:
return t
default:
return ProviderTypeUnknown
Expand Down Expand Up @@ -129,6 +130,10 @@ const (
// runtime extensions.
RuntimeExtensionProviderType = ProviderType("RuntimeExtensionProvider")

// AddonProviderType is the type associated with codebases that provide
// add-on capabilities.
AddonProviderType = ProviderType("AddonProvider")

// ProviderTypeUnknown is used when the type is unknown.
ProviderTypeUnknown = ProviderType("")
)
Expand All @@ -148,6 +153,8 @@ func (p ProviderType) Order() int {
return 4
case RuntimeExtensionProviderType:
return 5
case AddonProviderType:
return 6
default:
return 99
}
Expand Down
21 changes: 18 additions & 3 deletions cmd/clusterctl/client/config/providers_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ const (
KubeKeyK3sControlPlaneProviderName = "kubekey-k3s"
)

// Add-on providers.
const (
HelmAddonProviderName = "helm"
)

// Other.
const (
// ProvidersConfigKey is a constant for finding provider configurations with the ProvidersClient.
Expand Down Expand Up @@ -276,6 +281,7 @@ func (p *providersClient) defaults() []Provider {
url: "https://github.com/canonical/cluster-api-bootstrap-provider-microk8s/releases/latest/bootstrap-components.yaml",
providerType: clusterctlv1.BootstrapProviderType,
},

sbueringer marked this conversation as resolved.
Show resolved Hide resolved
// ControlPlane providers
&provider{
name: KubeadmControlPlaneProviderName,
Expand All @@ -302,6 +308,13 @@ func (p *providersClient) defaults() []Provider {
url: "https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/control-plane-components.yaml",
providerType: clusterctlv1.ControlPlaneProviderType,
},

sbueringer marked this conversation as resolved.
Show resolved Hide resolved
// Add-on providers
&provider{
name: HelmAddonProviderName,
url: "https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/latest/addon-components.yaml",
providerType: clusterctlv1.AddonProviderType,
},
}

return defaults
Expand Down Expand Up @@ -401,16 +414,18 @@ func validateProvider(r Provider) error {
clusterctlv1.InfrastructureProviderType,
clusterctlv1.ControlPlaneProviderType,
clusterctlv1.IPAMProviderType,
clusterctlv1.RuntimeExtensionProviderType:
clusterctlv1.RuntimeExtensionProviderType,
clusterctlv1.AddonProviderType:
break
default:
return errors.Errorf("invalid provider type. Allowed values are [%s, %s, %s, %s, %s, %s]",
return errors.Errorf("invalid provider type. Allowed values are [%s, %s, %s, %s, %s, %s, %s]",
clusterctlv1.CoreProviderType,
clusterctlv1.BootstrapProviderType,
clusterctlv1.InfrastructureProviderType,
clusterctlv1.ControlPlaneProviderType,
clusterctlv1.IPAMProviderType,
clusterctlv1.RuntimeExtensionProviderType)
clusterctlv1.RuntimeExtensionProviderType,
clusterctlv1.AddonProviderType)
}
return nil
}
8 changes: 8 additions & 0 deletions cmd/clusterctl/client/config/providers_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"testing"

. "github.com/onsi/gomega"
"github.com/onsi/gomega/format"

clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
Expand All @@ -41,6 +42,10 @@ func Test_providers_List(t *testing.T) {
})

defaultsAndZZZ := append(defaults, NewProvider("zzz", "https://zzz/infrastructure-components.yaml", "InfrastructureProvider"))
// AddonProviders are at the end of the list so we want to make sure this InfrastructureProvider is before the AddonProviders.
sort.Slice(defaultsAndZZZ, func(i, j int) bool {
Jont828 marked this conversation as resolved.
Show resolved Hide resolved
return defaultsAndZZZ[i].Less(defaultsAndZZZ[j])
})

defaultsWithOverride := append([]Provider{}, defaults...)
defaultsWithOverride[0] = NewProvider(defaults[0].Name(), "https://zzz/infrastructure-components.yaml", defaults[0].Type())
Expand Down Expand Up @@ -135,6 +140,9 @@ func Test_providers_List(t *testing.T) {
wantErr: true,
},
}

format.MaxLength = 15000 // This way it doesn't truncate the output on test failure

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
Expand Down
2 changes: 2 additions & 0 deletions cmd/clusterctl/client/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
config.VclusterProviderName,
config.VirtinkProviderName,
config.VSphereProviderName,
config.HelmAddonProviderName,
},
wantErr: false,
},
Expand Down Expand Up @@ -136,6 +137,7 @@ func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
config.VclusterProviderName,
config.VirtinkProviderName,
config.VSphereProviderName,
config.HelmAddonProviderName,
},
wantErr: false,
},
Expand Down
8 changes: 8 additions & 0 deletions cmd/clusterctl/client/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ type DeleteOptions struct {
// RuntimeExtensionProviders and versions (e.g. test:v0.0.1) to delete from the management cluster.
RuntimeExtensionProviders []string

// AddonProviders and versions (e.g. helm:v0.1.0) to delete from the management cluster.
AddonProviders []string

// DeleteAll set for deletion of all the providers.
DeleteAll bool

Expand Down Expand Up @@ -122,6 +125,11 @@ func (c *clusterctlClient) Delete(options DeleteOptions) error {
return err
}

providers, err = appendProviders(providers, clusterctlv1.AddonProviderType, options.AddonProviders...)
if err != nil {
return err
}

for _, provider := range providers {
// Try to detect the namespace where the provider lives
provider.Namespace, err = clusterClient.ProviderInventory().GetProviderNamespace(provider.ProviderName, provider.GetProviderType())
Expand Down
7 changes: 7 additions & 0 deletions cmd/clusterctl/client/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ type InitOptions struct {
// RuntimeExtensionProviders and versions (e.g. test:v0.0.1) to add to the management cluster.
RuntimeExtensionProviders []string

// AddonProviders and versions (e.g. helm:v0.1.0) to add to the management cluster.
AddonProviders []string

// TargetNamespace defines the namespace where the providers should be deployed. If unspecified, each provider
// will be installed in a provider's default namespace.
TargetNamespace string
Expand Down Expand Up @@ -255,6 +258,10 @@ func (c *clusterctlClient) setupInstaller(cluster cluster.Client, options InitOp
return nil, err
}

if err := c.addToInstaller(addOptions, clusterctlv1.AddonProviderType, options.AddonProviders...); err != nil {
return nil, err
}

return installer, nil
}

Expand Down
10 changes: 9 additions & 1 deletion cmd/clusterctl/client/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ type ApplyUpgradeOptions struct {
// RuntimeExtensionProviders instance and versions (e.g. runtime-extension-system/test:v0.0.1) to upgrade to. This field can be used as alternative to Contract.
RuntimeExtensionProviders []string

// AddonProviders instance and versions (e.g. caaph-system/helm:v0.1.0) to upgrade to. This field can be used as alternative to Contract.
AddonProviders []string

// WaitProviders instructs the upgrade apply command to wait till the providers are successfully upgraded.
WaitProviders bool

Expand Down Expand Up @@ -169,7 +172,8 @@ func (c *clusterctlClient) ApplyUpgrade(options ApplyUpgradeOptions) error {
len(options.ControlPlaneProviders) > 0 ||
len(options.InfrastructureProviders) > 0 ||
len(options.IPAMProviders) > 0 ||
len(options.RuntimeExtensionProviders) > 0
len(options.RuntimeExtensionProviders) > 0 ||
len(options.AddonProviders) > 0

opts := cluster.UpgradeOptions{
WaitProviders: options.WaitProviders,
Expand Down Expand Up @@ -207,6 +211,10 @@ func (c *clusterctlClient) ApplyUpgrade(options ApplyUpgradeOptions) error {
if err != nil {
return err
}
upgradeItems, err = addUpgradeItems(clusterClient, upgradeItems, clusterctlv1.AddonProviderType, options.AddonProviders...)
if err != nil {
return err
}

// Execute the upgrade using the custom upgrade items
return clusterClient.ProviderUpgrader().ApplyCustomPlan(opts, upgradeItems...)
Expand Down
12 changes: 10 additions & 2 deletions cmd/clusterctl/cmd/config_repositories_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"path/filepath"
"testing"

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

Expand All @@ -45,11 +46,13 @@ func Test_runGetRepositories(t *testing.T) {
out, err := io.ReadAll(buf)
g.Expect(err).ToNot(HaveOccurred())

var diff string
if val == RepositoriesOutputText {
g.Expect(string(out)).To(Equal(expectedOutputText))
diff = cmp.Diff(expectedOutputText, string(out))
} else if val == RepositoriesOutputYaml {
g.Expect(string(out)).To(Equal(expectedOutputYaml))
diff = cmp.Diff(expectedOutputYaml, string(out))
}
g.Expect(diff).To(BeEmpty()) // Use diff to compare as Gomega output does not actually print the string values on failure
}
})

Expand Down Expand Up @@ -137,6 +140,7 @@ vcd InfrastructureProvider https://github.com/vmware/cluster-a
vcluster InfrastructureProvider https://github.com/loft-sh/cluster-api-provider-vcluster/releases/latest/ infrastructure-components.yaml
virtink InfrastructureProvider https://github.com/smartxworks/cluster-api-provider-virtink/releases/latest/ infrastructure-components.yaml
vsphere InfrastructureProvider https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/latest/ infrastructure-components.yaml
helm AddonProvider https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/latest/ addon-components.yaml
`

var expectedOutputYaml = `- File: core_components.yaml
Expand Down Expand Up @@ -287,4 +291,8 @@ var expectedOutputYaml = `- File: core_components.yaml
Name: vsphere
ProviderType: InfrastructureProvider
URL: https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/latest/
- File: addon-components.yaml
Name: helm
ProviderType: AddonProvider
URL: https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/latest/
`
11 changes: 8 additions & 3 deletions cmd/clusterctl/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type deleteOptions struct {
infrastructureProviders []string
ipamProviders []string
runtimeExtensionProviders []string
addonProviders []string
includeNamespace bool
includeCRDs bool
deleteAll bool
Expand Down Expand Up @@ -109,6 +110,8 @@ func init() {
"IPAM providers and versions (e.g. infoblox:v0.0.1) to delete from the management cluster")
deleteCmd.Flags().StringSliceVar(&dd.runtimeExtensionProviders, "runtime-extension", nil,
"Runtime extension providers and versions (e.g. test:v0.0.1) to delete from the management cluster")
deleteCmd.Flags().StringSliceVar(&dd.addonProviders, "addon", nil,
"Add-on providers and versions (e.g. helm:v0.1.0) to delete from the management cluster")
sbueringer marked this conversation as resolved.
Show resolved Hide resolved

deleteCmd.Flags().BoolVar(&dd.deleteAll, "all", false,
"Force deletion of all the providers")
Expand All @@ -127,14 +130,15 @@ func runDelete() error {
(len(dd.controlPlaneProviders) > 0) ||
(len(dd.infrastructureProviders) > 0) ||
(len(dd.ipamProviders) > 0) ||
(len(dd.runtimeExtensionProviders) > 0)
(len(dd.runtimeExtensionProviders) > 0) ||
(len(dd.addonProviders) > 0)

if dd.deleteAll && hasProviderNames {
return errors.New("The --all flag can't be used in combination with --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension")
return errors.New("The --all flag can't be used in combination with --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon")
}

if !dd.deleteAll && !hasProviderNames {
return errors.New("At least one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be specified or the --all flag should be set")
return errors.New("At least one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be specified or the --all flag should be set")
}

return c.Delete(client.DeleteOptions{
Expand All @@ -147,6 +151,7 @@ func runDelete() error {
ControlPlaneProviders: dd.controlPlaneProviders,
IPAMProviders: dd.ipamProviders,
RuntimeExtensionProviders: dd.runtimeExtensionProviders,
AddonProviders: dd.addonProviders,
DeleteAll: dd.deleteAll,
})
}
22 changes: 16 additions & 6 deletions cmd/clusterctl/cmd/generate_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type generateProvidersOptions struct {
infrastructureProvider string
ipamProvider string
runtimeExtensionProvider string
addonProvider string
targetNamespace string
textOutput bool
raw bool
Expand Down Expand Up @@ -89,6 +90,8 @@ func init() {
"IPAM provider and version (e.g. infoblox:v0.0.1)")
generateProviderCmd.Flags().StringVar(&gpo.runtimeExtensionProvider, "runtime-extension", "",
"Runtime extension provider and version (e.g. test:v0.0.1)")
generateProviderCmd.Flags().StringVar(&gpo.addonProvider, "addon", "",
"Add-on provider and version (e.g. helm:v0.1.0)")
generateProviderCmd.Flags().StringVarP(&gpo.targetNamespace, "target-namespace", "n", "",
"The target namespace where the provider should be deployed. If unspecified, the components default namespace is used.")
generateProviderCmd.Flags().BoolVar(&gpo.textOutput, "describe", false,
Expand Down Expand Up @@ -134,41 +137,48 @@ func parseProvider() (string, clusterctlv1.ProviderType, error) {
providerType := clusterctlv1.CoreProviderType
if gpo.bootstrapProvider != "" {
if providerName != "" {
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be set")
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}
providerName = gpo.bootstrapProvider
providerType = clusterctlv1.BootstrapProviderType
}
if gpo.controlPlaneProvider != "" {
if providerName != "" {
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be set")
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}
providerName = gpo.controlPlaneProvider
providerType = clusterctlv1.ControlPlaneProviderType
}
if gpo.infrastructureProvider != "" {
if providerName != "" {
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be set")
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}
providerName = gpo.infrastructureProvider
providerType = clusterctlv1.InfrastructureProviderType
}
if gpo.ipamProvider != "" {
if providerName != "" {
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be set")
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}
providerName = gpo.ipamProvider
providerType = clusterctlv1.IPAMProviderType
}
if gpo.runtimeExtensionProvider != "" {
if providerName != "" {
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be set")
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}
providerName = gpo.runtimeExtensionProvider
providerType = clusterctlv1.RuntimeExtensionProviderType
}
Jont828 marked this conversation as resolved.
Show resolved Hide resolved
if gpo.addonProvider != "" {
if providerName != "" {
return "", "", errors.New("only one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}
providerName = gpo.addonProvider
providerType = clusterctlv1.AddonProviderType
}
if providerName == "" {
return "", "", errors.New("at least one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension should be set")
return "", "", errors.New("at least one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be set")
}

return providerName, providerType, nil
Expand Down
Loading