sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azuremachine_default.go (about) 1 /* 2 Copyright 2021 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 v1beta1 18 19 import ( 20 "context" 21 "encoding/base64" 22 "fmt" 23 "time" 24 25 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 26 "github.com/pkg/errors" 27 "golang.org/x/crypto/ssh" 28 kerrors "k8s.io/apimachinery/pkg/util/errors" 29 "k8s.io/apimachinery/pkg/util/uuid" 30 utilSSH "sigs.k8s.io/cluster-api-provider-azure/util/ssh" 31 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 ) 34 35 // ContributorRoleID is the ID of the built-in "Contributor" role. 36 const ContributorRoleID = "b24988ac-6180-42a0-ab88-20f7382dd24c" 37 38 // SetDefaultSSHPublicKey sets the default SSHPublicKey for an AzureMachine. 39 func (s *AzureMachineSpec) SetDefaultSSHPublicKey() error { 40 if sshKeyData := s.SSHPublicKey; sshKeyData == "" { 41 _, publicRsaKey, err := utilSSH.GenerateSSHKey() 42 if err != nil { 43 return err 44 } 45 46 s.SSHPublicKey = base64.StdEncoding.EncodeToString(ssh.MarshalAuthorizedKey(publicRsaKey)) 47 } 48 return nil 49 } 50 51 // SetDefaultCachingType sets the default cache type for an AzureMachine. 52 func (s *AzureMachineSpec) SetDefaultCachingType() { 53 if s.OSDisk.CachingType == "" { 54 s.OSDisk.CachingType = "None" 55 } 56 } 57 58 // SetDataDisksDefaults sets the data disk defaults for an AzureMachine. 59 func (s *AzureMachineSpec) SetDataDisksDefaults() { 60 set := make(map[int32]struct{}) 61 // populate all the existing values in the set 62 for _, disk := range s.DataDisks { 63 if disk.Lun != nil { 64 set[*disk.Lun] = struct{}{} 65 } 66 } 67 // Look for unique values for unassigned LUNs 68 for i, disk := range s.DataDisks { 69 if disk.Lun == nil { 70 for l := range s.DataDisks { 71 lun := int32(l) 72 if _, ok := set[lun]; !ok { 73 s.DataDisks[i].Lun = &lun 74 set[lun] = struct{}{} 75 break 76 } 77 } 78 } 79 if disk.CachingType == "" { 80 if s.DataDisks[i].ManagedDisk != nil && 81 s.DataDisks[i].ManagedDisk.StorageAccountType == string(armcompute.StorageAccountTypesUltraSSDLRS) { 82 s.DataDisks[i].CachingType = string(armcompute.CachingTypesNone) 83 } else { 84 s.DataDisks[i].CachingType = string(armcompute.CachingTypesReadWrite) 85 } 86 } 87 } 88 } 89 90 // SetIdentityDefaults sets the defaults for VM Identity. 91 func (s *AzureMachineSpec) SetIdentityDefaults(subscriptionID string) { 92 // Ensure the deprecated fields and new fields are not populated simultaneously 93 if s.RoleAssignmentName != "" && s.SystemAssignedIdentityRole != nil && s.SystemAssignedIdentityRole.Name != "" { 94 // Both the deprecated and the new fields are both set, return without changes 95 // and reject the request in the validating webhook which runs later. 96 return 97 } 98 if s.Identity == VMIdentitySystemAssigned { 99 if s.SystemAssignedIdentityRole == nil { 100 s.SystemAssignedIdentityRole = &SystemAssignedIdentityRole{} 101 } 102 if s.RoleAssignmentName != "" { 103 // Move the existing value from the deprecated RoleAssignmentName field. 104 s.SystemAssignedIdentityRole.Name = s.RoleAssignmentName 105 s.RoleAssignmentName = "" 106 } else if s.SystemAssignedIdentityRole.Name == "" { 107 // Default role name to a generated UUID. 108 s.SystemAssignedIdentityRole.Name = string(uuid.NewUUID()) 109 } 110 if s.SystemAssignedIdentityRole.Scope == "" && subscriptionID != "" { 111 // Default scope to the subscription. 112 s.SystemAssignedIdentityRole.Scope = fmt.Sprintf("/subscriptions/%s/", subscriptionID) 113 } 114 if s.SystemAssignedIdentityRole.DefinitionID == "" && subscriptionID != "" { 115 // Default role definition ID to Contributor role. 116 s.SystemAssignedIdentityRole.DefinitionID = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Authorization/roleDefinitions/%s", subscriptionID, ContributorRoleID) 117 } 118 } 119 } 120 121 // SetSpotEvictionPolicyDefaults sets the defaults for the spot VM eviction policy. 122 func (s *AzureMachineSpec) SetSpotEvictionPolicyDefaults() { 123 if s.SpotVMOptions != nil && s.SpotVMOptions.EvictionPolicy == nil { 124 defaultPolicy := SpotEvictionPolicyDeallocate 125 if s.OSDisk.DiffDiskSettings != nil && s.OSDisk.DiffDiskSettings.Option == "Local" { 126 defaultPolicy = SpotEvictionPolicyDelete 127 } 128 s.SpotVMOptions.EvictionPolicy = &defaultPolicy 129 } 130 } 131 132 // SetDiagnosticsDefaults sets the defaults for Diagnostic settings for an AzureMachinePool. 133 func (s *AzureMachineSpec) SetDiagnosticsDefaults() { 134 bootDiagnosticsDefault := &BootDiagnostics{ 135 StorageAccountType: ManagedDiagnosticsStorage, 136 } 137 138 diagnosticsDefault := &Diagnostics{Boot: bootDiagnosticsDefault} 139 140 if s.Diagnostics == nil { 141 s.Diagnostics = diagnosticsDefault 142 } 143 144 if s.Diagnostics.Boot == nil { 145 s.Diagnostics.Boot = bootDiagnosticsDefault 146 } 147 } 148 149 // SetNetworkInterfacesDefaults sets the defaults for the network interfaces. 150 func (s *AzureMachineSpec) SetNetworkInterfacesDefaults() { 151 // Ensure the deprecated fields and new fields are not populated simultaneously 152 if (s.SubnetName != "" || s.AcceleratedNetworking != nil) && len(s.NetworkInterfaces) > 0 { 153 // Both the deprecated and the new fields are both set, return without changes 154 // and reject the request in the validating webhook which runs later. 155 return 156 } 157 158 if len(s.NetworkInterfaces) == 0 { 159 s.NetworkInterfaces = []NetworkInterface{ 160 { 161 SubnetName: s.SubnetName, 162 AcceleratedNetworking: s.AcceleratedNetworking, 163 }, 164 } 165 s.SubnetName = "" 166 s.AcceleratedNetworking = nil 167 } 168 169 // Ensure that PrivateIPConfigs defaults to 1 if not specified. 170 for i := 0; i < len(s.NetworkInterfaces); i++ { 171 if s.NetworkInterfaces[i].PrivateIPConfigs == 0 { 172 s.NetworkInterfaces[i].PrivateIPConfigs = 1 173 } 174 } 175 } 176 177 // GetOwnerAzureClusterNameAndNamespace returns the owner azure cluster's name and namespace for the given cluster name and namespace. 178 func GetOwnerAzureClusterNameAndNamespace(cli client.Client, clusterName string, namespace string, maxAttempts int) (azureClusterName string, azureClusterNamespace string, err error) { 179 ctx := context.Background() 180 181 ownerCluster := &clusterv1.Cluster{} 182 key := client.ObjectKey{ 183 Namespace: namespace, 184 Name: clusterName, 185 } 186 187 for i := 1; ; i++ { 188 if err := cli.Get(ctx, key, ownerCluster); err != nil { 189 if i > maxAttempts { 190 return "", "", errors.Wrapf(err, "failed to find owner cluster for %s/%s", namespace, clusterName) 191 } 192 time.Sleep(1 * time.Second) 193 continue 194 } 195 break 196 } 197 198 return ownerCluster.Spec.InfrastructureRef.Name, ownerCluster.Spec.InfrastructureRef.Namespace, nil 199 } 200 201 // GetSubscriptionID returns the subscription ID for the AzureCluster given the cluster name and namespace. 202 func GetSubscriptionID(cli client.Client, ownerAzureClusterName string, ownerAzureClusterNamespace string, maxAttempts int) (string, error) { 203 ctx := context.Background() 204 205 ownerAzureCluster := &AzureCluster{} 206 key := client.ObjectKey{ 207 Namespace: ownerAzureClusterNamespace, 208 Name: ownerAzureClusterName, 209 } 210 for i := 1; ; i++ { 211 if err := cli.Get(ctx, key, ownerAzureCluster); err != nil { 212 if i >= maxAttempts { 213 return "", errors.Wrapf(err, "failed to find AzureCluster for owner cluster %s/%s", ownerAzureClusterNamespace, ownerAzureClusterName) 214 } 215 time.Sleep(1 * time.Second) 216 continue 217 } 218 break 219 } 220 221 return ownerAzureCluster.Spec.SubscriptionID, nil 222 } 223 224 // SetDefaults sets to the defaults for the AzureMachineSpec. 225 func (m *AzureMachine) SetDefaults(client client.Client) error { 226 var errs []error 227 if err := m.Spec.SetDefaultSSHPublicKey(); err != nil { 228 errs = append(errs, errors.Wrap(err, "failed to set default SSH public key")) 229 } 230 231 // Fetch the Cluster. 232 clusterName, ok := m.Labels[clusterv1.ClusterNameLabel] 233 if !ok { 234 errs = append(errs, errors.Errorf("failed to fetch ClusterName for AzureMachine %s/%s", m.Namespace, m.Name)) 235 } 236 237 ownerAzureClusterName, ownerAzureClusterNamespace, err := GetOwnerAzureClusterNameAndNamespace(client, clusterName, m.Namespace, 5) 238 if err != nil { 239 errs = append(errs, errors.Wrapf(err, "failed to fetch owner cluster for AzureMachine %s/%s", m.Namespace, m.Name)) 240 } 241 242 subscriptionID, err := GetSubscriptionID(client, ownerAzureClusterName, ownerAzureClusterNamespace, 5) 243 if err != nil { 244 errs = append(errs, errors.Wrapf(err, "failed to fetch subscription ID for AzureMachine %s/%s", m.Namespace, m.Name)) 245 } 246 247 m.Spec.SetDefaultCachingType() 248 m.Spec.SetDataDisksDefaults() 249 m.Spec.SetIdentityDefaults(subscriptionID) 250 m.Spec.SetSpotEvictionPolicyDefaults() 251 m.Spec.SetDiagnosticsDefaults() 252 m.Spec.SetNetworkInterfacesDefaults() 253 254 return kerrors.NewAggregate(errs) 255 }