sigs.k8s.io/cluster-api@v1.7.1/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  // IPAM providers.
    96  const (
    97  	InClusterIPAMProviderName = "in-cluster"
    98  )
    99  
   100  // Add-on providers.
   101  const (
   102  	HelmAddonProviderName = "helm"
   103  )
   104  
   105  // Other.
   106  const (
   107  	// ProvidersConfigKey is a constant for finding provider configurations with the ProvidersClient.
   108  	ProvidersConfigKey = "providers"
   109  )
   110  
   111  // ProvidersClient has methods to work with provider configurations.
   112  type ProvidersClient interface {
   113  	// List returns all the provider configurations, including provider configurations hard-coded in clusterctl
   114  	// and user-defined provider configurations read from the clusterctl configuration file.
   115  	// In case of conflict, user-defined provider override the hard-coded configurations.
   116  	List() ([]Provider, error)
   117  
   118  	// Get returns the configuration for the provider with a given name/type.
   119  	// In case the name/type does not correspond to any existing provider, an error is returned.
   120  	Get(name string, providerType clusterctlv1.ProviderType) (Provider, error)
   121  }
   122  
   123  // providersClient implements ProvidersClient.
   124  type providersClient struct {
   125  	reader Reader
   126  }
   127  
   128  // ensure providersClient implements ProvidersClient.
   129  var _ ProvidersClient = &providersClient{}
   130  
   131  func newProvidersClient(reader Reader) *providersClient {
   132  	return &providersClient{
   133  		reader: reader,
   134  	}
   135  }
   136  
   137  func (p *providersClient) defaults() []Provider {
   138  	// clusterctl includes a predefined list of Cluster API providers sponsored by SIG-cluster-lifecycle to provide users the simplest
   139  	// out-of-box experience. This is an opt-in feature; other providers can be added by using the clusterctl configuration file.
   140  
   141  	// if you are a developer of a SIG-cluster-lifecycle project, you can send a PR to extend the following list.
   142  
   143  	defaults := []Provider{
   144  		// cluster API core provider
   145  		&provider{
   146  			name:         ClusterAPIProviderName,
   147  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/core-components.yaml",
   148  			providerType: clusterctlv1.CoreProviderType,
   149  		},
   150  
   151  		// Infrastructure providers
   152  		&provider{
   153  			name:         AWSProviderName,
   154  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/latest/infrastructure-components.yaml",
   155  			providerType: clusterctlv1.InfrastructureProviderType,
   156  		},
   157  		&provider{
   158  			name:         AzureProviderName,
   159  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/latest/infrastructure-components.yaml",
   160  			providerType: clusterctlv1.InfrastructureProviderType,
   161  		},
   162  		&provider{
   163  			// NB. The Docker provider is not designed for production use and is intended for development environments only.
   164  			name:         DockerProviderName,
   165  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/infrastructure-components-development.yaml",
   166  			providerType: clusterctlv1.InfrastructureProviderType,
   167  		},
   168  		&provider{
   169  			name:         CloudStackProviderName,
   170  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-cloudstack/releases/latest/infrastructure-components.yaml",
   171  			providerType: clusterctlv1.InfrastructureProviderType,
   172  		},
   173  		&provider{
   174  			name:         DOProviderName,
   175  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-digitalocean/releases/latest/infrastructure-components.yaml",
   176  			providerType: clusterctlv1.InfrastructureProviderType,
   177  		},
   178  		&provider{
   179  			name:         GCPProviderName,
   180  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-gcp/releases/latest/infrastructure-components.yaml",
   181  			providerType: clusterctlv1.InfrastructureProviderType,
   182  		},
   183  		&provider{
   184  			name:         PacketProviderName,
   185  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-packet/releases/latest/infrastructure-components.yaml",
   186  			providerType: clusterctlv1.InfrastructureProviderType,
   187  		},
   188  		&provider{
   189  			name:         Metal3ProviderName,
   190  			url:          "https://github.com/metal3-io/cluster-api-provider-metal3/releases/latest/infrastructure-components.yaml",
   191  			providerType: clusterctlv1.InfrastructureProviderType,
   192  		},
   193  		&provider{
   194  			name:         NestedProviderName,
   195  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/infrastructure-components.yaml",
   196  			providerType: clusterctlv1.InfrastructureProviderType,
   197  		},
   198  		&provider{
   199  			name:         OCIProviderName,
   200  			url:          "https://github.com/oracle/cluster-api-provider-oci/releases/latest/infrastructure-components.yaml",
   201  			providerType: clusterctlv1.InfrastructureProviderType,
   202  		},
   203  		&provider{
   204  			name:         OpenStackProviderName,
   205  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-openstack/releases/latest/infrastructure-components.yaml",
   206  			providerType: clusterctlv1.InfrastructureProviderType,
   207  		},
   208  		&provider{
   209  			name:         SideroProviderName,
   210  			url:          "https://github.com/siderolabs/sidero/releases/latest/infrastructure-components.yaml",
   211  			providerType: clusterctlv1.InfrastructureProviderType,
   212  		},
   213  		&provider{
   214  			name:         VCloudDirectorProviderName,
   215  			url:          "https://github.com/vmware/cluster-api-provider-cloud-director/releases/latest/infrastructure-components.yaml",
   216  			providerType: clusterctlv1.InfrastructureProviderType,
   217  		},
   218  		&provider{
   219  			name:         VSphereProviderName,
   220  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/releases/latest/infrastructure-components.yaml",
   221  			providerType: clusterctlv1.InfrastructureProviderType,
   222  		},
   223  		&provider{
   224  			name:         MAASProviderName,
   225  			url:          "https://github.com/spectrocloud/cluster-api-provider-maas/releases/latest/infrastructure-components.yaml",
   226  			providerType: clusterctlv1.InfrastructureProviderType,
   227  		},
   228  		&provider{
   229  			name:         CoxEdgeProviderName,
   230  			url:          "https://github.com/coxedge/cluster-api-provider-coxedge/releases/latest/infrastructure-components.yaml",
   231  			providerType: clusterctlv1.InfrastructureProviderType,
   232  		},
   233  		&provider{
   234  			name:         BYOHProviderName,
   235  			url:          "https://github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/releases/latest/infrastructure-components.yaml",
   236  			providerType: clusterctlv1.InfrastructureProviderType,
   237  		},
   238  		&provider{
   239  			name:         HetznerProviderName,
   240  			url:          "https://github.com/syself/cluster-api-provider-hetzner/releases/latest/infrastructure-components.yaml",
   241  			providerType: clusterctlv1.InfrastructureProviderType,
   242  		},
   243  		&provider{
   244  			name:         HivelocityProviderName,
   245  			url:          "https://github.com/hivelocity/cluster-api-provider-hivelocity/releases/latest/infrastructure-components.yaml",
   246  			providerType: clusterctlv1.InfrastructureProviderType,
   247  		},
   248  		&provider{
   249  			name:         OutscaleProviderName,
   250  			url:          "https://github.com/outscale/cluster-api-provider-outscale/releases/latest/infrastructure-components.yaml",
   251  			providerType: clusterctlv1.InfrastructureProviderType,
   252  		},
   253  		&provider{
   254  			name:         IBMCloudProviderName,
   255  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-ibmcloud/releases/latest/infrastructure-components.yaml",
   256  			providerType: clusterctlv1.InfrastructureProviderType,
   257  		},
   258  		&provider{
   259  			name:         InMemoryProviderName,
   260  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/infrastructure-components-in-memory-development.yaml",
   261  			providerType: clusterctlv1.InfrastructureProviderType,
   262  		},
   263  		&provider{
   264  			name:         NutanixProviderName,
   265  			url:          "https://github.com/nutanix-cloud-native/cluster-api-provider-nutanix/releases/latest/infrastructure-components.yaml",
   266  			providerType: clusterctlv1.InfrastructureProviderType,
   267  		},
   268  		&provider{
   269  			name:         KubeKeyProviderName,
   270  			url:          "https://github.com/kubesphere/kubekey/releases/latest/infrastructure-components.yaml",
   271  			providerType: clusterctlv1.InfrastructureProviderType,
   272  		},
   273  		&provider{
   274  			name:         KubevirtProviderName,
   275  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-kubevirt/releases/latest/infrastructure-components.yaml",
   276  			providerType: clusterctlv1.InfrastructureProviderType,
   277  		},
   278  		&provider{
   279  			name:         VclusterProviderName,
   280  			url:          "https://github.com/loft-sh/cluster-api-provider-vcluster/releases/latest/infrastructure-components.yaml",
   281  			providerType: clusterctlv1.InfrastructureProviderType,
   282  		},
   283  		&provider{
   284  			name:         VirtinkProviderName,
   285  			url:          "https://github.com/smartxworks/cluster-api-provider-virtink/releases/latest/infrastructure-components.yaml",
   286  			providerType: clusterctlv1.InfrastructureProviderType,
   287  		},
   288  		&provider{
   289  			name:         ProxmoxProviderName,
   290  			url:          "https://github.com/ionos-cloud/cluster-api-provider-proxmox/releases/latest/infrastructure-components.yaml",
   291  			providerType: clusterctlv1.InfrastructureProviderType,
   292  		},
   293  		&provider{
   294  			name:         K0smotronProviderName,
   295  			url:          "https://github.com/k0sproject/k0smotron/releases/latest/infrastructure-components.yaml",
   296  			providerType: clusterctlv1.InfrastructureProviderType,
   297  		},
   298  
   299  		// Bootstrap providers
   300  		&provider{
   301  			name:         KubeadmBootstrapProviderName,
   302  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/bootstrap-components.yaml",
   303  			providerType: clusterctlv1.BootstrapProviderType,
   304  		},
   305  		&provider{
   306  			name:         KubeKeyK3sBootstrapProviderName,
   307  			url:          "https://github.com/kubesphere/kubekey/releases/latest/bootstrap-components.yaml",
   308  			providerType: clusterctlv1.BootstrapProviderType,
   309  		},
   310  		&provider{
   311  			name:         TalosBootstrapProviderName,
   312  			url:          "https://github.com/siderolabs/cluster-api-bootstrap-provider-talos/releases/latest/bootstrap-components.yaml",
   313  			providerType: clusterctlv1.BootstrapProviderType,
   314  		},
   315  		&provider{
   316  			name:         MicroK8sBootstrapProviderName,
   317  			url:          "https://github.com/canonical/cluster-api-bootstrap-provider-microk8s/releases/latest/bootstrap-components.yaml",
   318  			providerType: clusterctlv1.BootstrapProviderType,
   319  		},
   320  		&provider{
   321  			name:         OracleCloudNativeBootstrapProviderName,
   322  			url:          "https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/bootstrap-components.yaml",
   323  			providerType: clusterctlv1.BootstrapProviderType,
   324  		},
   325  		&provider{
   326  			name:         RKE2BootstrapProviderName,
   327  			url:          "https://github.com/rancher-sandbox/cluster-api-provider-rke2/releases/latest/bootstrap-components.yaml",
   328  			providerType: clusterctlv1.BootstrapProviderType,
   329  		},
   330  		&provider{
   331  			name:         K0smotronBootstrapProviderName,
   332  			url:          "https://github.com/k0sproject/k0smotron/releases/latest/bootstrap-components.yaml",
   333  			providerType: clusterctlv1.BootstrapProviderType,
   334  		},
   335  
   336  		// ControlPlane providers
   337  		&provider{
   338  			name:         KubeadmControlPlaneProviderName,
   339  			url:          "https://github.com/kubernetes-sigs/cluster-api/releases/latest/control-plane-components.yaml",
   340  			providerType: clusterctlv1.ControlPlaneProviderType,
   341  		},
   342  		&provider{
   343  			name:         KubeKeyK3sControlPlaneProviderName,
   344  			url:          "https://github.com/kubesphere/kubekey/releases/latest/control-plane-components.yaml",
   345  			providerType: clusterctlv1.ControlPlaneProviderType,
   346  		},
   347  		&provider{
   348  			name:         TalosControlPlaneProviderName,
   349  			url:          "https://github.com/siderolabs/cluster-api-control-plane-provider-talos/releases/latest/control-plane-components.yaml",
   350  			providerType: clusterctlv1.ControlPlaneProviderType,
   351  		},
   352  		&provider{
   353  			name:         MicroK8sControlPlaneProviderName,
   354  			url:          "https://github.com/canonical/cluster-api-control-plane-provider-microk8s/releases/latest/control-plane-components.yaml",
   355  			providerType: clusterctlv1.ControlPlaneProviderType,
   356  		},
   357  		&provider{
   358  			name:         NestedControlPlaneProviderName,
   359  			url:          "https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/control-plane-components.yaml",
   360  			providerType: clusterctlv1.ControlPlaneProviderType,
   361  		},
   362  		&provider{
   363  			name:         OracleCloudNativeControlPlaneProviderName,
   364  			url:          "https://github.com/verrazzano/cluster-api-provider-ocne/releases/latest/control-plane-components.yaml",
   365  			providerType: clusterctlv1.ControlPlaneProviderType,
   366  		},
   367  		&provider{
   368  			name:         KamajiControlPlaneProviderName,
   369  			url:          "https://github.com/clastix/cluster-api-control-plane-provider-kamaji/releases/latest/control-plane-components.yaml",
   370  			providerType: clusterctlv1.ControlPlaneProviderType,
   371  		},
   372  		&provider{
   373  			name:         RKE2ControlPlaneProviderName,
   374  			url:          "https://github.com/rancher-sandbox/cluster-api-provider-rke2/releases/latest/control-plane-components.yaml",
   375  			providerType: clusterctlv1.ControlPlaneProviderType,
   376  		},
   377  		&provider{
   378  			name:         K0smotronControlPlaneProviderName,
   379  			url:          "https://github.com/k0sproject/k0smotron/releases/latest/control-plane-components.yaml",
   380  			providerType: clusterctlv1.ControlPlaneProviderType,
   381  		},
   382  
   383  		// IPAM providers
   384  		&provider{
   385  			name:         InClusterIPAMProviderName,
   386  			url:          "https://github.com/kubernetes-sigs/cluster-api-ipam-provider-in-cluster/releases/latest/ipam-components.yaml",
   387  			providerType: clusterctlv1.IPAMProviderType,
   388  		},
   389  
   390  		// Add-on providers
   391  		&provider{
   392  			name:         HelmAddonProviderName,
   393  			url:          "https://github.com/kubernetes-sigs/cluster-api-addon-provider-helm/releases/latest/addon-components.yaml",
   394  			providerType: clusterctlv1.AddonProviderType,
   395  		},
   396  	}
   397  
   398  	return defaults
   399  }
   400  
   401  // configProvider mirrors config.Provider interface and allows serialization of the corresponding info.
   402  type configProvider struct {
   403  	Name string                    `json:"name,omitempty"`
   404  	URL  string                    `json:"url,omitempty"`
   405  	Type clusterctlv1.ProviderType `json:"type,omitempty"`
   406  }
   407  
   408  func (p *providersClient) List() ([]Provider, error) {
   409  	// Creates a maps with all the defaults provider configurations
   410  	providers := p.defaults()
   411  
   412  	// Gets user defined provider configurations, validate them, and merges with
   413  	// hard-coded configurations handling conflicts (user defined take precedence on hard-coded)
   414  
   415  	userDefinedProviders := []configProvider{}
   416  	if err := p.reader.UnmarshalKey(ProvidersConfigKey, &userDefinedProviders); err != nil {
   417  		return nil, errors.Wrap(err, "failed to unmarshal providers from the clusterctl configuration file")
   418  	}
   419  
   420  	for _, u := range userDefinedProviders {
   421  		var err error
   422  		u.URL, err = envsubst.Eval(u.URL, os.Getenv)
   423  		if err != nil {
   424  			return nil, errors.Wrapf(err, "unable to evaluate url: %q", u.URL)
   425  		}
   426  
   427  		provider := NewProvider(u.Name, u.URL, u.Type)
   428  		if err := validateProvider(provider); err != nil {
   429  			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())
   430  		}
   431  
   432  		override := false
   433  		for i := range providers {
   434  			if providers[i].SameAs(provider) {
   435  				providers[i] = provider
   436  				override = true
   437  			}
   438  		}
   439  
   440  		if !override {
   441  			providers = append(providers, provider)
   442  		}
   443  	}
   444  
   445  	// ensure provider configurations are consistently sorted
   446  	sort.Slice(providers, func(i, j int) bool {
   447  		return providers[i].Less(providers[j])
   448  	})
   449  
   450  	return providers, nil
   451  }
   452  
   453  func (p *providersClient) Get(name string, providerType clusterctlv1.ProviderType) (Provider, error) {
   454  	l, err := p.List()
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	provider := NewProvider(name, "", providerType) // NB. Having the url empty is fine because the url is not considered by SameAs.
   460  	for _, r := range l {
   461  		if r.SameAs(provider) {
   462  			return r, nil
   463  		}
   464  	}
   465  
   466  	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)
   467  }
   468  
   469  func validateProvider(r Provider) error {
   470  	if r.Name() == "" {
   471  		return errors.New("name value cannot be empty")
   472  	}
   473  
   474  	if (r.Name() == ClusterAPIProviderName) != (r.Type() == clusterctlv1.CoreProviderType) {
   475  		return errors.Errorf("name %s must be used with the %s type (name: %s, type: %s)", ClusterAPIProviderName, clusterctlv1.CoreProviderType, r.Name(), r.Type())
   476  	}
   477  
   478  	if errMsgs := validation.IsDNS1123Subdomain(r.Name()); len(errMsgs) != 0 {
   479  		return errors.Errorf("invalid provider name: %s", strings.Join(errMsgs, "; "))
   480  	}
   481  	if r.URL() == "" {
   482  		return errors.New("provider URL value cannot be empty")
   483  	}
   484  
   485  	if _, err := url.Parse(r.URL()); err != nil {
   486  		return errors.Wrap(err, "error parsing provider URL")
   487  	}
   488  
   489  	switch r.Type() {
   490  	case clusterctlv1.CoreProviderType,
   491  		clusterctlv1.BootstrapProviderType,
   492  		clusterctlv1.InfrastructureProviderType,
   493  		clusterctlv1.ControlPlaneProviderType,
   494  		clusterctlv1.IPAMProviderType,
   495  		clusterctlv1.RuntimeExtensionProviderType,
   496  		clusterctlv1.AddonProviderType:
   497  		break
   498  	default:
   499  		return errors.Errorf("invalid provider type. Allowed values are [%s, %s, %s, %s, %s, %s, %s]",
   500  			clusterctlv1.CoreProviderType,
   501  			clusterctlv1.BootstrapProviderType,
   502  			clusterctlv1.InfrastructureProviderType,
   503  			clusterctlv1.ControlPlaneProviderType,
   504  			clusterctlv1.IPAMProviderType,
   505  			clusterctlv1.RuntimeExtensionProviderType,
   506  			clusterctlv1.AddonProviderType)
   507  	}
   508  	return nil
   509  }