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 to show config provider without setting env vars #2905

Merged
merged 1 commit into from
Apr 17, 2020
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
3 changes: 3 additions & 0 deletions cmd/clusterctl/client/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type Provider config.Provider
// Components wraps a YAML file that defines the provider's components (CRDs, controller, RBAC rules etc.).
type Components repository.Components

// ComponentsOptions wraps inputs to get provider's components
type ComponentsOptions repository.ComponentsOptions

// Template wraps a YAML file that defines the cluster objects (Cluster, Machines etc.).
type Template repository.Template

Expand Down
4 changes: 2 additions & 2 deletions cmd/clusterctl/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ type Client interface {
// GetProvidersConfig returns the list of providers configured for this instance of clusterctl.
GetProvidersConfig() ([]Provider, error)

// GetProviderComponents returns the provider components for a given provider, targetNamespace, watchingNamespace.
GetProviderComponents(provider string, providerType clusterctlv1.ProviderType, targetNameSpace, watchingNamespace string) (Components, error)
// GetProviderComponents returns the provider components for a given provider with options including targetNamespace, watchingNamespace.
GetProviderComponents(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)
Expand Down
14 changes: 7 additions & 7 deletions cmd/clusterctl/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ func (f fakeClient) GetProvidersConfig() ([]Provider, error) {
return f.internalClient.GetProvidersConfig()
}

func (f fakeClient) GetProviderComponents(provider string, providerType clusterctlv1.ProviderType, targetNameSpace, watchingNamespace string) (Components, error) {
return f.internalClient.GetProviderComponents(provider, providerType, targetNameSpace, watchingNamespace)
func (f fakeClient) GetProviderComponents(provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) {
return f.internalClient.GetProviderComponents(provider, providerType, options)
}

func (f fakeClient) GetClusterTemplate(options GetClusterTemplateOptions) (Template, error) {
Expand Down Expand Up @@ -443,16 +443,16 @@ type fakeComponentClient struct {
configClient config.Client
}

func (f *fakeComponentClient) Get(version, targetNamespace, watchingNamespace string) (repository.Components, error) {
if version == "" {
version = f.fakeRepository.DefaultVersion()
func (f *fakeComponentClient) Get(options repository.ComponentsOptions) (repository.Components, error) {
if options.Version == "" {
options.Version = f.fakeRepository.DefaultVersion()
}
path := f.fakeRepository.ComponentsPath()

content, err := f.fakeRepository.GetFile(version, path)
content, err := f.fakeRepository.GetFile(options.Version, path)
if err != nil {
return nil, err
}

return repository.NewComponents(f.provider, version, content, f.configClient, targetNamespace, watchingNamespace)
return repository.NewComponents(f.provider, f.configClient, content, options)
}
7 changes: 6 additions & 1 deletion cmd/clusterctl/client/cluster/upgrader.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,12 @@ func (u *providerUpgrader) getUpgradeComponents(provider UpgradeItem) (repositor
return nil, err
}

components, err := providerRepository.Components().Get(provider.NextVersion, provider.Namespace, provider.WatchedNamespace)
options := repository.ComponentsOptions{
Version: provider.NextVersion,
TargetNamespace: provider.Namespace,
WatchingNamespace: provider.WatchedNamespace,
}
components, err := providerRepository.Components().Get(options)
if err != nil {
return nil, err
}
Expand Down
16 changes: 9 additions & 7 deletions cmd/clusterctl/client/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,33 @@ import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
)

// getComponentsByName is a utility method that returns components for a given provider, targetNamespace, and watchingNamespace.
func (c *clusterctlClient) getComponentsByName(provider string, providerType clusterctlv1.ProviderType, targetNamespace string, watchingNamespace string) (repository.Components, error) {
// getComponentsByName is a utility method that returns components
// for a given provider with options including targetNamespace, and watchingNamespace.
func (c *clusterctlClient) getComponentsByName(provider string, providerType clusterctlv1.ProviderType, options repository.ComponentsOptions) (repository.Components, error) {

// parse the abbreviated syntax for name[:version]
// Parse the abbreviated syntax for name[:version]
name, version, err := parseProviderName(provider)
if err != nil {
return nil, err
}
options.Version = version

// gets the provider configuration (that includes the location of the provider repository)
// Gets the provider configuration (that includes the location of the provider repository)
providerConfig, err := c.configClient.Providers().Get(name, providerType)
if err != nil {
return nil, err
}

// get a client for the provider repository and read the provider components;
// Get a client for the provider repository and read the provider components;
// during the process, provider components will be processed performing variable substitution, customization of target
// and watching namespace etc.

repository, err := c.repositoryClientFactory(providerConfig)
repositoryClientFactory, err := c.repositoryClientFactory(providerConfig)
if err != nil {
return nil, err
}

components, err := repository.Components().Get(version, targetNamespace, watchingNamespace)
components, err := repositoryClientFactory.Components().Get(options)
if err != nil {
return nil, err
}
Expand Down
12 changes: 10 additions & 2 deletions cmd/clusterctl/client/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/version"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
)

func (c *clusterctlClient) GetProvidersConfig() ([]Provider, error) {
Expand All @@ -42,8 +43,15 @@ func (c *clusterctlClient) GetProvidersConfig() ([]Provider, error) {
return rr, nil
}

func (c *clusterctlClient) GetProviderComponents(provider string, providerType clusterctlv1.ProviderType, targetNameSpace, watchingNamespace string) (Components, error) {
components, err := c.getComponentsByName(provider, providerType, targetNameSpace, watchingNamespace)
func (c *clusterctlClient) GetProviderComponents(provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) {
// ComponentsOptions is an alias for repository.ComponentsOptions; this makes the conversion
inputOptions := repository.ComponentsOptions{
Version: options.Version,
TargetNamespace: options.TargetNamespace,
WatchingNamespace: options.WatchingNamespace,
SkipVariables: true,
}
components, err := c.getComponentsByName(provider, providerType, inputOptions)
if err != nil {
return nil, err
}
Expand Down
47 changes: 44 additions & 3 deletions cmd/clusterctl/client/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@ import (
"path/filepath"
"testing"

"k8s.io/utils/pointer"

. "github.com/onsi/gomega"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
Expand Down Expand Up @@ -157,7 +156,11 @@ func Test_clusterctlClient_GetProviderComponents(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

got, err := client.GetProviderComponents(tt.args.provider, capiProviderConfig.Type(), tt.args.targetNameSpace, tt.args.watchingNamespace)
options := ComponentsOptions{
TargetNamespace: tt.args.targetNameSpace,
WatchingNamespace: tt.args.watchingNamespace,
}
got, err := client.GetProviderComponents(tt.args.provider, capiProviderConfig.Type(), options)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
Expand All @@ -170,6 +173,44 @@ func Test_clusterctlClient_GetProviderComponents(t *testing.T) {
}
}

func Test_getComponentsByName_withEmptyVariables(t *testing.T) {
g := NewWithT(t)

// Create a fake config with a provider named P1 and a variable named foo.
repository1Config := config.NewProvider("p1", "url", clusterctlv1.InfrastructureProviderType)

config1 := newFakeConfig().
WithProvider(repository1Config)

repository1 := newFakeRepository(repository1Config, config1).
WithPaths("root", "components.yaml").
WithDefaultVersion("v1.0.0").
WithFile("v1.0.0", "components.yaml", componentsYAML("${FOO}")).
WithMetadata("v1.0.0", &clusterctlv1.Metadata{
ReleaseSeries: []clusterctlv1.ReleaseSeries{
{Major: 1, Minor: 0, Contract: "v1alpha3"},
},
})

// Create a fake cluster, eventually adding some existing runtime objects to it.
cluster1 := newFakeCluster(cluster.Kubeconfig{Path: "kubeconfig", Context: "mgmt-context"}, config1).WithObjs()

// Create a new fakeClient that allows to execute tests on the fake config,
// the fake repositories and the fake cluster.
client := newFakeClient(config1).
WithRepository(repository1).
WithCluster(cluster1)

options := ComponentsOptions{
TargetNamespace: "ns1",
WatchingNamespace: "",
}
components, err := client.GetProviderComponents(repository1Config.Name(), repository1Config.Type(), options)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(len(components.Variables())).To(Equal(1))
g.Expect(components.Name()).To(Equal("p1"))
}

func Test_clusterctlClient_templateOptionsToVariables(t *testing.T) {
type args struct {
options GetClusterTemplateOptions
Expand Down
8 changes: 6 additions & 2 deletions cmd/clusterctl/client/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
)

Expand Down Expand Up @@ -231,8 +232,11 @@ func (c *clusterctlClient) addToInstaller(options addToInstallerOptions, provide
}
continue
}

components, err := c.getComponentsByName(provider, providerType, options.targetNamespace, options.watchingNamespace)
componentsOptions := repository.ComponentsOptions{
TargetNamespace: options.targetNamespace,
WatchingNamespace: options.watchingNamespace,
}
components, err := c.getComponentsByName(provider, providerType, componentsOptions)
if err != nil {
return errors.Wrapf(err, "failed to get provider components for the %q provider", provider)
}
Expand Down
63 changes: 38 additions & 25 deletions cmd/clusterctl/client/repository/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,40 +174,53 @@ func (c *components) Yaml() ([]byte, error) {
return util.FromUnstructured(objs)
}

// newComponents returns a new objects embedding a component YAML file
// ComponentsOptions is the inputs needed by the NewComponents
type ComponentsOptions struct {
Version string
TargetNamespace string
WatchingNamespace string
SkipVariables bool
}

// NewComponents returns a new objects embedding a component YAML file
//
// It is important to notice that clusterctl applies a set of processing steps to the “raw” component YAML read
// from the provider repositories:
// 1. Checks for all the variables in the component YAML file and replace with corresponding config values
// 2. Ensure all the provider components are deployed in the target namespace (apply only to namespaced objects)
// 3. Ensure all the ClusterRoleBinding which are referencing namespaced objects have the name prefixed with the namespace name
// 4. Set the watching namespace for the provider controller
// 5. Adds labels to all the components in order to allow easy identification of the provider objects
func NewComponents(provider config.Provider, version string, rawyaml []byte, configClient config.Client, targetNamespace, watchingNamespace string) (*components, error) {
// inspect the yaml read from the repository for variables
// 2. The variables replacement can be skipped using the SkipVariables flag in the input options
// 3. Ensure all the provider components are deployed in the target namespace (apply only to namespaced objects)
// 4. Ensure all the ClusterRoleBinding which are referencing namespaced objects have the name prefixed with the namespace name
// 5. Set the watching namespace for the provider controller
// 6. Adds labels to all the components in order to allow easy identification of the provider objects
func NewComponents(provider config.Provider, configClient config.Client, rawyaml []byte, options ComponentsOptions) (*components, error) {
// Inspect the yaml read from the repository for variables.
variables := inspectVariables(rawyaml)

// Replace variables with corresponding values read from the config
yaml, err := replaceVariables(rawyaml, variables, configClient.Variables())
if err != nil {
return nil, errors.Wrap(err, "failed to perform variable substitution")
yaml := rawyaml
var err error
if !options.SkipVariables {
// Replace variables with corresponding values read from the config
yaml, err = replaceVariables(rawyaml, variables, configClient.Variables())
if err != nil {
return nil, errors.Wrap(err, "failed to perform variable substitution")
}
}

// transform the yaml in a list of objects, so following transformation can work on typed objects (instead of working on a string/slice of bytes)
// Transform the yaml in a list of objects, so following transformation can work on typed objects (instead of working on a string/slice of bytes)
objs, err := util.ToUnstructured(yaml)
if err != nil {
return nil, errors.Wrap(err, "failed to parse yaml")
}

// apply image overrides, if defined
// Apply image overrides, if defined
objs, err = util.FixImages(objs, func(image string) (string, error) {
return configClient.ImageMeta().AlterImage(provider.ManifestLabel(), image)
})
if err != nil {
return nil, errors.Wrap(err, "failed to apply image overrides")
}

// inspect the list of objects for the images required by the provider component
// Inspect the list of objects for the images required by the provider component.
images, err := util.InspectImages(objs)
if err != nil {
return nil, errors.Wrap(err, "failed to detect required images")
Expand All @@ -232,24 +245,24 @@ func NewComponents(provider config.Provider, version string, rawyaml []byte, con
// if targetNamespace is not specified, then defaultTargetNamespace is used. In case both targetNamespace and defaultTargetNamespace
// are empty, an error is returned

if targetNamespace == "" {
targetNamespace = defaultTargetNamespace
if options.TargetNamespace == "" {
options.TargetNamespace = defaultTargetNamespace
}

if targetNamespace == "" {
if options.TargetNamespace == "" {
return nil, errors.New("target namespace can't be defaulted. Please specify a target namespace")
}

// add a Namespace object if missing (ensure the targetNamespace will be created)
instanceObjs = addNamespaceIfMissing(instanceObjs, targetNamespace)
instanceObjs = addNamespaceIfMissing(instanceObjs, options.TargetNamespace)

// fix Namespace name in all the objects
instanceObjs = fixTargetNamespace(instanceObjs, targetNamespace)
instanceObjs = fixTargetNamespace(instanceObjs, options.TargetNamespace)

// ensures all the ClusterRole and ClusterRoleBinding have the name prefixed with the namespace name and that
// all the clusterRole/clusterRoleBinding namespaced subjects refers to targetNamespace
// Nb. Making all the RBAC rules "namespaced" is required for supporting multi-tenancy
instanceObjs, err = fixRBAC(instanceObjs, targetNamespace)
instanceObjs, err = fixRBAC(instanceObjs, options.TargetNamespace)
if err != nil {
return nil, errors.Wrap(err, "failed to fix ClusterRoleBinding names")
}
Expand All @@ -262,8 +275,8 @@ func NewComponents(provider config.Provider, version string, rawyaml []byte, con
}

// if the requested watchingNamespace is different from the defaultWatchingNamespace, fix it
if defaultWatchingNamespace != watchingNamespace {
instanceObjs, err = fixWatchNamespace(instanceObjs, watchingNamespace)
if defaultWatchingNamespace != options.WatchingNamespace {
instanceObjs, err = fixWatchNamespace(instanceObjs, options.WatchingNamespace)
if err != nil {
return nil, errors.Wrap(err, "failed to set watching namespace")
}
Expand All @@ -280,11 +293,11 @@ func NewComponents(provider config.Provider, version string, rawyaml []byte, con

return &components{
Provider: provider,
version: version,
version: options.Version,
variables: variables,
images: images,
targetNamespace: targetNamespace,
watchingNamespace: watchingNamespace,
targetNamespace: options.TargetNamespace,
watchingNamespace: options.WatchingNamespace,
instanceObjs: instanceObjs,
sharedObjs: sharedObjs,
}, nil
Expand Down
Loading