Skip to content

Commit

Permalink
clusterctl to show config provider without setting env vars
Browse files Browse the repository at this point in the history
changing the code path so we don't have to replace variables
  • Loading branch information
nader-ziada committed Apr 16, 2020
1 parent c96fd59 commit fc90b01
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 16 deletions.
14 changes: 14 additions & 0 deletions cmd/clusterctl/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,17 @@ func (f *fakeComponentClient) Get(version, targetNamespace, watchingNamespace st

return repository.NewComponents(f.provider, version, content, f.configClient, targetNamespace, watchingNamespace)
}

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

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

return repository.NewComponentsWithValues(f.provider, version, content, f.configClient, targetNamespace, watchingNamespace)
}
41 changes: 37 additions & 4 deletions cmd/clusterctl/client/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,55 @@ 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.
// getComponentsByNameAndValue is a utility method that returns components
// for a given provider, targetNamespace, and watchingNamespace.
func (c *clusterctlClient) getComponentsByNameAndValue(provider string, providerType clusterctlv1.ProviderType, targetNamespace string, watchingNamespace string) (repository.Components, error) {

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

// 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;
// during the process, provider components will be processed performing variable substitution, customization of target
// and watching namespace etc.

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

components, err := repository.Components().GetWithValues(version, targetNamespace, watchingNamespace)
if err != nil {
return nil, err
}
return components, nil
}

// 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) {

// 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
}

// 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.

Expand Down
35 changes: 35 additions & 0 deletions cmd/clusterctl/client/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

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 @@ -170,6 +171,40 @@ 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("cluster1", 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)

components, err := client.GetProviderComponents(repository1Config.Name(), repository1Config.Type(), "ns1", "")
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
2 changes: 1 addition & 1 deletion cmd/clusterctl/client/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func (c *clusterctlClient) addToInstaller(options addToInstallerOptions, provide
continue
}

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

// newComponents returns a new objects embedding a component YAML file
// NewComponentsWithValues 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:
Expand All @@ -183,7 +183,7 @@ func (c *components) Yaml() ([]byte, error) {
// 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) {
func NewComponentsWithValues(provider config.Provider, version string, rawyaml []byte, configClient config.Client, targetNamespace, watchingNamespace string) (*components, error) {
// inspect the yaml read from the repository for variables
variables := inspectVariables(rawyaml)

Expand All @@ -193,21 +193,40 @@ func NewComponents(provider config.Provider, version string, rawyaml []byte, con
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)
return processComponentsYaml(provider, version, yaml, variables, configClient, targetNamespace, watchingNamespace)
}

// 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 without replacing the 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.
variables := inspectVariables(rawyaml)
return processComponentsYaml(provider, version, rawyaml, variables, configClient, targetNamespace, watchingNamespace)
}

func processComponentsYaml(provider config.Provider, version string, yaml []byte, variables []string, configClient config.Client, targetNamespace, watchingNamespace string) (*components, error) {
// 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 Down
33 changes: 28 additions & 5 deletions cmd/clusterctl/client/repository/components_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
// ComponentsClient has methods to work with yaml file for generating provider components.
// Assets are yaml files to be used for deploying a provider into a management cluster.
type ComponentsClient interface {
GetWithValues(version, targetNamespace, watchingNamespace string) (Components, error)
Get(version, targetNamespace, watchingNamespace string) (Components, error)
}

Expand All @@ -47,18 +48,41 @@ func newComponentsClient(provider config.Provider, repository Repository, config
}
}

// GetWithValues returns the components from a repository
// and replaces variables with their values
func (f *componentsClient) GetWithValues(version, targetNamespace, watchingNamespace string) (Components, error) {
file, err := f.getComponentYaml(version)
if err != nil {
return nil, err
}

return NewComponentsWithValues(f.provider, version, file, f.configClient, targetNamespace, watchingNamespace)
}

// Get returns the components from a repository without variable replacement
func (f *componentsClient) Get(version, targetNamespace, watchingNamespace string) (Components, error) {

file, err := f.getComponentYaml(version)
if err != nil {
return nil, err
}

return NewComponents(f.provider, version, file, f.configClient, targetNamespace, watchingNamespace)
}

// getComponentYaml util func to get details needed to get components
func (f *componentsClient) getComponentYaml(version string) ([]byte, error) {
log := logf.Log

// if the request does not target a specific version, read from the default repository version that is derived from the repository URL, e.g. latest.
// If the request does not target a specific version, read from the default repository version that is derived from the repository URL, e.g. latest.
if version == "" {
version = f.repository.DefaultVersion()
}

// retrieve the path where the path is stored
// Retrieve the path where the path is stored
path := f.repository.ComponentsPath()

// read the component YAML, reading the local override file if it exists, otherwise read from the provider repository
// Read the component YAML, reading the local override file if it exists, otherwise read from the provider repository
file, err := getLocalOverride(&newOverrideInput{
configVariablesClient: f.configClient.Variables(),
provider: f.provider,
Expand All @@ -78,6 +102,5 @@ func (f *componentsClient) Get(version, targetNamespace, watchingNamespace strin
} else {
log.V(1).Info("Using", "Override", path, "Provider", f.provider.ManifestLabel(), "Version", version)
}

return NewComponents(f.provider, version, file, f.configClient, targetNamespace, watchingNamespace)
return file, nil
}
2 changes: 1 addition & 1 deletion cmd/clusterctl/client/repository/components_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func Test_componentsClient_Get(t *testing.T) {
gs := NewWithT(t)

f := newComponentsClient(tt.fields.provider, tt.fields.repository, configClient)
got, err := f.Get(tt.args.version, tt.args.targetNamespace, tt.args.watchingNamespace)
got, err := f.GetWithValues(tt.args.version, tt.args.targetNamespace, tt.args.watchingNamespace)
if tt.wantErr {
gs.Expect(err).To(HaveOccurred())
return
Expand Down

0 comments on commit fc90b01

Please sign in to comment.