Skip to content

⚠️ Clusterctlv2 add interfaces for the low-level Repository library #1842

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

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
153 changes: 153 additions & 0 deletions cmd/clusterctl/pkg/client/repository/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
Copyright 2019 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 repository

import (
"net/url"

"github.com/pkg/errors"
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/internal/test"
)

// Client is used to interact with provider repositories.
// Provider repository are expected to contain two types of YAML files:
// - YAML files defining the provider components (CRD, Controller, RBAC etc.)
// - YAML files defining the cluster templates (Cluster, Machines)
type Client interface {
config.Provider

// Components provide access to YAML file for creating provider components.
Components() ComponentsClient

// Templates provide access to YAML file for generating workload cluster templates.
// Please note that templates are expected to exist for the infrastructure providers only.
Templates(version string) TemplatesClient
}

// repositoryClient implements Client.
type repositoryClient struct {
config.Provider
configVariablesClient config.VariablesClient
repository Repository
}

// ensure repositoryClient implements Client.
var _ Client = &repositoryClient{}

func (c *repositoryClient) Components() ComponentsClient {
return newComponentsClient(c.Provider, c.repository, c.configVariablesClient)
}

func (c *repositoryClient) Templates(version string) TemplatesClient {
return newTemplatesClient(c.Provider, version, c.repository, c.configVariablesClient)
}

// NewOptions carries the options supported by New
type NewOptions struct {
injectRepository Repository
}

// Option is a configuration option supplied to New
type Option func(*NewOptions)

// InjectRepository allows to override the repository implementation to use;
// by default, the repository implementation to use is created according to the
// repository URL.
func InjectRepository(repository Repository) Option {
return func(c *NewOptions) {
c.injectRepository = repository
}
}

// New returns a Client.
func New(provider config.Provider, configVariablesClient config.VariablesClient, options Options) (Client, error) {
return newRepositoryClient(provider, configVariablesClient, options)
}

func newRepositoryClient(provider config.Provider, configVariablesClient config.VariablesClient, options Options) (*repositoryClient, error) {
repository := options.InjectRepository
if repository == nil {
r, err := repositoryFactory(provider, configVariablesClient)
if err != nil {
return nil, errors.Wrapf(err, "failed to get repository client for %q", provider.Name())
}
repository = r
}

return &repositoryClient{
Provider: provider,
repository: repository,
configVariablesClient: configVariablesClient,
}, nil
}

// Options allow to set Client options
type Options struct {
InjectRepository Repository
}

// Repository defines the behavior of a repository implementation.
// clusterctl is designed to support different repository types; each repository implementation should consider
// the following capabilities and provide best support considering the underlying technology.
// 1. Versions awareness: repositories are expected to be aware of the provider version they are hosting, and
// possibly to host more than one version.
// 2. Kustomize awareness: even if it is recommended that provider expose “pre-compiled” YAML files, we want
// to allow usage of clusterctl reading from the “raw” /config directory generated by kubebuilder (developer friendly)
type Repository interface {
// 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

// RootPath returns the path inside the repository where the YAML file for creating provider components and
// the YAML file for generating workload cluster templates are stored.
// This value is derived from the repository URL; all the paths returned by this interface should be relative to this path.
RootPath() string

// ComponentsPath return the path (a folder name or file name) of the YAML file for creating provider components.
// This value is derived from the repository URL.
ComponentsPath() string

// KustomizeDir returns the path to a folder containing the kustomization.yaml for creating the YAML file provider components.
// This value is derived from the repository URL, and it is used only when the YAML for provider components is spread
// across nested folders inside the repository like e.g. the /config folder generated by kubebuilder.
KustomizeDir() string

// GetFiles returns files for a given provider version.
// If file is a path, the entire content of the path is returned.
GetFiles(version string, path string) (map[string][]byte, error)
}

var _ Repository = &test.FakeRepository{}

//repositoryFactory returns the repository implementation corresponding to the provider URL.
func repositoryFactory(providerConfig config.Provider, configVariablesClient config.VariablesClient) (Repository, error) { //nolint
// parse the repository url
rURL, err := url.Parse(providerConfig.URL())
if err != nil {
return nil, errors.Errorf("failed to parse repository url %q", providerConfig.URL())
}

// if the url is a github repository
//TODO: implement in a follow up PR

// if the url is a local repository
//TODO: implement in a follow up PR

return nil, errors.Errorf("invalid provider url. there are no provider implementation for %q schema", rURL.Scheme)
}
66 changes: 66 additions & 0 deletions cmd/clusterctl/pkg/client/repository/components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2019 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 repository

import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

