github.com/openshift/installer@v1.4.17/pkg/asset/machines/azure/azuremachines.go (about) 1 // Package azure generates Machine objects for azure. 2 package azure 3 4 import ( 5 "fmt" 6 "strings" 7 8 v1 "k8s.io/api/core/v1" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/util/sets" 11 "k8s.io/utils/ptr" 12 capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 13 capi "sigs.k8s.io/cluster-api/api/v1beta1" 14 15 "github.com/openshift/api/machine/v1beta1" 16 "github.com/openshift/installer/pkg/asset" 17 "github.com/openshift/installer/pkg/asset/manifests/capiutils" 18 "github.com/openshift/installer/pkg/types" 19 "github.com/openshift/installer/pkg/types/azure" 20 ) 21 22 const ( 23 genV2Suffix string = "-gen2" 24 ) 25 26 // MachineInput defines the inputs needed to generate a machine asset. 27 type MachineInput struct { 28 Subnet string 29 Role string 30 UserDataSecret string 31 HyperVGen string 32 UseImageGallery bool 33 Private bool 34 UserTags map[string]string 35 Platform *azure.Platform 36 Pool *types.MachinePool 37 } 38 39 // GenerateMachines returns manifests and runtime objects to provision the control plane (including bootstrap, if applicable) nodes using CAPI. 40 func GenerateMachines(clusterID, resourceGroup, subscriptionID string, in *MachineInput) ([]*asset.RuntimeFile, error) { 41 if poolPlatform := in.Pool.Platform.Name(); poolPlatform != azure.Name { 42 return nil, fmt.Errorf("non-Azure machine-pool: %q", poolPlatform) 43 } 44 mpool := in.Pool.Platform.Azure 45 46 total := int64(1) 47 if in.Pool.Replicas != nil { 48 total = *in.Pool.Replicas 49 } 50 51 if len(mpool.Zones) == 0 { 52 // if no azs are given we set to []string{""} for convenience over later operations. 53 // It means no-zoned for the machine API 54 mpool.Zones = []string{""} 55 } 56 tags, err := CapzTagsFromUserTags(clusterID, in.UserTags) 57 if err != nil { 58 return nil, fmt.Errorf("failed to create machineapi.TagSpecifications from UserTags: %w", err) 59 } 60 61 userAssignedIdentityID := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s/providers/Microsoft.ManagedIdentity/userAssignedIdentities/%s-identity", subscriptionID, resourceGroup, clusterID) 62 63 var image *capz.Image 64 osImage := mpool.OSImage 65 galleryName := strings.ReplaceAll(clusterID, "-", "_") 66 67 switch { 68 case osImage.Publisher != "": 69 image = &capz.Image{ 70 Marketplace: &capz.AzureMarketplaceImage{ 71 ImagePlan: capz.ImagePlan{ 72 Publisher: osImage.Publisher, 73 Offer: osImage.Offer, 74 SKU: osImage.SKU, 75 }, 76 Version: osImage.Version, 77 ThirdPartyImage: osImage.Plan != azure.ImageNoPurchasePlan, 78 }, 79 } 80 case in.UseImageGallery: 81 // image gallery names cannot have dashes 82 id := clusterID 83 if in.HyperVGen == "V2" { 84 id += genV2Suffix 85 } 86 imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s/versions/latest", resourceGroup, galleryName, id) 87 image = &capz.Image{ID: &imageID} 88 default: 89 imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s", subscriptionID, resourceGroup, galleryName, clusterID) 90 if in.HyperVGen == "V2" && in.Platform.CloudName != azure.StackCloud { 91 imageID += genV2Suffix 92 } 93 image = &capz.Image{ID: &imageID} 94 } 95 96 osDisk := capz.OSDisk{ 97 OSType: "Linux", 98 DiskSizeGB: &mpool.DiskSizeGB, 99 ManagedDisk: &capz.ManagedDiskParameters{ 100 StorageAccountType: mpool.DiskType, 101 }, 102 CachingType: "ReadWrite", 103 } 104 ultrassd := mpool.UltraSSDCapability == "Enabled" 105 additionalCapabilities := &capz.AdditionalCapabilities{ 106 UltraSSDEnabled: &ultrassd, 107 } 108 if in.Pool.Platform.Azure.DiskEncryptionSet != nil { 109 osDisk.ManagedDisk.DiskEncryptionSet = &capz.DiskEncryptionSetParameters{ 110 ID: mpool.OSDisk.DiskEncryptionSet.ToID(), 111 } 112 } 113 114 machineProfile := generateSecurityProfile(mpool) 115 securityProfile := &capz.SecurityProfile{ 116 EncryptionAtHost: machineProfile.EncryptionAtHost, 117 SecurityType: capz.SecurityTypes(machineProfile.Settings.SecurityType), 118 } 119 if machineProfile.Settings.ConfidentialVM != nil { 120 securityProfile.UefiSettings = &capz.UefiSettings{ 121 VTpmEnabled: ptr.To[bool](machineProfile.Settings.ConfidentialVM.UEFISettings.VirtualizedTrustedPlatformModule == v1beta1.VirtualizedTrustedPlatformModulePolicyEnabled), 122 SecureBootEnabled: ptr.To[bool](machineProfile.Settings.ConfidentialVM.UEFISettings.SecureBoot == v1beta1.SecureBootPolicyEnabled), 123 } 124 } else if machineProfile.Settings.TrustedLaunch != nil { 125 securityProfile.UefiSettings = &capz.UefiSettings{ 126 VTpmEnabled: ptr.To(machineProfile.Settings.TrustedLaunch.UEFISettings.VirtualizedTrustedPlatformModule == v1beta1.VirtualizedTrustedPlatformModulePolicyEnabled), 127 SecureBootEnabled: ptr.To(machineProfile.Settings.TrustedLaunch.UEFISettings.SecureBoot == v1beta1.SecureBootPolicyEnabled), 128 } 129 } 130 131 var result []*asset.RuntimeFile 132 for idx := int64(0); idx < total; idx++ { 133 zone := mpool.Zones[int(idx)%len(mpool.Zones)] 134 azureMachine := &capz.AzureMachine{ 135 ObjectMeta: metav1.ObjectMeta{ 136 Name: fmt.Sprintf("%s-%s-%d", clusterID, in.Pool.Name, idx), 137 Labels: map[string]string{ 138 "cluster.x-k8s.io/control-plane": "", 139 "cluster.x-k8s.io/cluster-name": clusterID, 140 }, 141 }, 142 Spec: capz.AzureMachineSpec{ 143 VMSize: mpool.InstanceType, 144 FailureDomain: ptr.To(zone), 145 Image: image, 146 OSDisk: osDisk, // required 147 AdditionalTags: tags, 148 AdditionalCapabilities: additionalCapabilities, 149 DisableExtensionOperations: ptr.To(true), 150 AllocatePublicIP: false, 151 EnableIPForwarding: false, 152 SecurityProfile: securityProfile, 153 NetworkInterfaces: []capz.NetworkInterface{ 154 { 155 SubnetName: in.Subnet, 156 AcceleratedNetworking: ptr.To(mpool.VMNetworkingType == string(azure.VMnetworkingTypeAccelerated) || mpool.VMNetworkingType == string(azure.AcceleratedNetworkingEnabled)), 157 }, 158 }, 159 Identity: capz.VMIdentityUserAssigned, 160 UserAssignedIdentities: []capz.UserAssignedIdentity{ 161 { 162 ProviderID: userAssignedIdentityID, 163 }, 164 }, 165 }, 166 } 167 azureMachine.SetGroupVersionKind(capz.GroupVersion.WithKind("AzureMachine")) 168 result = append(result, &asset.RuntimeFile{ 169 File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", azureMachine.Name)}, 170 Object: azureMachine, 171 }) 172 173 controlPlaneMachine := &capi.Machine{ 174 ObjectMeta: metav1.ObjectMeta{ 175 Name: azureMachine.Name, 176 Labels: map[string]string{ 177 "cluster.x-k8s.io/control-plane": "", 178 }, 179 }, 180 Spec: capi.MachineSpec{ 181 ClusterName: clusterID, 182 Bootstrap: capi.Bootstrap{ 183 DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, in.Role)), 184 }, 185 InfrastructureRef: v1.ObjectReference{ 186 APIVersion: capz.GroupVersion.String(), 187 Kind: "AzureMachine", 188 Name: azureMachine.Name, 189 }, 190 }, 191 } 192 controlPlaneMachine.SetGroupVersionKind(capi.GroupVersion.WithKind("Machine")) 193 194 result = append(result, &asset.RuntimeFile{ 195 File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", azureMachine.Name)}, 196 Object: controlPlaneMachine, 197 }) 198 } 199 200 bootstrapAzureMachine := &capz.AzureMachine{ 201 ObjectMeta: metav1.ObjectMeta{ 202 Name: capiutils.GenerateBoostrapMachineName(clusterID), 203 Labels: map[string]string{ 204 "cluster.x-k8s.io/control-plane": "", 205 "install.openshift.io/bootstrap": "", 206 "cluster.x-k8s.io/cluster-name": clusterID, 207 }, 208 }, 209 Spec: capz.AzureMachineSpec{ 210 VMSize: mpool.InstanceType, 211 Image: image, 212 FailureDomain: ptr.To(mpool.Zones[0]), 213 OSDisk: osDisk, 214 AdditionalTags: tags, 215 DisableExtensionOperations: ptr.To(true), 216 AllocatePublicIP: !in.Private, 217 AdditionalCapabilities: additionalCapabilities, 218 SecurityProfile: securityProfile, 219 Identity: capz.VMIdentityUserAssigned, 220 UserAssignedIdentities: []capz.UserAssignedIdentity{ 221 { 222 ProviderID: userAssignedIdentityID, 223 }, 224 }, 225 }, 226 } 227 bootstrapAzureMachine.SetGroupVersionKind(capz.GroupVersion.WithKind("AzureMachine")) 228 229 result = append(result, &asset.RuntimeFile{ 230 File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", bootstrapAzureMachine.Name)}, 231 Object: bootstrapAzureMachine, 232 }) 233 234 bootstrapMachine := &capi.Machine{ 235 ObjectMeta: metav1.ObjectMeta{ 236 Name: bootstrapAzureMachine.Name, 237 Labels: map[string]string{ 238 "cluster.x-k8s.io/control-plane": "", 239 }, 240 }, 241 Spec: capi.MachineSpec{ 242 ClusterName: clusterID, 243 Bootstrap: capi.Bootstrap{ 244 DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, "bootstrap")), 245 }, 246 InfrastructureRef: v1.ObjectReference{ 247 APIVersion: capz.GroupVersion.String(), 248 Kind: "AzureMachine", 249 Name: bootstrapAzureMachine.Name, 250 }, 251 }, 252 } 253 bootstrapMachine.SetGroupVersionKind(capi.GroupVersion.WithKind("Machine")) 254 255 result = append(result, &asset.RuntimeFile{ 256 File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", bootstrapMachine.Name)}, 257 Object: bootstrapMachine, 258 }) 259 260 return result, nil 261 } 262 263 // CapzTagsFromUserTags converts a map of user tags to a map of capz.Tags. 264 func CapzTagsFromUserTags(clusterID string, usertags map[string]string) (capz.Tags, error) { 265 tags := capz.Tags{} 266 tags[fmt.Sprintf("kubernetes.io_cluster.%s", clusterID)] = "owned" 267 268 forbiddenTags := sets.New[string]() 269 for key := range tags { 270 forbiddenTags.Insert(key) 271 } 272 273 userTagKeys := sets.New[string]() 274 for key := range usertags { 275 userTagKeys.Insert(key) 276 } 277 if clobberedTags := userTagKeys.Intersection(forbiddenTags); clobberedTags.Len() > 0 { 278 return nil, fmt.Errorf("user tag keys %v are not allowed", sets.List(clobberedTags)) 279 } 280 for _, k := range sets.List(userTagKeys) { 281 tags[k] = usertags[k] 282 } 283 return tags, nil 284 }