sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/scalesets/scalesets_test.go (about) 1 /* 2 Copyright 2020 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 scalesets 18 19 import ( 20 "context" 21 "io" 22 "net/http" 23 "strings" 24 "testing" 25 26 "github.com/Azure/azure-sdk-for-go/sdk/azcore" 27 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 28 . "github.com/onsi/gomega" 29 "go.uber.org/mock/gomock" 30 "k8s.io/client-go/kubernetes/scheme" 31 "k8s.io/utils/ptr" 32 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 33 "sigs.k8s.io/cluster-api-provider-azure/azure" 34 "sigs.k8s.io/cluster-api-provider-azure/azure/converters" 35 "sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async" 36 "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" 37 "sigs.k8s.io/cluster-api-provider-azure/azure/services/scalesets/mock_scalesets" 38 gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" 39 azureutil "sigs.k8s.io/cluster-api-provider-azure/util/azure" 40 "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" 41 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 42 ) 43 44 const ( 45 defaultSubscriptionID = "123" 46 defaultResourceGroup = "my-rg" 47 defaultVMSSName = "my-vmss" 48 vmSizeEPH = "VM_SIZE_EPH" 49 vmSizeUSSD = "VM_SIZE_USSD" 50 defaultVMSSID = "subscriptions/1234/resourceGroups/my_resource_group/providers/Microsoft.Compute/virtualMachines/my-vm-id" 51 sshKeyData = "ZmFrZXNzaGtleQo=" 52 ) 53 54 var ( 55 defaultImage = infrav1.Image{ 56 Marketplace: &infrav1.AzureMarketplaceImage{ 57 ImagePlan: infrav1.ImagePlan{ 58 Publisher: "fake-publisher", 59 Offer: "my-offer", 60 SKU: "sku-id", 61 }, 62 Version: "1.0", 63 }, 64 } 65 66 notFoundError = &azcore.ResponseError{StatusCode: http.StatusNotFound} 67 ) 68 69 func internalError() *azcore.ResponseError { 70 return &azcore.ResponseError{ 71 RawResponse: &http.Response{ 72 Body: io.NopCloser(strings.NewReader("#: Internal Server Error: StatusCode=500")), 73 StatusCode: http.StatusInternalServerError, 74 }, 75 } 76 } 77 78 func init() { 79 _ = clusterv1.AddToScheme(scheme.Scheme) 80 } 81 82 func getDefaultVMSSSpec() azure.ResourceSpecGetter { 83 defaultSpec := newDefaultVMSSSpec() 84 defaultSpec.DataDisks = append(defaultSpec.DataDisks, infrav1.DataDisk{ 85 NameSuffix: "my_disk_with_ultra_disks", 86 DiskSizeGB: 128, 87 Lun: ptr.To[int32](3), 88 ManagedDisk: &infrav1.ManagedDiskParameters{ 89 StorageAccountType: "UltraSSD_LRS", 90 }, 91 }) 92 93 return &defaultSpec 94 } 95 96 func getResultVMSS() armcompute.VirtualMachineScaleSet { 97 resultVMSS := newDefaultVMSS("VM_SIZE") 98 resultVMSS.ID = ptr.To(defaultVMSSID) 99 100 return resultVMSS 101 } 102 103 func TestReconcileVMSS(t *testing.T) { 104 defaultInstances := newDefaultInstances() 105 resultVMSS := newDefaultVMSS("VM_SIZE") 106 resultVMSS.ID = ptr.To(defaultVMSSID) 107 fetchedVMSS := converters.SDKToVMSS(getResultVMSS(), defaultInstances) 108 109 testcases := []struct { 110 name string 111 expect func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) 112 expectedError string 113 }{ 114 { 115 name: "update an existing vmss", 116 expectedError: "", 117 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 118 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 119 spec := getDefaultVMSSSpec() 120 // Validate spec 121 s.ScaleSetSpec(gomockinternal.AContext()).Return(spec).AnyTimes() 122 m.Get(gomockinternal.AContext(), &defaultSpec).Return(&resultVMSS, nil) 123 m.ListInstances(gomockinternal.AContext(), defaultSpec.ResourceGroup, defaultSpec.Name).Return(defaultInstances, nil) 124 r.CreateOrUpdateResource(gomockinternal.AContext(), spec, serviceName).Return(getResultVMSS(), nil) 125 s.UpdatePutStatus(infrav1.BootstrapSucceededCondition, serviceName, nil) 126 127 s.ReconcileReplicas(gomockinternal.AContext(), &fetchedVMSS).Return(nil) 128 s.SetProviderID(azureutil.ProviderIDPrefix + defaultVMSSID) 129 s.SetVMSSState(&fetchedVMSS) 130 }, 131 }, 132 { 133 name: "create a vmss, skip list instances if vmss doesn't exist", 134 expectedError: "", 135 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 136 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 137 spec := getDefaultVMSSSpec() 138 // Validate spec 139 s.ScaleSetSpec(gomockinternal.AContext()).Return(spec).AnyTimes() 140 m.Get(gomockinternal.AContext(), &defaultSpec).Return(nil, notFoundError) 141 r.CreateOrUpdateResource(gomockinternal.AContext(), spec, serviceName).Return(getResultVMSS(), nil) 142 s.UpdatePutStatus(infrav1.BootstrapSucceededCondition, serviceName, nil) 143 144 s.ReconcileReplicas(gomockinternal.AContext(), &fetchedVMSS).Return(nil) 145 s.SetProviderID(azureutil.ProviderIDPrefix + defaultVMSSID) 146 s.SetVMSSState(&fetchedVMSS) 147 }, 148 }, 149 { 150 name: "error getting existing vmss", 151 expectedError: "failed to get existing VMSS:.*#: Internal Server Error: StatusCode=500", 152 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 153 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 154 spec := getDefaultVMSSSpec() 155 // Validate spec 156 s.ScaleSetSpec(gomockinternal.AContext()).Return(spec).AnyTimes() 157 m.Get(gomockinternal.AContext(), &defaultSpec).Return(nil, internalError()) 158 }, 159 }, 160 { 161 name: "failed to list instances", 162 expectedError: "failed to get existing VMSS instances:.*#: Internal Server Error: StatusCode=500", 163 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 164 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 165 spec := getDefaultVMSSSpec() 166 // Validate spec 167 s.ScaleSetSpec(gomockinternal.AContext()).Return(spec).AnyTimes() 168 m.Get(gomockinternal.AContext(), &defaultSpec).Return(&resultVMSS, nil) 169 m.ListInstances(gomockinternal.AContext(), defaultSpec.ResourceGroup, defaultSpec.Name).Return(defaultInstances, internalError()) 170 s.UpdatePutStatus(infrav1.BootstrapSucceededCondition, serviceName, gomockinternal.ErrStrEq("failed to get existing VMSS instances: "+internalError().Error())) 171 }, 172 }, 173 { 174 name: "failed to create a vmss", 175 expectedError: "#: Internal Server Error: StatusCode=500", 176 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 177 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 178 spec := getDefaultVMSSSpec() 179 s.ScaleSetSpec(gomockinternal.AContext()).Return(spec).AnyTimes() 180 m.Get(gomockinternal.AContext(), &defaultSpec).Return(&resultVMSS, nil) 181 m.ListInstances(gomockinternal.AContext(), defaultSpec.ResourceGroup, defaultSpec.Name).Return(defaultInstances, nil) 182 183 r.CreateOrUpdateResource(gomockinternal.AContext(), spec, serviceName). 184 Return(nil, internalError()) 185 s.UpdatePutStatus(infrav1.BootstrapSucceededCondition, serviceName, internalError()) 186 }, 187 }, 188 { 189 name: "failed to reconcile replicas", 190 expectedError: "unable to reconcile VMSS replicas:.*#: Internal Server Error: StatusCode=500", 191 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 192 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 193 spec := getDefaultVMSSSpec() 194 s.ScaleSetSpec(gomockinternal.AContext()).Return(spec).AnyTimes() 195 m.Get(gomockinternal.AContext(), &defaultSpec).Return(&resultVMSS, nil) 196 m.ListInstances(gomockinternal.AContext(), defaultSpec.ResourceGroup, defaultSpec.Name).Return(defaultInstances, nil) 197 198 r.CreateOrUpdateResource(gomockinternal.AContext(), spec, serviceName).Return(getResultVMSS(), nil) 199 s.UpdatePutStatus(infrav1.BootstrapSucceededCondition, serviceName, nil) 200 201 s.ReconcileReplicas(gomockinternal.AContext(), &fetchedVMSS).Return(internalError()) 202 }, 203 }, 204 { 205 name: "validate spec failure: less than 2 vCPUs", 206 expectedError: "reconcile error that cannot be recovered occurred: vm size should be bigger or equal to at least 2 vCPUs. Object will not be requeued", 207 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 208 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 209 spec := newDefaultVMSSSpec() 210 spec.Size = "VM_SIZE_1_CPU" 211 spec.Capacity = 2 212 spec.SSHKeyData = sshKeyData 213 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 214 }, 215 }, 216 { 217 name: "validate spec failure: Memory is less than 2Gi", 218 expectedError: "reconcile error that cannot be recovered occurred: vm memory should be bigger or equal to at least 2Gi. Object will not be requeued", 219 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 220 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 221 spec := newDefaultVMSSSpec() 222 spec.Size = "VM_SIZE_1_MEM" 223 spec.Capacity = 2 224 spec.SSHKeyData = sshKeyData 225 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 226 }, 227 }, 228 { 229 name: "validate spec failure: failed to get SKU", 230 expectedError: "failed to get SKU INVALID_VM_SIZE in compute api: reconcile error that cannot be recovered occurred: resource sku with name 'INVALID_VM_SIZE' and category 'virtualMachines' not found in location 'test-location'. Object will not be requeued", 231 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 232 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 233 spec := newDefaultVMSSSpec() 234 spec.Size = "INVALID_VM_SIZE" 235 spec.Capacity = 2 236 spec.SSHKeyData = sshKeyData 237 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 238 }, 239 }, 240 { 241 name: "validate spec failure: fail to create a vm with ultra disk implicitly enabled by data disk, when location not supported", 242 expectedError: "reconcile error that cannot be recovered occurred: vm size VM_SIZE_USSD does not support ultra disks in location test-location. select a different vm size or disable ultra disks. Object will not be requeued", 243 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 244 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 245 spec := newDefaultVMSSSpec() 246 spec.Size = vmSizeUSSD 247 spec.Capacity = 2 248 spec.SSHKeyData = sshKeyData 249 spec.DataDisks = append(spec.DataDisks, infrav1.DataDisk{ 250 ManagedDisk: &infrav1.ManagedDiskParameters{ 251 StorageAccountType: "UltraSSD_LRS", 252 }, 253 }) 254 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 255 }, 256 }, 257 { 258 name: "validate spec failure: fail to create a vm with ultra disk explicitly enabled via additional capabilities, when location not supported", 259 expectedError: "reconcile error that cannot be recovered occurred: vm size VM_SIZE_USSD does not support ultra disks in location test-location. select a different vm size or disable ultra disks. Object will not be requeued", 260 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 261 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 262 spec := newDefaultVMSSSpec() 263 spec.Size = vmSizeUSSD 264 spec.Capacity = 2 265 spec.SSHKeyData = sshKeyData 266 spec.AdditionalCapabilities = &infrav1.AdditionalCapabilities{ 267 UltraSSDEnabled: ptr.To(true), 268 } 269 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 270 }, 271 }, 272 { 273 name: "validate spec failure: fail to create a vm with ultra disk explicitly enabled via additional capabilities, when location not supported", 274 expectedError: "reconcile error that cannot be recovered occurred: vm size VM_SIZE_USSD does not support ultra disks in location test-location. select a different vm size or disable ultra disks. Object will not be requeued", 275 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 276 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 277 spec := newDefaultVMSSSpec() 278 spec.Size = vmSizeUSSD 279 spec.Capacity = 2 280 spec.SSHKeyData = sshKeyData 281 spec.DataDisks = append(spec.DataDisks, infrav1.DataDisk{ 282 ManagedDisk: &infrav1.ManagedDiskParameters{ 283 StorageAccountType: "UltraSSD_LRS", 284 }, 285 }) 286 spec.AdditionalCapabilities = &infrav1.AdditionalCapabilities{ 287 UltraSSDEnabled: ptr.To(true), 288 } 289 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 290 }, 291 }, 292 { 293 name: "validate spec failure: fail to create a vm with diagnostics set to User Managed but empty StorageAccountURI", 294 expectedError: "reconcile error that cannot be recovered occurred: userManaged must be specified when storageAccountType is 'UserManaged'. Object will not be requeued", 295 expect: func(g *WithT, s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 296 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 297 spec := newDefaultVMSSSpec() 298 spec.Size = vmSizeUSSD 299 spec.Capacity = 2 300 spec.SSHKeyData = sshKeyData 301 spec.DiagnosticsProfile = &infrav1.Diagnostics{ 302 Boot: &infrav1.BootDiagnostics{ 303 StorageAccountType: infrav1.UserManagedDiagnosticsStorage, 304 UserManaged: nil, 305 }, 306 } 307 s.ScaleSetSpec(gomockinternal.AContext()).Return(&spec).AnyTimes() 308 }, 309 }, 310 } 311 312 for _, tc := range testcases { 313 tc := tc 314 t.Run(tc.name, func(t *testing.T) { 315 g := NewWithT(t) 316 t.Parallel() 317 mockCtrl := gomock.NewController(t) 318 defer mockCtrl.Finish() 319 320 scopeMock := mock_scalesets.NewMockScaleSetScope(mockCtrl) 321 asyncMock := mock_async.NewMockReconciler(mockCtrl) 322 clientMock := mock_scalesets.NewMockClient(mockCtrl) 323 324 tc.expect(g, scopeMock.EXPECT(), asyncMock.EXPECT(), clientMock.EXPECT()) 325 326 s := &Service{ 327 Scope: scopeMock, 328 Reconciler: asyncMock, 329 Client: clientMock, 330 resourceSKUCache: resourceskus.NewStaticCache(getFakeSkus(), "test-location"), 331 } 332 333 err := s.Reconcile(context.TODO()) 334 if tc.expectedError != "" { 335 g.Expect(err).To(HaveOccurred()) 336 g.Expect(strings.ReplaceAll(err.Error(), "\n", "")).To(MatchRegexp(tc.expectedError), err.Error()) 337 } else { 338 g.Expect(err).NotTo(HaveOccurred()) 339 } 340 }) 341 } 342 } 343 344 func TestDeleteVMSS(t *testing.T) { 345 defaultSpec := newDefaultVMSSSpec() 346 defaultInstances := newDefaultInstances() 347 resultVMSS := newDefaultVMSS("VM_SIZE") 348 resultVMSS.ID = ptr.To(defaultVMSSID) 349 fetchedVMSS := converters.SDKToVMSS(getResultVMSS(), defaultInstances) 350 // Be careful about race conditions if you need modify these. 351 352 testcases := []struct { 353 name string 354 expectedError string 355 expect func(s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) 356 }{ 357 { 358 name: "successfully delete an existing vmss", 359 expectedError: "", 360 expect: func(s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 361 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 362 s.ScaleSetSpec(gomockinternal.AContext()).Return(&defaultSpec).AnyTimes() 363 r.DeleteResource(gomockinternal.AContext(), &defaultSpec, serviceName).Return(nil) 364 s.UpdateDeleteStatus(infrav1.BootstrapSucceededCondition, serviceName, nil) 365 366 m.Get(gomockinternal.AContext(), &defaultSpec).Return(resultVMSS, nil) 367 m.ListInstances(gomockinternal.AContext(), defaultSpec.ResourceGroup, defaultSpec.Name).Return(defaultInstances, nil) 368 s.SetVMSSState(&fetchedVMSS) 369 }, 370 }, 371 { 372 name: "successfully delete an existing vmss, fetch call returns error", 373 expectedError: "", 374 expect: func(s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 375 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 376 s.ScaleSetSpec(gomockinternal.AContext()).Return(&defaultSpec).AnyTimes() 377 r.DeleteResource(gomockinternal.AContext(), &defaultSpec, serviceName).Return(nil) 378 s.UpdateDeleteStatus(infrav1.BootstrapSucceededCondition, serviceName, nil) 379 m.Get(gomockinternal.AContext(), &defaultSpec).Return(armcompute.VirtualMachineScaleSet{}, notFoundError) 380 }, 381 }, 382 { 383 name: "failed to delete an existing vmss", 384 expectedError: "#: Internal Server Error: StatusCode=500", 385 expect: func(s *mock_scalesets.MockScaleSetScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder, m *mock_scalesets.MockClientMockRecorder) { 386 s.DefaultedAzureServiceReconcileTimeout().Return(reconciler.DefaultAzureServiceReconcileTimeout) 387 s.ScaleSetSpec(gomockinternal.AContext()).Return(&defaultSpec).AnyTimes() 388 r.DeleteResource(gomockinternal.AContext(), &defaultSpec, serviceName).Return(internalError()) 389 s.UpdateDeleteStatus(infrav1.BootstrapSucceededCondition, serviceName, internalError()) 390 m.Get(gomockinternal.AContext(), &defaultSpec).Return(armcompute.VirtualMachineScaleSet{}, notFoundError) 391 }, 392 }, 393 } 394 395 for _, tc := range testcases { 396 tc := tc 397 t.Run(tc.name, func(t *testing.T) { 398 g := NewWithT(t) 399 t.Parallel() 400 mockCtrl := gomock.NewController(t) 401 defer mockCtrl.Finish() 402 scopeMock := mock_scalesets.NewMockScaleSetScope(mockCtrl) 403 asyncMock := mock_async.NewMockReconciler(mockCtrl) 404 mockClient := mock_scalesets.NewMockClient(mockCtrl) 405 406 tc.expect(scopeMock.EXPECT(), asyncMock.EXPECT(), mockClient.EXPECT()) 407 408 s := &Service{ 409 Scope: scopeMock, 410 Reconciler: asyncMock, 411 Client: mockClient, 412 } 413 414 err := s.Delete(context.TODO()) 415 if tc.expectedError != "" { 416 g.Expect(err).To(HaveOccurred()) 417 g.Expect(err.Error()).To(ContainSubstring(tc.expectedError)) 418 } else { 419 g.Expect(err).NotTo(HaveOccurred()) 420 } 421 }) 422 } 423 } 424 425 func getFakeSkus() []armcompute.ResourceSKU { 426 return []armcompute.ResourceSKU{ 427 { 428 Name: ptr.To("VM_SIZE"), 429 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 430 Kind: ptr.To(string(resourceskus.VirtualMachines)), 431 Locations: []*string{ 432 ptr.To("test-location"), 433 }, 434 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 435 { 436 Location: ptr.To("test-location"), 437 Zones: []*string{ptr.To("1"), ptr.To("3")}, 438 ZoneDetails: []*armcompute.ResourceSKUZoneDetails{ 439 { 440 Capabilities: []*armcompute.ResourceSKUCapabilities{ 441 { 442 Name: ptr.To("UltraSSDAvailable"), 443 Value: ptr.To("True"), 444 }, 445 }, 446 Name: []*string{ptr.To("1"), ptr.To("3")}, 447 }, 448 }, 449 }, 450 }, 451 Capabilities: []*armcompute.ResourceSKUCapabilities{ 452 { 453 Name: ptr.To(resourceskus.AcceleratedNetworking), 454 Value: ptr.To(string(resourceskus.CapabilityUnsupported)), 455 }, 456 { 457 Name: ptr.To(resourceskus.VCPUs), 458 Value: ptr.To("4"), 459 }, 460 { 461 Name: ptr.To(resourceskus.MemoryGB), 462 Value: ptr.To("4"), 463 }, 464 }, 465 }, 466 { 467 Name: ptr.To("VM_SIZE_AN"), 468 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 469 Kind: ptr.To(string(resourceskus.VirtualMachines)), 470 Locations: []*string{ 471 ptr.To("test-location"), 472 }, 473 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 474 { 475 Location: ptr.To("test-location"), 476 Zones: []*string{ptr.To("1"), ptr.To("3")}, 477 }, 478 }, 479 Capabilities: []*armcompute.ResourceSKUCapabilities{ 480 { 481 Name: ptr.To(resourceskus.AcceleratedNetworking), 482 Value: ptr.To(string(resourceskus.CapabilitySupported)), 483 }, 484 { 485 Name: ptr.To(resourceskus.VCPUs), 486 Value: ptr.To("4"), 487 }, 488 { 489 Name: ptr.To(resourceskus.MemoryGB), 490 Value: ptr.To("6"), 491 }, 492 }, 493 }, 494 { 495 Name: ptr.To("VM_SIZE_1_CPU"), 496 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 497 Kind: ptr.To(string(resourceskus.VirtualMachines)), 498 Locations: []*string{ 499 ptr.To("test-location"), 500 }, 501 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 502 { 503 Location: ptr.To("test-location"), 504 Zones: []*string{ptr.To("1"), ptr.To("3")}, 505 }, 506 }, 507 Capabilities: []*armcompute.ResourceSKUCapabilities{ 508 { 509 Name: ptr.To(resourceskus.AcceleratedNetworking), 510 Value: ptr.To(string(resourceskus.CapabilityUnsupported)), 511 }, 512 { 513 Name: ptr.To(resourceskus.VCPUs), 514 Value: ptr.To("1"), 515 }, 516 { 517 Name: ptr.To(resourceskus.MemoryGB), 518 Value: ptr.To("4"), 519 }, 520 }, 521 }, 522 { 523 Name: ptr.To("VM_SIZE_1_MEM"), 524 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 525 Kind: ptr.To(string(resourceskus.VirtualMachines)), 526 Locations: []*string{ 527 ptr.To("test-location"), 528 }, 529 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 530 { 531 Location: ptr.To("test-location"), 532 Zones: []*string{ptr.To("1"), ptr.To("3")}, 533 }, 534 }, 535 Capabilities: []*armcompute.ResourceSKUCapabilities{ 536 { 537 Name: ptr.To(resourceskus.AcceleratedNetworking), 538 Value: ptr.To(string(resourceskus.CapabilityUnsupported)), 539 }, 540 { 541 Name: ptr.To(resourceskus.VCPUs), 542 Value: ptr.To("2"), 543 }, 544 { 545 Name: ptr.To(resourceskus.MemoryGB), 546 Value: ptr.To("1"), 547 }, 548 }, 549 }, 550 { 551 Name: ptr.To("VM_SIZE_EAH"), 552 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 553 Kind: ptr.To(string(resourceskus.VirtualMachines)), 554 Locations: []*string{ 555 ptr.To("test-location"), 556 }, 557 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 558 { 559 Location: ptr.To("test-location"), 560 Zones: []*string{ptr.To("1"), ptr.To("3")}, 561 }, 562 }, 563 Capabilities: []*armcompute.ResourceSKUCapabilities{ 564 { 565 Name: ptr.To(resourceskus.VCPUs), 566 Value: ptr.To("4"), 567 }, 568 { 569 Name: ptr.To(resourceskus.MemoryGB), 570 Value: ptr.To("8"), 571 }, 572 { 573 Name: ptr.To(resourceskus.EncryptionAtHost), 574 Value: ptr.To(string(resourceskus.CapabilitySupported)), 575 }, 576 }, 577 }, 578 { 579 Name: ptr.To(vmSizeUSSD), 580 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 581 Kind: ptr.To(string(resourceskus.VirtualMachines)), 582 Locations: []*string{ 583 ptr.To("test-location"), 584 }, 585 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 586 { 587 Location: ptr.To("test-location"), 588 Zones: []*string{ptr.To("1"), ptr.To("3")}, 589 }, 590 }, 591 Capabilities: []*armcompute.ResourceSKUCapabilities{ 592 { 593 Name: ptr.To(resourceskus.AcceleratedNetworking), 594 Value: ptr.To(string(resourceskus.CapabilitySupported)), 595 }, 596 { 597 Name: ptr.To(resourceskus.VCPUs), 598 Value: ptr.To("4"), 599 }, 600 { 601 Name: ptr.To(resourceskus.MemoryGB), 602 Value: ptr.To("6"), 603 }, 604 }, 605 }, 606 { 607 Name: ptr.To("VM_SIZE_EPH"), 608 ResourceType: ptr.To(string(resourceskus.VirtualMachines)), 609 Kind: ptr.To(string(resourceskus.VirtualMachines)), 610 Locations: []*string{ 611 ptr.To("test-location"), 612 }, 613 LocationInfo: []*armcompute.ResourceSKULocationInfo{ 614 { 615 Location: ptr.To("test-location"), 616 Zones: []*string{ptr.To("1"), ptr.To("3")}, 617 ZoneDetails: []*armcompute.ResourceSKUZoneDetails{ 618 { 619 Capabilities: []*armcompute.ResourceSKUCapabilities{ 620 { 621 Name: ptr.To("UltraSSDAvailable"), 622 Value: ptr.To("True"), 623 }, 624 }, 625 Name: []*string{ptr.To("1"), ptr.To("3")}, 626 }, 627 }, 628 }, 629 }, 630 Capabilities: []*armcompute.ResourceSKUCapabilities{ 631 { 632 Name: ptr.To(resourceskus.AcceleratedNetworking), 633 Value: ptr.To(string(resourceskus.CapabilityUnsupported)), 634 }, 635 { 636 Name: ptr.To(resourceskus.VCPUs), 637 Value: ptr.To("4"), 638 }, 639 { 640 Name: ptr.To(resourceskus.MemoryGB), 641 Value: ptr.To("4"), 642 }, 643 { 644 Name: ptr.To(resourceskus.EphemeralOSDisk), 645 Value: ptr.To("True"), 646 }, 647 }, 648 }, 649 } 650 } 651 652 func newDefaultVMSSSpec() ScaleSetSpec { 653 return ScaleSetSpec{ 654 Name: defaultVMSSName, 655 Size: "VM_SIZE", 656 Capacity: 2, 657 SSHKeyData: sshKeyData, 658 OSDisk: infrav1.OSDisk{ 659 OSType: "Linux", 660 DiskSizeGB: ptr.To[int32](120), 661 ManagedDisk: &infrav1.ManagedDiskParameters{ 662 StorageAccountType: "Premium_LRS", 663 }, 664 }, 665 DataDisks: []infrav1.DataDisk{ 666 { 667 NameSuffix: "my_disk", 668 DiskSizeGB: 128, 669 Lun: ptr.To[int32](0), 670 }, 671 { 672 NameSuffix: "my_disk_with_managed_disk", 673 DiskSizeGB: 128, 674 Lun: ptr.To[int32](1), 675 ManagedDisk: &infrav1.ManagedDiskParameters{ 676 StorageAccountType: "Standard_LRS", 677 }, 678 }, 679 { 680 NameSuffix: "managed_disk_with_encryption", 681 DiskSizeGB: 128, 682 Lun: ptr.To[int32](2), 683 ManagedDisk: &infrav1.ManagedDiskParameters{ 684 StorageAccountType: "Standard_LRS", 685 DiskEncryptionSet: &infrav1.DiskEncryptionSetParameters{ 686 ID: "encryption_id", 687 }, 688 }, 689 }, 690 }, 691 DiagnosticsProfile: &infrav1.Diagnostics{ 692 Boot: &infrav1.BootDiagnostics{ 693 StorageAccountType: infrav1.ManagedDiagnosticsStorage, 694 }, 695 }, 696 SubnetName: "my-subnet", 697 VNetName: "my-vnet", 698 VNetResourceGroup: defaultResourceGroup, 699 PublicLBName: "capz-lb", 700 PublicLBAddressPoolName: "backendPool", 701 AcceleratedNetworking: nil, 702 TerminateNotificationTimeout: ptr.To(7), 703 FailureDomains: []string{"1", "3"}, 704 NetworkInterfaces: []infrav1.NetworkInterface{ 705 { 706 SubnetName: "my-subnet", 707 PrivateIPConfigs: 1, 708 }, 709 }, 710 SubscriptionID: "123", 711 ResourceGroup: "my-rg", 712 Location: "test-location", 713 ClusterName: "my-cluster", 714 VMSSInstances: newDefaultInstances(), 715 VMImage: &defaultImage, 716 BootstrapData: "fake-bootstrap-data", 717 VMSSExtensionSpecs: []azure.ResourceSpecGetter{ 718 &VMSSExtensionSpec{ 719 ExtensionSpec: azure.ExtensionSpec{ 720 Name: "someExtension", 721 VMName: "my-vmss", 722 Publisher: "somePublisher", 723 Version: "someVersion", 724 Settings: map[string]string{ 725 "someSetting": "someValue", 726 }, 727 ProtectedSettings: map[string]string{ 728 "commandToExecute": "echo hello", 729 }, 730 }, 731 ResourceGroup: "my-rg", 732 }, 733 }, 734 PlatformFaultDomainCount: ptr.To(int32(1)), 735 ZoneBalance: ptr.To(true), 736 } 737 } 738 739 func newWindowsVMSSSpec() ScaleSetSpec { 740 vmss := newDefaultVMSSSpec() 741 vmss.OSDisk.OSType = azure.WindowsOS 742 return vmss 743 } 744 745 func newDefaultExistingVMSS(vmSize string) armcompute.VirtualMachineScaleSet { 746 vmss := newDefaultVMSS(vmSize) 747 vmss.ID = ptr.To("subscriptions/1234/resourceGroups/my_resource_group/providers/Microsoft.Compute/virtualMachines/my-vm") 748 return vmss 749 } 750 751 func newDefaultWindowsVMSS() armcompute.VirtualMachineScaleSet { 752 vmss := newDefaultVMSS("VM_SIZE") 753 vmss.Properties.VirtualMachineProfile.StorageProfile.OSDisk.OSType = ptr.To(armcompute.OperatingSystemTypesWindows) 754 vmss.Properties.VirtualMachineProfile.OSProfile.LinuxConfiguration = nil 755 vmss.Properties.VirtualMachineProfile.OSProfile.WindowsConfiguration = &armcompute.WindowsConfiguration{ 756 EnableAutomaticUpdates: ptr.To(false), 757 } 758 return vmss 759 } 760 761 func newDefaultVMSS(vmSize string) armcompute.VirtualMachineScaleSet { 762 dataDisk := fetchDataDiskBasedOnSize(vmSize) 763 return armcompute.VirtualMachineScaleSet{ 764 Location: ptr.To("test-location"), 765 Tags: map[string]*string{ 766 "Name": ptr.To(defaultVMSSName), 767 "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"), 768 "sigs.k8s.io_cluster-api-provider-azure_role": ptr.To("node"), 769 }, 770 SKU: &armcompute.SKU{ 771 Name: ptr.To(vmSize), 772 Tier: ptr.To("Standard"), 773 Capacity: ptr.To[int64](2), 774 }, 775 Zones: []*string{ptr.To("1"), ptr.To("3")}, 776 Properties: &armcompute.VirtualMachineScaleSetProperties{ 777 SinglePlacementGroup: ptr.To(false), 778 UpgradePolicy: &armcompute.UpgradePolicy{ 779 Mode: ptr.To(armcompute.UpgradeModeManual), 780 }, 781 Overprovision: ptr.To(false), 782 OrchestrationMode: ptr.To(armcompute.OrchestrationModeUniform), 783 VirtualMachineProfile: &armcompute.VirtualMachineScaleSetVMProfile{ 784 OSProfile: &armcompute.VirtualMachineScaleSetOSProfile{ 785 ComputerNamePrefix: ptr.To(defaultVMSSName), 786 AdminUsername: ptr.To(azure.DefaultUserName), 787 CustomData: ptr.To("fake-bootstrap-data"), 788 LinuxConfiguration: &armcompute.LinuxConfiguration{ 789 SSH: &armcompute.SSHConfiguration{ 790 PublicKeys: []*armcompute.SSHPublicKey{ 791 { 792 Path: ptr.To("/home/capi/.ssh/authorized_keys"), 793 KeyData: ptr.To("fakesshkey\n"), 794 }, 795 }, 796 }, 797 DisablePasswordAuthentication: ptr.To(true), 798 }, 799 }, 800 ScheduledEventsProfile: &armcompute.ScheduledEventsProfile{ 801 TerminateNotificationProfile: &armcompute.TerminateNotificationProfile{ 802 NotBeforeTimeout: ptr.To("PT7M"), 803 Enable: ptr.To(true), 804 }, 805 }, 806 StorageProfile: &armcompute.VirtualMachineScaleSetStorageProfile{ 807 ImageReference: &armcompute.ImageReference{ 808 Publisher: ptr.To("fake-publisher"), 809 Offer: ptr.To("my-offer"), 810 SKU: ptr.To("sku-id"), 811 Version: ptr.To("1.0"), 812 }, 813 OSDisk: &armcompute.VirtualMachineScaleSetOSDisk{ 814 OSType: ptr.To(armcompute.OperatingSystemTypesLinux), 815 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesFromImage), 816 DiskSizeGB: ptr.To[int32](120), 817 ManagedDisk: &armcompute.VirtualMachineScaleSetManagedDiskParameters{ 818 StorageAccountType: ptr.To(armcompute.StorageAccountTypesPremiumLRS), 819 }, 820 }, 821 DataDisks: dataDisk, 822 }, 823 DiagnosticsProfile: &armcompute.DiagnosticsProfile{ 824 BootDiagnostics: &armcompute.BootDiagnostics{ 825 Enabled: ptr.To(true), 826 }, 827 }, 828 NetworkProfile: &armcompute.VirtualMachineScaleSetNetworkProfile{ 829 NetworkInterfaceConfigurations: []*armcompute.VirtualMachineScaleSetNetworkConfiguration{ 830 { 831 Name: ptr.To("my-vmss-nic-0"), 832 Properties: &armcompute.VirtualMachineScaleSetNetworkConfigurationProperties{ 833 Primary: ptr.To(true), 834 EnableAcceleratedNetworking: ptr.To(false), 835 EnableIPForwarding: ptr.To(true), 836 IPConfigurations: []*armcompute.VirtualMachineScaleSetIPConfiguration{ 837 { 838 Name: ptr.To("ipConfig0"), 839 Properties: &armcompute.VirtualMachineScaleSetIPConfigurationProperties{ 840 Subnet: &armcompute.APIEntityReference{ 841 ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet"), 842 }, 843 Primary: ptr.To(true), 844 PrivateIPAddressVersion: ptr.To(armcompute.IPVersionIPv4), 845 LoadBalancerBackendAddressPools: []*armcompute.SubResource{{ID: ptr.To("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/capz-lb/backendAddressPools/backendPool")}}, 846 }, 847 }, 848 }, 849 }, 850 }, 851 }, 852 }, 853 ExtensionProfile: &armcompute.VirtualMachineScaleSetExtensionProfile{ 854 Extensions: []*armcompute.VirtualMachineScaleSetExtension{ 855 { 856 Name: ptr.To("someExtension"), 857 Properties: &armcompute.VirtualMachineScaleSetExtensionProperties{ 858 Publisher: ptr.To("somePublisher"), 859 Type: ptr.To("someExtension"), 860 TypeHandlerVersion: ptr.To("someVersion"), 861 Settings: map[string]string{ 862 "someSetting": "someValue", 863 }, 864 ProtectedSettings: map[string]string{ 865 "commandToExecute": "echo hello", 866 }, 867 }, 868 }, 869 }, 870 }, 871 }, 872 PlatformFaultDomainCount: ptr.To(int32(1)), 873 ZoneBalance: ptr.To(true), 874 // AdditionalCapabilities: &armcompute.AdditionalCapabilities{UltraSSDEnabled: ptr.To(true)}, 875 }, 876 } 877 } 878 879 func fetchDataDiskBasedOnSize(vmSize string) []*armcompute.VirtualMachineScaleSetDataDisk { 880 var dataDisk []*armcompute.VirtualMachineScaleSetDataDisk 881 if vmSize == "VM_SIZE" { 882 dataDisk = []*armcompute.VirtualMachineScaleSetDataDisk{ 883 { 884 Lun: ptr.To[int32](0), 885 Name: ptr.To("my-vmss_my_disk"), 886 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 887 DiskSizeGB: ptr.To[int32](128), 888 }, 889 { 890 Lun: ptr.To[int32](1), 891 Name: ptr.To("my-vmss_my_disk_with_managed_disk"), 892 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 893 DiskSizeGB: ptr.To[int32](128), 894 ManagedDisk: &armcompute.VirtualMachineScaleSetManagedDiskParameters{ 895 StorageAccountType: ptr.To(armcompute.StorageAccountTypesStandardLRS), 896 }, 897 }, 898 { 899 Lun: ptr.To[int32](2), 900 Name: ptr.To("my-vmss_managed_disk_with_encryption"), 901 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 902 DiskSizeGB: ptr.To[int32](128), 903 ManagedDisk: &armcompute.VirtualMachineScaleSetManagedDiskParameters{ 904 StorageAccountType: ptr.To(armcompute.StorageAccountTypesStandardLRS), 905 DiskEncryptionSet: &armcompute.DiskEncryptionSetParameters{ 906 ID: ptr.To("encryption_id"), 907 }, 908 }, 909 }, 910 { 911 Lun: ptr.To[int32](3), 912 Name: ptr.To("my-vmss_my_disk_with_ultra_disks"), 913 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 914 DiskSizeGB: ptr.To[int32](128), 915 ManagedDisk: &armcompute.VirtualMachineScaleSetManagedDiskParameters{ 916 StorageAccountType: ptr.To(armcompute.StorageAccountTypesUltraSSDLRS), 917 }, 918 }, 919 } 920 } else { 921 dataDisk = []*armcompute.VirtualMachineScaleSetDataDisk{ 922 { 923 Lun: ptr.To[int32](0), 924 Name: ptr.To("my-vmss_my_disk"), 925 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 926 DiskSizeGB: ptr.To[int32](128), 927 }, 928 { 929 Lun: ptr.To[int32](1), 930 Name: ptr.To("my-vmss_my_disk_with_managed_disk"), 931 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 932 DiskSizeGB: ptr.To[int32](128), 933 ManagedDisk: &armcompute.VirtualMachineScaleSetManagedDiskParameters{ 934 StorageAccountType: ptr.To(armcompute.StorageAccountTypesStandardLRS), 935 }, 936 }, 937 { 938 Lun: ptr.To[int32](2), 939 Name: ptr.To("my-vmss_managed_disk_with_encryption"), 940 CreateOption: ptr.To(armcompute.DiskCreateOptionTypesEmpty), 941 DiskSizeGB: ptr.To[int32](128), 942 ManagedDisk: &armcompute.VirtualMachineScaleSetManagedDiskParameters{ 943 StorageAccountType: ptr.To(armcompute.StorageAccountTypesStandardLRS), 944 DiskEncryptionSet: &armcompute.DiskEncryptionSetParameters{ 945 ID: ptr.To("encryption_id"), 946 }, 947 }, 948 }, 949 } 950 } 951 return dataDisk 952 } 953 954 func newDefaultInstances() []armcompute.VirtualMachineScaleSetVM { 955 return []armcompute.VirtualMachineScaleSetVM{ 956 { 957 ID: ptr.To("my-vm-id"), 958 InstanceID: ptr.To("my-vm-1"), 959 Name: ptr.To("my-vm"), 960 Properties: &armcompute.VirtualMachineScaleSetVMProperties{ 961 ProvisioningState: ptr.To("Succeeded"), 962 OSProfile: &armcompute.OSProfile{ 963 ComputerName: ptr.To("instance-000001"), 964 }, 965 StorageProfile: &armcompute.StorageProfile{ 966 ImageReference: &armcompute.ImageReference{ 967 Publisher: ptr.To("fake-publisher"), 968 Offer: ptr.To("my-offer"), 969 SKU: ptr.To("sku-id"), 970 Version: ptr.To("1.0"), 971 }, 972 }, 973 }, 974 }, 975 { 976 ID: ptr.To("my-vm-id"), 977 InstanceID: ptr.To("my-vm-2"), 978 Name: ptr.To("my-vm"), 979 Properties: &armcompute.VirtualMachineScaleSetVMProperties{ 980 ProvisioningState: ptr.To("Succeeded"), 981 OSProfile: &armcompute.OSProfile{ 982 ComputerName: ptr.To("instance-000002"), 983 }, 984 StorageProfile: &armcompute.StorageProfile{ 985 ImageReference: &armcompute.ImageReference{ 986 Publisher: ptr.To("fake-publisher"), 987 Offer: ptr.To("my-offer"), 988 SKU: ptr.To("sku-id"), 989 Version: ptr.To("1.0"), 990 }, 991 }, 992 }, 993 }, 994 } 995 }