Skip to content

Commit

Permalink
azure: Create capz machine manifests
Browse files Browse the repository at this point in the history
Creating machine manifests for azure CAPI implementation.
  • Loading branch information
rna-afk committed Feb 1, 2024
1 parent a07aae8 commit 4a7b570
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 4 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ require (
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
sigs.k8s.io/cluster-api v1.5.3
sigs.k8s.io/cluster-api-provider-aws/v2 v2.0.0-00010101000000-000000000000
sigs.k8s.io/cluster-api-provider-azure v0.0.0-00010101000000-000000000000
sigs.k8s.io/cluster-api-provider-azure v1.13.0
sigs.k8s.io/cluster-api-provider-gcp v1.5.0
sigs.k8s.io/controller-runtime v0.16.2
sigs.k8s.io/controller-tools v0.12.0
Expand Down
275 changes: 275 additions & 0 deletions pkg/asset/machines/azure/azuremachines.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// Package azure generates Machine objects for azure.
package azure

import (
"fmt"
"sort"
"strings"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/ptr"
capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
capi "sigs.k8s.io/cluster-api/api/v1beta1"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/azure"
)

// GenerateMachines returns manifests and runtime objects to provision the control plane (including bootstrap, if applicable) nodes using CAPI.
func GenerateMachines(platform *azure.Platform, pool *types.MachinePool, userDataSecret string, clusterID string, role string, capabilities map[string]string, useImageGallery bool, userTags map[string]string, hyperVGen string, subnet string, resourceGroup string) ([]*asset.RuntimeFile, error) {
if poolPlatform := pool.Platform.Name(); poolPlatform != azure.Name {
return nil, fmt.Errorf("non-Azure machine-pool: %q", poolPlatform)
}
mpool := pool.Platform.Azure

total := int64(1)
if pool.Replicas != nil {
total = *pool.Replicas
}

if len(mpool.Zones) == 0 {
// if no azs are given we set to []string{""} for convenience over later operations.
// It means no-zoned for the machine API
mpool.Zones = []string{""}
}
tags, err := CapzTagsFromUserTags(clusterID, userTags)
if err != nil {
return nil, fmt.Errorf("failed to create machineapi.TagSpecifications from UserTags: %w", err)
}

var image capz.Image
osImage := mpool.OSImage
if osImage.Publisher != "" {
image = capz.Image{
Marketplace: &capz.AzureMarketplaceImage{
ImagePlan: capz.ImagePlan{
Publisher: osImage.Publisher,
Offer: osImage.Offer,
SKU: osImage.SKU,
},
Version: osImage.Version,
},
}
} else if useImageGallery {
// image gallery names cannot have dashes
galleryName := strings.Replace(clusterID, "-", "_", -1)
id := clusterID
if hyperVGen == "V2" {
id += "-gen2"
}
imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/galleries/gallery_%s/images/%s/versions/latest", resourceGroup, galleryName, id)
image.ID = &imageID
} else {
imageID := fmt.Sprintf("/resourceGroups/%s/providers/Microsoft.Compute/images/%s", resourceGroup, clusterID)
if hyperVGen == "V2" && platform.CloudName != azure.StackCloud {
imageID += "-gen2"
}
image.ID = &imageID
}

osDisk := capz.OSDisk{
OSType: "Linux",
DiskSizeGB: &mpool.DiskSizeGB,
ManagedDisk: &capz.ManagedDiskParameters{
StorageAccountType: mpool.DiskType,
},
}
ultrassd := mpool.UltraSSDCapability == "Enabled"
additionalCapabilities := capz.AdditionalCapabilities{
UltraSSDEnabled: &ultrassd,
}

diagnostics := capz.Diagnostics{
Boot: nil,
}

machineProfile := generateSecurityProfile(mpool)
securityProfile := capz.SecurityProfile{
EncryptionAtHost: machineProfile.EncryptionAtHost,
SecurityType: capz.SecurityTypes(machineProfile.Settings.SecurityType),
}
if machineProfile.Settings.ConfidentialVM != nil {
securityProfile.UefiSettings = &capz.UefiSettings{
VTpmEnabled: ptr.To(machineProfile.Settings.ConfidentialVM.UEFISettings.VirtualizedTrustedPlatformModule == "Enabled"),
SecureBootEnabled: ptr.To(machineProfile.Settings.ConfidentialVM.UEFISettings.SecureBoot == "Enabled"),
}
} else if machineProfile.Settings.TrustedLaunch != nil {
securityProfile.UefiSettings = &capz.UefiSettings{
VTpmEnabled: ptr.To(machineProfile.Settings.TrustedLaunch.UEFISettings.VirtualizedTrustedPlatformModule == "Enabled"),
SecureBootEnabled: ptr.To(machineProfile.Settings.TrustedLaunch.UEFISettings.SecureBoot == "Enabled"),
}
}

userAssignedIdentity := []capz.UserAssignedIdentity{{
ProviderID: "",
}}

var result []*asset.RuntimeFile
for idx := int64(0); idx < total; idx++ {
zone := mpool.Zones[int(idx)%len(mpool.Zones)]

networkInterfaces := []capz.NetworkInterface{{
SubnetName: subnet,
AcceleratedNetworking: additionalCapabilities.UltraSSDEnabled,
}}

azureMachine := &capz.AzureMachine{
TypeMeta: metav1.TypeMeta{
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
Kind: "AzureMachine",
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s-%d", clusterID, pool.Name, idx),
Labels: map[string]string{
"cluster.x-k8s.io/control-plane": "",
},
},
Spec: capz.AzureMachineSpec{
// ProviderID: ptr.ToStringPtr(),
VMSize: mpool.InstanceType, // required
Image: image.DeepCopy(),
FailureDomain: ptr.To(zone),
Identity: capz.VMIdentityUserAssigned,
UserAssignedIdentities: userAssignedIdentity,
SystemAssignedIdentityRole: nil,
OSDisk: osDisk, // required
SSHPublicKey: "",
AdditionalTags: tags,
AdditionalCapabilities: additionalCapabilities.DeepCopy(),
AllocatePublicIP: true,
EnableIPForwarding: false,
Diagnostics: diagnostics.DeepCopy(),
SecurityProfile: securityProfile.DeepCopy(),
NetworkInterfaces: networkInterfaces,
},
}

result = append(result, &asset.RuntimeFile{
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", azureMachine.Name)},
Object: azureMachine,
})

controlPlaneMachine := &capi.Machine{
ObjectMeta: metav1.ObjectMeta{
Name: azureMachine.Name,
Labels: map[string]string{
"cluster.x-k8s.io/control-plane": "",
},
},
Spec: capi.MachineSpec{
ClusterName: clusterID,
Bootstrap: capi.Bootstrap{
DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, role)),
},
InfrastructureRef: v1.ObjectReference{
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
Kind: "AzureMachine",
Name: azureMachine.Name,
},
},
}

result = append(result, &asset.RuntimeFile{
File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", azureMachine.Name)},
Object: controlPlaneMachine,
})
}

