sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/defaults.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package azure 18 19 import ( 20 "fmt" 21 "net/http" 22 "regexp" 23 "strings" 24 25 "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" 26 "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" 27 "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" 28 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 29 "sigs.k8s.io/cluster-api-provider-azure/util/tele" 30 "sigs.k8s.io/cluster-api-provider-azure/version" 31 ) 32 33 const ( 34 // DefaultUserName is the default username for a created VM. 35 DefaultUserName = "capi" 36 // DefaultAKSUserName is the default username for a created AKS VM. 37 DefaultAKSUserName = "azureuser" 38 // PublicCloudName is the name of the Azure public cloud. 39 PublicCloudName = "AzurePublicCloud" 40 // ChinaCloudName is the name of the Azure China cloud. 41 ChinaCloudName = "AzureChinaCloud" 42 // USGovernmentCloudName is the name of the Azure US Government cloud. 43 USGovernmentCloudName = "AzureUSGovernmentCloud" 44 ) 45 46 const ( 47 // DefaultImageOfferID is the default Azure Marketplace offer ID. 48 DefaultImageOfferID = "capi" 49 // DefaultWindowsImageOfferID is the default Azure Marketplace offer ID for Windows. 50 DefaultWindowsImageOfferID = "capi-windows" 51 // DefaultImagePublisherID is the default Azure Marketplace publisher ID. 52 DefaultImagePublisherID = "cncf-upstream" 53 // LatestVersion is the image version latest. 54 LatestVersion = "latest" 55 ) 56 57 const ( 58 // LinuxOS is Linux OS value for OSDisk.OSType. 59 LinuxOS = "Linux" 60 // WindowsOS is Windows OS value for OSDisk.OSType. 61 WindowsOS = "Windows" 62 ) 63 64 const ( 65 // BootstrappingExtensionLinux is the name of the Linux CAPZ bootstrapping VM extension. 66 BootstrappingExtensionLinux = "CAPZ.Linux.Bootstrapping" 67 // BootstrappingExtensionWindows is the name of the Windows CAPZ bootstrapping VM extension. 68 BootstrappingExtensionWindows = "CAPZ.Windows.Bootstrapping" 69 ) 70 71 const ( 72 // DefaultWindowsOsAndVersion is the default Windows Server version to use when 73 // generating default images for Windows nodes. 74 DefaultWindowsOsAndVersion = "windows-2019" 75 ) 76 77 const ( 78 // Global is the Azure global location value. 79 Global = "global" 80 ) 81 82 const ( 83 // PrivateAPIServerHostname will be used as the api server hostname for private clusters. 84 PrivateAPIServerHostname = "apiserver" 85 ) 86 87 const ( 88 // ControlPlaneNodeGroup will be used to create availability set for control plane machines. 89 ControlPlaneNodeGroup = "control-plane" 90 ) 91 92 const ( 93 // bootstrapExtensionRetries is the number of retries in the BootstrapExtensionCommand. 94 // NOTE: the overall timeout will be number of retries * retry sleep, in this case 60 * 5s = 300s. 95 bootstrapExtensionRetries = 60 96 // bootstrapExtensionSleep is the duration in seconds to sleep before each retry in the BootstrapExtensionCommand. 97 bootstrapExtensionSleep = 5 98 // bootstrapSentinelFile is the file written by bootstrap provider on machines to indicate successful bootstrapping, 99 // as defined by the Cluster API Bootstrap Provider contract (https://cluster-api.sigs.k8s.io/developer/providers/bootstrap.html). 100 bootstrapSentinelFile = "/run/cluster-api/bootstrap-success.complete" 101 ) 102 103 const ( 104 // CustomHeaderPrefix is the prefix of annotations that enable additional cluster / node pool features. 105 // Whatever follows the prefix will be passed as a header to cluster/node pool creation/update requests. 106 // E.g. add `"infrastructure.cluster.x-k8s.io/custom-header-UseGPUDedicatedVHD": "true"` annotation to 107 // AzureManagedMachinePool CR to enable creating GPU nodes by the node pool. 108 CustomHeaderPrefix = "infrastructure.cluster.x-k8s.io/custom-header-" 109 ) 110 111 var ( 112 // LinuxBootstrapExtensionCommand is the command the VM bootstrap extension will execute to verify Linux nodes bootstrap completes successfully. 113 LinuxBootstrapExtensionCommand = fmt.Sprintf("for i in $(seq 1 %d); do test -f %s && break; if [ $i -eq %d ]; then exit 1; else sleep %d; fi; done", bootstrapExtensionRetries, bootstrapSentinelFile, bootstrapExtensionRetries, bootstrapExtensionSleep) 114 // WindowsBootstrapExtensionCommand is the command the VM bootstrap extension will execute to verify Windows nodes bootstrap completes successfully. 115 WindowsBootstrapExtensionCommand = fmt.Sprintf("powershell.exe -Command \"for ($i = 0; $i -lt %d; $i++) {if (Test-Path '%s') {exit 0} else {Start-Sleep -Seconds %d}} exit -2\"", 116 bootstrapExtensionRetries, bootstrapSentinelFile, bootstrapExtensionSleep) 117 ) 118 119 // GenerateBackendAddressPoolName generates a load balancer backend address pool name. 120 func GenerateBackendAddressPoolName(lbName string) string { 121 return fmt.Sprintf("%s-%s", lbName, "backendPool") 122 } 123 124 // GenerateOutboundBackendAddressPoolName generates a load balancer outbound backend address pool name. 125 func GenerateOutboundBackendAddressPoolName(lbName string) string { 126 return fmt.Sprintf("%s-%s", lbName, "outboundBackendPool") 127 } 128 129 // GenerateFrontendIPConfigName generates a load balancer frontend IP config name. 130 func GenerateFrontendIPConfigName(lbName string) string { 131 return fmt.Sprintf("%s-%s", lbName, "frontEnd") 132 } 133 134 // GenerateNodeOutboundIPName generates a public IP name, based on the cluster name. 135 func GenerateNodeOutboundIPName(clusterName string) string { 136 return fmt.Sprintf("pip-%s-node-outbound", clusterName) 137 } 138 139 // GenerateNodePublicIPName generates a node public IP name, based on the machine name. 140 func GenerateNodePublicIPName(machineName string) string { 141 return fmt.Sprintf("pip-%s", machineName) 142 } 143 144 // GenerateControlPlaneOutboundLBName generates the name of the control plane outbound LB. 145 func GenerateControlPlaneOutboundLBName(clusterName string) string { 146 return fmt.Sprintf("%s-outbound-lb", clusterName) 147 } 148 149 // GenerateControlPlaneOutboundIPName generates a public IP name, based on the cluster name. 150 func GenerateControlPlaneOutboundIPName(clusterName string) string { 151 return fmt.Sprintf("pip-%s-controlplane-outbound", clusterName) 152 } 153 154 // GeneratePrivateDNSZoneName generates the name of a private DNS zone based on the cluster name. 155 func GeneratePrivateDNSZoneName(clusterName string) string { 156 return fmt.Sprintf("%s.capz.io", clusterName) 157 } 158 159 // GeneratePrivateFQDN generates the FQDN for a private API Server based on the private DNS zone name. 160 func GeneratePrivateFQDN(zoneName string) string { 161 return fmt.Sprintf("%s.%s", PrivateAPIServerHostname, zoneName) 162 } 163 164 // GenerateVNetLinkName generates the name of a virtual network link name based on the vnet name. 165 func GenerateVNetLinkName(vnetName string) string { 166 return fmt.Sprintf("%s-link", vnetName) 167 } 168 169 // GenerateNICName generates the name of a network interface based on the name of a VM. 170 func GenerateNICName(machineName string, multiNIC bool, index int) string { 171 if multiNIC { 172 return fmt.Sprintf("%s-nic-%d", machineName, index) 173 } 174 return fmt.Sprintf("%s-nic", machineName) 175 } 176 177 // GeneratePublicNICName generates the name of a public network interface based on the name of a VM. 178 func GeneratePublicNICName(machineName string) string { 179 return fmt.Sprintf("%s-public-nic", machineName) 180 } 181 182 // GenerateOSDiskName generates the name of an OS disk based on the name of a VM. 183 func GenerateOSDiskName(machineName string) string { 184 return fmt.Sprintf("%s_OSDisk", machineName) 185 } 186 187 // GenerateDataDiskName generates the name of a data disk based on the name of a VM. 188 func GenerateDataDiskName(machineName, nameSuffix string) string { 189 return fmt.Sprintf("%s_%s", machineName, nameSuffix) 190 } 191 192 // GenerateVnetPeeringName generates the name for a peering between two vnets. 193 func GenerateVnetPeeringName(sourceVnetName string, remoteVnetName string) string { 194 return fmt.Sprintf("%s-To-%s", sourceVnetName, remoteVnetName) 195 } 196 197 // GenerateAvailabilitySetName generates the name of a availability set based on the cluster name and the node group. 198 // node group identifies the set of nodes that belong to this availability set: 199 // For control plane nodes, this will be `control-plane`. 200 // For worker nodes, this will be the machine deployment name. 201 func GenerateAvailabilitySetName(clusterName, nodeGroup string) string { 202 return fmt.Sprintf("%s_%s-as", clusterName, nodeGroup) 203 } 204 205 // WithIndex appends the index as suffix to a generated name. 206 func WithIndex(name string, n int) string { 207 return fmt.Sprintf("%s-%d", name, n) 208 } 209 210 // ResourceGroupID returns the azure resource ID for a given resource group. 211 func ResourceGroupID(subscriptionID, resourceGroup string) string { 212 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", subscriptionID, resourceGroup) 213 } 214 215 // VMID returns the azure resource ID for a given VM. 216 func VMID(subscriptionID, resourceGroup, vmName string) string { 217 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, vmName) 218 } 219 220 // VNetID returns the azure resource ID for a given VNet. 221 func VNetID(subscriptionID, resourceGroup, vnetName string) string { 222 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s", subscriptionID, resourceGroup, vnetName) 223 } 224 225 // SubnetID returns the azure resource ID for a given subnet. 226 func SubnetID(subscriptionID, resourceGroup, vnetName, subnetName string) string { 227 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s", subscriptionID, resourceGroup, vnetName, subnetName) 228 } 229 230 // PublicIPID returns the azure resource ID for a given public IP. 231 func PublicIPID(subscriptionID, resourceGroup, ipName string) string { 232 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/publicIPAddresses/%s", subscriptionID, resourceGroup, ipName) 233 } 234 235 // PublicIPPrefixID returns the azure resource ID for a given public IP prefix. 236 func PublicIPPrefixID(subscriptionID, resourceGroup, ipName string) string { 237 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/publicipprefixes/%s", subscriptionID, resourceGroup, ipName) 238 } 239 240 // RouteTableID returns the azure resource ID for a given route table. 241 func RouteTableID(subscriptionID, resourceGroup, routeTableName string) string { 242 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/routeTables/%s", subscriptionID, resourceGroup, routeTableName) 243 } 244 245 // SecurityGroupID returns the azure resource ID for a given security group. 246 func SecurityGroupID(subscriptionID, resourceGroup, nsgName string) string { 247 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/networkSecurityGroups/%s", subscriptionID, resourceGroup, nsgName) 248 } 249 250 // NatGatewayID returns the azure resource ID for a given NAT gateway. 251 func NatGatewayID(subscriptionID, resourceGroup, natgatewayName string) string { 252 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/natGateways/%s", subscriptionID, resourceGroup, natgatewayName) 253 } 254 255 // NetworkInterfaceID returns the azure resource ID for a given network interface. 256 func NetworkInterfaceID(subscriptionID, resourceGroup, nicName string) string { 257 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/networkInterfaces/%s", subscriptionID, resourceGroup, nicName) 258 } 259 260 // FrontendIPConfigID returns the azure resource ID for a given frontend IP config. 261 func FrontendIPConfigID(subscriptionID, resourceGroup, loadBalancerName, configName string) string { 262 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers/%s/frontendIPConfigurations/%s", subscriptionID, resourceGroup, loadBalancerName, configName) 263 } 264 265 // AddressPoolID returns the azure resource ID for a given backend address pool. 266 func AddressPoolID(subscriptionID, resourceGroup, loadBalancerName, backendPoolName string) string { 267 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers/%s/backendAddressPools/%s", subscriptionID, resourceGroup, loadBalancerName, backendPoolName) 268 } 269 270 // ProbeID returns the azure resource ID for a given probe. 271 func ProbeID(subscriptionID, resourceGroup, loadBalancerName, probeName string) string { 272 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers/%s/probes/%s", subscriptionID, resourceGroup, loadBalancerName, probeName) 273 } 274 275 // NATRuleID returns the azure resource ID for a inbound NAT rule. 276 func NATRuleID(subscriptionID, resourceGroup, loadBalancerName, natRuleName string) string { 277 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/loadBalancers/%s/inboundNatRules/%s", subscriptionID, resourceGroup, loadBalancerName, natRuleName) 278 } 279 280 // AvailabilitySetID returns the azure resource ID for a given availability set. 281 func AvailabilitySetID(subscriptionID, resourceGroup, availabilitySetName string) string { 282 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/availabilitySets/%s", subscriptionID, resourceGroup, availabilitySetName) 283 } 284 285 // PrivateDNSZoneID returns the azure resource ID for a given private DNS zone. 286 func PrivateDNSZoneID(subscriptionID, resourceGroup, privateDNSZoneName string) string { 287 return fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/privateDnsZones/%s", subscriptionID, resourceGroup, privateDNSZoneName) 288 } 289 290 // VirtualNetworkLinkID returns the azure resource ID for a given virtual network link. 291 func VirtualNetworkLinkID(subscriptionID, resourceGroup, privateDNSZoneName, virtualNetworkLinkName string) string { 292 return fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/privateDnsZones/%s/virtualNetworkLinks/%s", subscriptionID, resourceGroup, privateDNSZoneName, virtualNetworkLinkName) 293 } 294 295 // ManagedClusterID returns the azure resource ID for a given managed cluster. 296 func ManagedClusterID(subscriptionID, resourceGroup, managedClusterName string) string { 297 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerService/managedClusters/%s", subscriptionID, resourceGroup, managedClusterName) 298 } 299 300 // FleetID returns the azure resource ID for a given fleet manager. 301 func FleetID(subscriptionID, resourceGroup, fleetName string) string { 302 return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerService/fleets/%s", subscriptionID, resourceGroup, fleetName) 303 } 304 305 // GetBootstrappingVMExtension returns the CAPZ Bootstrapping VM extension. 306 // The CAPZ Bootstrapping extension is a simple clone of https://github.com/Azure/custom-script-extension-linux for Linux or 307 // https://learn.microsoft.com/azure/virtual-machines/extensions/custom-script-windows for Windows. 308 // This extension allows running arbitrary scripts on the VM. 309 // Its role is to detect and report Kubernetes bootstrap failure or success. 310 func GetBootstrappingVMExtension(osType string, cloud string, vmName string, cpuArchitectureType string) *ExtensionSpec { 311 // currently, the bootstrap extension is only available in AzurePublicCloud. 312 if osType == LinuxOS && cloud == PublicCloudName { 313 // The command checks for the existence of the bootstrapSentinelFile on the machine, with retries and sleep between retries. 314 // We set the version to 1.1 (will target 1.1.1) for arm64 machines and 1.0 for x64. This is due to a known issue with newer versions of 315 // Go on Ubuntu 20.04. The issue is being tracked here: https://github.com/golang/go/issues/58550 316 // TODO: Remove this once the issue is fixed, or when Ubuntu 20.04 is no longer supported. 317 // We are using 1.1 instead of 1.1.1 for Arm64 as AzureAPI do not allow us to specify the full version. 318 extensionVersion := "1.0" 319 if cpuArchitectureType == string(armcompute.ArchitectureTypesArm64) { 320 extensionVersion = "1.1" 321 } 322 return &ExtensionSpec{ 323 Name: BootstrappingExtensionLinux, 324 VMName: vmName, 325 Publisher: "Microsoft.Azure.ContainerUpstream", 326 Version: extensionVersion, 327 ProtectedSettings: map[string]string{ 328 "commandToExecute": LinuxBootstrapExtensionCommand, 329 }, 330 } 331 } else if osType == WindowsOS && cloud == PublicCloudName { 332 // This command for the existence of the bootstrapSentinelFile on the machine, with retries and sleep between reties. 333 // If the file is not present after the retries are exhausted the extension fails with return code '-2' - ERROR_FILE_NOT_FOUND. 334 return &ExtensionSpec{ 335 Name: BootstrappingExtensionWindows, 336 VMName: vmName, 337 Publisher: "Microsoft.Azure.ContainerUpstream", 338 Version: "1.0", 339 ProtectedSettings: map[string]string{ 340 "commandToExecute": WindowsBootstrapExtensionCommand, 341 }, 342 } 343 } 344 345 return nil 346 } 347 348 // UserAgent specifies a string to append to the agent identifier. 349 func UserAgent() string { 350 return fmt.Sprintf("cluster-api-provider-azure/%s", version.Get().String()) 351 } 352 353 // ARMClientOptions returns default ARM client options for CAPZ SDK v2 requests. 354 func ARMClientOptions(azureEnvironment string, extraPolicies ...policy.Policy) (*arm.ClientOptions, error) { 355 opts := &arm.ClientOptions{} 356 357 switch azureEnvironment { 358 case PublicCloudName: 359 opts.Cloud = cloud.AzurePublic 360 case ChinaCloudName: 361 opts.Cloud = cloud.AzureChina 362 case USGovernmentCloudName: 363 opts.Cloud = cloud.AzureGovernment 364 case "": 365 // No cloud name provided, so leave at defaults. 366 default: 367 return nil, fmt.Errorf("invalid cloud name %q", azureEnvironment) 368 } 369 opts.PerCallPolicies = []policy.Policy{ 370 correlationIDPolicy{}, 371 userAgentPolicy{}, 372 } 373 opts.PerCallPolicies = append(opts.PerCallPolicies, extraPolicies...) 374 opts.Retry.MaxRetries = -1 // Less than zero means one try and no retries. 375 376 return opts, nil 377 } 378 379 // correlationIDPolicy adds the "x-ms-correlation-request-id" header to requests. 380 // It implements the policy.Policy interface. 381 type correlationIDPolicy struct{} 382 383 // Do adds the "x-ms-correlation-request-id" header if a request has a correlation ID in its context. 384 func (p correlationIDPolicy) Do(req *policy.Request) (*http.Response, error) { 385 if corrID, ok := tele.CorrIDFromCtx(req.Raw().Context()); ok { 386 req.Raw().Header.Set(string(tele.CorrIDKeyVal), string(corrID)) 387 } 388 return req.Next() 389 } 390 391 // userAgentPolicy extends the "User-Agent" header on requests. 392 // It implements the policy.Policy interface. 393 type userAgentPolicy struct{} 394 395 // Do extends the "User-Agent" header of a request by appending CAPZ's user agent. 396 func (p userAgentPolicy) Do(req *policy.Request) (*http.Response, error) { 397 req.Raw().Header.Set("User-Agent", req.Raw().UserAgent()+" "+UserAgent()) 398 return req.Next() 399 } 400 401 // CustomPutPatchHeaderPolicy adds custom headers to a PUT or PATCH request. 402 // It implements the policy.Policy interface. 403 type CustomPutPatchHeaderPolicy struct { 404 Headers map[string]string 405 } 406 407 // Do adds any custom headers to a PUT or PATCH request. 408 func (p CustomPutPatchHeaderPolicy) Do(req *policy.Request) (*http.Response, error) { 409 if req.Raw().Method == http.MethodPut || req.Raw().Method == http.MethodPatch { 410 for key, element := range p.Headers { 411 req.Raw().Header.Set(key, element) 412 } 413 } 414 415 return req.Next() 416 } 417 418 // GetNormalizedKubernetesName returns a normalized name for a Kubernetes resource. 419 func GetNormalizedKubernetesName(name string) string { 420 // Remove non-alphanumeric characters, convert to lowercase, and replace underscores with hyphens 421 name = strings.ToLower(name) 422 re := regexp.MustCompile(`[^a-z0-9\-]+`) 423 name = re.ReplaceAllString(name, "-") 424 425 // Remove leading and trailing hyphens 426 name = strings.Trim(name, "-") 427 return name 428 }