sigs.k8s.io/cluster-api-provider-azure@v1.14.3/util/azure/azure.go (about) 1 /* 2 Copyright 2022 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 "context" 21 "os" 22 "strings" 23 "time" 24 25 "github.com/Azure/azure-sdk-for-go/sdk/azcore" 26 "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" 27 "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" 28 "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 29 "github.com/Azure/go-autorest/autorest" 30 azureautorest "github.com/Azure/go-autorest/autorest/azure" 31 "github.com/Azure/go-autorest/autorest/azure/auth" 32 "github.com/jongio/azidext/go/azidext" 33 "github.com/pkg/errors" 34 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 35 "sigs.k8s.io/controller-runtime/pkg/client" 36 ) 37 38 // AzureSystemNodeLabelPrefix is a standard node label prefix for Azure features, e.g., kubernetes.azure.com/scalesetpriority. 39 const AzureSystemNodeLabelPrefix = "kubernetes.azure.com" 40 41 const ( 42 // ProviderIDPrefix will be appended to the beginning of Azure resource IDs to form the Kubernetes Provider ID. 43 // NOTE: this format matches the 2 slashes format used in cloud-provider and cluster-autoscaler. 44 ProviderIDPrefix = "azure://" 45 ) 46 47 // IsAzureSystemNodeLabelKey is a helper function that determines whether a node label key is an Azure "system" label. 48 func IsAzureSystemNodeLabelKey(labelKey string) bool { 49 return strings.HasPrefix(labelKey, AzureSystemNodeLabelPrefix) 50 } 51 52 func getCloudConfig(environment azureautorest.Environment) cloud.Configuration { 53 var config cloud.Configuration 54 switch environment.Name { 55 case "AzureStackCloud": 56 config = cloud.Configuration{ 57 ActiveDirectoryAuthorityHost: environment.ActiveDirectoryEndpoint, 58 Services: map[cloud.ServiceName]cloud.ServiceConfiguration{ 59 cloud.ResourceManager: { 60 Audience: environment.TokenAudience, 61 Endpoint: environment.ResourceManagerEndpoint, 62 }, 63 }, 64 } 65 case "AzureChinaCloud": 66 config = cloud.AzureChina 67 case "AzureUSGovernmentCloud": 68 config = cloud.AzureGovernment 69 default: 70 config = cloud.AzurePublic 71 } 72 return config 73 } 74 75 // GetAuthorizer returns an autorest.Authorizer-compatible object from MSAL. 76 func GetAuthorizer(settings auth.EnvironmentSettings) (autorest.Authorizer, error) { 77 // azidentity uses different envvars for certificate authentication: 78 // azidentity: AZURE_CLIENT_CERTIFICATE_{PATH,PASSWORD} 79 // autorest: AZURE_CERTIFICATE_{PATH,PASSWORD} 80 // Let's set them according to the envvars used by autorest, in case they are present 81 _, azidSet := os.LookupEnv("AZURE_CLIENT_CERTIFICATE_PATH") 82 path, autorestSet := os.LookupEnv("AZURE_CERTIFICATE_PATH") 83 if !azidSet && autorestSet { 84 os.Setenv("AZURE_CLIENT_CERTIFICATE_PATH", path) 85 os.Setenv("AZURE_CLIENT_CERTIFICATE_PASSWORD", os.Getenv("AZURE_CERTIFICATE_PASSWORD")) 86 } 87 88 options := azidentity.DefaultAzureCredentialOptions{ 89 ClientOptions: azcore.ClientOptions{ 90 Cloud: getCloudConfig(settings.Environment), 91 }, 92 } 93 cred, err := azidentity.NewDefaultAzureCredential(&options) 94 if err != nil { 95 return nil, err 96 } 97 98 // We must use TokenAudience for StackCloud, otherwise we get an 99 // AADSTS500011 error from the API 100 scope := settings.Environment.TokenAudience 101 if !strings.HasSuffix(scope, "/.default") { 102 scope += "/.default" 103 } 104 return azidext.NewTokenCredentialAdapter(cred, []string{scope}), nil 105 } 106 107 // FindParentMachinePool finds the parent MachinePool for the AzureMachinePool. 108 func FindParentMachinePool(ampName string, cli client.Client) (*expv1.MachinePool, error) { 109 ctx := context.Background() 110 machinePoolList := &expv1.MachinePoolList{} 111 if err := cli.List(ctx, machinePoolList); err != nil { 112 return nil, errors.Wrapf(err, "failed to list MachinePools for %s", ampName) 113 } 114 for _, mp := range machinePoolList.Items { 115 if mp.Spec.Template.Spec.InfrastructureRef.Name == ampName { 116 return &mp, nil 117 } 118 } 119 return nil, errors.Errorf("failed to get MachinePool for %s", ampName) 120 } 121 122 // FindParentMachinePoolWithRetry finds the parent MachinePool for the AzureMachinePool with retry. 123 func FindParentMachinePoolWithRetry(ampName string, cli client.Client, maxAttempts int) (*expv1.MachinePool, error) { 124 for i := 1; ; i++ { 125 p, err := FindParentMachinePool(ampName, cli) 126 if err != nil { 127 if i >= maxAttempts { 128 return nil, errors.Wrap(err, "failed to find parent MachinePool") 129 } 130 time.Sleep(1 * time.Second) 131 continue 132 } 133 return p, nil 134 } 135 } 136 137 // ParseResourceID parses a string to an *arm.ResourceID, first removing any "azure://" prefix. 138 func ParseResourceID(id string) (*arm.ResourceID, error) { 139 return arm.ParseResourceID(strings.TrimPrefix(id, ProviderIDPrefix)) 140 }