networkInterface := []capz.NetworkInterface{{
SubnetName: subnet,
AcceleratedNetworking: additionalCapabilities.UltraSSDEnabled,
}}

bootstrapAzureMachine := &capz.AzureMachine{
TypeMeta: metav1.TypeMeta{
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
Kind: "AzureMachine",
},
ObjectMeta: metav1.ObjectMeta{
Name: capiutils.GenerateBoostrapMachineName(clusterID),
Labels: map[string]string{
"cluster.x-k8s.io/control-plane": "",
"install.openshift.io/bootstrap": "",
},
},
Spec: capz.AzureMachineSpec{
// ProviderID: ptr.ToStringPtr(),
VMSize: mpool.InstanceType, // required
Image: image.DeepCopy(),
FailureDomain: ptr.To(mpool.Zones[0]),
Identity: capz.VMIdentityUserAssigned,
UserAssignedIdentities: []capz.UserAssignedIdentity{},
SystemAssignedIdentityRole: nil,
OSDisk: osDisk, // required
SSHPublicKey: "",
AdditionalTags: tags,
AdditionalCapabilities: &additionalCapabilities,
AllocatePublicIP: true,
EnableIPForwarding: false,
SecurityProfile: &securityProfile,
Diagnostics: &diagnostics,
NetworkInterfaces: networkInterface,
},
}

result = append(result, &asset.RuntimeFile{
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", bootstrapAzureMachine.Name)},
Object: bootstrapAzureMachine,
})

bootstrapMachine := &capi.Machine{
ObjectMeta: metav1.ObjectMeta{
Name: bootstrapAzureMachine.Name,
Labels: map[string]string{
"cluster.x-k8s.io/control-plane": "",
},
},
Spec: capi.MachineSpec{
ClusterName: clusterID,
Bootstrap: capi.Bootstrap{
DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, "bootstrap")),
},
InfrastructureRef: v1.ObjectReference{
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
Kind: "AzureMachine",
Name: bootstrapAzureMachine.Name,
},
},
}

result = append(result, &asset.RuntimeFile{
File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", bootstrapMachine.Name)},
Object: bootstrapMachine,
})

return result, nil
}

// CapzTagsFromUserTags converts a map of user tags to a map of capz.Tags.
func CapzTagsFromUserTags(clusterID string, usertags map[string]string) (capz.Tags, error) {
tags := capz.Tags{}
tags[fmt.Sprintf("kubernetes.io/cluster/%s", clusterID)] = "owned"

forbiddenTags := sets.NewString()
for key := range tags {
forbiddenTags.Insert(key)
}

userTagKeys := make([]string, 0, len(usertags))
for key := range usertags {
userTagKeys = append(userTagKeys, key)
}
sort.Strings(userTagKeys)

for _, k := range userTagKeys {
if forbiddenTags.Has(k) {
return nil, fmt.Errorf("user tags may not clobber %s", k)
}
tags[k] = usertags[k]
}
return tags, nil
}
74 changes: 73 additions & 1 deletion pkg/asset/machines/clusterapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ import (
configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/installconfig"
icazure "github.com/openshift/installer/pkg/asset/installconfig/azure"
"github.com/openshift/installer/pkg/asset/machines/aws"
"github.com/openshift/installer/pkg/asset/machines/azure"
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
"github.com/openshift/installer/pkg/asset/rhcos"
"github.com/openshift/installer/pkg/clusterapi"
awstypes "github.com/openshift/installer/pkg/types/aws"
awsdefaults "github.com/openshift/installer/pkg/types/aws/defaults"
azuretypes "github.com/openshift/installer/pkg/types/azure"
azuredefaults "github.com/openshift/installer/pkg/types/azure/defaults"
)

