diff --git a/cmd/clusterctl/client/client.go b/cmd/clusterctl/client/client.go index ec32c11adc7a..5747dd5ac68f 100644 --- a/cmd/clusterctl/client/client.go +++ b/cmd/clusterctl/client/client.go @@ -33,6 +33,9 @@ type Client interface { // GetProviderComponents returns the provider components for a given provider with options including targetNamespace. GetProviderComponents(provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) + // GenerateProvider returns the provider components for a given provider with options including targetNamespace. + GenerateProvider(provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) + // Init initializes a management cluster by adding the requested list of providers. Init(options InitOptions) ([]Components, error) diff --git a/cmd/clusterctl/client/client_test.go b/cmd/clusterctl/client/client_test.go index fad51bcd1b1f..6407986b7385 100644 --- a/cmd/clusterctl/client/client_test.go +++ b/cmd/clusterctl/client/client_test.go @@ -80,6 +80,10 @@ func (f fakeClient) GetProviderComponents(provider string, providerType clusterc return f.internalClient.GetProviderComponents(provider, providerType, options) } +func (f fakeClient) GenerateProvider(provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) { + return f.internalClient.GenerateProvider(provider, providerType, options) +} + func (f fakeClient) GetClusterTemplate(options GetClusterTemplateOptions) (Template, error) { return f.internalClient.GetClusterTemplate(options) } diff --git a/cmd/clusterctl/client/generate_provider.go b/cmd/clusterctl/client/generate_provider.go new file mode 100644 index 000000000000..b9f24f8d8a31 --- /dev/null +++ b/cmd/clusterctl/client/generate_provider.go @@ -0,0 +1,67 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/version" + + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" +) + +func (c *clusterctlClient) GenerateProvider(provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) { + providerName, providerVersion, err := parseProviderName(provider) + if err != nil { + return nil, err + } + + configRepository, err := c.configClient.Providers().Get(providerName, providerType) + if err != nil { + return nil, err + } + + providerRepositoryClient, err := c.repositoryClientFactory(RepositoryClientFactoryInput{Provider: configRepository}) + if err != nil { + return nil, err + } + + if providerVersion == "" { + providerVersion = providerRepositoryClient.DefaultVersion() + } + + latestMetadata, err := providerRepositoryClient.Metadata(providerVersion).Get() + if err != nil { + return nil, err + } + + currentVersion, err := version.ParseSemantic(providerVersion) + if err != nil { + return nil, err + } + + releaseSeries := latestMetadata.GetReleaseSeriesForVersion(currentVersion) + if releaseSeries == nil { + return nil, errors.Errorf("invalid provider metadata: version %s for the provider %s does not match any release series", providerVersion, providerName) + } + + if releaseSeries.Contract != clusterv1.GroupVersion.Version { + return nil, errors.Errorf("current version of clusterctl is only compatible with %s providers, detected %s for provider %s", clusterv1.GroupVersion.Version, releaseSeries.Contract, providerName) + } + + return c.GetProviderComponents(provider, providerType, options) +} diff --git a/cmd/clusterctl/client/repository/client.go b/cmd/clusterctl/client/repository/client.go index 881a2386f8ca..d3de7ebbcf18 100644 --- a/cmd/clusterctl/client/repository/client.go +++ b/cmd/clusterctl/client/repository/client.go @@ -33,6 +33,11 @@ import ( type Client interface { config.Provider + // DefaultVersion returns the default provider version returned by a repository. + // In case the repository URL points to latest, this method returns the current latest version; in other cases + // it returns the version of the provider hosted in the repository. + DefaultVersion() string + // GetVersions return the list of versions that are available in a provider repository GetVersions() ([]string, error) @@ -62,6 +67,10 @@ type repositoryClient struct { // ensure repositoryClient implements Client. var _ Client = &repositoryClient{} +func (c *repositoryClient) DefaultVersion() string { + return c.repository.DefaultVersion() +} + func (c *repositoryClient) GetVersions() ([]string, error) { return c.repository.GetVersions() } diff --git a/cmd/clusterctl/cmd/generate_provider.go b/cmd/clusterctl/cmd/generate_provider.go index 930301bad227..d2919cc59aba 100644 --- a/cmd/clusterctl/cmd/generate_provider.go +++ b/cmd/clusterctl/cmd/generate_provider.go @@ -107,7 +107,7 @@ func runGenerateProviderComponents() error { SkipTemplateProcess: gpo.raw || gpo.textOutput, } - components, err := c.GetProviderComponents(providerName, providerType, options) + components, err := c.GenerateProvider(providerName, providerType, options) if err != nil { return err }