Skip to content

Commit

Permalink
Merge pull request #198 from HarrisonWAffel/azure-features
Browse files Browse the repository at this point in the history
[v2.7.1] Add flags for multiple Azure features
  • Loading branch information
HarrisonWAffel authored Nov 28, 2022
2 parents bd29474 + 6e3da01 commit 7c38a62
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 87 deletions.
156 changes: 102 additions & 54 deletions drivers/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
"net"
"net/url"
"os"
"strconv"

"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/rancher/machine/drivers/azure/azureutil"
"github.com/rancher/machine/libmachine/drivers"
"github.com/rancher/machine/libmachine/log"
"github.com/rancher/machine/libmachine/mcnflag"
"github.com/rancher/machine/libmachine/state"

"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
"github.com/Azure/go-autorest/autorest/azure"
)

const (
Expand All @@ -36,35 +36,39 @@ const (
)

const (
flAzureEnvironment = "azure-environment"
flAzureSubscriptionID = "azure-subscription-id"
flAzureTenantID = "azure-tenant-id"
flAzureResourceGroup = "azure-resource-group"
flAzureSSHUser = "azure-ssh-user"
flAzureDockerPort = "azure-docker-port"
flAzureLocation = "azure-location"
flAzureSize = "azure-size"
flAzureImage = "azure-image"
flAzureVNet = "azure-vnet"
flAzureSubnet = "azure-subnet"
flAzureSubnetPrefix = "azure-subnet-prefix"
flAzureAvailabilitySet = "azure-availability-set"
flAzureManagedDisks = "azure-managed-disks"
flAzureFaultDomainCount = "azure-fault-domain-count"
flAzureUpdateDomainCount = "azure-update-domain-count"
flAzureDiskSize = "azure-disk-size"
flAzurePorts = "azure-open-port"
flAzurePrivateIPAddr = "azure-private-ip-address"
flAzureUsePrivateIP = "azure-use-private-ip"
flAzureStaticPublicIP = "azure-static-public-ip"
flAzureNoPublicIP = "azure-no-public-ip"
flAzureDNSLabel = "azure-dns"
flAzureStorageType = "azure-storage-type"
flAzureCustomData = "azure-custom-data"
flAzureClientID = "azure-client-id"
flAzureClientSecret = "azure-client-secret"
flAzureNSG = "azure-nsg"
flAzurePlan = "azure-plan"
flAzureEnvironment = "azure-environment"
flAzureSubscriptionID = "azure-subscription-id"
flAzureTenantID = "azure-tenant-id"
flAzureResourceGroup = "azure-resource-group"
flAzureSSHUser = "azure-ssh-user"
flAzureDockerPort = "azure-docker-port"
flAzureLocation = "azure-location"
flAzureSize = "azure-size"
flAzureImage = "azure-image"
flAzureVNet = "azure-vnet"
flAzureSubnet = "azure-subnet"
flAzureSubnetPrefix = "azure-subnet-prefix"
flAzureAvailabilitySet = "azure-availability-set"
flAzureManagedDisks = "azure-managed-disks"
flAzureFaultDomainCount = "azure-fault-domain-count"
flAzureUpdateDomainCount = "azure-update-domain-count"
flAzureDiskSize = "azure-disk-size"
flAzurePorts = "azure-open-port"
flAzurePrivateIPAddr = "azure-private-ip-address"
flAzureUsePrivateIP = "azure-use-private-ip"
flAzureStaticPublicIP = "azure-static-public-ip"
flAzureNoPublicIP = "azure-no-public-ip"
flAzureDNSLabel = "azure-dns"
flAzureStorageType = "azure-storage-type"
flAzureCustomData = "azure-custom-data"
flAzureClientID = "azure-client-id"
flAzureClientSecret = "azure-client-secret"
flAzureNSG = "azure-nsg"
flAzurePlan = "azure-plan"
flAzureTags = "azure-tags"
flAzureAcceleratedNetworking = "azure-accelerated-networking"
flAzureEnablePublicIPStandardSKU = "azure-enable-public-ip-standard-sku"
flAzureAvailabilityZones = "azure-availability-zone"
)

const (
Expand All @@ -84,21 +88,25 @@ type Driver struct {
TenantID string
ResourceGroup string

DockerPort int
Location string
Size string
Image string
VirtualNetwork string
SubnetName string
SubnetPrefix string
AvailabilitySet string
NSG string
Plan string
ManagedDisks bool
FaultCount int
UpdateCount int
DiskSize int
StorageType string
DockerPort int
Location string
Size string
Image string
VirtualNetwork string
SubnetName string
SubnetPrefix string
AvailabilitySet string
NSG string
Plan string
ManagedDisks bool
FaultCount int
UpdateCount int
DiskSize int
StorageType string
Tags map[string]*string
AcceleratedNetworking bool
AvailabilityZone string
EnablePublicIPStandardSKU bool

OpenPorts []string
PrivateIPAddr string
Expand Down Expand Up @@ -289,6 +297,21 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
Usage: "Azure Service Principal Account password (optional, browser auth is used if not specified)",
EnvVar: "AZURE_CLIENT_SECRET",
},
mcnflag.StringFlag{
Name: flAzureTags,
Usage: "Tags to be applied to the Azure VM instance",
EnvVar: "AZURE_TAGS",
},
mcnflag.StringFlag{
Name: flAzureAvailabilityZones,
Usage: "Specify the Availability Zones the Azure resources should be created in",
EnvVar: "AZURE_AVAILABILITY_ZONE",
},
mcnflag.BoolFlag{
Name: flAzureEnablePublicIPStandardSKU,
Usage: "Specify if a Standard SKU should be used for the Public IP of the Azure VM",
EnvVar: "AZURE_STANDARD_PUBLIC_IP_SKU",
},
}
}