// Components wraps a YAML file that defines the provider components
// to be installed in a management cluster (CRD, Controller, RBAC etc.)
// It is important to notice that clusterctl applies a set of processing steps to the “raw” component read
// from the provider repositories:
// 1. In case the provider exposed a component YAML that is not “pre-compiled” (e.g. the /config dir), it uses
// kustomize to create a single component YAML file
// 2. Checks for all the variables in the component YAML file and replace with corresponding config values
// 3. Ensures all the provider components are deployed in the target namespace (apply only to namespaced objects)
// 4. Ensures 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
type Components interface {
// configuration of the provider the template belongs to.
config.Provider

// Version of the provider.
Version() string

// Variables required by the template.
// This value is derived by the component YAML.
Variables() []string

// TargetNamespace where the provider components will be installed.
// By default this value is derived by the component YAML, but it is possible to override it
// during the creation of the Components object.
TargetNamespace() string

// WatchingNamespace defines the namespace where the provider controller is is watching (empty means all namespaces).
// By default this value is derived by the component YAML, but it is possible to override it
// during the creation of the Components object.
WatchingNamespace() string

// Metadata returns the clusterctl metadata object representing the provider that will be
// generated by this provider components.
Metadata() clusterctlv1.Provider

// Yaml return the provider components in the form of a YAML file.
Yaml() ([]byte, error)

// Objs return the provider components in the form of a list of Unstructured objects.
Objs() []unstructured.Unstructured
}
51 changes: 51 additions & 0 deletions cmd/clusterctl/pkg/client/repository/components_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2019 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 repository

import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

// 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 {
Get(version, targetNamespace, watchingNamespace string) (Components, error)
}

// componentsClient implements ComponentsClient.
type componentsClient struct {
provider config.Provider
repository Repository
configVariablesClient config.VariablesClient
}

// ensure componentsClient implements ComponentsClient.
var _ ComponentsClient = &componentsClient{}

// newComponentsClient returns a componentsClient.
func newComponentsClient(provider config.Provider, repository Repository, configVariablesClient config.VariablesClient) *componentsClient {
return &componentsClient{
provider: provider,
repository: repository,
configVariablesClient: configVariablesClient,
}
}

func (f *componentsClient) Get(version, targetNamespace, watchingNamespace string) (Components, error) {
//TODO: implement in a follow up PR
return nil, nil
}
53 changes: 53 additions & 0 deletions cmd/clusterctl/pkg/client/repository/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2019 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 repository

import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

// Template wraps a YAML file that defines the cluster objects (Cluster, Machines etc.).
// It is important to notice that clusterctl applies a set of processing steps to the “raw” cluster template YAML read
// from the provider repositories:
// 1. Checks for all the variables in the cluster template YAML file and replace with corresponding config values
// 2. Process go templates contained in the cluster template YAML file
// 3. Ensure all the cluster objects are deployed in the target namespace
type Template interface {
// configuration of the provider the template belongs to.
config.Provider

// Version of the provider the template belongs to.
Version() string

// Flavor implemented by the template (empty means default flavor).
// A flavor is a variant of cluster template supported by the provider, like e.g. Prod, Test.
Flavor() string

// Bootstrap provider used by the cluster template.
Bootstrap() string

// Variables required by the template.
// This value is derived by the template YAML.
Variables() []string

// TargetNamespace where the template objects will be installed.
// This value is inherited by the template options.
TargetNamespace() string

// Yaml file defining all the cluster objects.
Yaml() []byte
}
67 changes: 67 additions & 0 deletions cmd/clusterctl/pkg/client/repository/templates_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2019 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 repository

import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

// TemplateOptions defines a set of well-know variables that all the cluster templates are expected to manage;
// this set of variables defines a simple, day1 experience that will be made accessible via flags in the clusterctl CLI.
// Please note that each provider/each template is allowed to add more variables, but additional variables are exposed
// only via environment variables or the clusterctl configuration file.
type TemplateOptions struct {
ClusterName string
Namespace string
KubernetesVersion string
ControlplaneCount int
WorkerCount int
}

// TemplatesClient has methods to work with templatesClient hosted on a provider repository.
// Templates are yaml files to be used for creating a guest cluster.
type TemplatesClient interface {
Get(flavor, bootstrap string, options TemplateOptions) (Template, error)
}

// templatesClient implements TemplatesClient.
type templatesClient struct {
provider config.Provider
version string
repository Repository
configVariablesClient config.VariablesClient
}

// ensure templatesClient implements TemplatesClient.
var _ TemplatesClient = &templatesClient{}

// newTemplatesClient returns a templatesClient.
func newTemplatesClient(provider config.Provider, version string, repository Repository, configVariablesClient config.VariablesClient) *templatesClient {
return &templatesClient{
provider: provider,
version: version,
repository: repository,
configVariablesClient: configVariablesClient,
}
}

// Get return the template for the flavor/bootstrap provider specified.
// In case the template does not exists, an error is returned.
func (f *templatesClient) Get(flavor, bootstrap string, options TemplateOptions) (Template, error) {
//TODO: implement in a follow up PR
return nil, nil
}
Loading