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  }