var _ asset.WritableRuntimeAsset = (*ClusterAPI)(nil)
Expand Down Expand Up @@ -216,7 +219,76 @@ func (c *ClusterAPI) Generate(dependencies asset.Parents) error {
Object: bootstrapMachine,
})
case azuretypes.Name:
// TODO: implement
mpool := defaultAzureMachinePoolPlatform()
mpool.InstanceType = azuredefaults.ControlPlaneInstanceType(
installConfig.Config.Platform.Azure.CloudName,
installConfig.Config.Platform.Azure.Region,
installConfig.Config.ControlPlane.Architecture,
)
mpool.OSDisk.DiskSizeGB = 1024
if installConfig.Config.Platform.Azure.CloudName == azuretypes.StackCloud {
mpool.OSDisk.DiskSizeGB = azuredefaults.AzurestackMinimumDiskSize
}
mpool.Set(ic.Platform.Azure.DefaultMachinePlatform)
mpool.Set(pool.Platform.Azure)

session, err := installConfig.Azure.Session()
if err != nil {
return fmt.Errorf("failed to fetch session: %v", err)
}
client := icazure.NewClient(session)

if len(mpool.Zones) == 0 {
// if no azs are given we set to []string{""} for convenience over later operations.
// It means no-zoned for the machine API
mpool.Zones = []string{""}
}
if len(mpool.Zones) == 0 {
azs, err := client.GetAvailabilityZones(context.TODO(), ic.Platform.Azure.Region, mpool.InstanceType)
if err != nil {
return fmt.Errorf("failed to fetch availability zones: %v", err)
}
mpool.Zones = azs
if len(azs) == 0 {
// if no azs are given we set to []string{""} for convenience over later operations.
// It means no-zoned for the machine API
mpool.Zones = []string{""}
}
}
// client.GetControlPlaneSubnet(context.TODO(), ic.Platform.Azure.ResourceGroupName, ic.Platform.Azure.VirtualNetwork, )

if mpool.OSImage.Publisher != "" {
img, ierr := client.GetMarketplaceImage(context.TODO(), ic.Platform.Azure.Region, mpool.OSImage.Publisher, mpool.OSImage.Offer, mpool.OSImage.SKU, mpool.OSImage.Version)
if ierr != nil {
return fmt.Errorf("failed to fetch marketplace image: %w", ierr)
}
// Publisher is case-sensitive and matched against exactly. Also the
// Plan's publisher might not be exactly the same as the Image's
// publisher
if img.Plan != nil && img.Plan.Publisher != nil {
mpool.OSImage.Publisher = *img.Plan.Publisher
}
}
pool.Platform.Azure = &mpool
subnet := ic.Azure.ControlPlaneSubnet

capabilities, err := client.GetVMCapabilities(context.TODO(), mpool.InstanceType, installConfig.Config.Platform.Azure.Region)
if err != nil {
return err
}
hyperVGen, err := icazure.GetHyperVGenerationVersion(capabilities, "")
if err != nil {
return err
}
useImageGallery := installConfig.Azure.CloudName != azuretypes.StackCloud
masterUserDataSecretName := "master-user-data"

azureMachines, err := azure.GenerateMachines(installConfig.Config.Platform.Azure, &pool, masterUserDataSecretName, clusterID.InfraID, "master", capabilities, useImageGallery, installConfig.Config.Platform.Azure.UserTags, hyperVGen, subnet, ic.Azure.ResourceGroupName)
if err != nil {
return fmt.Errorf("failed to create master machine objects: %v", err)
}

c.FileList = append(c.FileList, azureMachines...)
default:
// TODO: support other platforms
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/asset/manifests/azure/cluster.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package azure

import (
"fmt"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -40,7 +42,7 @@ func GenerateClusterAssets(installConfig *installconfig.InstallConfig, clusterID
Namespace: capiutils.Namespace,
},
Spec: capz.AzureClusterSpec{
ResourceGroup: clusterID.InfraID,
ResourceGroup: fmt.Sprintf("%s-rg", clusterID.InfraID),
AzureClusterClassSpec: capz.AzureClusterClassSpec{
SubscriptionID: session.Credentials.SubscriptionID,
Location: installConfig.Config.Azure.Region,
Expand Down

0 comments on commit 4a7b570

Please sign in to comment.