Expand Down Expand Up @@ -323,6 +346,10 @@ func (d *Driver) SetConfigFromFlags(fl drivers.DriverOptions) error {
}

// Optional flags or Flags of other types
d.AvailabilityZone = fl.String(flAzureAvailabilityZones)
d.EnablePublicIPStandardSKU = fl.Bool(flAzureEnablePublicIPStandardSKU)
d.Tags = azureutil.BuildInstanceTags(fl.String(flAzureTags))
d.AcceleratedNetworking = fl.Bool(flAzureAcceleratedNetworking)
d.Environment = fl.String(flAzureEnvironment)
d.OpenPorts = fl.StringSlice(flAzurePorts)
d.PrivateIPAddr = fl.String(flAzurePrivateIPAddr)
Expand Down Expand Up @@ -362,6 +389,21 @@ func (d *Driver) PreCreateCheck() (err error) {
}
}

if d.AvailabilityZone != "" {
if !d.ManagedDisks {
return fmt.Errorf("Managed Disks must be used when creating resources in specific Availability Zones (--azure-managed-disks)")
}
if v, err := strconv.Atoi(d.AvailabilityZone); err != nil || v == 0 {
return fmt.Errorf("Each VM can only be assigned to a single Availability Zone. Each zone is denoted by an integer greater than 0")
}
if !d.EnablePublicIPStandardSKU {
return fmt.Errorf("The Standard Public IP SKU must be enabled when creating resources in specific Availablity Zones (--azure-enable-public-ip-standard-sku)")
}
if d.AvailabilitySet != "" {
log.Warn("Both an Availability Set and Availability Zone were specified. Skipping creation of the Availability Set, only creating resources in the specified Availability Zone.")
}
}

