sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azuremachine_validation_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 "crypto/rand" 21 "crypto/rsa" 22 "encoding/base64" 23 "fmt" 24 "testing" 25 26 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 27 "github.com/google/uuid" 28 . "github.com/onsi/gomega" 29 "golang.org/x/crypto/ssh" 30 "k8s.io/apimachinery/pkg/util/validation/field" 31 "k8s.io/utils/ptr" 32 ) 33 34 func TestAzureMachine_ValidateSSHKey(t *testing.T) { 35 tests := []struct { 36 name string 37 sshKey string 38 wantErr bool 39 }{ 40 { 41 name: "valid ssh key", 42 sshKey: generateSSHPublicKey(true), 43 wantErr: false, 44 }, 45 { 46 name: "invalid ssh key", 47 sshKey: "invalid ssh key", 48 wantErr: true, 49 }, 50 { 51 name: "ssh key not base64 encoded", 52 sshKey: generateSSHPublicKey(false), 53 wantErr: true, 54 }, 55 } 56 57 for _, tc := range tests { 58 t.Run(tc.name, func(t *testing.T) { 59 g := NewWithT(t) 60 err := ValidateSSHKey(tc.sshKey, field.NewPath("sshPublicKey")) 61 if tc.wantErr { 62 g.Expect(err).NotTo(BeEmpty()) 63 } else { 64 g.Expect(err).To(BeEmpty()) 65 } 66 }) 67 } 68 } 69 70 func generateSSHPublicKey(b64Enconded bool) string { 71 privateKey, _ := rsa.GenerateKey(rand.Reader, 2048) 72 publicRsaKey, _ := ssh.NewPublicKey(&privateKey.PublicKey) 73 if b64Enconded { 74 return base64.StdEncoding.EncodeToString(ssh.MarshalAuthorizedKey(publicRsaKey)) 75 } 76 return string(ssh.MarshalAuthorizedKey(publicRsaKey)) 77 } 78 79 type osDiskTestInput struct { 80 name string 81 wantErr bool 82 osDisk OSDisk 83 } 84 85 func TestAzureMachine_ValidateOSDisk(t *testing.T) { 86 testcases := []osDiskTestInput{ 87 { 88 name: "valid os disk spec", 89 wantErr: false, 90 osDisk: generateValidOSDisk(), 91 }, 92 { 93 name: "invalid os disk cache type", 94 wantErr: true, 95 osDisk: createOSDiskWithCacheType("invalid_cache_type"), 96 }, 97 { 98 name: "valid ephemeral os disk spec", 99 wantErr: false, 100 osDisk: OSDisk{ 101 DiskSizeGB: ptr.To[int32](30), 102 CachingType: "None", 103 OSType: "blah", 104 DiffDiskSettings: &DiffDiskSettings{ 105 Option: string(armcompute.DiffDiskOptionsLocal), 106 }, 107 ManagedDisk: &ManagedDiskParameters{ 108 StorageAccountType: "Standard_LRS", 109 }, 110 }, 111 }, 112 { 113 name: "byoc encryption with ephemeral os disk spec", 114 wantErr: true, 115 osDisk: OSDisk{ 116 DiskSizeGB: ptr.To[int32](30), 117 CachingType: "None", 118 OSType: "blah", 119 DiffDiskSettings: &DiffDiskSettings{ 120 Option: string(armcompute.DiffDiskOptionsLocal), 121 }, 122 ManagedDisk: &ManagedDiskParameters{ 123 StorageAccountType: "Standard_LRS", 124 DiskEncryptionSet: &DiskEncryptionSetParameters{ 125 ID: "disk-encryption-set", 126 }, 127 }, 128 }, 129 }, 130 } 131 testcases = append(testcases, generateNegativeTestCases()...) 132 133 for _, test := range testcases { 134 t.Run(test.name, func(t *testing.T) { 135 g := NewWithT(t) 136 err := ValidateOSDisk(test.osDisk, field.NewPath("osDisk")) 137 if test.wantErr { 138 g.Expect(err).NotTo(BeEmpty()) 139 } else { 140 g.Expect(err).To(BeEmpty()) 141 } 142 }) 143 } 144 } 145 146 func generateNegativeTestCases() []osDiskTestInput { 147 inputs := []osDiskTestInput{} 148 testCaseName := "invalid os disk spec" 149 150 invalidDiskSpecs := []OSDisk{ 151 {}, 152 { 153 DiskSizeGB: ptr.To[int32](0), 154 OSType: "blah", 155 }, 156 { 157 DiskSizeGB: ptr.To[int32](-10), 158 OSType: "blah", 159 }, 160 { 161 DiskSizeGB: ptr.To[int32](2050), 162 OSType: "blah", 163 }, 164 { 165 DiskSizeGB: ptr.To[int32](20), 166 OSType: "", 167 }, 168 { 169 DiskSizeGB: ptr.To[int32](30), 170 OSType: "blah", 171 ManagedDisk: &ManagedDiskParameters{}, 172 }, 173 { 174 DiskSizeGB: ptr.To[int32](30), 175 OSType: "blah", 176 ManagedDisk: &ManagedDiskParameters{ 177 StorageAccountType: "", 178 }, 179 }, 180 { 181 DiskSizeGB: ptr.To[int32](30), 182 OSType: "blah", 183 ManagedDisk: &ManagedDiskParameters{ 184 StorageAccountType: "invalid_type", 185 }, 186 }, 187 { 188 DiskSizeGB: ptr.To[int32](30), 189 OSType: "blah", 190 ManagedDisk: &ManagedDiskParameters{ 191 StorageAccountType: "Premium_LRS", 192 }, 193 DiffDiskSettings: &DiffDiskSettings{ 194 Option: string(armcompute.DiffDiskOptionsLocal), 195 }, 196 }, 197 } 198 199 for i, input := range invalidDiskSpecs { 200 inputs = append(inputs, osDiskTestInput{ 201 name: fmt.Sprintf("%s-%d", testCaseName, i), 202 wantErr: true, 203 osDisk: input, 204 }) 205 } 206 207 return inputs 208 } 209 210 func generateValidOSDisk() OSDisk { 211 return OSDisk{ 212 DiskSizeGB: ptr.To[int32](30), 213 OSType: LinuxOS, 214 ManagedDisk: &ManagedDiskParameters{ 215 StorageAccountType: "Premium_LRS", 216 }, 217 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 218 } 219 } 220 221 func createOSDiskWithCacheType(cacheType string) OSDisk { 222 osDisk := generateValidOSDisk() 223 osDisk.CachingType = cacheType 224 return osDisk 225 } 226 227 func TestAzureMachine_ValidateDataDisks(t *testing.T) { 228 testcases := []struct { 229 name string 230 disks []DataDisk 231 wantErr bool 232 }{ 233 { 234 name: "valid nil data disks", 235 disks: nil, 236 wantErr: false, 237 }, 238 { 239 name: "valid empty data disks", 240 disks: []DataDisk{}, 241 wantErr: false, 242 }, 243 { 244 name: "valid disks", 245 disks: []DataDisk{ 246 { 247 NameSuffix: "my_disk", 248 DiskSizeGB: 64, 249 Lun: ptr.To[int32](0), 250 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 251 }, 252 { 253 NameSuffix: "my_other_disk", 254 DiskSizeGB: 64, 255 Lun: ptr.To[int32](1), 256 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 257 }, 258 }, 259 wantErr: false, 260 }, 261 { 262 name: "duplicate names", 263 disks: []DataDisk{ 264 { 265 NameSuffix: "disk", 266 DiskSizeGB: 64, 267 Lun: ptr.To[int32](0), 268 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 269 }, 270 { 271 NameSuffix: "disk", 272 DiskSizeGB: 64, 273 Lun: ptr.To[int32](1), 274 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 275 }, 276 }, 277 wantErr: true, 278 }, 279 { 280 name: "duplicate LUNs", 281 disks: []DataDisk{ 282 { 283 NameSuffix: "my_disk", 284 DiskSizeGB: 64, 285 Lun: ptr.To[int32](0), 286 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 287 }, 288 { 289 NameSuffix: "my_other_disk", 290 DiskSizeGB: 64, 291 Lun: ptr.To[int32](0), 292 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 293 }, 294 }, 295 wantErr: true, 296 }, 297 { 298 name: "invalid disk size", 299 disks: []DataDisk{ 300 { 301 NameSuffix: "my_disk", 302 DiskSizeGB: 0, 303 Lun: ptr.To[int32](0), 304 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 305 }, 306 }, 307 wantErr: true, 308 }, 309 { 310 name: "empty name", 311 disks: []DataDisk{ 312 { 313 NameSuffix: "", 314 DiskSizeGB: 0, 315 Lun: ptr.To[int32](0), 316 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 317 }, 318 }, 319 wantErr: true, 320 }, 321 { 322 name: "invalid disk cachingType", 323 disks: []DataDisk{ 324 { 325 NameSuffix: "my_disk", 326 DiskSizeGB: 64, 327 Lun: ptr.To[int32](0), 328 CachingType: "invalidCacheType", 329 }, 330 }, 331 wantErr: true, 332 }, 333 { 334 name: "valid disk cachingType", 335 disks: []DataDisk{ 336 { 337 NameSuffix: "my_disk", 338 DiskSizeGB: 64, 339 Lun: ptr.To[int32](0), 340 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 341 }, 342 }, 343 wantErr: false, 344 }, 345 { 346 name: "valid managed disk storage account type", 347 disks: []DataDisk{ 348 { 349 NameSuffix: "my_disk_1", 350 DiskSizeGB: 64, 351 ManagedDisk: &ManagedDiskParameters{ 352 StorageAccountType: "Premium_LRS", 353 }, 354 Lun: ptr.To[int32](0), 355 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 356 }, 357 { 358 NameSuffix: "my_disk_2", 359 DiskSizeGB: 64, 360 ManagedDisk: &ManagedDiskParameters{ 361 StorageAccountType: "Standard_LRS", 362 }, 363 Lun: ptr.To[int32](1), 364 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 365 }, 366 }, 367 wantErr: false, 368 }, 369 { 370 name: "invalid managed disk storage account type", 371 disks: []DataDisk{ 372 { 373 NameSuffix: "my_disk_1", 374 DiskSizeGB: 64, 375 ManagedDisk: &ManagedDiskParameters{ 376 StorageAccountType: "invalid storage account", 377 }, 378 Lun: ptr.To[int32](0), 379 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 380 }, 381 }, 382 wantErr: true, 383 }, 384 { 385 name: "valid combination of managed disk storage account type UltraSSD_LRS and cachingType None", 386 disks: []DataDisk{ 387 { 388 NameSuffix: "my_disk_1", 389 DiskSizeGB: 64, 390 ManagedDisk: &ManagedDiskParameters{ 391 StorageAccountType: string(armcompute.StorageAccountTypesUltraSSDLRS), 392 }, 393 Lun: ptr.To[int32](0), 394 CachingType: string(armcompute.CachingTypesNone), 395 }, 396 }, 397 wantErr: false, 398 }, 399 { 400 name: "invalid combination of managed disk storage account type UltraSSD_LRS and cachingType ReadWrite", 401 disks: []DataDisk{ 402 { 403 NameSuffix: "my_disk_1", 404 DiskSizeGB: 64, 405 ManagedDisk: &ManagedDiskParameters{ 406 StorageAccountType: string(armcompute.StorageAccountTypesUltraSSDLRS), 407 }, 408 Lun: ptr.To[int32](0), 409 CachingType: string(armcompute.CachingTypesReadWrite), 410 }, 411 }, 412 wantErr: true, 413 }, 414 { 415 name: "invalid combination of managed disk storage account type UltraSSD_LRS and cachingType ReadOnly", 416 disks: []DataDisk{ 417 { 418 NameSuffix: "my_disk_1", 419 DiskSizeGB: 64, 420 ManagedDisk: &ManagedDiskParameters{ 421 StorageAccountType: string(armcompute.StorageAccountTypesUltraSSDLRS), 422 }, 423 Lun: ptr.To[int32](0), 424 CachingType: string(armcompute.CachingTypesReadOnly), 425 }, 426 }, 427 wantErr: true, 428 }, 429 } 430 431 for _, test := range testcases { 432 t.Run(test.name, func(t *testing.T) { 433 g := NewWithT(t) 434 err := ValidateDataDisks(test.disks, field.NewPath("dataDisks")) 435 if test.wantErr { 436 g.Expect(err).NotTo(BeEmpty()) 437 } else { 438 g.Expect(err).To(BeEmpty()) 439 } 440 }) 441 } 442 } 443 444 func TestAzureMachine_ValidateSystemAssignedIdentity(t *testing.T) { 445 tests := []struct { 446 name string 447 roleAssignmentName string 448 old string 449 Identity VMIdentity 450 wantErr bool 451 }{ 452 { 453 name: "valid UUID", 454 roleAssignmentName: uuid.New().String(), 455 Identity: VMIdentitySystemAssigned, 456 wantErr: false, 457 }, 458 { 459 name: "wrong Identity type", 460 roleAssignmentName: uuid.New().String(), 461 Identity: VMIdentityNone, 462 wantErr: true, 463 }, 464 { 465 name: "not a valid UUID", 466 roleAssignmentName: "notaguid", 467 Identity: VMIdentitySystemAssigned, 468 wantErr: true, 469 }, 470 { 471 name: "empty", 472 roleAssignmentName: "", 473 Identity: VMIdentitySystemAssigned, 474 wantErr: true, 475 }, 476 { 477 name: "changed", 478 roleAssignmentName: uuid.New().String(), 479 old: uuid.New().String(), 480 Identity: VMIdentitySystemAssigned, 481 wantErr: true, 482 }, 483 } 484 485 for _, tc := range tests { 486 t.Run(tc.name, func(t *testing.T) { 487 g := NewWithT(t) 488 err := ValidateSystemAssignedIdentity(tc.Identity, tc.old, tc.roleAssignmentName, field.NewPath("sshPublicKey")) 489 if tc.wantErr { 490 g.Expect(err).NotTo(BeEmpty()) 491 } else { 492 g.Expect(err).To(BeEmpty()) 493 } 494 }) 495 } 496 } 497 498 func TestAzureMachine_ValidateSystemAssignedIdentityRole(t *testing.T) { 499 tests := []struct { 500 name string 501 Identity VMIdentity 502 roleAssignmentName string 503 role *SystemAssignedIdentityRole 504 wantErr bool 505 }{ 506 { 507 name: "valid role", 508 Identity: VMIdentitySystemAssigned, 509 role: &SystemAssignedIdentityRole{ 510 Name: uuid.New().String(), 511 Scope: "fake-scope", 512 DefinitionID: "fake-definition-id", 513 }, 514 }, 515 { 516 name: "valid role using deprecated role assignment name", 517 Identity: VMIdentitySystemAssigned, 518 roleAssignmentName: uuid.New().String(), 519 role: &SystemAssignedIdentityRole{ 520 Scope: "fake-scope", 521 DefinitionID: "fake-definition-id", 522 }, 523 }, 524 { 525 name: "set both system assigned identity role and role assignment name", 526 Identity: VMIdentitySystemAssigned, 527 roleAssignmentName: uuid.New().String(), 528 role: &SystemAssignedIdentityRole{ 529 Name: uuid.New().String(), 530 Scope: "fake-scope", 531 DefinitionID: "fake-definition-id", 532 }, 533 wantErr: true, 534 }, 535 { 536 name: "wrong Identity type", 537 Identity: VMIdentityUserAssigned, 538 role: &SystemAssignedIdentityRole{ 539 Name: uuid.New().String(), 540 Scope: "fake-scope", 541 DefinitionID: "fake-definition-id", 542 }, 543 wantErr: true, 544 }, 545 { 546 name: "missing scope", 547 Identity: VMIdentitySystemAssigned, 548 role: &SystemAssignedIdentityRole{ 549 Name: uuid.New().String(), 550 DefinitionID: "fake-definition-id", 551 }, 552 wantErr: true, 553 }, 554 { 555 name: "missing definition id", 556 Identity: VMIdentitySystemAssigned, 557 role: &SystemAssignedIdentityRole{ 558 Name: uuid.New().String(), 559 Scope: "fake-scope", 560 }, 561 wantErr: true, 562 }, 563 } 564 565 for _, tc := range tests { 566 t.Run(tc.name, func(t *testing.T) { 567 g := NewWithT(t) 568 err := ValidateSystemAssignedIdentityRole(tc.Identity, tc.roleAssignmentName, tc.role, field.NewPath("systemAssignedIdentityRole")) 569 if tc.wantErr { 570 g.Expect(err).NotTo(BeEmpty()) 571 } else { 572 g.Expect(err).To(BeEmpty()) 573 } 574 }) 575 } 576 } 577 578 func TestAzureMachine_ValidateUserAssignedIdentity(t *testing.T) { 579 tests := []struct { 580 name string 581 idType VMIdentity 582 identities []UserAssignedIdentity 583 wantErr bool 584 }{ 585 { 586 name: "empty identity list", 587 idType: VMIdentityUserAssigned, 588 identities: []UserAssignedIdentity{}, 589 wantErr: true, 590 }, 591 { 592 name: "invalid: providerID must start with slash", 593 idType: VMIdentityUserAssigned, 594 identities: []UserAssignedIdentity{ 595 { 596 ProviderID: "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.Compute/virtualMachines/default-20202-control-plane-7w265", 597 }, 598 }, 599 wantErr: true, 600 }, 601 { 602 name: "invalid: providerID must start with subscriptions or providers", 603 idType: VMIdentityUserAssigned, 604 identities: []UserAssignedIdentity{ 605 { 606 ProviderID: "azure:///prescriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.Compute/virtualMachines/default-20202-control-plane-7w265", 607 }, 608 }, 609 wantErr: true, 610 }, 611 { 612 name: "valid", 613 idType: VMIdentityUserAssigned, 614 identities: []UserAssignedIdentity{ 615 { 616 ProviderID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.Compute/virtualMachines/default-20202-control-plane-7w265", 617 }, 618 }, 619 wantErr: false, 620 }, 621 { 622 name: "valid with provider prefix", 623 idType: VMIdentityUserAssigned, 624 identities: []UserAssignedIdentity{ 625 { 626 ProviderID: "azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.Compute/virtualMachines/default-20202-control-plane-7w265", 627 }, 628 }, 629 wantErr: false, 630 }, 631 } 632 633 for _, tc := range tests { 634 t.Run(tc.name, func(t *testing.T) { 635 g := NewWithT(t) 636 errs := ValidateUserAssignedIdentity(tc.idType, tc.identities, field.NewPath("userAssignedIdentities")) 637 if tc.wantErr { 638 g.Expect(errs).NotTo(BeEmpty()) 639 } else { 640 g.Expect(errs).To(BeEmpty()) 641 } 642 }) 643 } 644 } 645 646 func TestAzureMachine_ValidateDataDisksUpdate(t *testing.T) { 647 tests := []struct { 648 name string 649 disks []DataDisk 650 oldDisks []DataDisk 651 wantErr bool 652 }{ 653 { 654 name: "valid nil data disks", 655 disks: nil, 656 oldDisks: nil, 657 wantErr: false, 658 }, 659 { 660 name: "valid empty data disks", 661 disks: []DataDisk{}, 662 oldDisks: []DataDisk{}, 663 wantErr: false, 664 }, 665 { 666 name: "valid data disk updates", 667 disks: []DataDisk{ 668 { 669 NameSuffix: "my_disk", 670 DiskSizeGB: 64, 671 Lun: ptr.To[int32](0), 672 ManagedDisk: &ManagedDiskParameters{ 673 StorageAccountType: "Standard_LRS", 674 }, 675 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 676 }, 677 { 678 NameSuffix: "my_other_disk", 679 DiskSizeGB: 64, 680 Lun: ptr.To[int32](1), 681 ManagedDisk: &ManagedDiskParameters{ 682 StorageAccountType: "Standard_LRS", 683 }, 684 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 685 }, 686 }, 687 oldDisks: []DataDisk{ 688 { 689 NameSuffix: "my_disk", 690 DiskSizeGB: 64, 691 Lun: ptr.To[int32](0), 692 ManagedDisk: &ManagedDiskParameters{ 693 StorageAccountType: "Standard_LRS", 694 }, 695 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 696 }, 697 { 698 NameSuffix: "my_other_disk", 699 DiskSizeGB: 64, 700 Lun: ptr.To[int32](1), 701 ManagedDisk: &ManagedDiskParameters{ 702 StorageAccountType: "Standard_LRS", 703 }, 704 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 705 }, 706 }, 707 wantErr: false, 708 }, 709 { 710 name: "cannot update data disk fields after machine creation", 711 disks: []DataDisk{ 712 { 713 NameSuffix: "my_disk_1", 714 DiskSizeGB: 64, 715 ManagedDisk: &ManagedDiskParameters{ 716 StorageAccountType: "Standard_LRS", 717 }, 718 Lun: ptr.To[int32](0), 719 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 720 }, 721 }, 722 oldDisks: []DataDisk{ 723 { 724 NameSuffix: "my_disk_1", 725 DiskSizeGB: 128, 726 ManagedDisk: &ManagedDiskParameters{ 727 StorageAccountType: "Premium_LRS", 728 }, 729 Lun: ptr.To[int32](0), 730 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 731 }, 732 }, 733 wantErr: true, 734 }, 735 { 736 name: "validate updates to optional fields", 737 disks: []DataDisk{ 738 { 739 NameSuffix: "my_disk_1", 740 DiskSizeGB: 128, 741 ManagedDisk: &ManagedDiskParameters{ 742 StorageAccountType: "Standard_LRS", 743 }, 744 Lun: ptr.To[int32](0), 745 }, 746 }, 747 oldDisks: []DataDisk{ 748 { 749 NameSuffix: "my_disk_1", 750 DiskSizeGB: 128, 751 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 752 }, 753 }, 754 wantErr: true, 755 }, 756 { 757 name: "data disks cannot be added after machine creation", 758 disks: []DataDisk{ 759 { 760 NameSuffix: "my_disk_1", 761 DiskSizeGB: 64, 762 ManagedDisk: &ManagedDiskParameters{ 763 StorageAccountType: "Standard_LRS", 764 }, 765 Lun: ptr.To[int32](0), 766 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 767 }, 768 }, 769 oldDisks: []DataDisk{ 770 { 771 NameSuffix: "my_disk_1", 772 DiskSizeGB: 64, 773 ManagedDisk: &ManagedDiskParameters{ 774 StorageAccountType: "Premium_LRS", 775 }, 776 Lun: ptr.To[int32](0), 777 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 778 }, 779 { 780 NameSuffix: "my_disk_2", 781 DiskSizeGB: 64, 782 ManagedDisk: &ManagedDiskParameters{ 783 StorageAccountType: "Premium_LRS", 784 }, 785 Lun: ptr.To[int32](2), 786 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 787 }, 788 }, 789 wantErr: true, 790 }, 791 { 792 name: "data disks cannot be removed after machine creation", 793 disks: []DataDisk{ 794 { 795 NameSuffix: "my_disk_1", 796 DiskSizeGB: 64, 797 ManagedDisk: &ManagedDiskParameters{ 798 StorageAccountType: "Standard_LRS", 799 }, 800 Lun: ptr.To[int32](0), 801 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 802 }, 803 { 804 NameSuffix: "my_disk_2", 805 DiskSizeGB: 64, 806 ManagedDisk: &ManagedDiskParameters{ 807 StorageAccountType: "Premium_LRS", 808 }, 809 Lun: ptr.To[int32](2), 810 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 811 }, 812 }, 813 oldDisks: []DataDisk{ 814 { 815 NameSuffix: "my_disk_1", 816 DiskSizeGB: 64, 817 ManagedDisk: &ManagedDiskParameters{ 818 StorageAccountType: "Standard_LRS", 819 }, 820 Lun: ptr.To[int32](0), 821 CachingType: string(armcompute.PossibleCachingTypesValues()[0]), 822 }, 823 }, 824 wantErr: true, 825 }, 826 } 827 828 for _, test := range tests { 829 t.Run(test.name, func(t *testing.T) { 830 g := NewWithT(t) 831 err := ValidateDataDisksUpdate(test.oldDisks, test.disks, field.NewPath("dataDisks")) 832 if test.wantErr { 833 g.Expect(err).NotTo(BeEmpty()) 834 } else { 835 g.Expect(err).To(BeEmpty()) 836 } 837 }) 838 } 839 } 840 841 func TestAzureMachine_ValidateNetwork(t *testing.T) { 842 tests := []struct { 843 name string 844 subnetName string 845 acceleratedNetworking *bool 846 networkInterfaces []NetworkInterface 847 wantErr bool 848 }{ 849 { 850 name: "valid config with deprecated network fields", 851 subnetName: "subnet1", 852 acceleratedNetworking: ptr.To(true), 853 networkInterfaces: nil, 854 wantErr: false, 855 }, 856 { 857 name: "valid config with networkInterfaces fields", 858 subnetName: "", 859 acceleratedNetworking: nil, 860 networkInterfaces: []NetworkInterface{{ 861 SubnetName: "subnet1", 862 AcceleratedNetworking: ptr.To(true), 863 PrivateIPConfigs: 1, 864 }}, 865 wantErr: false, 866 }, 867 { 868 name: "valid config with multiple networkInterfaces", 869 subnetName: "", 870 acceleratedNetworking: nil, 871 networkInterfaces: []NetworkInterface{ 872 { 873 SubnetName: "subnet1", 874 AcceleratedNetworking: ptr.To(true), 875 PrivateIPConfigs: 1, 876 }, 877 { 878 SubnetName: "subnet2", 879 AcceleratedNetworking: ptr.To(true), 880 PrivateIPConfigs: 30, 881 }, 882 }, 883 wantErr: false, 884 }, 885 { 886 name: "invalid config using both deprecated subnetName and networkInterfaces", 887 subnetName: "subnet1", 888 acceleratedNetworking: nil, 889 networkInterfaces: []NetworkInterface{{ 890 SubnetName: "subnet1", 891 AcceleratedNetworking: nil, 892 PrivateIPConfigs: 1, 893 }}, 894 wantErr: true, 895 }, 896 { 897 name: "invalid config using both deprecated acceleratedNetworking and networkInterfaces", 898 subnetName: "", 899 acceleratedNetworking: ptr.To(true), 900 networkInterfaces: []NetworkInterface{{ 901 SubnetName: "subnet1", 902 AcceleratedNetworking: ptr.To(true), 903 PrivateIPConfigs: 1, 904 }}, 905 wantErr: true, 906 }, 907 { 908 name: "invalid config setting privateIPConfigs to less than 1", 909 subnetName: "", 910 acceleratedNetworking: nil, 911 networkInterfaces: []NetworkInterface{{ 912 SubnetName: "subnet1", 913 AcceleratedNetworking: ptr.To(true), 914 PrivateIPConfigs: 0, 915 }}, 916 wantErr: true, 917 }, 918 } 919 920 for _, test := range tests { 921 t.Run(test.name, func(t *testing.T) { 922 g := NewWithT(t) 923 err := ValidateNetwork(test.subnetName, test.acceleratedNetworking, test.networkInterfaces, field.NewPath("networkInterfaces")) 924 if test.wantErr { 925 g.Expect(err).NotTo(BeEmpty()) 926 } else { 927 g.Expect(err).To(BeEmpty()) 928 } 929 }) 930 } 931 } 932 933 func TestAzureMachine_ValidateConfidentialCompute(t *testing.T) { 934 tests := []struct { 935 name string 936 managedDisk *ManagedDiskParameters 937 securityProfile *SecurityProfile 938 wantErr bool 939 }{ 940 { 941 name: "valid configuration without confidential compute", 942 managedDisk: &ManagedDiskParameters{ 943 SecurityProfile: &VMDiskSecurityProfile{ 944 SecurityEncryptionType: "", 945 }, 946 }, 947 securityProfile: nil, 948 wantErr: false, 949 }, 950 { 951 name: "valid configuration without confidential compute and host encryption enabled", 952 managedDisk: &ManagedDiskParameters{ 953 SecurityProfile: &VMDiskSecurityProfile{ 954 SecurityEncryptionType: "", 955 }, 956 }, 957 securityProfile: &SecurityProfile{ 958 EncryptionAtHost: ptr.To(true), 959 }, 960 wantErr: false, 961 }, 962 { 963 name: "valid configuration with VMGuestStateOnly encryption and secure boot disabled", 964 managedDisk: &ManagedDiskParameters{ 965 SecurityProfile: &VMDiskSecurityProfile{ 966 SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly, 967 }, 968 }, 969 securityProfile: &SecurityProfile{ 970 SecurityType: SecurityTypesConfidentialVM, 971 UefiSettings: &UefiSettings{ 972 VTpmEnabled: ptr.To(true), 973 SecureBootEnabled: ptr.To(false), 974 }, 975 }, 976 wantErr: false, 977 }, 978 { 979 name: "valid configuration with VMGuestStateOnly encryption and secure boot enabled", 980 managedDisk: &ManagedDiskParameters{ 981 SecurityProfile: &VMDiskSecurityProfile{ 982 SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly, 983 }, 984 }, 985 securityProfile: &SecurityProfile{ 986 SecurityType: SecurityTypesConfidentialVM, 987 UefiSettings: &UefiSettings{ 988 VTpmEnabled: ptr.To(true), 989 SecureBootEnabled: ptr.To(true), 990 }, 991 }, 992 wantErr: false, 993 }, 994 { 995 name: "valid configuration with VMGuestStateOnly encryption and EncryptionAtHost enabled", 996 managedDisk: &ManagedDiskParameters{ 997 SecurityProfile: &VMDiskSecurityProfile{ 998 SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly, 999 }, 1000 }, 1001 securityProfile: &SecurityProfile{ 1002 EncryptionAtHost: ptr.To(true), 1003 SecurityType: SecurityTypesConfidentialVM, 1004 UefiSettings: &UefiSettings{ 1005 VTpmEnabled: ptr.To(true), 1006 }, 1007 }, 1008 wantErr: false, 1009 }, 1010 { 1011 name: "valid configuration with DiskWithVMGuestState encryption", 1012 managedDisk: &ManagedDiskParameters{ 1013 SecurityProfile: &VMDiskSecurityProfile{ 1014 SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState, 1015 }, 1016 }, 1017 securityProfile: &SecurityProfile{ 1018 SecurityType: SecurityTypesConfidentialVM, 1019 UefiSettings: &UefiSettings{ 1020 SecureBootEnabled: ptr.To(true), 1021 VTpmEnabled: ptr.To(true), 1022 }, 1023 }, 1024 wantErr: false, 1025 }, 1026 { 1027 name: "invalid configuration with DiskWithVMGuestState encryption and EncryptionAtHost enabled", 1028 managedDisk: &ManagedDiskParameters{ 1029 SecurityProfile: &VMDiskSecurityProfile{ 1030 SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState, 1031 }, 1032 }, 1033 securityProfile: &SecurityProfile{ 1034 EncryptionAtHost: ptr.To(true), 1035 }, 1036 wantErr: true, 1037 }, 1038 { 1039 name: "invalid configuration with DiskWithVMGuestState encryption and vTPM disabled", 1040 managedDisk: &ManagedDiskParameters{ 1041 SecurityProfile: &VMDiskSecurityProfile{ 1042 SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState, 1043 }, 1044 }, 1045 securityProfile: &SecurityProfile{ 1046 SecurityType: SecurityTypesConfidentialVM, 1047 UefiSettings: &UefiSettings{ 1048 VTpmEnabled: ptr.To(false), 1049 SecureBootEnabled: ptr.To(false), 1050 }, 1051 }, 1052 wantErr: true, 1053 }, 1054 { 1055 name: "invalid configuration with DiskWithVMGuestState encryption and secure boot disabled", 1056 managedDisk: &ManagedDiskParameters{ 1057 SecurityProfile: &VMDiskSecurityProfile{ 1058 SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState, 1059 }, 1060 }, 1061 securityProfile: &SecurityProfile{ 1062 SecurityType: SecurityTypesConfidentialVM, 1063 UefiSettings: &UefiSettings{ 1064 VTpmEnabled: ptr.To(true), 1065 SecureBootEnabled: ptr.To(false), 1066 }, 1067 }, 1068 wantErr: true, 1069 }, 1070 { 1071 name: "invalid configuration with DiskWithVMGuestState encryption and SecurityType not set to ConfidentialVM", 1072 managedDisk: &ManagedDiskParameters{ 1073 SecurityProfile: &VMDiskSecurityProfile{ 1074 SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState, 1075 }, 1076 }, 1077 securityProfile: &SecurityProfile{ 1078 UefiSettings: &UefiSettings{ 1079 VTpmEnabled: ptr.To(true), 1080 SecureBootEnabled: ptr.To(true), 1081 }, 1082 }, 1083 wantErr: true, 1084 }, 1085 { 1086 name: "invalid configuration with VMGuestStateOnly encryption and SecurityType not set to ConfidentialVM", 1087 managedDisk: &ManagedDiskParameters{ 1088 SecurityProfile: &VMDiskSecurityProfile{ 1089 SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly, 1090 }, 1091 }, 1092 securityProfile: &SecurityProfile{ 1093 UefiSettings: &UefiSettings{ 1094 VTpmEnabled: ptr.To(true), 1095 SecureBootEnabled: ptr.To(true), 1096 }, 1097 }, 1098 wantErr: true, 1099 }, 1100 } 1101 1102 for _, tc := range tests { 1103 t.Run(tc.name, func(t *testing.T) { 1104 g := NewWithT(t) 1105 err := ValidateConfidentialCompute(tc.managedDisk, tc.securityProfile, field.NewPath("securityProfile")) 1106 if tc.wantErr { 1107 g.Expect(err).NotTo(BeEmpty()) 1108 } else { 1109 g.Expect(err).To(BeEmpty()) 1110 } 1111 }) 1112 } 1113 }