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  }