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  }