sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azuremachine_webhook_test.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 "testing" 22 23 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 24 . "github.com/onsi/gomega" 25 "github.com/pkg/errors" 26 corev1 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/api/resource" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/utils/ptr" 30 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 31 "sigs.k8s.io/controller-runtime/pkg/client" 32 ) 33 34 var ( 35 validSSHPublicKey = generateSSHPublicKey(true) 36 validOSDisk = generateValidOSDisk() 37 ) 38 39 func TestAzureMachine_ValidateCreate(t *testing.T) { 40 tests := []struct { 41 name string 42 machine *AzureMachine 43 wantErr bool 44 }{ 45 { 46 name: "azuremachine with marketplace image - full", 47 machine: createMachineWithMarketPlaceImage("PUB1234", "OFFER1234", "SKU1234", "1.0.0"), 48 wantErr: false, 49 }, 50 { 51 name: "azuremachine with marketplace image - missing publisher", 52 machine: createMachineWithMarketPlaceImage("", "OFFER1235", "SKU1235", "2.0.0"), 53 wantErr: true, 54 }, 55 { 56 name: "azuremachine with shared gallery image - full", 57 machine: createMachineWithSharedImage("SUB123", "RG123", "NAME123", "GALLERY1", "1.0.0"), 58 wantErr: false, 59 }, 60 { 61 name: "azuremachine with marketplace image - missing subscription", 62 machine: createMachineWithSharedImage("", "RG124", "NAME124", "GALLERY1", "2.0.0"), 63 wantErr: true, 64 }, 65 { 66 name: "azuremachine with image by - with id", 67 machine: createMachineWithImageByID("ID123"), 68 wantErr: false, 69 }, 70 { 71 name: "azuremachine with image by - without id", 72 machine: createMachineWithImageByID(""), 73 wantErr: true, 74 }, 75 { 76 name: "azuremachine with valid SSHPublicKey", 77 machine: createMachineWithSSHPublicKey(validSSHPublicKey), 78 wantErr: false, 79 }, 80 { 81 name: "azuremachine without SSHPublicKey", 82 machine: createMachineWithSSHPublicKey(""), 83 wantErr: true, 84 }, 85 { 86 name: "azuremachine with invalid SSHPublicKey", 87 machine: createMachineWithSSHPublicKey("invalid ssh key"), 88 wantErr: true, 89 }, 90 { 91 name: "azuremachine with list of user-assigned identities", 92 machine: createMachineWithUserAssignedIdentities([]UserAssignedIdentity{ 93 {ProviderID: "azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.Compute/virtualMachines/default-12345-control-plane-9d5x5"}, 94 {ProviderID: "azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.Compute/virtualMachines/default-12345-control-plane-a1b2c"}, 95 }), 96 wantErr: false, 97 }, 98 { 99 name: "azuremachine with empty list of user-assigned identities", 100 machine: createMachineWithUserAssignedIdentities([]UserAssignedIdentity{}), 101 wantErr: true, 102 }, 103 { 104 name: "azuremachine with valid osDisk cache type", 105 machine: createMachineWithOsDiskCacheType(string(armcompute.PossibleCachingTypesValues()[1])), 106 wantErr: false, 107 }, 108 { 109 name: "azuremachine with invalid osDisk cache type", 110 machine: createMachineWithOsDiskCacheType("invalid_cache_type"), 111 wantErr: true, 112 }, 113 { 114 name: "azuremachinepool with managed diagnostics profile", 115 machine: createMachineWithDiagnostics(ManagedDiagnosticsStorage, nil), 116 wantErr: false, 117 }, 118 { 119 name: "azuremachine with disabled diagnostics profile", 120 machine: createMachineWithDiagnostics(ManagedDiagnosticsStorage, nil), 121 wantErr: false, 122 }, 123 { 124 name: "azuremachine with user managed diagnostics profile and defined user managed storage account", 125 machine: createMachineWithDiagnostics(UserManagedDiagnosticsStorage, &UserManagedBootDiagnostics{StorageAccountURI: "https://fakeurl"}), 126 wantErr: false, 127 }, 128 { 129 name: "azuremachine with empty diagnostics profile", 130 machine: createMachineWithDiagnostics("", nil), 131 wantErr: false, 132 }, 133 { 134 name: "azuremachine with user managed diagnostics profile, but empty user managed storage account", 135 machine: createMachineWithDiagnostics(UserManagedDiagnosticsStorage, nil), 136 wantErr: true, 137 }, 138 { 139 name: "azuremachine with invalid network configuration", 140 machine: createMachineWithNetworkConfig("subnet", nil, []NetworkInterface{{SubnetName: "subnet1"}}), 141 wantErr: true, 142 }, 143 { 144 name: "azuremachine with valid legacy network configuration", 145 machine: createMachineWithNetworkConfig("subnet", nil, []NetworkInterface{}), 146 wantErr: false, 147 }, 148 { 149 name: "azuremachine with valid network configuration", 150 machine: createMachineWithNetworkConfig("", nil, []NetworkInterface{{SubnetName: "subnet", PrivateIPConfigs: 1}}), 151 wantErr: false, 152 }, 153 { 154 name: "azuremachine without confidential compute properties and encryption at host enabled", 155 machine: createMachineWithConfidentialCompute("", "", true, false, false), 156 wantErr: false, 157 }, 158 { 159 name: "azuremachine with confidential compute VMGuestStateOnly encryption and encryption at host enabled", 160 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, true, false, false), 161 wantErr: true, 162 }, 163 { 164 name: "azuremachine with confidential compute DiskWithVMGuestState encryption and encryption at host enabled", 165 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, true, true, true), 166 wantErr: true, 167 }, 168 { 169 name: "azuremachine with confidential compute VMGuestStateOnly encryption, vTPM and SecureBoot enabled", 170 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, true, true), 171 wantErr: false, 172 }, 173 { 174 name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM enabled and SecureBoot disabled", 175 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, true, false), 176 wantErr: false, 177 }, 178 { 179 name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM disabled and SecureBoot enabled", 180 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, false, true), 181 wantErr: true, 182 }, 183 { 184 name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM enabled, SecureBoot disabled and SecurityType empty", 185 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, "", false, true, false), 186 wantErr: true, 187 }, 188 { 189 name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM and SecureBoot empty", 190 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, false, false), 191 wantErr: true, 192 }, 193 { 194 name: "azuremachine with confidential compute DiskWithVMGuestState encryption, vTPM and SecureBoot enabled", 195 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, true, true), 196 wantErr: false, 197 }, 198 { 199 name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM enabled and SecureBoot disabled", 200 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, true, false), 201 wantErr: true, 202 }, 203 { 204 name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM disabled and SecureBoot enabled", 205 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, false, true), 206 wantErr: true, 207 }, 208 { 209 name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM disabled and SecureBoot disabled", 210 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, false, false), 211 wantErr: true, 212 }, 213 { 214 name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM enabled, SecureBoot disabled and SecurityType empty", 215 machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, "", false, true, false), 216 wantErr: true, 217 }, 218 } 219 for _, tc := range tests { 220 t.Run(tc.name, func(t *testing.T) { 221 g := NewWithT(t) 222 mw := &azureMachineWebhook{} 223 _, err := mw.ValidateCreate(context.Background(), tc.machine) 224 if tc.wantErr { 225 g.Expect(err).To(HaveOccurred()) 226 } else { 227 g.Expect(err).NotTo(HaveOccurred()) 228 } 229 }) 230 } 231 } 232 233 func TestAzureMachine_ValidateUpdate(t *testing.T) { 234 tests := []struct { 235 name string 236 oldMachine *AzureMachine 237 newMachine *AzureMachine 238 wantErr bool 239 }{ 240 { 241 name: "invalidTest: azuremachine.spec.image is immutable", 242 oldMachine: &AzureMachine{ 243 Spec: AzureMachineSpec{ 244 Image: &Image{ 245 ID: ptr.To("imageID-1"), 246 }, 247 }, 248 }, 249 newMachine: &AzureMachine{ 250 Spec: AzureMachineSpec{ 251 Image: &Image{ 252 ID: ptr.To("imageID-2"), 253 }, 254 }, 255 }, 256 wantErr: true, 257 }, 258 { 259 name: "validTest: azuremachine.spec.image is immutable", 260 oldMachine: &AzureMachine{ 261 Spec: AzureMachineSpec{ 262 Image: &Image{ 263 ID: ptr.To("imageID-1"), 264 }, 265 }, 266 }, 267 newMachine: &AzureMachine{ 268 Spec: AzureMachineSpec{ 269 Image: &Image{ 270 ID: ptr.To("imageID-1"), 271 }, 272 }, 273 }, 274 wantErr: false, 275 }, 276 { 277 name: "invalidTest: azuremachine.spec.Identity is immutable", 278 oldMachine: &AzureMachine{ 279 Spec: AzureMachineSpec{ 280 Identity: VMIdentityUserAssigned, 281 }, 282 }, 283 newMachine: &AzureMachine{ 284 Spec: AzureMachineSpec{ 285 Identity: VMIdentityNone, 286 }, 287 }, 288 wantErr: true, 289 }, 290 { 291 name: "validTest: azuremachine.spec.Identity is immutable", 292 oldMachine: &AzureMachine{ 293 Spec: AzureMachineSpec{ 294 Identity: VMIdentityNone, 295 }, 296 }, 297 newMachine: &AzureMachine{ 298 Spec: AzureMachineSpec{ 299 Identity: VMIdentityNone, 300 }, 301 }, 302 wantErr: false, 303 }, 304 { 305 name: "invalidTest: azuremachine.spec.UserAssignedIdentities is immutable", 306 oldMachine: &AzureMachine{ 307 Spec: AzureMachineSpec{ 308 UserAssignedIdentities: []UserAssignedIdentity{ 309 {ProviderID: "providerID-1"}, 310 }, 311 }, 312 }, 313 newMachine: &AzureMachine{ 314 Spec: AzureMachineSpec{ 315 UserAssignedIdentities: []UserAssignedIdentity{ 316 {ProviderID: "providerID-2"}, 317 }, 318 }, 319 }, 320 wantErr: true, 321 }, 322 { 323 name: "validTest: azuremachine.spec.UserAssignedIdentities is immutable", 324 oldMachine: &AzureMachine{ 325 Spec: AzureMachineSpec{ 326 UserAssignedIdentities: []UserAssignedIdentity{ 327 {ProviderID: "providerID-1"}, 328 }, 329 }, 330 }, 331 newMachine: &AzureMachine{ 332 Spec: AzureMachineSpec{ 333 UserAssignedIdentities: []UserAssignedIdentity{ 334 {ProviderID: "providerID-1"}, 335 }, 336 }, 337 }, 338 wantErr: false, 339 }, 340 { 341 name: "invalidTest: azuremachine.spec.RoleAssignmentName is immutable", 342 oldMachine: &AzureMachine{ 343 Spec: AzureMachineSpec{ 344 RoleAssignmentName: "role", 345 }, 346 }, 347 newMachine: &AzureMachine{ 348 Spec: AzureMachineSpec{ 349 RoleAssignmentName: "not-role", 350 }, 351 }, 352 wantErr: true, 353 }, 354 { 355 name: "validTest: azuremachine.spec.RoleAssignmentName is immutable", 356 oldMachine: &AzureMachine{ 357 Spec: AzureMachineSpec{ 358 RoleAssignmentName: "role", 359 }, 360 }, 361 newMachine: &AzureMachine{ 362 Spec: AzureMachineSpec{ 363 RoleAssignmentName: "role", 364 }, 365 }, 366 wantErr: false, 367 }, 368 { 369 name: "invalidTest: azuremachine.spec.RoleAssignmentName is immutable", 370 oldMachine: &AzureMachine{ 371 Spec: AzureMachineSpec{ 372 SystemAssignedIdentityRole: &SystemAssignedIdentityRole{ 373 Name: "role", 374 Scope: "scope", 375 DefinitionID: "definitionID", 376 }, 377 }, 378 }, 379 newMachine: &AzureMachine{ 380 Spec: AzureMachineSpec{ 381 SystemAssignedIdentityRole: &SystemAssignedIdentityRole{ 382 Name: "not-role", 383 Scope: "scope", 384 DefinitionID: "definitionID", 385 }, 386 }, 387 }, 388 wantErr: true, 389 }, 390 { 391 name: "validTest: azuremachine.spec.SystemAssignedIdentityRole is immutable", 392 oldMachine: &AzureMachine{ 393 Spec: AzureMachineSpec{ 394 SystemAssignedIdentityRole: &SystemAssignedIdentityRole{ 395 Name: "role", 396 Scope: "scope", 397 DefinitionID: "definitionID", 398 }, 399 }, 400 }, 401 newMachine: &AzureMachine{ 402 Spec: AzureMachineSpec{ 403 SystemAssignedIdentityRole: &SystemAssignedIdentityRole{ 404 Name: "role", 405 Scope: "scope", 406 DefinitionID: "definitionID", 407 }, 408 }, 409 }, 410 wantErr: false, 411 }, 412 { 413 name: "invalidTest: azuremachine.spec.OSDisk is immutable", 414 oldMachine: &AzureMachine{ 415 Spec: AzureMachineSpec{ 416 OSDisk: OSDisk{ 417 OSType: "osType-0", 418 }, 419 }, 420 }, 421 newMachine: &AzureMachine{ 422 Spec: AzureMachineSpec{ 423 OSDisk: OSDisk{ 424 OSType: "osType-1", 425 }, 426 }, 427 }, 428 wantErr: true, 429 }, 430 { 431 name: "validTest: azuremachine.spec.OSDisk is immutable", 432 oldMachine: &AzureMachine{ 433 Spec: AzureMachineSpec{ 434 OSDisk: OSDisk{ 435 OSType: "osType-1", 436 }, 437 }, 438 }, 439 newMachine: &AzureMachine{ 440 Spec: AzureMachineSpec{ 441 OSDisk: OSDisk{ 442 OSType: "osType-1", 443 }, 444 }, 445 }, 446 wantErr: false, 447 }, 448 { 449 name: "invalidTest: azuremachine.spec.DataDisks is immutable", 450 oldMachine: &AzureMachine{ 451 Spec: AzureMachineSpec{ 452 DataDisks: []DataDisk{ 453 { 454 DiskSizeGB: 128, 455 }, 456 }, 457 }, 458 }, 459 newMachine: &AzureMachine{ 460 Spec: AzureMachineSpec{ 461 DataDisks: []DataDisk{ 462 { 463 DiskSizeGB: 64, 464 }, 465 }, 466 }, 467 }, 468 wantErr: true, 469 }, 470 { 471 name: "validTest: azuremachine.spec.DataDisks is immutable", 472 oldMachine: &AzureMachine{ 473 Spec: AzureMachineSpec{ 474 DataDisks: []DataDisk{ 475 { 476 DiskSizeGB: 128, 477 }, 478 }, 479 }, 480 }, 481 newMachine: &AzureMachine{ 482 Spec: AzureMachineSpec{ 483 DataDisks: []DataDisk{ 484 { 485 DiskSizeGB: 128, 486 }, 487 }, 488 }, 489 }, 490 wantErr: false, 491 }, 492 { 493 name: "invalidTest: azuremachine.spec.SSHPublicKey is immutable", 494 oldMachine: &AzureMachine{ 495 Spec: AzureMachineSpec{ 496 SSHPublicKey: "validKey", 497 }, 498 }, 499 newMachine: &AzureMachine{ 500 Spec: AzureMachineSpec{ 501 SSHPublicKey: "invalidKey", 502 }, 503 }, 504 wantErr: true, 505 }, 506 { 507 name: "validTest: azuremachine.spec.SSHPublicKey is immutable", 508 oldMachine: &AzureMachine{ 509 Spec: AzureMachineSpec{ 510 SSHPublicKey: "validKey", 511 }, 512 }, 513 newMachine: &AzureMachine{ 514 Spec: AzureMachineSpec{ 515 SSHPublicKey: "validKey", 516 }, 517 }, 518 wantErr: false, 519 }, 520 { 521 name: "invalidTest: azuremachine.spec.AllocatePublicIP is immutable", 522 oldMachine: &AzureMachine{ 523 Spec: AzureMachineSpec{ 524 AllocatePublicIP: true, 525 }, 526 }, 527 newMachine: &AzureMachine{ 528 Spec: AzureMachineSpec{ 529 AllocatePublicIP: false, 530 }, 531 }, 532 wantErr: true, 533 }, 534 { 535 name: "validTest: azuremachine.spec.AllocatePublicIP is immutable", 536 oldMachine: &AzureMachine{ 537 Spec: AzureMachineSpec{ 538 AllocatePublicIP: true, 539 }, 540 }, 541 newMachine: &AzureMachine{ 542 Spec: AzureMachineSpec{ 543 AllocatePublicIP: true, 544 }, 545 }, 546 wantErr: false, 547 }, 548 { 549 name: "invalidTest: azuremachine.spec.EnableIPForwarding is immutable", 550 oldMachine: &AzureMachine{ 551 Spec: AzureMachineSpec{ 552 EnableIPForwarding: true, 553 }, 554 }, 555 newMachine: &AzureMachine{ 556 Spec: AzureMachineSpec{ 557 EnableIPForwarding: false, 558 }, 559 }, 560 wantErr: true, 561 }, 562 { 563 name: "validTest: azuremachine.spec.EnableIPForwarding is immutable", 564 oldMachine: &AzureMachine{ 565 Spec: AzureMachineSpec{ 566 EnableIPForwarding: true, 567 }, 568 }, 569 newMachine: &AzureMachine{ 570 Spec: AzureMachineSpec{ 571 EnableIPForwarding: true, 572 }, 573 }, 574 wantErr: false, 575 }, 576 { 577 name: "invalidTest: azuremachine.spec.AcceleratedNetworking is immutable", 578 oldMachine: &AzureMachine{ 579 Spec: AzureMachineSpec{ 580 AcceleratedNetworking: ptr.To(true), 581 }, 582 }, 583 newMachine: &AzureMachine{ 584 Spec: AzureMachineSpec{ 585 AcceleratedNetworking: ptr.To(false), 586 }, 587 }, 588 wantErr: true, 589 }, 590 { 591 name: "validTest: azuremachine.spec.AcceleratedNetworking is immutable", 592 oldMachine: &AzureMachine{ 593 Spec: AzureMachineSpec{ 594 AcceleratedNetworking: ptr.To(true), 595 }, 596 }, 597 newMachine: &AzureMachine{ 598 Spec: AzureMachineSpec{ 599 AcceleratedNetworking: ptr.To(true), 600 }, 601 }, 602 wantErr: false, 603 }, 604 { 605 name: "validTest: azuremachine.spec.AcceleratedNetworking transition(from true) to nil is acceptable", 606 oldMachine: &AzureMachine{ 607 Spec: AzureMachineSpec{ 608 AcceleratedNetworking: ptr.To(true), 609 }, 610 }, 611 newMachine: &AzureMachine{ 612 Spec: AzureMachineSpec{ 613 AcceleratedNetworking: nil, 614 }, 615 }, 616 wantErr: false, 617 }, 618 { 619 name: "validTest: azuremachine.spec.AcceleratedNetworking transition(from false) to nil is acceptable", 620 oldMachine: &AzureMachine{ 621 Spec: AzureMachineSpec{ 622 AcceleratedNetworking: ptr.To(false), 623 }, 624 }, 625 newMachine: &AzureMachine{ 626 Spec: AzureMachineSpec{ 627 AcceleratedNetworking: nil, 628 }, 629 }, 630 wantErr: false, 631 }, 632 { 633 name: "invalidTest: azuremachine.spec.SpotVMOptions is immutable", 634 oldMachine: &AzureMachine{ 635 Spec: AzureMachineSpec{ 636 SpotVMOptions: &SpotVMOptions{ 637 MaxPrice: &resource.Quantity{Format: "vmoptions-0"}, 638 }, 639 }, 640 }, 641 newMachine: &AzureMachine{ 642 Spec: AzureMachineSpec{ 643 SpotVMOptions: &SpotVMOptions{ 644 MaxPrice: &resource.Quantity{Format: "vmoptions-1"}, 645 }, 646 }, 647 }, 648 wantErr: true, 649 }, 650 { 651 name: "validTest: azuremachine.spec.SpotVMOptions is immutable", 652 oldMachine: &AzureMachine{ 653 Spec: AzureMachineSpec{ 654 SpotVMOptions: &SpotVMOptions{ 655 MaxPrice: &resource.Quantity{Format: "vmoptions-1"}, 656 }, 657 }, 658 }, 659 newMachine: &AzureMachine{ 660 Spec: AzureMachineSpec{ 661 SpotVMOptions: &SpotVMOptions{ 662 MaxPrice: &resource.Quantity{Format: "vmoptions-1"}, 663 }, 664 }, 665 }, 666 wantErr: false, 667 }, 668 { 669 name: "invalidTest: azuremachine.spec.SecurityProfile is immutable", 670 oldMachine: &AzureMachine{ 671 Spec: AzureMachineSpec{ 672 SecurityProfile: &SecurityProfile{EncryptionAtHost: ptr.To(true)}, 673 }, 674 }, 675 newMachine: &AzureMachine{ 676 Spec: AzureMachineSpec{ 677 SecurityProfile: &SecurityProfile{EncryptionAtHost: ptr.To(false)}, 678 }, 679 }, 680 wantErr: true, 681 }, 682 { 683 name: "validTest: azuremachine.spec.SecurityProfile is immutable", 684 oldMachine: &AzureMachine{ 685 Spec: AzureMachineSpec{ 686 SecurityProfile: &SecurityProfile{EncryptionAtHost: ptr.To(true)}, 687 }, 688 }, 689 newMachine: &AzureMachine{ 690 Spec: AzureMachineSpec{ 691 SecurityProfile: &SecurityProfile{EncryptionAtHost: ptr.To(true)}, 692 }, 693 }, 694 wantErr: false, 695 }, 696 { 697 name: "invalidTest: azuremachine.spec.Diagnostics is immutable", 698 oldMachine: &AzureMachine{ 699 Spec: AzureMachineSpec{ 700 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: DisabledDiagnosticsStorage}}, 701 }, 702 }, 703 newMachine: &AzureMachine{ 704 Spec: AzureMachineSpec{ 705 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: ManagedDiagnosticsStorage}}, 706 }, 707 }, 708 wantErr: true, 709 }, 710 { 711 name: "validTest: azuremachine.spec.Diagnostics is immutable", 712 oldMachine: &AzureMachine{ 713 Spec: AzureMachineSpec{ 714 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: DisabledDiagnosticsStorage}}, 715 }, 716 }, 717 newMachine: &AzureMachine{ 718 Spec: AzureMachineSpec{ 719 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: DisabledDiagnosticsStorage}}, 720 }, 721 }, 722 wantErr: false, 723 }, 724 { 725 name: "validTest: azuremachine.spec.Diagnostics should not error on updating nil diagnostics", 726 oldMachine: &AzureMachine{ 727 Spec: AzureMachineSpec{}, 728 }, 729 newMachine: &AzureMachine{ 730 Spec: AzureMachineSpec{ 731 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: ManagedDiagnosticsStorage}}, 732 }, 733 }, 734 wantErr: false, 735 }, 736 { 737 name: "invalidTest: azuremachine.spec.Diagnostics is immutable", 738 oldMachine: &AzureMachine{ 739 Spec: AzureMachineSpec{ 740 Diagnostics: &Diagnostics{}, 741 }, 742 }, 743 newMachine: &AzureMachine{ 744 Spec: AzureMachineSpec{ 745 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: ManagedDiagnosticsStorage}}, 746 }, 747 }, 748 wantErr: true, 749 }, 750 { 751 name: "invalidTest: azuremachine.spec.Diagnostics is immutable", 752 oldMachine: &AzureMachine{ 753 Spec: AzureMachineSpec{ 754 Diagnostics: &Diagnostics{ 755 Boot: &BootDiagnostics{}, 756 }, 757 }, 758 }, 759 newMachine: &AzureMachine{ 760 Spec: AzureMachineSpec{ 761 Diagnostics: &Diagnostics{Boot: &BootDiagnostics{StorageAccountType: ManagedDiagnosticsStorage}}, 762 }, 763 }, 764 wantErr: true, 765 }, 766 { 767 name: "validTest: azuremachine.spec.networkInterfaces is immutable", 768 oldMachine: &AzureMachine{ 769 Spec: AzureMachineSpec{ 770 NetworkInterfaces: []NetworkInterface{{SubnetName: "subnet"}}, 771 }, 772 }, 773 newMachine: &AzureMachine{ 774 Spec: AzureMachineSpec{ 775 NetworkInterfaces: []NetworkInterface{{SubnetName: "subnet"}}, 776 }, 777 }, 778 wantErr: false, 779 }, 780 { 781 name: "invalidtest: azuremachine.spec.networkInterfaces is immutable", 782 oldMachine: &AzureMachine{ 783 Spec: AzureMachineSpec{ 784 NetworkInterfaces: []NetworkInterface{{SubnetName: "subnet1"}}, 785 }, 786 }, 787 newMachine: &AzureMachine{ 788 Spec: AzureMachineSpec{ 789 NetworkInterfaces: []NetworkInterface{{SubnetName: "subnet2"}}, 790 }, 791 }, 792 wantErr: true, 793 }, 794 { 795 name: "invalidtest: updating subnet name from empty to non empty", 796 oldMachine: &AzureMachine{ 797 Spec: AzureMachineSpec{ 798 NetworkInterfaces: []NetworkInterface{{SubnetName: ""}}, 799 }, 800 }, 801 newMachine: &AzureMachine{ 802 Spec: AzureMachineSpec{ 803 NetworkInterfaces: []NetworkInterface{{SubnetName: "subnet1"}}, 804 }, 805 }, 806 wantErr: true, 807 }, 808 } 809 810 for _, tc := range tests { 811 t.Run(tc.name, func(t *testing.T) { 812 g := NewWithT(t) 813 mw := &azureMachineWebhook{} 814 _, err := mw.ValidateUpdate(context.Background(), tc.oldMachine, tc.newMachine) 815 if tc.wantErr { 816 g.Expect(err).To(HaveOccurred()) 817 } else { 818 g.Expect(err).NotTo(HaveOccurred()) 819 } 820 }) 821 } 822 } 823 824 type mockDefaultClient struct { 825 client.Client 826 SubscriptionID string 827 } 828 829 func (m mockDefaultClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { 830 switch obj := obj.(type) { 831 case *AzureCluster: 832 obj.Spec.SubscriptionID = m.SubscriptionID 833 case *clusterv1.Cluster: 834 obj.Spec.InfrastructureRef = &corev1.ObjectReference{ 835 Kind: AzureClusterKind, 836 Name: "test-cluster", 837 } 838 default: 839 return errors.New("invalid object type") 840 } 841 return nil 842 } 843 844 func TestAzureMachine_Default(t *testing.T) { 845 g := NewWithT(t) 846 847 type test struct { 848 machine *AzureMachine 849 } 850 851 testSubscriptionID := "test-subscription-id" 852 mockClient := mockDefaultClient{SubscriptionID: testSubscriptionID} 853 existingPublicKey := validSSHPublicKey 854 publicKeyExistTest := test{machine: createMachineWithSSHPublicKey(existingPublicKey)} 855 publicKeyNotExistTest := test{machine: createMachineWithSSHPublicKey("")} 856 testObjectMeta := metav1.ObjectMeta{ 857 Labels: map[string]string{ 858 clusterv1.ClusterNameLabel: "test-cluster", 859 }, 860 } 861 862 mw := &azureMachineWebhook{ 863 Client: mockClient, 864 } 865 866 err := mw.Default(context.Background(), publicKeyExistTest.machine) 867 g.Expect(err).NotTo(HaveOccurred()) 868 g.Expect(publicKeyExistTest.machine.Spec.SSHPublicKey).To(Equal(existingPublicKey)) 869 870 err = mw.Default(context.Background(), publicKeyNotExistTest.machine) 871 g.Expect(err).NotTo(HaveOccurred()) 872 g.Expect(publicKeyNotExistTest.machine.Spec.SSHPublicKey).To(Not(BeEmpty())) 873 874 cacheTypeNotSpecifiedTest := test{machine: &AzureMachine{ObjectMeta: testObjectMeta, Spec: AzureMachineSpec{OSDisk: OSDisk{CachingType: ""}}}} 875 err = mw.Default(context.Background(), cacheTypeNotSpecifiedTest.machine) 876 g.Expect(err).NotTo(HaveOccurred()) 877 g.Expect(cacheTypeNotSpecifiedTest.machine.Spec.OSDisk.CachingType).To(Equal("None")) 878 879 for _, possibleCachingType := range armcompute.PossibleCachingTypesValues() { 880 cacheTypeSpecifiedTest := test{machine: &AzureMachine{ObjectMeta: testObjectMeta, Spec: AzureMachineSpec{OSDisk: OSDisk{CachingType: string(possibleCachingType)}}}} 881 err = mw.Default(context.Background(), cacheTypeSpecifiedTest.machine) 882 g.Expect(err).NotTo(HaveOccurred()) 883 g.Expect(cacheTypeSpecifiedTest.machine.Spec.OSDisk.CachingType).To(Equal(string(possibleCachingType))) 884 } 885 } 886 887 func createMachineWithNetworkConfig(subnetName string, acceleratedNetworking *bool, interfaces []NetworkInterface) *AzureMachine { 888 return &AzureMachine{ 889 Spec: AzureMachineSpec{ 890 SubnetName: subnetName, 891 NetworkInterfaces: interfaces, 892 AcceleratedNetworking: acceleratedNetworking, 893 OSDisk: validOSDisk, 894 SSHPublicKey: validSSHPublicKey, 895 }, 896 } 897 } 898 899 func createMachineWithSharedImage(subscriptionID, resourceGroup, name, gallery, version string) *AzureMachine { 900 image := &Image{ 901 SharedGallery: &AzureSharedGalleryImage{ 902 SubscriptionID: subscriptionID, 903 ResourceGroup: resourceGroup, 904 Name: name, 905 Gallery: gallery, 906 Version: version, 907 }, 908 } 909 910 return &AzureMachine{ 911 Spec: AzureMachineSpec{ 912 Image: image, 913 SSHPublicKey: validSSHPublicKey, 914 OSDisk: validOSDisk, 915 }, 916 } 917 } 918 919 func createMachineWithMarketPlaceImage(publisher, offer, sku, version string) *AzureMachine { 920 image := &Image{ 921 Marketplace: &AzureMarketplaceImage{ 922 ImagePlan: ImagePlan{ 923 Publisher: publisher, 924 Offer: offer, 925 SKU: sku, 926 }, 927 Version: version, 928 }, 929 } 930 931 return &AzureMachine{ 932 Spec: AzureMachineSpec{ 933 Image: image, 934 SSHPublicKey: validSSHPublicKey, 935 OSDisk: validOSDisk, 936 }, 937 } 938 } 939 940 func createMachineWithImageByID(imageID string) *AzureMachine { 941 image := &Image{ 942 ID: &imageID, 943 } 944 945 return &AzureMachine{ 946 Spec: AzureMachineSpec{ 947 Image: image, 948 SSHPublicKey: validSSHPublicKey, 949 OSDisk: validOSDisk, 950 }, 951 } 952 } 953 954 func createMachineWithOsDiskCacheType(cacheType string) *AzureMachine { 955 machine := &AzureMachine{ 956 Spec: AzureMachineSpec{ 957 SSHPublicKey: validSSHPublicKey, 958 OSDisk: validOSDisk, 959 }, 960 } 961 machine.Spec.OSDisk.CachingType = cacheType 962 return machine 963 } 964 965 func createMachineWithSystemAssignedIdentityRoleName() *AzureMachine { 966 machine := &AzureMachine{ 967 Spec: AzureMachineSpec{ 968 SSHPublicKey: validSSHPublicKey, 969 OSDisk: validOSDisk, 970 Identity: VMIdentitySystemAssigned, 971 SystemAssignedIdentityRole: &SystemAssignedIdentityRole{ 972 Name: "c6e3443d-bc11-4335-8819-ab6637b10586", 973 Scope: "test-scope", 974 DefinitionID: "test-definition-id", 975 }, 976 }, 977 } 978 return machine 979 } 980 981 func createMachineWithoutSystemAssignedIdentityRoleName() *AzureMachine { 982 machine := &AzureMachine{ 983 Spec: AzureMachineSpec{ 984 SSHPublicKey: validSSHPublicKey, 985 OSDisk: validOSDisk, 986 Identity: VMIdentitySystemAssigned, 987 SystemAssignedIdentityRole: &SystemAssignedIdentityRole{ 988 Scope: "test-scope", 989 DefinitionID: "test-definition-id", 990 }, 991 }, 992 } 993 return machine 994 } 995 996 func createMachineWithoutRoleAssignmentName() *AzureMachine { 997 machine := &AzureMachine{ 998 Spec: AzureMachineSpec{ 999 SSHPublicKey: validSSHPublicKey, 1000 OSDisk: validOSDisk, 1001 }, 1002 } 1003 return machine 1004 } 1005 1006 func createMachineWithRoleAssignmentName() *AzureMachine { 1007 machine := &AzureMachine{ 1008 Spec: AzureMachineSpec{ 1009 SSHPublicKey: validSSHPublicKey, 1010 OSDisk: validOSDisk, 1011 RoleAssignmentName: "test-role-assignment", 1012 }, 1013 } 1014 return machine 1015 } 1016 1017 func createMachineWithDiagnostics(diagnosticsType BootDiagnosticsStorageAccountType, userManaged *UserManagedBootDiagnostics) *AzureMachine { 1018 var diagnostics *Diagnostics 1019 1020 if diagnosticsType != "" { 1021 diagnostics = &Diagnostics{ 1022 Boot: &BootDiagnostics{ 1023 StorageAccountType: diagnosticsType, 1024 }, 1025 } 1026 } 1027 1028 if userManaged != nil { 1029 diagnostics.Boot.UserManaged = userManaged 1030 } 1031 1032 return &AzureMachine{ 1033 Spec: AzureMachineSpec{ 1034 SSHPublicKey: validSSHPublicKey, 1035 OSDisk: validOSDisk, 1036 Diagnostics: diagnostics, 1037 }, 1038 } 1039 } 1040 1041 func createMachineWithConfidentialCompute(securityEncryptionType SecurityEncryptionType, securityType SecurityTypes, encryptionAtHost, vTpmEnabled, secureBootEnabled bool) *AzureMachine { 1042 securityProfile := &SecurityProfile{ 1043 EncryptionAtHost: &encryptionAtHost, 1044 SecurityType: securityType, 1045 UefiSettings: &UefiSettings{ 1046 VTpmEnabled: &vTpmEnabled, 1047 SecureBootEnabled: &secureBootEnabled, 1048 }, 1049 } 1050 1051 osDisk := OSDisk{ 1052 DiskSizeGB: ptr.To[int32](30), 1053 OSType: LinuxOS, 1054 ManagedDisk: &ManagedDiskParameters{ 1055 StorageAccountType: "Premium_LRS", 1056 SecurityProfile: &VMDiskSecurityProfile{ 1057 SecurityEncryptionType: securityEncryptionType, 1058 }, 1059 }, 1060 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 1061 } 1062 1063 return &AzureMachine{ 1064 Spec: AzureMachineSpec{ 1065 SSHPublicKey: validSSHPublicKey, 1066 OSDisk: osDisk, 1067 SecurityProfile: securityProfile, 1068 }, 1069 } 1070 }