ctx := context.Background()
c, err := d.newAzureClient(ctx)
if err != nil {
Expand Down Expand Up @@ -432,8 +474,11 @@ func (d *Driver) Create() error {
if err := c.CreateResourceGroup(ctx, d.ResourceGroup, d.Location); err != nil {
return err
}
if err := c.CreateAvailabilitySetIfNotExists(ctx, d.deploymentCtx, d.ResourceGroup, d.AvailabilitySet, d.Location, d.ManagedDisks, int32(d.FaultCount), int32(d.UpdateCount)); err != nil {
return err
// availability sets and availability zones cannot be used together. The presence of an Availability Zone indicates that an Availability set should not be created / used
if d.AvailabilityZone == "" {
if err := c.CreateAvailabilitySetIfNotExists(ctx, d.deploymentCtx, d.ResourceGroup, d.AvailabilitySet, d.Location, d.ManagedDisks, int32(d.FaultCount), int32(d.UpdateCount)); err != nil {
return err
}
}
if err := c.CreateNetworkSecurityGroup(ctx, d.deploymentCtx, d.ResourceGroup, d.nsgResource, d.Location, d.nsgUsedInPool, d.deploymentCtx.FirewallRules); err != nil {
return err
Expand All @@ -448,12 +493,12 @@ func (d *Driver) Create() error {
if d.NoPublicIP {
log.Info("Not creating a public IP address.")
} else {
if err := c.CreatePublicIPAddress(ctx, d.deploymentCtx, d.ResourceGroup, d.naming().IP(), d.Location, d.StaticPublicIP, d.DNSLabel); err != nil {
if err := c.CreatePublicIPAddress(ctx, d.deploymentCtx, d.ResourceGroup, d.naming().IP(), d.Location, d.StaticPublicIP, d.DNSLabel, d.EnablePublicIPStandardSKU); err != nil {
return err
}
}
if err := c.CreateNetworkInterface(ctx, d.deploymentCtx, d.ResourceGroup, d.naming().NIC(), d.Location,
d.deploymentCtx.PublicIPAddressID, d.deploymentCtx.SubnetID, d.deploymentCtx.NetworkSecurityGroupID, d.PrivateIPAddr); err != nil {
d.deploymentCtx.PublicIPAddressID, d.deploymentCtx.SubnetID, d.deploymentCtx.NetworkSecurityGroupID, d.PrivateIPAddr, d.AcceleratedNetworking); err != nil {
return err
}
if !d.ManagedDisks {
Expand All @@ -467,7 +512,7 @@ func (d *Driver) Create() error {
}
if err := c.CreateVirtualMachine(ctx, d.ResourceGroup, d.naming().VM(), d.Location, d.Size, d.deploymentCtx.AvailabilitySetID,
d.deploymentCtx.NetworkInterfaceID, d.BaseDriver.SSHUser, d.deploymentCtx.SSHPublicKey, d.Image, d.Plan, customData, d.deploymentCtx.StorageAccount,
d.ManagedDisks, d.StorageType, int32(d.DiskSize)); err != nil {
d.ManagedDisks, d.StorageType, int32(d.DiskSize), d.Tags, d.AvailabilityZone); err != nil {
return err
}
ip, err := d.GetIP()
Expand Down Expand Up @@ -514,8 +559,11 @@ func (d *Driver) Remove() error {
if err := c.DeleteNetworkSecurityGroupIfExists(ctx, d.nsgResource, d.nsgUsedInPool); err != nil {
return err
}
if err := c.CleanupAvailabilitySetIfExists(ctx, d.ResourceGroup, d.AvailabilitySet); err != nil {
return err
// availability sets and availability zones cannot be used together. The absence of any Availability Zones indicates that an Availability set was created and should be deleted.
if d.AvailabilityZone == "" {
if err := c.CleanupAvailabilitySetIfExists(ctx, d.ResourceGroup, d.AvailabilitySet); err != nil {
return err
}
}
if err := c.CleanupSubnetIfExists(ctx, d.ResourceGroup, d.VirtualNetwork, d.SubnetName); err != nil {
return err
Expand Down
101 changes: 68 additions & 33 deletions drivers/azure/azureutil/azureutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (a AzureClient) DeleteNetworkSecurityGroupIfExists(ctx context.Context, res
}

// CreatePublicIPAddress creates a public IP address and adds it to our DeploymentContext
func (a AzureClient) CreatePublicIPAddress(ctx context.Context, deploymentCtx *DeploymentContext, resourceGroup, name, location string, isStatic bool, dnsLabel string) error {
func (a AzureClient) CreatePublicIPAddress(ctx context.Context, deploymentCtx *DeploymentContext, resourceGroup, name, location string, isStatic bool, dnsLabel string, enablePublicIPStandardSKU bool) error {
log.Info("Creating public IP address.", logutil.Fields{
"name": name,
"static": isStatic})
Expand All @@ -179,15 +179,19 @@ func (a AzureClient) CreatePublicIPAddress(ctx context.Context, deploymentCtx *D
}
}

publicIP := network.PublicIPAddress{
Location: to.StringPtr(location),
PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{
PublicIPAllocationMethod: ipType,
DNSSettings: dns,
},
}
if enablePublicIPStandardSKU {
publicIP.Sku = &network.PublicIPAddressSku{Name: network.PublicIPAddressSkuNameStandard}
}

publicIPAddressClient := a.publicIPAddressClient()
future, err := publicIPAddressClient.CreateOrUpdate(ctx, resourceGroup, name,
network.PublicIPAddress{
Location: to.StringPtr(location),
PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{
PublicIPAllocationMethod: ipType,
DNSSettings: dns,
},
})
future, err := publicIPAddressClient.CreateOrUpdate(ctx, resourceGroup, name, publicIP)
if err != nil {
return err
}
Expand Down Expand Up @@ -311,7 +315,7 @@ func (a AzureClient) CleanupSubnetIfExists(ctx context.Context, resourceGroup, v
}

// CreateNetworkInterface creates a network interface
func (a AzureClient) CreateNetworkInterface(ctx context.Context, deploymentCtx *DeploymentContext, resourceGroup, name, location, publicIPAddressID, subnetID, nsgID, privateIPAddress string) error {
func (a AzureClient) CreateNetworkInterface(ctx context.Context, deploymentCtx *DeploymentContext, resourceGroup, name, location, publicIPAddressID, subnetID, nsgID, privateIPAddress string, enabledAcceleratedNetworking bool) error {
// NOTE(ahmetalpbalkan) This method is expected to fail if the user
// specified Azure location is different than location of the virtual
// network as Azure does not support cross-region virtual networks. In this
Expand All @@ -331,6 +335,7 @@ func (a AzureClient) CreateNetworkInterface(ctx context.Context, deploymentCtx *
future, err := networkInterfacesClient.CreateOrUpdate(ctx, resourceGroup, name, network.Interface{
Location: to.StringPtr(location),
InterfacePropertiesFormat: &network.InterfacePropertiesFormat{
EnableAcceleratedNetworking: to.BoolPtr(enabledAcceleratedNetworking),
NetworkSecurityGroup: &network.SecurityGroup{
ID: to.StringPtr(nsgID),
},
Expand Down Expand Up @@ -559,7 +564,7 @@ func (a AzureClient) removeOSDiskBlob(ctx context.Context, resourceGroup, vmName
// CreateVirtualMachine creates a VM according to the specifications and adds an SSH key to access the VM
func (a AzureClient) CreateVirtualMachine(ctx context.Context, resourceGroup, name, location, size, availabilitySetID, networkInterfaceID,
username, sshPublicKey, imageName, imagePlan, customData string, storageAccount *storage.AccountProperties, isManaged bool,
storageType string, diskSize int32) error {
storageType string, diskSize int32, tags map[string]*string, availabilityZone string) error {
// TODO: "VM created from Image cannot have blob based disks. All disks have to be managed disks."
imgReference, err := a.getImageReference(ctx, imageName, location)
if err != nil {
Expand Down Expand Up @@ -607,31 +612,43 @@ func (a AzureClient) CreateVirtualMachine(ctx context.Context, resourceGroup, na
}

virtualMachinesClient := a.virtualMachinesClient()
future, err := virtualMachinesClient.CreateOrUpdate(ctx, resourceGroup, name,
compute.VirtualMachine{
Location: to.StringPtr(location),
VirtualMachineProperties: &compute.VirtualMachineProperties{
AvailabilitySet: &compute.SubResource{
ID: to.StringPtr(availabilitySetID),
},
HardwareProfile: &compute.HardwareProfile{
VMSize: compute.VirtualMachineSizeTypes(size),
},
NetworkProfile: &compute.NetworkProfile{
NetworkInterfaces: &[]compute.NetworkInterfaceReference{
{
ID: to.StringPtr(networkInterfaceID),
},

vm := compute.VirtualMachine{
Tags: tags,
Location: to.StringPtr(location),
VirtualMachineProperties: &compute.VirtualMachineProperties{
HardwareProfile: &compute.HardwareProfile{
VMSize: compute.VirtualMachineSizeTypes(size),
},
NetworkProfile: &compute.NetworkProfile{
NetworkInterfaces: &[]compute.NetworkInterfaceReference{
{
ID: to.StringPtr(networkInterfaceID),
},
},
OsProfile: osProfile,
StorageProfile: &compute.StorageProfile{
ImageReference: imgReference,
OsDisk: getOSDisk(name, storageAccount, isManaged, storageType, diskSize),
},
},
Plan: imagePurchasePlan,
})
OsProfile: osProfile,
StorageProfile: &compute.StorageProfile{
ImageReference: imgReference,
OsDisk: getOSDisk(name, storageAccount, isManaged, storageType, diskSize),
},
},
Plan: imagePurchasePlan,
}

// The Azure API does not allow you to specify particular Availability Sets
// in particular Availability Zones - you can only specify one or the other.
// if a user has provided an availability zone it is assumed that
// no availability sets should be created / used.
if availabilityZone == "" {
vm.VirtualMachineProperties.AvailabilitySet = &compute.SubResource{
ID: to.StringPtr(availabilitySetID),
}
} else {
vm.Zones = to.StringSlicePtr([]string{availabilityZone})
}

future, err := virtualMachinesClient.CreateOrUpdate(ctx, resourceGroup, name, vm)
if err != nil {
return err
}
Expand Down Expand Up @@ -978,3 +995,21 @@ func extractStorageAccountFromVHDURL(vhdURL string) string {
}
return parts[0]
}

func BuildInstanceTags(tagGroups string) map[string]*string {
tags := make(map[string]*string)
if tagGroups == "" {
return tags
}

allTags := strings.Split(tagGroups, ",")
if len(allTags)%2 != 0 {
fmt.Printf("Tags are not in key value pairs. %d elements found\n", len(allTags))
}

for i := 0; i < len(allTags)-1; i += 2 {
tags[allTags[i]] = &allTags[i+1]
}

return tags
}

0 comments on commit 7c38a62

Please sign in to comment.