diff --git a/pkg/asset/machines/azure/azuremachines.go b/pkg/asset/machines/azure/azuremachines.go index 6c7f09ff11..a0d4434dda 100644 --- a/pkg/asset/machines/azure/azuremachines.go +++ b/pkg/asset/machines/azure/azuremachines.go @@ -26,16 +26,17 @@ const ( // MachineInput defines the inputs needed to generate a machine asset. type MachineInput struct { - Subnet string - Role string - UserDataSecret string - HyperVGen string - StorageSuffix string - UseImageGallery bool - Private bool - UserTags map[string]string - Platform *aztypes.Platform - Pool *types.MachinePool + Subnet string + Role string + UserDataSecret string + HyperVGen string + StorageSuffix string + Environment aztypes.CloudEnvironment + Private bool + UserTags map[string]string + Platform *aztypes.Platform + Pool *types.MachinePool + RHCOS string } // GenerateMachines returns manifests and runtime objects to provision the control plane (including bootstrap, if applicable) nodes using CAPI. @@ -59,38 +60,7 @@ func GenerateMachines(clusterID, resourceGroup, subscriptionID string, session * if err != nil { return nil, fmt.Errorf("failed to create machineapi.TagSpecifications from UserTags: %w", err) } - - var image *capz.Image - osImage := mpool.OSImage - galleryName := strings.ReplaceAll(clusterID, "-", "_") - - switch { - case osImage.Publisher != "": - image = &capz.Image{ - Marketplace: &capz.AzureMarketplaceImage{ - ImagePlan: capz.ImagePlan{ - Publisher: osImage.Publisher, - Offer: osImage.Offer, - SKU: osImage.SKU, - }, - Version: osImage.Version, - ThirdPartyImage: osImage.Plan != aztypes.ImageNoPurchasePlan, - }, - } - case in.UseImageGallery: - // image gallery names cannot have dashes - imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s", subscriptionID, resourceGroup, galleryName, clusterID) - if in.HyperVGen == "V2" && in.Platform.CloudName != aztypes.StackCloud { - imageID += genV2Suffix - } - image = &capz.Image{ID: &imageID} - default: - // AzureStack is the only use for managed images & supports only Gen1 VMs: - // https://learn.microsoft.com/en-us/azure-stack/user/azure-stack-vm-considerations?view=azs-2501&tabs=az1%2Caz2#vm-differences - imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", subscriptionID, resourceGroup, clusterID) - image = &capz.Image{ID: &imageID} - } - + image := capzImage(mpool.OSImage, in.Environment, in.HyperVGen, resourceGroup, subscriptionID, clusterID, in.RHCOS) // Set up OSDisk osDisk := capz.OSDisk{ OSType: "Linux", @@ -383,3 +353,46 @@ func bootDiagStorageURIBuilder(diag *aztypes.BootDiagnostics, storageEndpointSuf } return "" } + +func capzImage(osImage aztypes.OSImage, azEnv aztypes.CloudEnvironment, gen, rg, sub, infraID, rhcosImg string) *capz.Image { + switch { + case osImage.Publisher != "": + return &capz.Image{ + Marketplace: &capz.AzureMarketplaceImage{ + ImagePlan: capz.ImagePlan{ + Publisher: osImage.Publisher, + Offer: osImage.Offer, + SKU: osImage.SKU, + }, + Version: osImage.Version, + ThirdPartyImage: osImage.Plan != aztypes.ImageNoPurchasePlan, + }, + } + case azEnv == aztypes.StackCloud: + // AzureStack is the only use for managed images & supports only Gen1 VMs: + // https://learn.microsoft.com/en-us/azure-stack/user/azure-stack-vm-considerations?view=azs-2501&tabs=az1%2Caz2#vm-differences + imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", sub, rg, infraID) + return &capz.Image{ID: &imageID} + case strings.Count(rhcosImg, ":") == 3: // Marketplace Image URN contains 3 colons + rhcosMktImg := strings.Split(rhcosImg, ":") + return &capz.Image{ + Marketplace: &capz.AzureMarketplaceImage{ + ImagePlan: capz.ImagePlan{ + Publisher: rhcosMktImg[0], + Offer: rhcosMktImg[1], + SKU: rhcosMktImg[2], + }, + Version: rhcosMktImg[3], + ThirdPartyImage: false, + }, + } + default: // Installer-created image gallery, should only be OKD. + // image gallery names cannot have dashes + galleryName := strings.ReplaceAll(infraID, "-", "_") + imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s", sub, rg, galleryName, infraID) + if gen == "V2" { + imageID += genV2Suffix + } + return &capz.Image{ID: &imageID} + } +} diff --git a/pkg/asset/machines/azure/machines.go b/pkg/asset/machines/azure/machines.go index 9cd8476b0f..51159f9002 100644 --- a/pkg/asset/machines/azure/machines.go +++ b/pkg/asset/machines/azure/machines.go @@ -27,7 +27,7 @@ const ( ) // Machines returns a list of machines for a machinepool. -func Machines(clusterID string, config *types.InstallConfig, pool *types.MachinePool, osImage, role, userDataSecret string, capabilities map[string]string, useImageGallery bool, session *icazure.Session) ([]machineapi.Machine, *machinev1.ControlPlaneMachineSet, error) { +func Machines(clusterID string, config *types.InstallConfig, pool *types.MachinePool, osImage, role, userDataSecret string, capabilities map[string]string, session *icazure.Session) ([]machineapi.Machine, *machinev1.ControlPlaneMachineSet, error) { if configPlatform := config.Platform.Name(); configPlatform != azure.Name { return nil, nil, fmt.Errorf("non-Azure configuration: %q", configPlatform) } @@ -62,7 +62,7 @@ func Machines(clusterID string, config *types.InstallConfig, pool *types.Machine azIndex = int(idx) % len(azs) } subnetIndex := int(idx) % len(subnets) - provider, err := provider(platform, mpool, osImage, userDataSecret, clusterID, role, &azIndex, capabilities, useImageGallery, session, networkResourceGroup, virtualNetworkName, subnets[subnetIndex]) + provider, err := provider(platform, mpool, osImage, userDataSecret, clusterID, role, &azIndex, capabilities, session, networkResourceGroup, virtualNetworkName, subnets[subnetIndex]) if err != nil { return nil, nil, errors.Wrap(err, "failed to create provider") } @@ -159,7 +159,7 @@ func Machines(clusterID string, config *types.InstallConfig, pool *types.Machine return machines, controlPlaneMachineSet, nil } -func provider(platform *azure.Platform, mpool *azure.MachinePool, osImage string, userDataSecret string, clusterID string, role string, azIdx *int, capabilities map[string]string, useImageGallery bool, session *icazure.Session, networkResourceGroup, virtualNetwork, subnet string) (*machineapi.AzureMachineProviderSpec, error) { +func provider(platform *azure.Platform, mpool *azure.MachinePool, osImage string, userDataSecret string, clusterID string, role string, azIdx *int, capabilities map[string]string, session *icazure.Session, networkResourceGroup, virtualNetwork, subnet string) (*machineapi.AzureMachineProviderSpec, error) { var az string if len(mpool.Zones) > 0 && azIdx != nil { az = mpool.Zones[*azIdx] @@ -179,32 +179,7 @@ func provider(platform *azure.Platform, mpool *azure.MachinePool, osImage string } rg := platform.ClusterResourceGroupName(clusterID) - var image machineapi.Image - if mpool.OSImage.Publisher != "" { - image.Type = machineapi.AzureImageTypeMarketplaceWithPlan - if mpool.OSImage.Plan == azure.ImageNoPurchasePlan { - image.Type = machineapi.AzureImageTypeMarketplaceNoPlan - } - image.Publisher = mpool.OSImage.Publisher - image.Offer = mpool.OSImage.Offer - image.SKU = mpool.OSImage.SKU - image.Version = mpool.OSImage.Version - } else if useImageGallery { - // image gallery names cannot have dashes - galleryName := strings.ReplaceAll(clusterID, "-", "_") - id := clusterID - if hyperVGen == "V2" { - id += "-gen2" - } - imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s/versions/latest", rg, galleryName, id) - image.ResourceID = imageID - } else { - imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/images/%s", rg, clusterID) - if hyperVGen == "V2" && platform.CloudName != azure.StackCloud { - imageID += "-gen2" - } - image.ResourceID = imageID - } + image := mapiImage(mpool.OSImage, platform.CloudName, hyperVGen, rg, session.Credentials.SubscriptionID, clusterID, osImage) if mpool.OSDisk.DiskType == "" { mpool.OSDisk.DiskType = "Premium_LRS" @@ -454,3 +429,30 @@ func generateSecurityProfile(mpool *azure.MachinePool) *machineapi.SecurityProfi return securityProfile } + +func mapiImage(osImage azure.OSImage, azEnv azure.CloudEnvironment, gen, rg, sub, infraID, rhcosImg string) machineapi.Image { + cImg := capzImage(osImage, azEnv, gen, rg, sub, infraID, rhcosImg) + mImg := machineapi.Image{} + + if cImg.ID != nil { + mImg.ResourceID = trimSubscriptionPrefix(*cImg.ID) + } else if cImg.Marketplace != nil { + mImg.Publisher = cImg.Marketplace.Publisher + mImg.Offer = cImg.Marketplace.Offer + mImg.SKU = cImg.Marketplace.SKU + mImg.Version = cImg.Marketplace.Version + mImg.Type = machineapi.AzureImageTypeMarketplaceNoPlan + if cImg.Marketplace.ThirdPartyImage { + mImg.Type = machineapi.AzureImageTypeMarketplaceWithPlan + } + } + return mImg +} + +// trimSubscriptionPrefix takes an image id string +// formatted for CAPZ and returns a string formatted +// for MAPI, by removing the /subspcription/ prefix. +func trimSubscriptionPrefix(image string) string { + rgIndex := strings.Index(image, "/resourceGroups/") + return image[rgIndex:] +} diff --git a/pkg/asset/machines/azure/machinesets.go b/pkg/asset/machines/azure/machinesets.go index 90f18cbd7e..1e8dbbcf92 100644 --- a/pkg/asset/machines/azure/machinesets.go +++ b/pkg/asset/machines/azure/machinesets.go @@ -18,7 +18,7 @@ import ( ) // MachineSets returns a list of machinesets for a machinepool. -func MachineSets(clusterID string, ic *installconfig.InstallConfig, pool *types.MachinePool, osImage, role, userDataSecret string, capabilities map[string]string, useImageGallery bool, subnetZones []string, session *icazure.Session) ([]*clusterapi.MachineSet, error) { +func MachineSets(clusterID string, ic *installconfig.InstallConfig, pool *types.MachinePool, osImage, role, userDataSecret string, capabilities map[string]string, subnetZones []string, session *icazure.Session) ([]*clusterapi.MachineSet, error) { config := ic.Config if configPlatform := config.Platform.Name(); configPlatform != azure.Name { return nil, fmt.Errorf("non-azure configuration: %q", configPlatform) @@ -63,7 +63,6 @@ func MachineSets(clusterID string, ic *installconfig.InstallConfig, pool *types. clusterID: clusterID, role: role, capabilities: capabilities, - useImageGallery: useImageGallery, session: session, subnetSpec: config.Azure.Subnets, replicas: total, @@ -78,7 +77,7 @@ func MachineSets(clusterID string, ic *installconfig.InstallConfig, pool *types. replicas++ } subnetIndex = (subnetIndex + 1) % len(subnets) - provider, err := provider(platform, mpool, osImage, userDataSecret, clusterID, role, &idx, capabilities, useImageGallery, session, networkResourceGroup, virtualNetworkName, subnets[subnetIndex]) + provider, err := provider(platform, mpool, osImage, userDataSecret, clusterID, role, &idx, capabilities, session, networkResourceGroup, virtualNetworkName, subnets[subnetIndex]) if err != nil { return nil, errors.Wrap(err, "failed to create provider") } @@ -137,7 +136,6 @@ type multiZoneMachineSetInput struct { clusterID string role string capabilities map[string]string - useImageGallery bool session *icazure.Session virtualNetworkName string subnetSpec []azure.SubnetSpec @@ -197,7 +195,7 @@ func getMultiZoneMachineSets(in multiZoneMachineSetInput) ([]*clusterapi.Machine currentReplica++ remainder-- } - provider, err := provider(in.platform, in.mpool, in.osImage, in.userDataSecret, in.clusterID, in.role, &idx, in.capabilities, in.useImageGallery, in.session, in.networkResourceGroup, in.virtualNetworkName, subnet) + provider, err := provider(in.platform, in.mpool, in.osImage, in.userDataSecret, in.clusterID, in.role, &idx, in.capabilities, in.session, in.networkResourceGroup, in.virtualNetworkName, subnet) if err != nil { return nil, errors.Wrap(err, "failed to create provider") } diff --git a/pkg/asset/machines/clusterapi.go b/pkg/asset/machines/clusterapi.go index bcec54b40d..697ee96327 100644 --- a/pkg/asset/machines/clusterapi.go +++ b/pkg/asset/machines/clusterapi.go @@ -296,16 +296,17 @@ func (c *ClusterAPI) Generate(ctx context.Context, dependencies asset.Parents) e session.Credentials.SubscriptionID, session, &azure.MachineInput{ - Subnet: subnet, - Role: "master", - UserDataSecret: "master-user-data", - HyperVGen: hyperVGen, - UseImageGallery: installConfig.Azure.CloudName != azuretypes.StackCloud, - Private: installConfig.Config.Publish == types.InternalPublishingStrategy, - UserTags: installConfig.Config.Platform.Azure.UserTags, - Platform: installConfig.Config.Platform.Azure, - Pool: &pool, - StorageSuffix: session.Environment.StorageEndpointSuffix, + Subnet: subnet, + Role: "master", + UserDataSecret: "master-user-data", + HyperVGen: hyperVGen, + Environment: installConfig.Azure.CloudName, + Private: installConfig.Config.Publish == types.InternalPublishingStrategy, + UserTags: installConfig.Config.Platform.Azure.UserTags, + Platform: installConfig.Config.Platform.Azure, + Pool: &pool, + StorageSuffix: session.Environment.StorageEndpointSuffix, + RHCOS: rhcosImage.ControlPlane, }, ) if err != nil { diff --git a/pkg/asset/machines/master.go b/pkg/asset/machines/master.go index 1718e58b91..d27f0c1c7b 100644 --- a/pkg/asset/machines/master.go +++ b/pkg/asset/machines/master.go @@ -405,8 +405,7 @@ func (m *Master) Generate(ctx context.Context, dependencies asset.Parents) error if err != nil { return err } - useImageGallery := installConfig.Azure.CloudName != azuretypes.StackCloud - machines, controlPlaneMachineSet, err = azure.Machines(clusterID.InfraID, ic, &pool, rhcosImage.ControlPlane, "master", masterUserDataSecretName, capabilities, useImageGallery, session) + machines, controlPlaneMachineSet, err = azure.Machines(clusterID.InfraID, ic, &pool, rhcosImage.ControlPlane, "master", masterUserDataSecretName, capabilities, session) if err != nil { return errors.Wrap(err, "failed to create master machine objects") } diff --git a/pkg/asset/machines/worker.go b/pkg/asset/machines/worker.go index 3ba9f45517..d45fa46985 100644 --- a/pkg/asset/machines/worker.go +++ b/pkg/asset/machines/worker.go @@ -615,8 +615,7 @@ func (w *Worker) Generate(ctx context.Context, dependencies asset.Parents) error return err } - useImageGallery := ic.Platform.Azure.CloudName != azuretypes.StackCloud - sets, err := azure.MachineSets(clusterID.InfraID, installConfig, &pool, rhcosImage.Compute, "worker", workerUserDataSecretName, capabilities, useImageGallery, subnetZones, session) + sets, err := azure.MachineSets(clusterID.InfraID, installConfig, &pool, rhcosImage.Compute, "worker", workerUserDataSecretName, capabilities, subnetZones, session) if err != nil { return errors.Wrap(err, "failed to create worker machine objects") }