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 }