github.com/openshift/installer@v1.4.17/pkg/asset/manifests/cloudproviderconfig.go (about)

     1  package manifests
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"path/filepath"
     8  
     9  	"github.com/pkg/errors"
    10  	corev1 "k8s.io/api/core/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"sigs.k8s.io/yaml"
    13  
    14  	"github.com/openshift/api/features"
    15  	"github.com/openshift/installer/pkg/asset"
    16  	"github.com/openshift/installer/pkg/asset/installconfig"
    17  	ibmcloudmachines "github.com/openshift/installer/pkg/asset/machines/ibmcloud"
    18  	"github.com/openshift/installer/pkg/asset/manifests/azure"
    19  	"github.com/openshift/installer/pkg/asset/manifests/capiutils"
    20  	gcpmanifests "github.com/openshift/installer/pkg/asset/manifests/gcp"
    21  	ibmcloudmanifests "github.com/openshift/installer/pkg/asset/manifests/ibmcloud"
    22  	nutanixmanifests "github.com/openshift/installer/pkg/asset/manifests/nutanix"
    23  	openstackmanifests "github.com/openshift/installer/pkg/asset/manifests/openstack"
    24  	powervsmanifests "github.com/openshift/installer/pkg/asset/manifests/powervs"
    25  	vspheremanifests "github.com/openshift/installer/pkg/asset/manifests/vsphere"
    26  	awstypes "github.com/openshift/installer/pkg/types/aws"
    27  	azuretypes "github.com/openshift/installer/pkg/types/azure"
    28  	baremetaltypes "github.com/openshift/installer/pkg/types/baremetal"
    29  	externaltypes "github.com/openshift/installer/pkg/types/external"
    30  	gcptypes "github.com/openshift/installer/pkg/types/gcp"
    31  	ibmcloudtypes "github.com/openshift/installer/pkg/types/ibmcloud"
    32  	nonetypes "github.com/openshift/installer/pkg/types/none"
    33  	nutanixtypes "github.com/openshift/installer/pkg/types/nutanix"
    34  	openstacktypes "github.com/openshift/installer/pkg/types/openstack"
    35  	ovirttypes "github.com/openshift/installer/pkg/types/ovirt"
    36  	powervstypes "github.com/openshift/installer/pkg/types/powervs"
    37  	vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
    38  )
    39  
    40  var (
    41  	cloudProviderConfigFileName = filepath.Join(manifestDir, "cloud-provider-config.yaml")
    42  )
    43  
    44  const (
    45  	cloudProviderConfigDataKey         = "config"
    46  	cloudProviderConfigCABundleDataKey = "ca-bundle.pem"
    47  	cloudProviderEndpointsKey          = "endpoints"
    48  )
    49  
    50  // CloudProviderConfig generates the cloud-provider-config.yaml files.
    51  type CloudProviderConfig struct {
    52  	ConfigMap *corev1.ConfigMap
    53  	File      *asset.File
    54  }
    55  
    56  var _ asset.WritableAsset = (*CloudProviderConfig)(nil)
    57  
    58  // Name returns a human friendly name for the asset.
    59  func (*CloudProviderConfig) Name() string {
    60  	return "Cloud Provider Config"
    61  }
    62  
    63  // Dependencies returns all of the dependencies directly needed to generate
    64  // the asset.
    65  func (*CloudProviderConfig) Dependencies() []asset.Asset {
    66  	return []asset.Asset{
    67  		&installconfig.InstallConfig{},
    68  		&installconfig.ClusterID{},
    69  
    70  		// PlatformCredsCheck just checks the creds (and asks, if needed)
    71  		// We do not actually use it in this asset directly, hence
    72  		// it is put in the dependencies but not fetched in Generate
    73  		&installconfig.PlatformCredsCheck{},
    74  	}
    75  }
    76  
    77  // Generate generates the CloudProviderConfig.
    78  //
    79  //nolint:gocyclo
    80  func (cpc *CloudProviderConfig) Generate(ctx context.Context, dependencies asset.Parents) error {
    81  	installConfig := &installconfig.InstallConfig{}
    82  	clusterID := &installconfig.ClusterID{}
    83  	dependencies.Get(installConfig, clusterID)
    84  
    85  	cm := &corev1.ConfigMap{
    86  		TypeMeta: metav1.TypeMeta{
    87  			APIVersion: corev1.SchemeGroupVersion.String(),
    88  			Kind:       "ConfigMap",
    89  		},
    90  		ObjectMeta: metav1.ObjectMeta{
    91  			Namespace: "openshift-config",
    92  			Name:      "cloud-provider-config",
    93  		},
    94  		Data: map[string]string{},
    95  	}
    96  
    97  	switch installConfig.Config.Platform.Name() {
    98  	case externaltypes.Name, nonetypes.Name, baremetaltypes.Name, ovirttypes.Name:
    99  		return nil
   100  	case awstypes.Name:
   101  		// Store the additional trust bundle in the ca-bundle.pem key if the cluster is being installed on a C2S region.
   102  		trustBundle := installConfig.Config.AdditionalTrustBundle
   103  		if trustBundle != "" && awstypes.IsSecretRegion(installConfig.Config.AWS.Region) {
   104  			cm.Data[cloudProviderConfigCABundleDataKey] = trustBundle
   105  		}
   106  
   107  		// Include a non-empty kube config to appease components--such as the kube-apiserver--that
   108  		// expect there to be a kube config if the cloud-provider-config ConfigMap exists. See
   109  		// https://bugzilla.redhat.com/show_bug.cgi?id=1926975.
   110  		// Note that the newline is required in order to be valid yaml.
   111  		cm.Data[cloudProviderConfigDataKey] = `[Global]
   112  `
   113  	case openstacktypes.Name:
   114  		cloudProviderConfigData, cloudProviderConfigCABundleData, err := openstackmanifests.GenerateCloudProviderConfig(ctx, *installConfig.Config)
   115  		if err != nil {
   116  			return errors.Wrap(err, "failed to generate OpenStack provider config")
   117  		}
   118  		cm.Data[cloudProviderConfigDataKey] = cloudProviderConfigData
   119  		if cloudProviderConfigCABundleData != "" {
   120  			cm.Data[cloudProviderConfigCABundleDataKey] = cloudProviderConfigCABundleData
   121  		}
   122  
   123  	case azuretypes.Name:
   124  		session, err := installConfig.Azure.Session()
   125  		if err != nil {
   126  			return errors.Wrap(err, "could not get azure session")
   127  		}
   128  
   129  		nsg := installConfig.Config.Azure.NetworkSecurityGroupName(clusterID.InfraID)
   130  		nrg := installConfig.Config.Azure.ClusterResourceGroupName(clusterID.InfraID)
   131  		if installConfig.Config.Azure.NetworkResourceGroupName != "" {
   132  			nrg = installConfig.Config.Azure.NetworkResourceGroupName
   133  		}
   134  		vnet := fmt.Sprintf("%s-vnet", clusterID.InfraID)
   135  		if installConfig.Config.Azure.VirtualNetwork != "" {
   136  			vnet = installConfig.Config.Azure.VirtualNetwork
   137  		}
   138  		subnet := fmt.Sprintf("%s-worker-subnet", clusterID.InfraID)
   139  		if installConfig.Config.Azure.ComputeSubnet != "" {
   140  			subnet = installConfig.Config.Azure.ComputeSubnet
   141  		}
   142  		azureConfig, err := azure.CloudProviderConfig{
   143  			CloudName:                installConfig.Config.Azure.CloudName,
   144  			ResourceGroupName:        installConfig.Config.Azure.ClusterResourceGroupName(clusterID.InfraID),
   145  			GroupLocation:            installConfig.Config.Azure.Region,
   146  			ResourcePrefix:           clusterID.InfraID,
   147  			SubscriptionID:           session.Credentials.SubscriptionID,
   148  			TenantID:                 session.Credentials.TenantID,
   149  			NetworkResourceGroupName: nrg,
   150  			NetworkSecurityGroupName: nsg,
   151  			VirtualNetworkName:       vnet,
   152  			SubnetName:               subnet,
   153  			ResourceManagerEndpoint:  installConfig.Config.Azure.ARMEndpoint,
   154  			ARO:                      installConfig.Config.Azure.IsARO(),
   155  		}.JSON()
   156  		if err != nil {
   157  			return errors.Wrap(err, "could not create cloud provider config")
   158  		}
   159  		cm.Data[cloudProviderConfigDataKey] = azureConfig
   160  
   161  		if installConfig.Azure.CloudName == azuretypes.StackCloud {
   162  			b, err := json.Marshal(session.Environment)
   163  			if err != nil {
   164  				return errors.Wrap(err, "could not serialize Azure Stack endpoints")
   165  			}
   166  			cm.Data[cloudProviderEndpointsKey] = string(b)
   167  		}
   168  	case gcptypes.Name:
   169  		subnet := fmt.Sprintf("%s-worker-subnet", clusterID.InfraID)
   170  		if installConfig.Config.GCP.ComputeSubnet != "" {
   171  			subnet = installConfig.Config.GCP.ComputeSubnet
   172  		}
   173  		gcpConfig, err := gcpmanifests.CloudProviderConfig(clusterID.InfraID, installConfig.Config.GCP.ProjectID, subnet, installConfig.Config.GCP.NetworkProjectID)
   174  		if err != nil {
   175  			return errors.Wrap(err, "could not create cloud provider config")
   176  		}
   177  		cm.Data[cloudProviderConfigDataKey] = gcpConfig
   178  	case ibmcloudtypes.Name:
   179  		accountID, err := installConfig.IBMCloud.AccountID(ctx)
   180  		if err != nil {
   181  			return err
   182  		}
   183  
   184  		subnetNames := []string{}
   185  		cpSubnets, err := installConfig.IBMCloud.ControlPlaneSubnets(ctx)
   186  		if err != nil {
   187  			return errors.Wrap(err, "could not retrieve IBM Cloud control plane subnets")
   188  		}
   189  		for _, cpSubnet := range cpSubnets {
   190  			subnetNames = append(subnetNames, cpSubnet.Name)
   191  		}
   192  
   193  		computeSubnets, err := installConfig.IBMCloud.ComputeSubnets(ctx)
   194  		if err != nil {
   195  			return errors.Wrap(err, "could not retrieve IBM Cloud compute subnets")
   196  		}
   197  		for _, computeSubnet := range computeSubnets {
   198  			subnetNames = append(subnetNames, computeSubnet.Name)
   199  		}
   200  
   201  		controlPlane := &ibmcloudtypes.MachinePool{}
   202  		controlPlane.Set(installConfig.Config.Platform.IBMCloud.DefaultMachinePlatform)
   203  		controlPlane.Set(installConfig.Config.ControlPlane.Platform.IBMCloud)
   204  		compute := &ibmcloudtypes.MachinePool{}
   205  		compute.Set(installConfig.Config.Platform.IBMCloud.DefaultMachinePlatform)
   206  		compute.Set(installConfig.Config.WorkerMachinePool().Platform.IBMCloud)
   207  
   208  		if len(controlPlane.Zones) == 0 || len(compute.Zones) == 0 {
   209  			zones, err := ibmcloudmachines.AvailabilityZones(installConfig.Config.IBMCloud.Region, installConfig.Config.Platform.IBMCloud.ServiceEndpoints)
   210  			if err != nil {
   211  				return errors.Wrapf(err, "could not get availability zones for %s", installConfig.Config.IBMCloud.Region)
   212  			}
   213  			if len(controlPlane.Zones) == 0 {
   214  				controlPlane.Zones = zones
   215  			}
   216  			if len(compute.Zones) == 0 {
   217  				compute.Zones = zones
   218  			}
   219  		}
   220  
   221  		ibmcloudConfig, err := ibmcloudmanifests.CloudProviderConfig(
   222  			clusterID.InfraID,
   223  			accountID,
   224  			installConfig.Config.IBMCloud.Region,
   225  			installConfig.Config.Platform.IBMCloud.ClusterResourceGroupName(clusterID.InfraID),
   226  			installConfig.Config.Platform.IBMCloud.GetVPCName(),
   227  			subnetNames,
   228  			controlPlane.Zones,
   229  			compute.Zones,
   230  			installConfig.Config.Platform.IBMCloud.ServiceEndpoints,
   231  		)
   232  		if err != nil {
   233  			return errors.Wrap(err, "could not create cloud provider config")
   234  		}
   235  		cm.Data[cloudProviderConfigDataKey] = ibmcloudConfig
   236  	case powervstypes.Name:
   237  		var (
   238  			accountID, vpcRegion string
   239  			err                  error
   240  		)
   241  
   242  		if accountID, err = installConfig.PowerVS.AccountID(ctx); err != nil {
   243  			return err
   244  		}
   245  
   246  		vpcRegion = installConfig.Config.PowerVS.VPCRegion
   247  		if vpcRegion == "" {
   248  			vpcRegion, err = powervstypes.VPCRegionForPowerVSRegion(installConfig.Config.PowerVS.Region)
   249  		}
   250  		if err != nil {
   251  			return err
   252  		}
   253  
   254  		vpc := installConfig.Config.PowerVS.VPCName
   255  		vpcSubnets := installConfig.Config.PowerVS.VPCSubnets
   256  		if vpc == "" {
   257  			vpc = fmt.Sprintf("vpc-%s", clusterID.InfraID)
   258  		} else {
   259  			existingSubnets, err := installConfig.PowerVS.GetVPCSubnets(ctx, vpc)
   260  			if err != nil {
   261  				return err
   262  			}
   263  
   264  			// cluster-api-provider-ibm requires any existing VPC subnet to be specified in the cluster
   265  			// manifest and as such we need to also specify these in the cloudproviderconfig.
   266  			// @TODO: Deprecate platform.powervs.vpcSubnets?
   267  			for _, subnet := range existingSubnets {
   268  				vpcSubnets = append(vpcSubnets, *subnet.Name)
   269  			}
   270  		}
   271  
   272  		if len(vpcSubnets) == 0 {
   273  			if capiutils.IsEnabled(installConfig) {
   274  				vpcZones, err := powervstypes.AvailableVPCZones(installConfig.Config.PowerVS.Region)
   275  				if err != nil {
   276  					return err
   277  				}
   278  
   279  				// The PowerVS CAPI provider generates three subnets.  One for
   280  				// each of the endpoint.
   281  				// @TODO the provider should export a function which gives us
   282  				// an array
   283  				for _, zone := range vpcZones {
   284  					vpcSubnets = append(vpcSubnets,
   285  						fmt.Sprintf("%s-vpcsubnet-%s", clusterID.InfraID, zone))
   286  				}
   287  			} else {
   288  				vpcSubnets = append(vpcSubnets, fmt.Sprintf("vpc-subnet-%s", clusterID.InfraID))
   289  			}
   290  		}
   291  
   292  		var (
   293  			serviceGUID string
   294  			serviceName string
   295  		)
   296  
   297  		if installConfig.Config.PowerVS.ServiceInstanceGUID == "" {
   298  			serviceName = fmt.Sprintf("%s-power-iaas", clusterID.InfraID)
   299  		} else {
   300  			serviceGUID = installConfig.Config.PowerVS.ServiceInstanceGUID
   301  		}
   302  
   303  		powervsConfig, err := powervsmanifests.CloudProviderConfig(
   304  			clusterID.InfraID,
   305  			accountID,
   306  			vpc,
   307  			vpcRegion,
   308  			installConfig.Config.Platform.PowerVS.PowerVSResourceGroup,
   309  			vpcSubnets,
   310  			serviceGUID,
   311  			serviceName,
   312  			installConfig.Config.PowerVS.Region,
   313  			installConfig.Config.PowerVS.Zone,
   314  		)
   315  		if err != nil {
   316  			return errors.Wrap(err, "could not create cloud provider config")
   317  		}
   318  		cm.Data[cloudProviderConfigDataKey] = powervsConfig
   319  	case vspheretypes.Name:
   320  		var vsphereConfig string
   321  		var err error
   322  		// When we GA multi vcenter, we should only support yaml generation here.
   323  		if installConfig.Config.EnabledFeatureGates().Enabled(features.FeatureGateVSphereMultiVCenters) {
   324  			vsphereConfig, err = vspheremanifests.CloudProviderConfigYaml(clusterID.InfraID, installConfig.Config.Platform.VSphere)
   325  		} else {
   326  			vsphereConfig, err = vspheremanifests.CloudProviderConfigIni(clusterID.InfraID, installConfig.Config.Platform.VSphere)
   327  		}
   328  
   329  		if err != nil {
   330  			return errors.Wrap(err, "could not create cloud provider config")
   331  		}
   332  		cm.Data[cloudProviderConfigDataKey] = vsphereConfig
   333  	case nutanixtypes.Name:
   334  		configJSON, err := nutanixmanifests.CloudConfigJSON(installConfig.Config.Nutanix)
   335  		if err != nil {
   336  			return errors.Wrap(err, "could not create Nutanix Cloud provider config")
   337  		}
   338  		cm.Data[cloudProviderConfigDataKey] = configJSON
   339  	default:
   340  		return errors.New("invalid Platform")
   341  	}
   342  
   343  	cmData, err := yaml.Marshal(cm)
   344  	if err != nil {
   345  		return errors.Wrapf(err, "failed to create %s manifest", cpc.Name())
   346  	}
   347  	cpc.ConfigMap = cm
   348  	cpc.File = &asset.File{
   349  		Filename: cloudProviderConfigFileName,
   350  		Data:     cmData,
   351  	}
   352  	return nil
   353  }
   354  
   355  // Files returns the files generated by the asset.
   356  func (cpc *CloudProviderConfig) Files() []*asset.File {
   357  	if cpc.File != nil {
   358  		return []*asset.File{cpc.File}
   359  	}
   360  	return []*asset.File{}
   361  }
   362  
   363  // Load loads the already-rendered files back from disk.
   364  func (cpc *CloudProviderConfig) Load(f asset.FileFetcher) (bool, error) {
   365  	return false, nil
   366  }