github.com/openshift/installer@v1.4.17/pkg/asset/machines/master.go (about) 1 package machines 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 baremetalhost "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 corev1 "k8s.io/api/core/v1" 14 "k8s.io/apimachinery/pkg/runtime" 15 "k8s.io/apimachinery/pkg/runtime/serializer" 16 ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1" 17 "sigs.k8s.io/yaml" 18 19 configv1 "github.com/openshift/api/config/v1" 20 machinev1 "github.com/openshift/api/machine/v1" 21 machinev1alpha1 "github.com/openshift/api/machine/v1alpha1" 22 machinev1beta1 "github.com/openshift/api/machine/v1beta1" 23 mcfgv1 "github.com/openshift/api/machineconfiguration/v1" 24 baremetalapi "github.com/openshift/cluster-api-provider-baremetal/pkg/apis" 25 baremetalprovider "github.com/openshift/cluster-api-provider-baremetal/pkg/apis/baremetal/v1alpha1" 26 libvirtapi "github.com/openshift/cluster-api-provider-libvirt/pkg/apis" 27 libvirtprovider "github.com/openshift/cluster-api-provider-libvirt/pkg/apis/libvirtproviderconfig/v1beta1" 28 ovirtproviderapi "github.com/openshift/cluster-api-provider-ovirt/pkg/apis" 29 ovirtprovider "github.com/openshift/cluster-api-provider-ovirt/pkg/apis/ovirtprovider/v1beta1" 30 "github.com/openshift/installer/pkg/asset" 31 "github.com/openshift/installer/pkg/asset/ignition/machine" 32 "github.com/openshift/installer/pkg/asset/installconfig" 33 icazure "github.com/openshift/installer/pkg/asset/installconfig/azure" 34 "github.com/openshift/installer/pkg/asset/machines/aws" 35 "github.com/openshift/installer/pkg/asset/machines/azure" 36 "github.com/openshift/installer/pkg/asset/machines/baremetal" 37 "github.com/openshift/installer/pkg/asset/machines/gcp" 38 "github.com/openshift/installer/pkg/asset/machines/ibmcloud" 39 "github.com/openshift/installer/pkg/asset/machines/machineconfig" 40 "github.com/openshift/installer/pkg/asset/machines/nutanix" 41 "github.com/openshift/installer/pkg/asset/machines/openstack" 42 "github.com/openshift/installer/pkg/asset/machines/ovirt" 43 "github.com/openshift/installer/pkg/asset/machines/powervs" 44 "github.com/openshift/installer/pkg/asset/machines/vsphere" 45 "github.com/openshift/installer/pkg/asset/manifests/capiutils" 46 "github.com/openshift/installer/pkg/asset/rhcos" 47 rhcosutils "github.com/openshift/installer/pkg/rhcos" 48 "github.com/openshift/installer/pkg/types" 49 awstypes "github.com/openshift/installer/pkg/types/aws" 50 awsdefaults "github.com/openshift/installer/pkg/types/aws/defaults" 51 azuretypes "github.com/openshift/installer/pkg/types/azure" 52 azuredefaults "github.com/openshift/installer/pkg/types/azure/defaults" 53 baremetaltypes "github.com/openshift/installer/pkg/types/baremetal" 54 externaltypes "github.com/openshift/installer/pkg/types/external" 55 gcptypes "github.com/openshift/installer/pkg/types/gcp" 56 ibmcloudtypes "github.com/openshift/installer/pkg/types/ibmcloud" 57 nonetypes "github.com/openshift/installer/pkg/types/none" 58 nutanixtypes "github.com/openshift/installer/pkg/types/nutanix" 59 openstacktypes "github.com/openshift/installer/pkg/types/openstack" 60 ovirttypes "github.com/openshift/installer/pkg/types/ovirt" 61 powervstypes "github.com/openshift/installer/pkg/types/powervs" 62 vspheretypes "github.com/openshift/installer/pkg/types/vsphere" 63 ibmcloudapi "github.com/openshift/machine-api-provider-ibmcloud/pkg/apis" 64 ibmcloudprovider "github.com/openshift/machine-api-provider-ibmcloud/pkg/apis/ibmcloudprovider/v1" 65 ) 66 67 // Master generates the machines for the `master` machine pool. 68 type Master struct { 69 UserDataFile *asset.File 70 MachineConfigFiles []*asset.File 71 MachineFiles []*asset.File 72 ControlPlaneMachineSet *asset.File 73 IPClaimFiles []*asset.File 74 IPAddrFiles []*asset.File 75 76 // SecretFiles is used by the baremetal platform to register the 77 // credential information for communicating with management 78 // controllers on hosts. 79 SecretFiles []*asset.File 80 81 // NetworkConfigSecretFiles is used by the baremetal platform to 82 // store the networking configuration per host 83 NetworkConfigSecretFiles []*asset.File 84 85 // HostFiles is the list of baremetal hosts provided in the 86 // installer configuration. 87 HostFiles []*asset.File 88 } 89 90 const ( 91 directory = "openshift" 92 93 // secretFileName is the format string for constructing the Secret 94 // filenames for baremetal clusters. 95 secretFileName = "99_openshift-cluster-api_host-bmc-secrets-%s.yaml" 96 97 // networkConfigSecretFileName is the format string for constructing 98 // the networking configuration Secret filenames for baremetal 99 // clusters. 100 networkConfigSecretFileName = "99_openshift-cluster-api_host-network-config-secrets-%s.yaml" 101 102 // hostFileName is the format string for constucting the Host 103 // filenames for baremetal clusters. 104 hostFileName = "99_openshift-cluster-api_hosts-%s.yaml" 105 106 // masterMachineFileName is the format string for constucting the 107 // master Machine filenames. 108 masterMachineFileName = "99_openshift-cluster-api_master-machines-%s.yaml" 109 110 // masterUserDataFileName is the filename used for the master 111 // user-data secret. 112 masterUserDataFileName = "99_openshift-cluster-api_master-user-data-secret.yaml" 113 114 // controlPlaneMachineSetFileName is the filename used for the control plane machine sets. 115 controlPlaneMachineSetFileName = "99_openshift-machine-api_master-control-plane-machine-set.yaml" 116 117 // ipClaimFileName is the filename used for the ip claims list. 118 ipClaimFileName = "99_openshift-machine-api_claim-%s.yaml" 119 120 // ipAddressFileName is the filename used for the ip addresses list. 121 ipAddressFileName = "99_openshift-machine-api_address-%s.yaml" 122 ) 123 124 var ( 125 secretFileNamePattern = fmt.Sprintf(secretFileName, "*") 126 networkConfigSecretFileNamePattern = fmt.Sprintf(networkConfigSecretFileName, "*") 127 hostFileNamePattern = fmt.Sprintf(hostFileName, "*") 128 masterMachineFileNamePattern = fmt.Sprintf(masterMachineFileName, "*") 129 masterIPClaimFileNamePattern = fmt.Sprintf(ipClaimFileName, "*master*") 130 masterIPAddressFileNamePattern = fmt.Sprintf(ipAddressFileName, "*master*") 131 132 _ asset.WritableAsset = (*Master)(nil) 133 ) 134 135 // Name returns a human friendly name for the Master Asset. 136 func (m *Master) Name() string { 137 return "Master Machines" 138 } 139 140 // Dependencies returns all of the dependencies directly needed by the 141 // Master asset 142 func (m *Master) Dependencies() []asset.Asset { 143 return []asset.Asset{ 144 &installconfig.ClusterID{}, 145 // PlatformCredsCheck just checks the creds (and asks, if needed) 146 // We do not actually use it in this asset directly, hence 147 // it is put in the dependencies but not fetched in Generate 148 &installconfig.PlatformCredsCheck{}, 149 &installconfig.InstallConfig{}, 150 new(rhcos.Image), 151 &machine.Master{}, 152 } 153 } 154 155 // Generate generates the Master asset. 156 // 157 //nolint:gocyclo 158 func (m *Master) Generate(ctx context.Context, dependencies asset.Parents) error { 159 clusterID := &installconfig.ClusterID{} 160 installConfig := &installconfig.InstallConfig{} 161 rhcosImage := new(rhcos.Image) 162 mign := &machine.Master{} 163 dependencies.Get(clusterID, installConfig, rhcosImage, mign) 164 165 masterUserDataSecretName := "master-user-data" 166 167 ic := installConfig.Config 168 169 pool := *ic.ControlPlane 170 var err error 171 machines := []machinev1beta1.Machine{} 172 var ipClaims []ipamv1.IPAddressClaim 173 var ipAddrs []ipamv1.IPAddress 174 var controlPlaneMachineSet *machinev1.ControlPlaneMachineSet 175 switch ic.Platform.Name() { 176 case awstypes.Name: 177 subnets := map[string]string{} 178 if len(ic.Platform.AWS.Subnets) > 0 { 179 subnetMeta, err := installConfig.AWS.PrivateSubnets(ctx) 180 if err != nil { 181 return err 182 } 183 for id, subnet := range subnetMeta { 184 subnets[subnet.Zone.Name] = id 185 } 186 } 187 188 mpool := defaultAWSMachinePoolPlatform("master") 189 190 osImage := strings.SplitN(rhcosImage.ControlPlane, ",", 2) 191 osImageID := osImage[0] 192 if len(osImage) == 2 { 193 osImageID = "" // the AMI will be generated later on 194 } 195 mpool.AMIID = osImageID 196 197 mpool.Set(ic.Platform.AWS.DefaultMachinePlatform) 198 mpool.Set(pool.Platform.AWS) 199 zoneDefaults := false 200 if len(mpool.Zones) == 0 { 201 if len(subnets) > 0 { 202 for zone := range subnets { 203 mpool.Zones = append(mpool.Zones, zone) 204 } 205 } else { 206 mpool.Zones, err = installConfig.AWS.AvailabilityZones(ctx) 207 if err != nil { 208 return err 209 } 210 zoneDefaults = true 211 } 212 } 213 214 if mpool.InstanceType == "" { 215 topology := configv1.HighlyAvailableTopologyMode 216 if pool.Replicas != nil && *pool.Replicas == 1 { 217 topology = configv1.SingleReplicaTopologyMode 218 } 219 mpool.InstanceType, err = aws.PreferredInstanceType(ctx, installConfig.AWS, awsdefaults.InstanceTypes(installConfig.Config.Platform.AWS.Region, installConfig.Config.ControlPlane.Architecture, topology), mpool.Zones) 220 if err != nil { 221 logrus.Warn(errors.Wrap(err, "failed to find default instance type")) 222 mpool.InstanceType = awsdefaults.InstanceTypes(installConfig.Config.Platform.AWS.Region, installConfig.Config.ControlPlane.Architecture, topology)[0] 223 } 224 } 225 226 // if the list of zones is the default we need to try to filter the list in case there are some zones where the instance might not be available 227 if zoneDefaults { 228 mpool.Zones, err = aws.FilterZonesBasedOnInstanceType(ctx, installConfig.AWS, mpool.InstanceType, mpool.Zones) 229 if err != nil { 230 logrus.Warn(errors.Wrap(err, "failed to filter zone list")) 231 } 232 } 233 234 pool.Platform.AWS = &mpool 235 machines, controlPlaneMachineSet, err = aws.Machines( 236 clusterID.InfraID, 237 installConfig.Config.Platform.AWS.Region, 238 subnets, 239 &pool, 240 "master", 241 masterUserDataSecretName, 242 installConfig.Config.Platform.AWS.UserTags, 243 ) 244 if err != nil { 245 return errors.Wrap(err, "failed to create master machine objects") 246 } 247 aws.ConfigMasters(machines, controlPlaneMachineSet, clusterID.InfraID, ic.Publish) 248 case gcptypes.Name: 249 mpool := defaultGCPMachinePoolPlatform(pool.Architecture) 250 mpool.Set(ic.Platform.GCP.DefaultMachinePlatform) 251 mpool.Set(pool.Platform.GCP) 252 if len(mpool.Zones) == 0 { 253 azs, err := gcp.ZonesForInstanceType(ic.Platform.GCP.ProjectID, ic.Platform.GCP.Region, mpool.InstanceType) 254 if err != nil { 255 return errors.Wrap(err, "failed to fetch availability zones") 256 } 257 mpool.Zones = azs 258 } 259 pool.Platform.GCP = &mpool 260 machines, controlPlaneMachineSet, err = gcp.Machines(clusterID.InfraID, ic, &pool, rhcosImage.ControlPlane, "master", masterUserDataSecretName) 261 if err != nil { 262 return errors.Wrap(err, "failed to create master machine objects") 263 } 264 err := gcp.ConfigMasters(machines, controlPlaneMachineSet, clusterID.InfraID, ic.Publish) 265 if err != nil { 266 return err 267 } 268 269 // CAPG-based installs will use only backend services--no target pools, 270 // so we don't want to include target pools in the control plane machineset. 271 // TODO(padillon): once this feature gate is the default and we are 272 // no longer using Terraform, we can update ConfigMasters not to populate this. 273 if capiutils.IsEnabled(installConfig) { 274 for _, machine := range machines { 275 providerSpec, ok := machine.Spec.ProviderSpec.Value.Object.(*machinev1beta1.GCPMachineProviderSpec) 276 if !ok { 277 return errors.New("unable to convert ProviderSpec to GCPMachineProviderSpec") 278 } 279 providerSpec.TargetPools = nil 280 } 281 cpms := controlPlaneMachineSet.Spec.Template.OpenShiftMachineV1Beta1Machine.Spec.ProviderSpec.Value.Object 282 providerSpec, ok := cpms.(*machinev1beta1.GCPMachineProviderSpec) 283 if !ok { 284 return errors.New("Unable to set target pools to control plane machine set") 285 } 286 providerSpec.TargetPools = nil 287 } 288 case ibmcloudtypes.Name: 289 subnets := map[string]string{} 290 if len(ic.Platform.IBMCloud.ControlPlaneSubnets) > 0 { 291 subnetMetas, err := installConfig.IBMCloud.ControlPlaneSubnets(ctx) 292 if err != nil { 293 return err 294 } 295 for _, subnet := range subnetMetas { 296 subnets[subnet.Zone] = subnet.Name 297 } 298 } 299 mpool := defaultIBMCloudMachinePoolPlatform() 300 mpool.Set(ic.Platform.IBMCloud.DefaultMachinePlatform) 301 mpool.Set(pool.Platform.IBMCloud) 302 if len(mpool.Zones) == 0 { 303 azs, err := ibmcloud.AvailabilityZones(ic.Platform.IBMCloud.Region, ic.Platform.IBMCloud.ServiceEndpoints) 304 if err != nil { 305 return errors.Wrap(err, "failed to fetch availability zones") 306 } 307 mpool.Zones = azs 308 } 309 pool.Platform.IBMCloud = &mpool 310 machines, err = ibmcloud.Machines(clusterID.InfraID, ic, subnets, &pool, "master", masterUserDataSecretName) 311 if err != nil { 312 return errors.Wrap(err, "failed to create master machine objects") 313 } 314 // TODO: IBM: implement ConfigMasters() if needed 315 // ibmcloud.ConfigMasters(machines, clusterID.InfraID, ic.Publish) 316 case openstacktypes.Name: 317 mpool := defaultOpenStackMachinePoolPlatform() 318 mpool.Set(ic.Platform.OpenStack.DefaultMachinePlatform) 319 mpool.Set(pool.Platform.OpenStack) 320 pool.Platform.OpenStack = &mpool 321 322 imageName, _ := rhcosutils.GenerateOpenStackImageName(rhcosImage.ControlPlane, clusterID.InfraID) 323 324 machines, controlPlaneMachineSet, err = openstack.Machines(ctx, clusterID.InfraID, ic, &pool, imageName, "master", masterUserDataSecretName) 325 if err != nil { 326 return fmt.Errorf("failed to create master machine objects: %w", err) 327 } 328 openstack.ConfigMasters(machines, clusterID.InfraID) 329 case azuretypes.Name: 330 mpool := defaultAzureMachinePoolPlatform() 331 mpool.InstanceType = azuredefaults.ControlPlaneInstanceType( 332 installConfig.Config.Platform.Azure.CloudName, 333 installConfig.Config.Platform.Azure.Region, 334 installConfig.Config.ControlPlane.Architecture, 335 ) 336 mpool.OSDisk.DiskSizeGB = 1024 337 if installConfig.Config.Platform.Azure.CloudName == azuretypes.StackCloud { 338 mpool.OSDisk.DiskSizeGB = azuredefaults.AzurestackMinimumDiskSize 339 } 340 mpool.Set(ic.Platform.Azure.DefaultMachinePlatform) 341 mpool.Set(pool.Platform.Azure) 342 343 session, err := installConfig.Azure.Session() 344 if err != nil { 345 return errors.Wrap(err, "failed to fetch session") 346 } 347 348 client := icazure.NewClient(session) 349 if len(mpool.Zones) == 0 { 350 azs, err := client.GetAvailabilityZones(ctx, ic.Platform.Azure.Region, mpool.InstanceType) 351 if err != nil { 352 return errors.Wrap(err, "failed to fetch availability zones") 353 } 354 mpool.Zones = azs 355 if len(azs) == 0 { 356 // if no azs are given we set to []string{""} for convenience over later operations. 357 // It means no-zoned for the machine API 358 mpool.Zones = []string{""} 359 } 360 } 361 362 if mpool.OSImage.Publisher != "" { 363 img, ierr := client.GetMarketplaceImage(ctx, ic.Platform.Azure.Region, mpool.OSImage.Publisher, mpool.OSImage.Offer, mpool.OSImage.SKU, mpool.OSImage.Version) 364 if ierr != nil { 365 return fmt.Errorf("failed to fetch marketplace image: %w", ierr) 366 } 367 // Publisher is case-sensitive and matched against exactly. Also the 368 // Plan's publisher might not be exactly the same as the Image's 369 // publisher 370 if img.Plan != nil && img.Plan.Publisher != nil { 371 mpool.OSImage.Publisher = *img.Plan.Publisher 372 } 373 } 374 pool.Platform.Azure = &mpool 375 376 capabilities, err := client.GetVMCapabilities(ctx, mpool.InstanceType, installConfig.Config.Platform.Azure.Region) 377 if err != nil { 378 return err 379 } 380 useImageGallery := installConfig.Azure.CloudName != azuretypes.StackCloud 381 machines, controlPlaneMachineSet, err = azure.Machines(clusterID.InfraID, ic, &pool, rhcosImage.ControlPlane, "master", masterUserDataSecretName, capabilities, useImageGallery) 382 if err != nil { 383 return errors.Wrap(err, "failed to create master machine objects") 384 } 385 err = azure.ConfigMasters(machines, controlPlaneMachineSet, clusterID.InfraID) 386 if err != nil { 387 return err 388 } 389 case baremetaltypes.Name: 390 mpool := defaultBareMetalMachinePoolPlatform() 391 mpool.Set(ic.Platform.BareMetal.DefaultMachinePlatform) 392 mpool.Set(pool.Platform.BareMetal) 393 pool.Platform.BareMetal = &mpool 394 395 // Use managed user data secret, since we always have up to date images 396 // available in the cluster 397 masterUserDataSecretName = "master-user-data-managed" 398 enabledCaps := installConfig.Config.GetEnabledCapabilities() 399 if !enabledCaps.Has(configv1.ClusterVersionCapabilityMachineAPI) { 400 break 401 } 402 machines, err = baremetal.Machines(clusterID.InfraID, ic, &pool, "master", masterUserDataSecretName) 403 if err != nil { 404 return errors.Wrap(err, "failed to create master machine objects") 405 } 406 407 hostSettings, err := baremetal.Hosts(ic, machines, masterUserDataSecretName) 408 if err != nil { 409 return errors.Wrap(err, "failed to assemble host data") 410 } 411 412 hosts, err := createHostAssetFiles(hostSettings.Hosts, hostFileName) 413 if err != nil { 414 return err 415 } 416 m.HostFiles = append(m.HostFiles, hosts...) 417 418 secrets, err := createSecretAssetFiles(hostSettings.Secrets, secretFileName) 419 if err != nil { 420 return err 421 } 422 m.SecretFiles = append(m.SecretFiles, secrets...) 423 424 networkSecrets, err := createSecretAssetFiles(hostSettings.NetworkConfigSecrets, networkConfigSecretFileName) 425 if err != nil { 426 return err 427 } 428 m.NetworkConfigSecretFiles = append(m.NetworkConfigSecretFiles, networkSecrets...) 429 430 case ovirttypes.Name: 431 mpool := defaultOvirtMachinePoolPlatform() 432 mpool.VMType = ovirttypes.VMTypeHighPerformance 433 mpool.Set(ic.Platform.Ovirt.DefaultMachinePlatform) 434 mpool.Set(pool.Platform.Ovirt) 435 pool.Platform.Ovirt = &mpool 436 437 imageName, _ := rhcosutils.GenerateOpenStackImageName(rhcosImage.ControlPlane, clusterID.InfraID) 438 439 machines, err = ovirt.Machines(clusterID.InfraID, ic, &pool, imageName, "master", masterUserDataSecretName) 440 if err != nil { 441 return errors.Wrap(err, "failed to create master machine objects for ovirt provider") 442 } 443 case vspheretypes.Name: 444 mpool := defaultVSphereMachinePoolPlatform() 445 mpool.NumCPUs = 4 446 mpool.NumCoresPerSocket = 4 447 mpool.MemoryMiB = 16384 448 mpool.Set(ic.Platform.VSphere.DefaultMachinePlatform) 449 mpool.Set(pool.Platform.VSphere) 450 451 // The machinepool has no zones defined, there are FailureDomains 452 // This is a vSphere zonal installation. Generate machinepool zone 453 // list. 454 455 fdCount := int64(len(ic.Platform.VSphere.FailureDomains)) 456 var idx int64 457 if len(mpool.Zones) == 0 && len(ic.VSphere.FailureDomains) != 0 { 458 for i := int64(0); i < *(ic.ControlPlane.Replicas); i++ { 459 idx = i 460 if idx >= fdCount { 461 idx = i % fdCount 462 } 463 mpool.Zones = append(mpool.Zones, ic.VSphere.FailureDomains[idx].Name) 464 } 465 } 466 467 pool.Platform.VSphere = &mpool 468 templateName := clusterID.InfraID + "-rhcos" 469 470 data, err := vsphere.Machines(clusterID.InfraID, ic, &pool, templateName, "master", masterUserDataSecretName) 471 if err != nil { 472 return errors.Wrap(err, "failed to create master machine objects") 473 } 474 machines = data.Machines 475 controlPlaneMachineSet = data.ControlPlaneMachineSet 476 ipClaims = data.IPClaims 477 ipAddrs = data.IPAddresses 478 479 vsphere.ConfigMasters(machines, clusterID.InfraID) 480 case powervstypes.Name: 481 mpool := defaultPowerVSMachinePoolPlatform(ic) 482 mpool.Set(ic.Platform.PowerVS.DefaultMachinePlatform) 483 mpool.Set(pool.Platform.PowerVS) 484 // Only the service instance is guaranteed to exist and be passed via the install config 485 // The other two, we should standardize a name including the cluster id. At this point, all 486 // we have are names. 487 pool.Platform.PowerVS = &mpool 488 machines, controlPlaneMachineSet, err = powervs.Machines(clusterID.InfraID, ic, &pool, "master", "master-user-data") 489 if err != nil { 490 return errors.Wrap(err, "failed to create master machine objects") 491 } 492 if err := powervs.ConfigMasters(machines, controlPlaneMachineSet, clusterID.InfraID, ic.Publish); err != nil { 493 return errors.Wrap(err, "failed to to configure master machine objects") 494 } 495 case externaltypes.Name, nonetypes.Name: 496 case nutanixtypes.Name: 497 mpool := defaultNutanixMachinePoolPlatform() 498 mpool.NumCPUs = 8 499 mpool.Set(ic.Platform.Nutanix.DefaultMachinePlatform) 500 mpool.Set(pool.Platform.Nutanix) 501 if err = mpool.ValidateConfig(ic.Platform.Nutanix, "master"); err != nil { 502 return errors.Wrap(err, "failed to create master machine objects") 503 } 504 pool.Platform.Nutanix = &mpool 505 templateName := nutanixtypes.RHCOSImageName(clusterID.InfraID) 506 507 machines, controlPlaneMachineSet, err = nutanix.Machines(clusterID.InfraID, ic, &pool, templateName, "master", masterUserDataSecretName) 508 if err != nil { 509 return errors.Wrap(err, "failed to create master machine objects") 510 } 511 nutanix.ConfigMasters(machines, clusterID.InfraID) 512 default: 513 return fmt.Errorf("invalid Platform") 514 } 515 516 data, err := userDataSecret(masterUserDataSecretName, mign.File.Data) 517 if err != nil { 518 return errors.Wrap(err, "failed to create user-data secret for master machines") 519 } 520 521 m.UserDataFile = &asset.File{ 522 Filename: filepath.Join(directory, masterUserDataFileName), 523 Data: data, 524 } 525 526 machineConfigs := []*mcfgv1.MachineConfig{} 527 if pool.Hyperthreading == types.HyperthreadingDisabled { 528 ignHT, err := machineconfig.ForHyperthreadingDisabled("master") 529 if err != nil { 530 return errors.Wrap(err, "failed to create ignition for hyperthreading disabled for master machines") 531 } 532 machineConfigs = append(machineConfigs, ignHT) 533 } 534 if ic.SSHKey != "" { 535 ignSSH, err := machineconfig.ForAuthorizedKeys(ic.SSHKey, "master") 536 if err != nil { 537 return errors.Wrap(err, "failed to create ignition for authorized SSH keys for master machines") 538 } 539 machineConfigs = append(machineConfigs, ignSSH) 540 } 541 if ic.FIPS { 542 ignFIPS, err := machineconfig.ForFIPSEnabled("master") 543 if err != nil { 544 return errors.Wrap(err, "failed to create ignition for FIPS enabled for master machines") 545 } 546 machineConfigs = append(machineConfigs, ignFIPS) 547 } 548 if ic.Platform.Name() == powervstypes.Name { 549 // always enable multipath for powervs. 550 ignMultipath, err := machineconfig.ForMultipathEnabled("master") 551 if err != nil { 552 return errors.Wrap(err, "failed to create ignition for Multipath enabled for master machines") 553 } 554 machineConfigs = append(machineConfigs, ignMultipath) 555 556 // set SMT level if specified for powervs. 557 if pool.Platform.PowerVS.SMTLevel != "" { 558 ignPowerSMT, err := machineconfig.ForPowerSMT("master", pool.Platform.PowerVS.SMTLevel) 559 if err != nil { 560 return errors.Wrap(err, "failed to create ignition for Power SMT for master machines") 561 } 562 machineConfigs = append(machineConfigs, ignPowerSMT) 563 } 564 } 565 // The maximum number of networks supported on ServiceNetwork is two, one IPv4 and one IPv6 network. 566 // The cluster-network-operator handles the validation of this field. 567 // Reference: https://github.com/openshift/cluster-network-operator/blob/fc3e0e25b4cfa43e14122bdcdd6d7f2585017d75/pkg/network/cluster_config.go#L45-L52 568 if ic.Networking != nil && len(ic.Networking.ServiceNetwork) == 2 && 569 (ic.Platform.Name() == openstacktypes.Name || ic.Platform.Name() == vspheretypes.Name) { 570 // Only configure kernel args for dual-stack clusters. 571 ignIPv6, err := machineconfig.ForDualStackAddresses("master") 572 if err != nil { 573 return errors.Wrap(err, "failed to create ignition to configure IPv6 for master machines") 574 } 575 machineConfigs = append(machineConfigs, ignIPv6) 576 } 577 578 m.MachineConfigFiles, err = machineconfig.Manifests(machineConfigs, "master", directory) 579 if err != nil { 580 return errors.Wrap(err, "failed to create MachineConfig manifests for master machines") 581 } 582 583 m.MachineFiles = make([]*asset.File, len(machines)) 584 if controlPlaneMachineSet != nil && *pool.Replicas > 1 { 585 data, err := yaml.Marshal(controlPlaneMachineSet) 586 if err != nil { 587 return errors.Wrapf(err, "marshal control plane machine set") 588 } 589 m.ControlPlaneMachineSet = &asset.File{ 590 Filename: filepath.Join(directory, controlPlaneMachineSetFileName), 591 Data: data, 592 } 593 } 594 595 m.IPClaimFiles = make([]*asset.File, len(ipClaims)) 596 for i, claim := range ipClaims { 597 data, err := yaml.Marshal(claim) 598 if err != nil { 599 return errors.Wrapf(err, "unable to marshal ip claim %v", claim.Name) 600 } 601 602 m.IPClaimFiles[i] = &asset.File{ 603 Filename: filepath.Join(directory, fmt.Sprintf(ipClaimFileName, claim.Name)), 604 Data: data, 605 } 606 } 607 608 m.IPAddrFiles = make([]*asset.File, len(ipAddrs)) 609 for i, address := range ipAddrs { 610 data, err := yaml.Marshal(address) 611 if err != nil { 612 return errors.Wrapf(err, "unable to marshal ip claim %v", address.Name) 613 } 614 615 m.IPAddrFiles[i] = &asset.File{ 616 Filename: filepath.Join(directory, fmt.Sprintf(ipAddressFileName, address.Name)), 617 Data: data, 618 } 619 } 620 621 padFormat := fmt.Sprintf("%%0%dd", len(fmt.Sprintf("%d", len(machines)))) 622 for i, machine := range machines { 623 data, err := yaml.Marshal(machine) 624 if err != nil { 625 return errors.Wrapf(err, "marshal master %d", i) 626 } 627 628 padded := fmt.Sprintf(padFormat, i) 629 m.MachineFiles[i] = &asset.File{ 630 Filename: filepath.Join(directory, fmt.Sprintf(masterMachineFileName, padded)), 631 Data: data, 632 } 633 } 634 return nil 635 } 636 637 // Files returns the files generated by the asset. 638 func (m *Master) Files() []*asset.File { 639 files := make([]*asset.File, 0, 1+len(m.MachineConfigFiles)+len(m.MachineFiles)) 640 if m.UserDataFile != nil { 641 files = append(files, m.UserDataFile) 642 } 643 files = append(files, m.MachineConfigFiles...) 644 // Hosts refer to secrets, so place the secrets before the hosts 645 // to avoid unnecessary reconciliation errors. 646 files = append(files, m.SecretFiles...) 647 files = append(files, m.NetworkConfigSecretFiles...) 648 // Machines are linked to hosts via the machineRef, so we create 649 // the hosts first to ensure if the operator starts trying to 650 // reconcile a machine it can pick up the related host. 651 files = append(files, m.HostFiles...) 652 files = append(files, m.MachineFiles...) 653 if m.ControlPlaneMachineSet != nil { 654 files = append(files, m.ControlPlaneMachineSet) 655 } 656 files = append(files, m.IPClaimFiles...) 657 files = append(files, m.IPAddrFiles...) 658 return files 659 } 660 661 // Load reads the asset files from disk. 662 func (m *Master) Load(f asset.FileFetcher) (found bool, err error) { 663 file, err := f.FetchByName(filepath.Join(directory, masterUserDataFileName)) 664 if err != nil { 665 if os.IsNotExist(err) { 666 return false, nil 667 } 668 return false, err 669 } 670 m.UserDataFile = file 671 672 m.MachineConfigFiles, err = machineconfig.Load(f, "master", directory) 673 if err != nil { 674 return true, err 675 } 676 677 var fileList []*asset.File 678 679 fileList, err = f.FetchByPattern(filepath.Join(directory, secretFileNamePattern)) 680 if err != nil { 681 return true, err 682 } 683 m.SecretFiles = fileList 684 685 fileList, err = f.FetchByPattern(filepath.Join(directory, networkConfigSecretFileNamePattern)) 686 if err != nil { 687 return true, err 688 } 689 m.NetworkConfigSecretFiles = fileList 690 691 fileList, err = f.FetchByPattern(filepath.Join(directory, hostFileNamePattern)) 692 if err != nil { 693 return true, err 694 } 695 m.HostFiles = fileList 696 697 fileList, err = f.FetchByPattern(filepath.Join(directory, masterMachineFileNamePattern)) 698 if err != nil { 699 return true, err 700 } 701 m.MachineFiles = fileList 702 703 file, err = f.FetchByName(filepath.Join(directory, controlPlaneMachineSetFileName)) 704 if err != nil { 705 if os.IsNotExist(err) { 706 // Choosing to ignore the CPMS file if it does not exist since UPI does not need it. 707 logrus.Debugf("CPMS file missing. Ignoring it while loading machine asset.") 708 return true, nil 709 } 710 return true, err 711 } 712 m.ControlPlaneMachineSet = file 713 714 fileList, err = f.FetchByPattern(filepath.Join(directory, masterIPClaimFileNamePattern)) 715 if err != nil { 716 return true, err 717 } 718 m.IPClaimFiles = fileList 719 720 fileList, err = f.FetchByPattern(filepath.Join(directory, masterIPAddressFileNamePattern)) 721 if err != nil { 722 return true, err 723 } 724 m.IPAddrFiles = fileList 725 726 return true, nil 727 } 728 729 // Machines returns master Machine manifest structures. 730 func (m *Master) Machines() ([]machinev1beta1.Machine, error) { 731 scheme := runtime.NewScheme() 732 baremetalapi.AddToScheme(scheme) 733 ibmcloudapi.AddToScheme(scheme) 734 libvirtapi.AddToScheme(scheme) 735 ovirtproviderapi.AddToScheme(scheme) 736 scheme.AddKnownTypes(machinev1alpha1.GroupVersion, 737 &machinev1alpha1.OpenstackProviderSpec{}, 738 ) 739 scheme.AddKnownTypes(machinev1beta1.SchemeGroupVersion, 740 &machinev1beta1.AWSMachineProviderConfig{}, 741 &machinev1beta1.VSphereMachineProviderSpec{}, 742 &machinev1beta1.AzureMachineProviderSpec{}, 743 &machinev1beta1.GCPMachineProviderSpec{}, 744 ) 745 scheme.AddKnownTypes(machinev1.GroupVersion, 746 &machinev1.NutanixMachineProviderConfig{}, 747 &machinev1.PowerVSMachineProviderConfig{}, 748 &machinev1.ControlPlaneMachineSet{}, 749 ) 750 751 machinev1beta1.AddToScheme(scheme) 752 machinev1.Install(scheme) 753 decoder := serializer.NewCodecFactory(scheme).UniversalDecoder( 754 machinev1.GroupVersion, 755 baremetalprovider.SchemeGroupVersion, 756 ibmcloudprovider.SchemeGroupVersion, 757 libvirtprovider.SchemeGroupVersion, 758 machinev1alpha1.GroupVersion, 759 machinev1beta1.SchemeGroupVersion, 760 ovirtprovider.SchemeGroupVersion, 761 ) 762 763 machines := []machinev1beta1.Machine{} 764 for i, file := range m.MachineFiles { 765 machine := &machinev1beta1.Machine{} 766 err := yaml.Unmarshal(file.Data, &machine) 767 if err != nil { 768 return machines, errors.Wrapf(err, "unmarshal master %d", i) 769 } 770 771 obj, _, err := decoder.Decode(machine.Spec.ProviderSpec.Value.Raw, nil, nil) 772 if err != nil { 773 return machines, errors.Wrapf(err, "unmarshal master %d", i) 774 } 775 776 machine.Spec.ProviderSpec.Value = &runtime.RawExtension{Object: obj} 777 machines = append(machines, *machine) 778 } 779 780 return machines, nil 781 } 782 783 // IsMachineManifest tests whether a file is a manifest that belongs to the 784 // Master Machines or Worker Machines asset. 785 func IsMachineManifest(file *asset.File) bool { 786 if filepath.Dir(file.Filename) != directory { 787 return false 788 } 789 filename := filepath.Base(file.Filename) 790 if filename == masterUserDataFileName || filename == workerUserDataFileName || filename == controlPlaneMachineSetFileName { 791 return true 792 } 793 if matched, err := machineconfig.IsManifest(filename); err != nil { 794 panic(err) 795 } else if matched { 796 return true 797 } 798 for _, pattern := range []struct { 799 Pattern string 800 Type string 801 }{ 802 {Pattern: secretFileNamePattern, Type: "secret"}, 803 {Pattern: networkConfigSecretFileNamePattern, Type: "network config secret"}, 804 {Pattern: hostFileNamePattern, Type: "host"}, 805 {Pattern: masterMachineFileNamePattern, Type: "master machine"}, 806 {Pattern: workerMachineSetFileNamePattern, Type: "worker machineset"}, 807 {Pattern: masterIPAddressFileNamePattern, Type: "master ip address"}, 808 {Pattern: masterIPClaimFileNamePattern, Type: "master ip address claim"}, 809 } { 810 if matched, err := filepath.Match(pattern.Pattern, filename); err != nil { 811 panic(fmt.Sprintf("bad format for %s file name pattern", pattern.Type)) 812 } else if matched { 813 return true 814 } 815 } 816 return false 817 } 818 819 // IPAddresses returns IPAddress manifest structures. 820 func (m *Master) IPAddresses() ([]ipamv1.IPAddress, error) { 821 ipAddresses := []ipamv1.IPAddress{} 822 for i, file := range m.IPAddrFiles { 823 logrus.Debugf("Attempting to load address %v.", file.Filename) 824 address := &ipamv1.IPAddress{} 825 err := yaml.Unmarshal(file.Data, &address) 826 if err != nil { 827 return ipAddresses, errors.Wrapf(err, "unable to unmarshal ip address %d", i) 828 } 829 830 ipAddresses = append(ipAddresses, *address) 831 } 832 833 return ipAddresses, nil 834 } 835 836 func createSecretAssetFiles(resources []corev1.Secret, fileName string) ([]*asset.File, error) { 837 838 var objects []interface{} 839 for _, r := range resources { 840 objects = append(objects, r) 841 } 842 843 return createAssetFiles(objects, fileName) 844 } 845 846 func createHostAssetFiles(resources []baremetalhost.BareMetalHost, fileName string) ([]*asset.File, error) { 847 848 var objects []interface{} 849 for _, r := range resources { 850 objects = append(objects, r) 851 } 852 853 return createAssetFiles(objects, fileName) 854 } 855 856 func createAssetFiles(objects []interface{}, fileName string) ([]*asset.File, error) { 857 858 assetFiles := make([]*asset.File, len(objects)) 859 padFormat := fmt.Sprintf("%%0%dd", len(fmt.Sprintf("%d", len(objects)))) 860 for i, obj := range objects { 861 data, err := yaml.Marshal(obj) 862 if err != nil { 863 return nil, errors.Wrapf(err, "marshal resource %d", i) 864 } 865 padded := fmt.Sprintf(padFormat, i) 866 assetFiles[i] = &asset.File{ 867 Filename: filepath.Join(directory, fmt.Sprintf(fileName, padded)), 868 Data: data, 869 } 870 } 871 872 return assetFiles, nil 873 }