sigs.k8s.io/cluster-api@v1.6.3/cmd/clusterctl/client/config/providers_client.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package config
    18  
    19  import (
    20  	"net/url"
    21  	"os"
    22  	"sort"
    23  	"strings"
    24  
    25  	"github.com/drone/envsubst/v2"
    26  	"github.com/pkg/errors"
    27  	"k8s.io/apimachinery/pkg/util/validation"
    28  
    29  	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
    30  )
    31  
    32  // core providers.
    33  const (
    34  	// ClusterAPIProviderName is the name for the core provider.
    35  	ClusterAPIProviderName = "cluster-api"
    36  )
    37  
    38  // Infra providers.
    39  const (
    40  	AWSProviderName            = "aws"
    41  	AzureProviderName          = "azure"
    42  	BYOHProviderName           = "byoh"
    43  	CloudStackProviderName     = "cloudstack"
    44  	DockerProviderName         = "docker"
    45  	DOProviderName             = "digitalocean"
    46  	GCPProviderName            = "gcp"
    47  	HetznerProviderName        = "hetzner"
    48  	HivelocityProviderName     = "hivelocity-hivelocity"
    49  	OutscaleProviderName       = "outscale"
    50  	IBMCloudProviderName       = "ibmcloud"
    51  	InMemoryProviderName       = "in-memory"
    52  	Metal3ProviderName         = "metal3"
    53  	NestedProviderName         = "nested"
    54  	NutanixProviderName        = "nutanix"
    55  	OCIProviderName            = "oci"
    56  	OpenStackProviderName      = "openstack"
    57  	PacketProviderName         = "packet"
    58  	SideroProviderName         = "sidero"
    59  	VCloudDirectorProviderName = "vcd"
    60  	VSphereProviderName        = "vsphere"
    61  	MAASProviderName           = "maas"
    62  	KubevirtProviderName       = "kubevirt"
    63  	KubeKeyProviderName        = "kubekey"
    64  	VclusterProviderName       = "vcluster"
    65  	VirtinkProviderName        = "virtink"
    66  	CoxEdgeProviderName        = "coxedge"
    67  	ProxmoxProviderName        = "proxmox"
    68  	K0smotronProviderName      = "k0sproject-k0smotron"
    69  )
    70  
    71  // Bootstrap providers.
    72  const (
    73  	KubeadmBootstrapProviderName           = "kubeadm"
    74  	TalosBootstrapProviderName             = "talos"
    75  	MicroK8sBootstrapProviderName          = "microk8s"
    76  	OracleCloudNativeBootstrapProviderName = "ocne"
    77  	KubeKeyK3sBootstrapProviderName        = "kubekey-k3s"
    78  	RKE2BootstrapProviderName              = "rke2"
    79  	K0smotronBootstrapProviderName         = "k0sproject-k0smotron"
    80  )
    81  
    82  // ControlPlane providers.
    83  const (
    84  	KubeadmControlPlaneProviderName           = "kubeadm"
    85  	TalosControlPlaneProviderName             = "talos"
    86  	MicroK8sControlPlaneProviderName          = "microk8s"
    87  	NestedControlPlaneProviderName            = "nested"
    88  	OracleCloudNativeControlPlaneProviderName = "ocne"
    89  	KubeKeyK3sControlPlaneProviderName        = "kubekey-k3s"
    90  	KamajiControlPlaneProviderName            = "kamaji"
    91  	RKE2ControlPlaneProviderName              = "rke2"
    92  	K0smotronControlPlaneProviderName         = "k0sproject-k0smotron"
    93  )
    94  
    95  // Add-on providers.
    96  const (
    97  	HelmAddonProviderName = "helm"
    98  )
    99  
   100  // Other.
   101  const (
   102  	// ProvidersConfigKey is a constant for finding provider configurations with the ProvidersClient.
   103  	ProvidersConfigKey = "providers"
   104  )
   105  
   106  // ProvidersClient has methods to work with provider configurations.
   107  type ProvidersClient interface {
   108  	// List returns all the provider configurations, including provider configurations hard-coded in clusterctl
   109  	// and user-defined provider configurations read from the clusterctl configuration file.
   110  	// In case of conflict, user-defined provider override the hard-coded configurations.
   111  	List() ([]Provider, error)
   112  
   113  	// Get returns the configuration for the provider with a given name/type.
   114  	// In case the name/type does not correspond to any existing provider, an error is returned.
   115  	Get(name string, providerType clusterctlv1.ProviderType) (Provider, error)
   116  }
   117  
   118  // providersClient implements ProvidersClient.
   119  type providersClient struct {
   120  	reader Reader
   121  }
   122  
   123  // ensure providersClient implements ProvidersClient.
   124  var _ ProvidersClient = &providersClient{}
   125  
   126  func newProvidersClient(reader Reader) *providersClient {
   127  	return &providersClient{
   128  		reader: reader,
   129  	}
   130  }
   131  
   132  func (p *providersClient) defaults() []Provider {
   133  	// clusterctl includes a predefined list of Cluster API providers sponsored by SIG-cluster-lifecycle to provide users the simplest
   134  	// out-of-box experience. This is an opt-in feature; other providers can be added by using the clusterctl configuration file.
   135  
   136  	// if you are a developer of a SIG-cluster-lifecycle project, you can send a PR to extend the following list.
   137  
   138  	defaults := []Provider{
   139  		// cluster API core provider
   140  		&provider{
   141  			name:         ClusterAPIProviderName,
   142  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/core-components.yaml",
   143  			providerType: clusterctlv1.CoreProviderType,
   144  		},
   145  
   146  		// Infrastructure providers
   147  		&provider{
   148  			name:         AWSProviderName,
   149  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/latest/infrastructure-components.yaml",
   150  			providerType: clusterctlv1.InfrastructureProviderType,
   151  		},
   152  		&provider{
   153  			name:         AzureProviderName,
   154  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/latest/infrastructure-components.yaml",
   155  			providerType: clusterctlv1.InfrastructureProviderType,
   156  		},
   157  		&provider{
   158  			// NB. The Docker provider is not designed for production use and is intended for development environments only.
   159  			name:         DockerProviderName,
   160  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/infrastructure-components-development.yaml",
   161  			providerType: clusterctlv1.InfrastructureProviderType,
   162  		},
   163  		&provider{
   164  			name:         CloudStackProviderName,
   165  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-cloudstack/releases/latest/infrastructure-components.yaml",
   166  			providerType: clusterctlv1.InfrastructureProviderType,
   167  		},
   168  		&provider{
   169  			name:         DOProviderName,
   170  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-digitalocean/releases/latest/infrastructure-components.yaml",
   171  			providerType: clusterctlv1.InfrastructureProviderType,
   172  		},
   173  		&provider{
   174  			name:         GCPProviderName,
   175  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-gcp/releases/latest/infrastructure-components.yaml",
   176  			providerType: clusterctlv1.InfrastructureProviderType,
   177  		},
   178  		&provider{
   179  			name:         PacketProviderName,
   180  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-packet/releases/latest/infrastructure-components.yaml",
   181  			providerType: clusterctlv1.InfrastructureProviderType,
   182  		},
   183  		&provider{
   184  			name:         Metal3ProviderName,
   185  			url:          "https://github.com/metal3-io/cluster-api-provider-metal3/releases/latest/infrastructure-components.yaml",
   186  			providerType: clusterctlv1.InfrastructureProviderType,
   187  		},
   188  		&provider{
   189  			name:         NestedProviderName,
   190  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/infrastructure-components.yaml",
   191  			providerType: clusterctlv1.InfrastructureProviderType,
   192  		},
   193  		&provider{
   194  			name:         OCIProviderName,
   195  			url:          "https://github.com/oracle/cluster-api-provider-oci/releases/latest/infrastructure-components.yaml",
   196  			providerType: clusterctlv1.InfrastructureProviderType,
   197  		},
   198  		&provider{
   199  			name:         OpenStackProviderName,
   200  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-openstack/releases/latest/infrastructure-components.yaml",
   201  			providerType: clusterctlv1.InfrastructureProviderType,
   202  		},
   203  		&provider{
   204  			name:         SideroProviderName,
   205  			url:          "https://github.com/siderolabs/sidero/releases/latest/infrastructure-components.yaml",
   206  			providerType: clusterctlv1.InfrastructureProviderType,
   207  		},
   208  		&provider{
   209  			name:         VCloudDirectorProviderName,
   210  			url:          "https://github.com/vmware/cluster-api-provider-cloud-director/releases/latest/infrastructure-components.yaml",
   211  			providerType: clusterctlv1.InfrastructureProviderType,
   212  		},
   213  		&provider{
   214  			name:         VSphereProviderName,
   215  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/latest/infrastructure-components.yaml",
   216  			providerType: clusterctlv1.InfrastructureProviderType,
   217  		},
   218  		&provider{
   219  			name:         MAASProviderName,
   220  			url:          "https://github.com/spectrocloud/cluster-api-provider-maas/releases/latest/infrastructure-components.yaml",
   221  			providerType: clusterctlv1.InfrastructureProviderType,
   222  		},
   223  		&provider{
   224  			name:         CoxEdgeProviderName,
   225  			url:          "https://github.com/coxedge/cluster-api-provider-coxedge/releases/latest/infrastructure-components.yaml",
   226  			providerType: clusterctlv1.InfrastructureProviderType,
   227  		},
   228  		&provider{
   229  			name:         BYOHProviderName,
   230  			url:          "https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/latest/infrastructure-components.yaml",
   231  			providerType: clusterctlv1.InfrastructureProviderType,
   232  		},
   233  		&provider{
   234  			name:         HetznerProviderName,
   235  			url:          "https://github.com/syself/cluster-api-provider-hetzner/releases/latest/infrastructure-components.yaml",
   236  			providerType: clusterctlv1.InfrastructureProviderType,
   237  		},
   238  		&provider{
   239  			name:         HivelocityProviderName,
   240  			url:          "https://github.com/hivelocity/cluster-api-provider-hivelocity/releases/latest/infrastructure-components.yaml",
   241  			providerType: clusterctlv1.InfrastructureProviderType,
   242  		},
   243  		&provider{
   244  			name:         OutscaleProviderName,
   245  			url:          "https://github.com/outscale/cluster-api-provider-outscale/releases/latest/infrastructure-components.yaml",
   246  			providerType: clusterctlv1.InfrastructureProviderType,
   247  		},
   248  		&provider{
   249  			name:         IBMCloudProviderName,
   250  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-ibmcloud/releases/latest/infrastructure-components.yaml",
   251  			providerType: clusterctlv1.InfrastructureProviderType,
   252  		},
   253  		&provider{
   254  			name:         InMemoryProviderName,
   255  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/infrastructure-components-in-memory-development.yaml",
   256  			providerType: clusterctlv1.InfrastructureProviderType,
   257  		},
   258  		&provider{
   259  			name:         NutanixProviderName,
   260  			url:          "https://github.com/nutanix-cloud-native/cluster-api-provider-nutanix/releases/latest/infrastructure-components.yaml",
   261  			providerType: clusterctlv1.InfrastructureProviderType,
   262  		},
   263  		&provider{
   264  			name:         KubeKeyProviderName,
   265  			url:          "https://github.com/kubesphere/kubekey/releases/latest/infrastructure-components.yaml",
   266  			providerType: clusterctlv1.InfrastructureProviderType,
   267  		},
   268  		&provider{
   269  			name:         KubevirtProviderName,
   270  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-kubevirt/releases/latest/infrastructure-components.yaml",
   271  			providerType: clusterctlv1.InfrastructureProviderType,
   272  		},
   273  		&provider{
   274  			name:         VclusterProviderName,
   275  			url:          "https://github.com/loft-sh/cluster-api-provider-vcluster/releases/latest/infrastructure-components.yaml",
   276  			providerType: clusterctlv1.InfrastructureProviderType,
   277  		},
   278  		&provider{
   279  			name:         VirtinkProviderName,
   280  			url:          "https://github.com/smartxworks/cluster-api-provider-virtink/releases/latest/infrastructure-components.yaml",
   281  			providerType: clusterctlv1.InfrastructureProviderType,
   282  		},
   283  		&provider{
   284  			name:         ProxmoxProviderName,
   285  			url:          "https://github.com/ionos-cloud/cluster-api-provider-proxmox/releases/latest/infrastructure-components.yaml",
   286  			providerType: clusterctlv1.InfrastructureProviderType,
   287  		},
   288  		&provider{
   289  			name:         K0smotronProviderName,
   290  			url:          "https://github.com/k0sproject/k0smotron/releases/latest/infrastructure-components.yaml",
   291  			providerType: clusterctlv1.InfrastructureProviderType,
   292  		},
   293  
   294  		// Bootstrap providers
   295  		&provider{
   296  			name:         KubeadmBootstrapProviderName,
   297  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/bootstrap-components.yaml",
   298  			providerType: clusterctlv1.BootstrapProviderType,
   299  		},
   300  		&provider{
   301  			name:         KubeKeyK3sBootstrapProviderName,
   302  			url:          "https://github.com/kubesphere/kubekey/releases/latest/bootstrap-components.yaml",
   303  			providerType: clusterctlv1.BootstrapProviderType,
   304  		},
   305  		&provider{
   306  			name:         TalosBootstrapProviderName,
   307  			url:          "https://github.com/siderolabs/cluster-api-bootstrap-provider-talos/releases/latest/bootstrap-components.yaml",
   308  			providerType: clusterctlv1.BootstrapProviderType,
   309  		},
   310  		&provider{
   311  			name:         MicroK8sBootstrapProviderName,
   312  			url:          "https://github.com/canonical/cluster-api-bootstrap-provider-microk8s/releases/latest/bootstrap-components.yaml",
   313  			providerType: clusterctlv1.BootstrapProviderType,
   314  		},
   315  		&provider{
   316  			name:         OracleCloudNativeBootstrapProviderName,
   317  			url:          "https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/bootstrap-components.yaml",
   318  			providerType: clusterctlv1.BootstrapProviderType,
   319  		},
   320  		&provider{
   321  			name:         RKE2BootstrapProviderName,
   322  			url:          "https://github.com/rancher-sandbox/cluster-api-provider-rke2/releases/latest/bootstrap-components.yaml",
   323  			providerType: clusterctlv1.BootstrapProviderType,
   324  		},
   325  		&provider{
   326  			name:         K0smotronBootstrapProviderName,
   327  			url:          "https://github.com/k0sproject/k0smotron/releases/latest/bootstrap-components.yaml",
   328  			providerType: clusterctlv1.BootstrapProviderType,
   329  		},
   330  
   331  		// ControlPlane providers
   332  		&provider{
   333  			name:         KubeadmControlPlaneProviderName,
   334  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/control-plane-components.yaml",
   335  			providerType: clusterctlv1.ControlPlaneProviderType,
   336  		},
   337  		&provider{
   338  			name:         KubeKeyK3sControlPlaneProviderName,
   339  			url:          "https://github.com/kubesphere/kubekey/releases/latest/control-plane-components.yaml",
   340  			providerType: clusterctlv1.ControlPlaneProviderType,
   341  		},
   342  		&provider{
   343  			name:         TalosControlPlaneProviderName,
   344  			url:          "https://github.com/siderolabs/cluster-api-control-plane-provider-talos/releases/latest/control-plane-components.yaml",
   345  			providerType: clusterctlv1.ControlPlaneProviderType,
   346  		},
   347  		&provider{
   348  			name:         MicroK8sControlPlaneProviderName,
   349  			url:          "https://github.com/canonical/cluster-api-control-plane-provider-microk8s/releases/latest/control-plane-components.yaml",
   350  			providerType: clusterctlv1.ControlPlaneProviderType,
   351  		},
   352  		&provider{
   353  			name:         NestedControlPlaneProviderName,
   354  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/control-plane-components.yaml",
   355  			providerType: clusterctlv1.ControlPlaneProviderType,
   356  		},
   357  		&provider{
   358  			name:         OracleCloudNativeControlPlaneProviderName,
   359  			url:          "https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/control-plane-components.yaml",
   360  			providerType: clusterctlv1.ControlPlaneProviderType,
   361  		},
   362  		&provider{
   363  			name:         KamajiControlPlaneProviderName,
   364  			url:          "https://github.com/clastix/cluster-api-control-plane-provider-kamaji/releases/latest/control-plane-components.yaml",
   365  			providerType: clusterctlv1.ControlPlaneProviderType,
   366  		},
   367  		&provider{
   368  			name:         RKE2ControlPlaneProviderName,
   369  			url:          "https://github.com/rancher-sandbox/cluster-api-provider-rke2/releases/latest/control-plane-components.yaml",
   370  			providerType: clusterctlv1.ControlPlaneProviderType,
   371  		},
   372  		&provider{
   373  			name:         K0smotronControlPlaneProviderName,
   374  			url:          "https://github.com/k0sproject/k0smotron/releases/latest/control-plane-components.yaml",
   375  			providerType: clusterctlv1.ControlPlaneProviderType,
   376  		},
   377  
   378  		// Add-on providers
   379  		&provider{
   380  			name:         HelmAddonProviderName,
   381  			url:          "https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/latest/addon-components.yaml",
   382  			providerType: clusterctlv1.AddonProviderType,
   383  		},
   384  	}
   385  
   386  	return defaults
   387  }
   388  
   389  // configProvider mirrors config.Provider interface and allows serialization of the corresponding info.
   390  type configProvider struct {
   391  	Name string                    `json:"name,omitempty"`
   392  	URL  string                    `json:"url,omitempty"`
   393  	Type clusterctlv1.ProviderType `json:"type,omitempty"`
   394  }
   395  
   396  func (p *providersClient) List() ([]Provider, error) {
   397  	// Creates a maps with all the defaults provider configurations
   398  	providers := p.defaults()
   399  
   400  	// Gets user defined provider configurations, validate them, and merges with
   401  	// hard-coded configurations handling conflicts (user defined take precedence on hard-coded)
   402  
   403  	userDefinedProviders := []configProvider{}
   404  	if err := p.reader.UnmarshalKey(ProvidersConfigKey, &userDefinedProviders); err != nil {
   405  		return nil, errors.Wrap(err, "failed to unmarshal providers from the clusterctl configuration file")
   406  	}
   407  
   408  	for _, u := range userDefinedProviders {
   409  		var err error
   410  		u.URL, err = envsubst.Eval(u.URL, os.Getenv)
   411  		if err != nil {
   412  			return nil, errors.Wrapf(err, "unable to evaluate url: %q", u.URL)
   413  		}
   414  
   415  		provider := NewProvider(u.Name, u.URL, u.Type)
   416  		if err := validateProvider(provider); err != nil {
   417  			return nil, errors.Wrapf(err, "error validating configuration for the %s with name %s. Please fix the providers value in clusterctl configuration file", provider.Type(), provider.Name())
   418  		}
   419  
   420  		override := false
   421  		for i := range providers {
   422  			if providers[i].SameAs(provider) {
   423  				providers[i] = provider
   424  				override = true
   425  			}
   426  		}
   427  
   428  		if !override {
   429  			providers = append(providers, provider)
   430  		}
   431  	}
   432  
   433  	// ensure provider configurations are consistently sorted
   434  	sort.Slice(providers, func(i, j int) bool {
   435  		return providers[i].Less(providers[j])
   436  	})
   437  
   438  	return providers, nil
   439  }
   440  
   441  func (p *providersClient) Get(name string, providerType clusterctlv1.ProviderType) (Provider, error) {
   442  	l, err := p.List()
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  
   447  	provider := NewProvider(name, "", providerType) // NB. Having the url empty is fine because the url is not considered by SameAs.
   448  	for _, r := range l {
   449  		if r.SameAs(provider) {
   450  			return r, nil
   451  		}
   452  	}
   453  
   454  	return nil, errors.Errorf("failed to get configuration for the %s with name %s. Please check the provider name and/or add configuration for new providers using the .clusterctl config file", providerType, name)
   455  }
   456  
   457  func validateProvider(r Provider) error {
   458  	if r.Name() == "" {
   459  		return errors.New("name value cannot be empty")
   460  	}
   461  
   462  	if (r.Name() == ClusterAPIProviderName) != (r.Type() == clusterctlv1.CoreProviderType) {
   463  		return errors.Errorf("name %s must be used with the %s type (name: %s, type: %s)", ClusterAPIProviderName, clusterctlv1.CoreProviderType, r.Name(), r.Type())
   464  	}
   465  
   466  	if errMsgs := validation.IsDNS1123Subdomain(r.Name()); len(errMsgs) != 0 {
   467  		return errors.Errorf("invalid provider name: %s", strings.Join(errMsgs, "; "))
   468  	}
   469  	if r.URL() == "" {
   470  		return errors.New("provider URL value cannot be empty")
   471  	}
   472  
   473  	if _, err := url.Parse(r.URL()); err != nil {
   474  		return errors.Wrap(err, "error parsing provider URL")
   475  	}
   476  
   477  	switch r.Type() {
   478  	case clusterctlv1.CoreProviderType,
   479  		clusterctlv1.BootstrapProviderType,
   480  		clusterctlv1.InfrastructureProviderType,
   481  		clusterctlv1.ControlPlaneProviderType,
   482  		clusterctlv1.IPAMProviderType,
   483  		clusterctlv1.RuntimeExtensionProviderType,
   484  		clusterctlv1.AddonProviderType:
   485  		break
   486  	default:
   487  		return errors.Errorf("invalid provider type. Allowed values are [%s, %s, %s, %s, %s, %s, %s]",
   488  			clusterctlv1.CoreProviderType,
   489  			clusterctlv1.BootstrapProviderType,
   490  			clusterctlv1.InfrastructureProviderType,
   491  			clusterctlv1.ControlPlaneProviderType,
   492  			clusterctlv1.IPAMProviderType,
   493  			clusterctlv1.RuntimeExtensionProviderType,
   494  			clusterctlv1.AddonProviderType)
   495  	}
   496  	return nil
   497  }