github.com/openshift/installer@v1.4.17/pkg/asset/machines/azure/machines.go (about)

     1  // Package azure generates Machine objects for azure.
     2  package azure
     3  
     4  import (
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/sirupsen/logrus"
    11  	corev1 "k8s.io/api/core/v1"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/runtime"
    14  
    15  	v1 "github.com/openshift/api/config/v1"
    16  	machinev1 "github.com/openshift/api/machine/v1"
    17  	machineapi "github.com/openshift/api/machine/v1beta1"
    18  	icazure "github.com/openshift/installer/pkg/asset/installconfig/azure"
    19  	"github.com/openshift/installer/pkg/types"
    20  	"github.com/openshift/installer/pkg/types/azure"
    21  )
    22  
    23  const (
    24  	cloudsSecret          = "azure-cloud-credentials"
    25  	cloudsSecretNamespace = "openshift-machine-api"
    26  )
    27  
    28  // Machines returns a list of machines for a machinepool.
    29  func Machines(clusterID string, config *types.InstallConfig, pool *types.MachinePool, osImage, role, userDataSecret string, capabilities map[string]string, useImageGallery bool) ([]machineapi.Machine, *machinev1.ControlPlaneMachineSet, error) {
    30  	if configPlatform := config.Platform.Name(); configPlatform != azure.Name {
    31  		return nil, nil, fmt.Errorf("non-Azure configuration: %q", configPlatform)
    32  	}
    33  	if poolPlatform := pool.Platform.Name(); poolPlatform != azure.Name {
    34  		return nil, nil, fmt.Errorf("non-Azure machine-pool: %q", poolPlatform)
    35  	}
    36  	platform := config.Platform.Azure
    37  	mpool := pool.Platform.Azure
    38  
    39  	if len(mpool.Zones) == 0 {
    40  		// if no azs are given we set to []string{""} for convenience over later operations.
    41  		// It means no-zoned for the machine API
    42  		mpool.Zones = []string{""}
    43  	}
    44  	azs := mpool.Zones
    45  
    46  	total := int64(1)
    47  	if pool.Replicas != nil {
    48  		total = *pool.Replicas
    49  	}
    50  	var machines []machineapi.Machine
    51  	machineSetProvider := &machineapi.AzureMachineProviderSpec{}
    52  	for idx := int64(0); idx < total; idx++ {
    53  		var azIndex int
    54  		if len(azs) > 0 {
    55  			azIndex = int(idx) % len(azs)
    56  		}
    57  		provider, err := provider(platform, mpool, osImage, userDataSecret, clusterID, role, &azIndex, capabilities, useImageGallery)
    58  		if err != nil {
    59  			return nil, nil, errors.Wrap(err, "failed to create provider")
    60  		}
    61  		machine := machineapi.Machine{
    62  			TypeMeta: metav1.TypeMeta{
    63  				APIVersion: "machine.openshift.io/v1beta1",
    64  				Kind:       "Machine",
    65  			},
    66  			ObjectMeta: metav1.ObjectMeta{
    67  				Namespace: "openshift-machine-api",
    68  				Name:      fmt.Sprintf("%s-%s-%d", clusterID, pool.Name, idx),
    69  				Labels: map[string]string{
    70  					"machine.openshift.io/cluster-api-cluster":      clusterID,
    71  					"machine.openshift.io/cluster-api-machine-role": role,
    72  					"machine.openshift.io/cluster-api-machine-type": role,
    73  				},
    74  			},
    75  			Spec: machineapi.MachineSpec{
    76  				ProviderSpec: machineapi.ProviderSpec{
    77  					Value: &runtime.RawExtension{Object: provider},
    78  				},
    79  				// we don't need to set Versions, because we control those via operators.
    80  			},
    81  		}
    82  		*machineSetProvider = *provider
    83  		machines = append(machines, machine)
    84  	}
    85  	replicas := int32(total)
    86  
    87  	failureDomains := []machinev1.AzureFailureDomain{}
    88  	if len(mpool.Zones) > 1 {
    89  		sort.Strings(mpool.Zones)
    90  		for _, zone := range mpool.Zones {
    91  			domain := machinev1.AzureFailureDomain{
    92  				Zone: zone,
    93  			}
    94  
    95  			failureDomains = append(failureDomains, domain)
    96  		}
    97  		machineSetProvider.Zone = ""
    98  	} else if len(mpool.Zones) == 1 {
    99  		machineSetProvider.Zone = mpool.Zones[0]
   100  	}
   101  
   102  	controlPlaneMachineSet := &machinev1.ControlPlaneMachineSet{
   103  		TypeMeta: metav1.TypeMeta{
   104  			APIVersion: "machine.openshift.io/v1",
   105  			Kind:       "ControlPlaneMachineSet",
   106  		},
   107  		ObjectMeta: metav1.ObjectMeta{
   108  			Namespace: "openshift-machine-api",
   109  			Name:      "cluster",
   110  			Labels: map[string]string{
   111  				"machine.openshift.io/cluster-api-cluster": clusterID,
   112  			},
   113  		},
   114  		Spec: machinev1.ControlPlaneMachineSetSpec{
   115  			Replicas: &replicas,
   116  			State:    machinev1.ControlPlaneMachineSetStateActive,
   117  			Selector: metav1.LabelSelector{
   118  				MatchLabels: map[string]string{
   119  					"machine.openshift.io/cluster-api-machine-role": role,
   120  					"machine.openshift.io/cluster-api-machine-type": role,
   121  					"machine.openshift.io/cluster-api-cluster":      clusterID,
   122  				},
   123  			},
   124  			Template: machinev1.ControlPlaneMachineSetTemplate{
   125  				MachineType: machinev1.OpenShiftMachineV1Beta1MachineType,
   126  				OpenShiftMachineV1Beta1Machine: &machinev1.OpenShiftMachineV1Beta1MachineTemplate{
   127  					ObjectMeta: machinev1.ControlPlaneMachineSetTemplateObjectMeta{
   128  						Labels: map[string]string{
   129  							"machine.openshift.io/cluster-api-cluster":      clusterID,
   130  							"machine.openshift.io/cluster-api-machine-role": role,
   131  							"machine.openshift.io/cluster-api-machine-type": role,
   132  						},
   133  					},
   134  					Spec: machineapi.MachineSpec{
   135  						ProviderSpec: machineapi.ProviderSpec{
   136  							Value: &runtime.RawExtension{Object: machineSetProvider},
   137  						},
   138  					},
   139  				},
   140  			},
   141  		},
   142  	}
   143  
   144  	if len(failureDomains) > 0 {
   145  		controlPlaneMachineSet.Spec.Template.OpenShiftMachineV1Beta1Machine.FailureDomains = &machinev1.FailureDomains{
   146  			Platform: v1.AzurePlatformType,
   147  			Azure:    &failureDomains,
   148  		}
   149  	}
   150  
   151  	return machines, controlPlaneMachineSet, nil
   152  }
   153  
   154  func provider(platform *azure.Platform, mpool *azure.MachinePool, osImage string, userDataSecret string, clusterID string, role string, azIdx *int, capabilities map[string]string, useImageGallery bool) (*machineapi.AzureMachineProviderSpec, error) {
   155  	var az string
   156  	if len(mpool.Zones) > 0 && azIdx != nil {
   157  		az = mpool.Zones[*azIdx]
   158  	}
   159  
   160  	hyperVGen, err := icazure.GetHyperVGenerationVersion(capabilities, "")
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	if mpool.VMNetworkingType == "" {
   166  		acceleratedNetworking := icazure.GetVMNetworkingCapability(capabilities)
   167  		if acceleratedNetworking {
   168  			mpool.VMNetworkingType = string(azure.VMnetworkingTypeAccelerated)
   169  		} else {
   170  			logrus.Infof("Instance type %s does not support Accelerated Networking. Using Basic Networking instead.", mpool.InstanceType)
   171  		}
   172  	}
   173  	rg := platform.ClusterResourceGroupName(clusterID)
   174  
   175  	var image machineapi.Image
   176  	if mpool.OSImage.Publisher != "" {
   177  		image.Type = machineapi.AzureImageTypeMarketplaceWithPlan
   178  		if mpool.OSImage.Plan == azure.ImageNoPurchasePlan {
   179  			image.Type = machineapi.AzureImageTypeMarketplaceNoPlan
   180  		}
   181  		image.Publisher = mpool.OSImage.Publisher
   182  		image.Offer = mpool.OSImage.Offer
   183  		image.SKU = mpool.OSImage.SKU
   184  		image.Version = mpool.OSImage.Version
   185  	} else if useImageGallery {
   186  		// image gallery names cannot have dashes
   187  		galleryName := strings.ReplaceAll(clusterID, "-", "_")
   188  		id := clusterID
   189  		if hyperVGen == "V2" {
   190  			id += "-gen2"
   191  		}
   192  		imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s/versions/latest", rg, galleryName, id)
   193  		image.ResourceID = imageID
   194  	} else {
   195  		imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/images/%s", rg, clusterID)
   196  		if hyperVGen == "V2" && platform.CloudName != azure.StackCloud {
   197  			imageID += "-gen2"
   198  		}
   199  		image.ResourceID = imageID
   200  	}
   201  
   202  	networkResourceGroup, virtualNetwork, subnet, err := getNetworkInfo(platform, clusterID, role)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	if mpool.OSDisk.DiskType == "" {
   208  		mpool.OSDisk.DiskType = "Premium_LRS"
   209  	}
   210  
   211  	publicLB := clusterID
   212  	if platform.OutboundType == azure.UserDefinedRoutingOutboundType {
   213  		publicLB = ""
   214  	}
   215  
   216  	managedIdentity := fmt.Sprintf("%s-identity", clusterID)
   217  	if platform.IsARO() || platform.CloudName == azure.StackCloud {
   218  		managedIdentity = ""
   219  	}
   220  
   221  	var diskEncryptionSet *machineapi.DiskEncryptionSetParameters
   222  	if mpool.OSDisk.DiskEncryptionSet != nil {
   223  		diskEncryptionSet = &machineapi.DiskEncryptionSetParameters{
   224  			ID: mpool.OSDisk.DiskEncryptionSet.ToID(),
   225  		}
   226  	}
   227  
   228  	var diskSecurityProfile machineapi.VMDiskSecurityProfile
   229  	if mpool.OSDisk.SecurityProfile != nil && mpool.OSDisk.SecurityProfile.SecurityEncryptionType != "" {
   230  		diskSecurityProfile = machineapi.VMDiskSecurityProfile{
   231  			SecurityEncryptionType: machineapi.SecurityEncryptionTypes(mpool.OSDisk.SecurityProfile.SecurityEncryptionType),
   232  		}
   233  
   234  		if mpool.OSDisk.SecurityProfile.DiskEncryptionSet != nil {
   235  			diskSecurityProfile.DiskEncryptionSet = machineapi.DiskEncryptionSetParameters{
   236  				ID: mpool.OSDisk.SecurityProfile.DiskEncryptionSet.ToID(),
   237  			}
   238  		}
   239  	}
   240  
   241  	securityProfile := generateSecurityProfile(mpool)
   242  
   243  	ultraSSDCapability := machineapi.AzureUltraSSDCapabilityState(mpool.UltraSSDCapability)
   244  
   245  	spec := &machineapi.AzureMachineProviderSpec{
   246  		TypeMeta: metav1.TypeMeta{
   247  			APIVersion: "machine.openshift.io/v1beta1",
   248  			Kind:       "AzureMachineProviderSpec",
   249  		},
   250  		UserDataSecret:    &corev1.SecretReference{Name: userDataSecret},
   251  		CredentialsSecret: &corev1.SecretReference{Name: cloudsSecret, Namespace: cloudsSecretNamespace},
   252  		Location:          platform.Region,
   253  		VMSize:            mpool.InstanceType,
   254  		Image:             image,
   255  		OSDisk: machineapi.OSDisk{
   256  			OSType:     "Linux",
   257  			DiskSizeGB: mpool.OSDisk.DiskSizeGB,
   258  			ManagedDisk: machineapi.OSDiskManagedDiskParameters{
   259  				StorageAccountType: mpool.OSDisk.DiskType,
   260  				DiskEncryptionSet:  diskEncryptionSet,
   261  				SecurityProfile:    diskSecurityProfile,
   262  			},
   263  		},
   264  		SecurityProfile:       securityProfile,
   265  		UltraSSDCapability:    ultraSSDCapability,
   266  		Zone:                  az,
   267  		Subnet:                subnet,
   268  		ManagedIdentity:       managedIdentity,
   269  		Vnet:                  virtualNetwork,
   270  		ResourceGroup:         rg,
   271  		NetworkResourceGroup:  networkResourceGroup,
   272  		PublicLoadBalancer:    publicLB,
   273  		AcceleratedNetworking: getVMNetworkingType(mpool.VMNetworkingType),
   274  		Tags:                  platform.UserTags,
   275  	}
   276  
   277  	if platform.CloudName == azure.StackCloud {
   278  		spec.AvailabilitySet = fmt.Sprintf("%s-cluster", clusterID)
   279  	}
   280  
   281  	return spec, nil
   282  }
   283  
   284  // ConfigMasters sets the PublicIP flag and assigns a set of load balancers to the given machines
   285  func ConfigMasters(machines []machineapi.Machine, controlPlane *machinev1.ControlPlaneMachineSet, clusterID string) error {
   286  	internalLB := fmt.Sprintf("%s-internal", clusterID)
   287  
   288  	for _, machine := range machines {
   289  		providerSpec := machine.Spec.ProviderSpec.Value.Object.(*machineapi.AzureMachineProviderSpec)
   290  		providerSpec.InternalLoadBalancer = internalLB
   291  	}
   292  	providerSpec, ok := controlPlane.Spec.Template.OpenShiftMachineV1Beta1Machine.Spec.ProviderSpec.Value.Object.(*machineapi.AzureMachineProviderSpec)
   293  	if !ok {
   294  		return errors.New("Unable to set internal load balancers to control plane machine set")
   295  	}
   296  	providerSpec.InternalLoadBalancer = internalLB
   297  	return nil
   298  }
   299  
   300  func getNetworkInfo(platform *azure.Platform, clusterID, role string) (string, string, string, error) {
   301  	networkResourceGroupName := platform.NetworkResourceGroupName
   302  	if platform.VirtualNetwork == "" {
   303  		networkResourceGroupName = platform.ClusterResourceGroupName(clusterID)
   304  	}
   305  
   306  	switch role {
   307  	case "worker":
   308  		return networkResourceGroupName, platform.VirtualNetworkName(clusterID), platform.ComputeSubnetName(clusterID), nil
   309  	case "master":
   310  		return networkResourceGroupName, platform.VirtualNetworkName(clusterID), platform.ControlPlaneSubnetName(clusterID), nil
   311  	default:
   312  		return "", "", "", fmt.Errorf("unrecognized machine role %s", role)
   313  	}
   314  }
   315  
   316  // getVMNetworkingType should set the correct capability for instance type
   317  func getVMNetworkingType(value string) bool {
   318  	return value == string(azure.VMnetworkingTypeAccelerated)
   319  }
   320  
   321  func generateSecurityProfile(mpool *azure.MachinePool) *machineapi.SecurityProfile {
   322  	securityProfile := &machineapi.SecurityProfile{}
   323  
   324  	if mpool.EncryptionAtHost {
   325  		securityProfile.EncryptionAtHost = &mpool.EncryptionAtHost
   326  	}
   327  
   328  	if mpool.Settings != nil && mpool.Settings.SecurityType != "" {
   329  		securityProfile.Settings = machineapi.SecuritySettings{
   330  			SecurityType: machineapi.SecurityTypes(mpool.Settings.SecurityType),
   331  		}
   332  
   333  		var uefiSettings machineapi.UEFISettings
   334  		if securityProfile.Settings.SecurityType == machineapi.SecurityTypesTrustedLaunch {
   335  			if mpool.Settings.TrustedLaunch != nil && mpool.Settings.TrustedLaunch.UEFISettings != nil {
   336  				if sb := mpool.Settings.TrustedLaunch.UEFISettings.SecureBoot; sb != nil {
   337  					uefiSettings.SecureBoot = machineapi.SecureBootPolicy(*sb)
   338  				}
   339  				if vtpm := mpool.Settings.TrustedLaunch.UEFISettings.VirtualizedTrustedPlatformModule; vtpm != nil {
   340  					uefiSettings.VirtualizedTrustedPlatformModule = machineapi.VirtualizedTrustedPlatformModulePolicy(*vtpm)
   341  				}
   342  
   343  				securityProfile.Settings.TrustedLaunch = &machineapi.TrustedLaunch{
   344  					UEFISettings: uefiSettings,
   345  				}
   346  			}
   347  		} else if securityProfile.Settings.SecurityType == machineapi.SecurityTypesConfidentialVM {
   348  			if mpool.Settings.ConfidentialVM != nil && mpool.Settings.ConfidentialVM.UEFISettings != nil {
   349  				if sb := mpool.Settings.ConfidentialVM.UEFISettings.SecureBoot; sb != nil {
   350  					uefiSettings.SecureBoot = machineapi.SecureBootPolicy(*sb)
   351  				}
   352  				if vtpm := mpool.Settings.ConfidentialVM.UEFISettings.VirtualizedTrustedPlatformModule; vtpm != nil {
   353  					uefiSettings.VirtualizedTrustedPlatformModule = machineapi.VirtualizedTrustedPlatformModulePolicy(*vtpm)
   354  				}
   355  
   356  				securityProfile.Settings.ConfidentialVM = &machineapi.ConfidentialVM{
   357  					UEFISettings: uefiSettings,
   358  				}
   359  			}
   360  		}
   361  	}
   362  
   363  	return securityProfile
   364  }