sigs.k8s.io/cluster-api-provider-azure@v1.17.0/azure/scope/machine_test.go (about)

     1  /*
     2  Copyright 2018 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 scope
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    25  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
    26  	azureautorest "github.com/Azure/go-autorest/autorest/azure"
    27  	"github.com/Azure/go-autorest/autorest/azure/auth"
    28  	"github.com/google/go-cmp/cmp"
    29  	. "github.com/onsi/gomega"
    30  	"go.uber.org/mock/gomock"
    31  	"k8s.io/apimachinery/pkg/api/resource"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/utils/ptr"
    34  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    35  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    36  	"sigs.k8s.io/cluster-api-provider-azure/azure/mock_azure"
    37  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/disks"
    38  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/inboundnatrules"
    39  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/networkinterfaces"
    40  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/publicips"
    41  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus"
    42  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/roleassignments"
    43  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachineimages"
    44  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachineimages/mock_virtualmachineimages"
    45  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/vmextensions"
    46  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    47  )
    48  
    49  func TestMachineScope_Name(t *testing.T) {
    50  	tests := []struct {
    51  		name         string
    52  		machineScope MachineScope
    53  		want         string
    54  		testLength   bool
    55  	}{
    56  		{
    57  			name: "if provider ID exists, use it",
    58  			machineScope: MachineScope{
    59  				AzureMachine: &infrav1.AzureMachine{
    60  					ObjectMeta: metav1.ObjectMeta{
    61  						Name: "machine-with-a-long-name",
    62  					},
    63  					Spec: infrav1.AzureMachineSpec{
    64  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
    65  						OSDisk: infrav1.OSDisk{
    66  							OSType: "Windows",
    67  						},
    68  					},
    69  				},
    70  			},
    71  			want: "machine-name",
    72  		},
    73  		{
    74  			name: "linux can be any length",
    75  			machineScope: MachineScope{
    76  				AzureMachine: &infrav1.AzureMachine{
    77  					ObjectMeta: metav1.ObjectMeta{
    78  						Name: "machine-with-really-really-long-name",
    79  					},
    80  					Spec: infrav1.AzureMachineSpec{
    81  						OSDisk: infrav1.OSDisk{
    82  							OSType: "Linux",
    83  						},
    84  					},
    85  				},
    86  			},
    87  			want: "machine-with-really-really-long-name",
    88  		},
    89  		{
    90  			name: "Windows name with long MachineName and short cluster name",
    91  			machineScope: MachineScope{
    92  				ClusterScoper: &ClusterScope{
    93  					Cluster: &clusterv1.Cluster{
    94  						ObjectMeta: metav1.ObjectMeta{
    95  							Name: "cluster",
    96  						},
    97  					},
    98  				},
    99  				AzureMachine: &infrav1.AzureMachine{
   100  					TypeMeta: metav1.TypeMeta{},
   101  					ObjectMeta: metav1.ObjectMeta{
   102  						Name: "machine-90123456",
   103  					},
   104  					Spec: infrav1.AzureMachineSpec{
   105  						OSDisk: infrav1.OSDisk{
   106  							OSType: "Windows",
   107  						},
   108  					},
   109  					Status: infrav1.AzureMachineStatus{},
   110  				},
   111  			},
   112  			want:       "machine-9-23456",
   113  			testLength: true,
   114  		},
   115  		{
   116  			name: "Windows name with long MachineName and long cluster name",
   117  			machineScope: MachineScope{
   118  				ClusterScoper: &ClusterScope{
   119  					Cluster: &clusterv1.Cluster{
   120  						ObjectMeta: metav1.ObjectMeta{
   121  							Name: "cluster8901234",
   122  						},
   123  					},
   124  				},
   125  				AzureMachine: &infrav1.AzureMachine{
   126  					TypeMeta: metav1.TypeMeta{},
   127  					ObjectMeta: metav1.ObjectMeta{
   128  						Name: "machine-90123456",
   129  					},
   130  					Spec: infrav1.AzureMachineSpec{
   131  						OSDisk: infrav1.OSDisk{
   132  							OSType: "Windows",
   133  						},
   134  					},
   135  					Status: infrav1.AzureMachineStatus{},
   136  				},
   137  			},
   138  			want:       "machine-9-23456",
   139  			testLength: true,
   140  		},
   141  	}
   142  	for _, tt := range tests {
   143  		t.Run(tt.name, func(t *testing.T) {
   144  			got := tt.machineScope.Name()
   145  			if got != tt.want {
   146  				t.Errorf("MachineScope.Name() = %v, want %v", got, tt.want)
   147  			}
   148  
   149  			if tt.testLength && len(got) > 15 {
   150  				t.Errorf("Length of MachineScope.Name() = %v, want less than %v", len(got), 15)
   151  			}
   152  		})
   153  	}
   154  }
   155  
   156  func TestMachineScope_GetVMID(t *testing.T) {
   157  	tests := []struct {
   158  		name         string
   159  		machineScope MachineScope
   160  		want         string
   161  	}{
   162  		{
   163  			name: "returns the vm name from provider ID",
   164  			machineScope: MachineScope{
   165  				AzureMachine: &infrav1.AzureMachine{
   166  					ObjectMeta: metav1.ObjectMeta{
   167  						Name: "not-this-name",
   168  					},
   169  					Spec: infrav1.AzureMachineSpec{
   170  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
   171  					},
   172  				},
   173  			},
   174  			want: "machine-name",
   175  		},
   176  		{
   177  			name: "returns empty if provider ID is invalid",
   178  			machineScope: MachineScope{
   179  				AzureMachine: &infrav1.AzureMachine{
   180  					ObjectMeta: metav1.ObjectMeta{
   181  						Name: "machine-name",
   182  					},
   183  					Spec: infrav1.AzureMachineSpec{
   184  						ProviderID: ptr.To("foo"),
   185  					},
   186  				},
   187  			},
   188  			want: "",
   189  		},
   190  	}
   191  	for _, tt := range tests {
   192  		t.Run(tt.name, func(t *testing.T) {
   193  			got := tt.machineScope.GetVMID()
   194  			if got != tt.want {
   195  				t.Errorf("MachineScope.GetVMID() = %v, want %v", got, tt.want)
   196  			}
   197  		})
   198  	}
   199  }
   200  
   201  func TestMachineScope_ProviderID(t *testing.T) {
   202  	tests := []struct {
   203  		name         string
   204  		machineScope MachineScope
   205  		want         string
   206  	}{
   207  		{
   208  			name: "returns the entire provider ID",
   209  			machineScope: MachineScope{
   210  				AzureMachine: &infrav1.AzureMachine{
   211  					ObjectMeta: metav1.ObjectMeta{
   212  						Name: "not-this-name",
   213  					},
   214  					Spec: infrav1.AzureMachineSpec{
   215  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
   216  					},
   217  				},
   218  			},
   219  			want: "azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name",
   220  		},
   221  		{
   222  			name: "returns empty if provider ID is empty",
   223  			machineScope: MachineScope{
   224  				AzureMachine: &infrav1.AzureMachine{
   225  					ObjectMeta: metav1.ObjectMeta{
   226  						Name: "machine-name",
   227  					},
   228  					Spec: infrav1.AzureMachineSpec{
   229  						ProviderID: ptr.To(""),
   230  					},
   231  				},
   232  			},
   233  			want: "",
   234  		},
   235  	}
   236  	for _, tt := range tests {
   237  		t.Run(tt.name, func(t *testing.T) {
   238  			got := tt.machineScope.ProviderID()
   239  			if got != tt.want {
   240  				t.Errorf("MachineScope.ProviderID() = %v, want %v", got, tt.want)
   241  			}
   242  		})
   243  	}
   244  }
   245  
   246  func TestMachineScope_PublicIPSpecs(t *testing.T) {
   247  	tests := []struct {
   248  		name         string
   249  		machineScope MachineScope
   250  		want         []azure.ResourceSpecGetter
   251  	}{
   252  		{
   253  			name: "returns nil if AllocatePublicIP is false",
   254  			machineScope: MachineScope{
   255  				AzureMachine: &infrav1.AzureMachine{
   256  					ObjectMeta: metav1.ObjectMeta{
   257  						Name: "machine-name",
   258  					},
   259  					Spec: infrav1.AzureMachineSpec{
   260  						AllocatePublicIP: false,
   261  					},
   262  				},
   263  			},
   264  			want: nil,
   265  		},
   266  		{
   267  			name: "appends to PublicIPSpec for node if AllocatePublicIP is true",
   268  			machineScope: MachineScope{
   269  				AzureMachine: &infrav1.AzureMachine{
   270  					ObjectMeta: metav1.ObjectMeta{
   271  						Name: "machine-name",
   272  					},
   273  					Spec: infrav1.AzureMachineSpec{
   274  						AllocatePublicIP: true,
   275  					},
   276  				},
   277  				ClusterScoper: &ClusterScope{
   278  					Cluster: &clusterv1.Cluster{
   279  						ObjectMeta: metav1.ObjectMeta{
   280  							Name: "my-cluster",
   281  							// Note: m.ClusterName() takes the value from the Cluster object, not the AzureCluster object
   282  						},
   283  					},
   284  					AzureCluster: &infrav1.AzureCluster{
   285  						ObjectMeta: metav1.ObjectMeta{
   286  							Name: "my-cluster",
   287  						},
   288  						Status: infrav1.AzureClusterStatus{
   289  							FailureDomains: map[string]clusterv1.FailureDomainSpec{
   290  								"failure-domain-id-1": {},
   291  								"failure-domain-id-2": {},
   292  								"failure-domain-id-3": {},
   293  							},
   294  						},
   295  						Spec: infrav1.AzureClusterSpec{
   296  							ResourceGroup: "my-rg",
   297  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   298  								SubscriptionID: "123",
   299  								Location:       "centralIndia",
   300  								AdditionalTags: infrav1.Tags{
   301  									"Name": "my-publicip-ipv6",
   302  									"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   303  								},
   304  							},
   305  							NetworkSpec: infrav1.NetworkSpec{
   306  								APIServerLB: infrav1.LoadBalancerSpec{
   307  									LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   308  										Type: infrav1.Internal,
   309  									},
   310  								},
   311  							},
   312  						},
   313  					},
   314  				},
   315  			},
   316  			want: []azure.ResourceSpecGetter{
   317  				&publicips.PublicIPSpec{
   318  					Name:           "pip-machine-name",
   319  					ResourceGroup:  "my-rg",
   320  					DNSName:        "",
   321  					IsIPv6:         false,
   322  					ClusterName:    "my-cluster",
   323  					Location:       "centralIndia",
   324  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   325  					AdditionalTags: infrav1.Tags{
   326  						"Name": "my-publicip-ipv6",
   327  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   328  					},
   329  				},
   330  			},
   331  		},
   332  	}
   333  	for _, tt := range tests {
   334  		t.Run(tt.name, func(t *testing.T) {
   335  			if got := tt.machineScope.PublicIPSpecs(); !reflect.DeepEqual(got, tt.want) {
   336  				t.Errorf("PublicIPSpecs() expected but got: %s", cmp.Diff(tt.want, got))
   337  			}
   338  		})
   339  	}
   340  }
   341  
   342  func TestMachineScope_InboundNatSpecs(t *testing.T) {
   343  	tests := []struct {
   344  		name         string
   345  		machineScope MachineScope
   346  		want         []azure.ResourceSpecGetter
   347  	}{
   348  		{
   349  			name: "returns empty when infra is not control plane",
   350  			machineScope: MachineScope{
   351  				Machine: &clusterv1.Machine{},
   352  				AzureMachine: &infrav1.AzureMachine{
   353  					ObjectMeta: metav1.ObjectMeta{
   354  						Name: "machine-name",
   355  					},
   356  				},
   357  			},
   358  			want: []azure.ResourceSpecGetter{},
   359  		},
   360  		{
   361  			name: "returns InboundNatSpec when infra is control plane",
   362  			machineScope: MachineScope{
   363  				Machine: &clusterv1.Machine{
   364  					ObjectMeta: metav1.ObjectMeta{
   365  						Labels: map[string]string{
   366  							clusterv1.MachineControlPlaneLabel: "",
   367  						},
   368  					},
   369  				},
   370  				AzureMachine: &infrav1.AzureMachine{
   371  					ObjectMeta: metav1.ObjectMeta{
   372  						Name: "machine-name",
   373  					},
   374  				},
   375  				ClusterScoper: &ClusterScope{
   376  					AzureClients: AzureClients{
   377  						EnvironmentSettings: auth.EnvironmentSettings{
   378  							Values: map[string]string{
   379  								auth.SubscriptionID: "123",
   380  							},
   381  						},
   382  					},
   383  					AzureCluster: &infrav1.AzureCluster{
   384  						Spec: infrav1.AzureClusterSpec{
   385  							ResourceGroup: "my-rg",
   386  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   387  								SubscriptionID: "123",
   388  							},
   389  							NetworkSpec: infrav1.NetworkSpec{
   390  								APIServerLB: infrav1.LoadBalancerSpec{
   391  									Name: "foo-loadbalancer",
   392  									FrontendIPs: []infrav1.FrontendIP{
   393  										{
   394  											Name: "foo-frontend-ip",
   395  										},
   396  									},
   397  								},
   398  							},
   399  						},
   400  					},
   401  				},
   402  			},
   403  			want: []azure.ResourceSpecGetter{
   404  				&inboundnatrules.InboundNatSpec{
   405  					Name:                      "machine-name",
   406  					LoadBalancerName:          "foo-loadbalancer",
   407  					ResourceGroup:             "my-rg",
   408  					FrontendIPConfigurationID: ptr.To(azure.FrontendIPConfigID("123", "my-rg", "foo-loadbalancer", "foo-frontend-ip")),
   409  				},
   410  			},
   411  		},
   412  	}
   413  	for _, tt := range tests {
   414  		tt := tt
   415  		t.Run(tt.name, func(t *testing.T) {
   416  			t.Parallel()
   417  			if got := tt.machineScope.InboundNatSpecs(); !reflect.DeepEqual(got, tt.want) {
   418  				t.Errorf("InboundNatSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
   419  			}
   420  		})
   421  	}
   422  }
   423  
   424  func TestMachineScope_RoleAssignmentSpecs(t *testing.T) {
   425  	tests := []struct {
   426  		name         string
   427  		machineScope MachineScope
   428  		want         []azure.ResourceSpecGetter
   429  	}{
   430  		{
   431  			name: "returns empty if VM identity is not system assigned",
   432  			machineScope: MachineScope{
   433  				Machine: &clusterv1.Machine{},
   434  				AzureMachine: &infrav1.AzureMachine{
   435  					ObjectMeta: metav1.ObjectMeta{
   436  						Name: "machine-name",
   437  					},
   438  				},
   439  			},
   440  			want: []azure.ResourceSpecGetter{},
   441  		},
   442  		{
   443  			name: "returns RoleAssignmentSpec if VM identity is system assigned",
   444  			machineScope: MachineScope{
   445  				Machine: &clusterv1.Machine{},
   446  				AzureMachine: &infrav1.AzureMachine{
   447  					ObjectMeta: metav1.ObjectMeta{
   448  						Name: "machine-name",
   449  					},
   450  					Spec: infrav1.AzureMachineSpec{
   451  						Identity: infrav1.VMIdentitySystemAssigned,
   452  						SystemAssignedIdentityRole: &infrav1.SystemAssignedIdentityRole{
   453  							Name: "azure-role-assignment-name",
   454  						},
   455  					},
   456  				},
   457  				ClusterScoper: &ClusterScope{
   458  					AzureClients: AzureClients{
   459  						EnvironmentSettings: auth.EnvironmentSettings{
   460  							Values: map[string]string{
   461  								auth.SubscriptionID: "123",
   462  							},
   463  						},
   464  					},
   465  					AzureCluster: &infrav1.AzureCluster{
   466  						Spec: infrav1.AzureClusterSpec{
   467  							ResourceGroup: "my-rg",
   468  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   469  								Location: "westus",
   470  							},
   471  						},
   472  					},
   473  				},
   474  			},
   475  			want: []azure.ResourceSpecGetter{
   476  				&roleassignments.RoleAssignmentSpec{
   477  					ResourceType:  azure.VirtualMachine,
   478  					MachineName:   "machine-name",
   479  					Name:          "azure-role-assignment-name",
   480  					ResourceGroup: "my-rg",
   481  					PrincipalID:   ptr.To("fakePrincipalID"),
   482  					PrincipalType: armauthorization.PrincipalTypeServicePrincipal,
   483  				},
   484  			},
   485  		},
   486  		{
   487  			name: "returns RoleAssignmentSpec with specified scope and role assignment id",
   488  			machineScope: MachineScope{
   489  				Machine: &clusterv1.Machine{},
   490  				AzureMachine: &infrav1.AzureMachine{
   491  					ObjectMeta: metav1.ObjectMeta{
   492  						Name: "machine-name",
   493  					},
   494  					Spec: infrav1.AzureMachineSpec{
   495  						Identity: infrav1.VMIdentitySystemAssigned,
   496  						SystemAssignedIdentityRole: &infrav1.SystemAssignedIdentityRole{
   497  							Name:         "azure-role-assignment-name",
   498  							Scope:        "/subscriptions/123/resourceGroups/my-rg",
   499  							DefinitionID: "/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/123",
   500  						},
   501  					},
   502  				},
   503  				ClusterScoper: &ClusterScope{
   504  					AzureClients: AzureClients{
   505  						EnvironmentSettings: auth.EnvironmentSettings{
   506  							Values: map[string]string{
   507  								auth.SubscriptionID: "123",
   508  							},
   509  						},
   510  					},
   511  					AzureCluster: &infrav1.AzureCluster{
   512  						Spec: infrav1.AzureClusterSpec{
   513  							ResourceGroup: "my-rg",
   514  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   515  								Location: "westus",
   516  							},
   517  						},
   518  					},
   519  				},
   520  			},
   521  			want: []azure.ResourceSpecGetter{
   522  				&roleassignments.RoleAssignmentSpec{
   523  					ResourceType:     azure.VirtualMachine,
   524  					MachineName:      "machine-name",
   525  					Name:             "azure-role-assignment-name",
   526  					ResourceGroup:    "my-rg",
   527  					Scope:            "/subscriptions/123/resourceGroups/my-rg",
   528  					RoleDefinitionID: "/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/123",
   529  					PrincipalID:      ptr.To("fakePrincipalID"),
   530  					PrincipalType:    armauthorization.PrincipalTypeServicePrincipal,
   531  				},
   532  			},
   533  		},
   534  	}
   535  	for _, tt := range tests {
   536  		t.Run(tt.name, func(t *testing.T) {
   537  			if got := tt.machineScope.RoleAssignmentSpecs(ptr.To("fakePrincipalID")); !reflect.DeepEqual(got, tt.want) {
   538  				t.Errorf("RoleAssignmentSpecs() = %v, want %v", got, tt.want)
   539  			}
   540  		})
   541  	}
   542  }
   543  
   544  func TestMachineScope_VMExtensionSpecs(t *testing.T) {
   545  	tests := []struct {
   546  		name         string
   547  		machineScope MachineScope
   548  		want         []azure.ResourceSpecGetter
   549  	}{
   550  		{
   551  			name: "If OS type is Linux and cloud is AzurePublicCloud, it returns ExtensionSpec",
   552  			machineScope: MachineScope{
   553  				Machine: &clusterv1.Machine{},
   554  				AzureMachine: &infrav1.AzureMachine{
   555  					ObjectMeta: metav1.ObjectMeta{
   556  						Name: "machine-name",
   557  					},
   558  					Spec: infrav1.AzureMachineSpec{
   559  						OSDisk: infrav1.OSDisk{
   560  							OSType: "Linux",
   561  						},
   562  					},
   563  				},
   564  				ClusterScoper: &ClusterScope{
   565  					AzureClients: AzureClients{
   566  						EnvironmentSettings: auth.EnvironmentSettings{
   567  							Environment: azureautorest.Environment{
   568  								Name: azureautorest.PublicCloud.Name,
   569  							},
   570  						},
   571  					},
   572  					AzureCluster: &infrav1.AzureCluster{
   573  						Spec: infrav1.AzureClusterSpec{
   574  							ResourceGroup: "my-rg",
   575  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   576  								Location: "westus",
   577  							},
   578  						},
   579  					},
   580  				},
   581  				cache: &MachineCache{
   582  					VMSKU: resourceskus.SKU{},
   583  				},
   584  			},
   585  			want: []azure.ResourceSpecGetter{
   586  				&vmextensions.VMExtensionSpec{
   587  					ExtensionSpec: azure.ExtensionSpec{
   588  						Name:      "CAPZ.Linux.Bootstrapping",
   589  						VMName:    "machine-name",
   590  						Publisher: "Microsoft.Azure.ContainerUpstream",
   591  						Version:   "1.0",
   592  						ProtectedSettings: map[string]string{
   593  							"commandToExecute": azure.LinuxBootstrapExtensionCommand,
   594  						},
   595  					},
   596  					ResourceGroup: "my-rg",
   597  					Location:      "westus",
   598  				},
   599  			},
   600  		},
   601  		{
   602  			name: "If OS type is Linux and cloud is AzurePublicCloud and DisableExtensionOperations is true, it returns empty",
   603  			machineScope: MachineScope{
   604  				Machine: &clusterv1.Machine{},
   605  				AzureMachine: &infrav1.AzureMachine{
   606  					ObjectMeta: metav1.ObjectMeta{
   607  						Name: "machine-name",
   608  					},
   609  					Spec: infrav1.AzureMachineSpec{
   610  						DisableExtensionOperations: ptr.To(true),
   611  						OSDisk: infrav1.OSDisk{
   612  							OSType: "Linux",
   613  						},
   614  					},
   615  				},
   616  				ClusterScoper: &ClusterScope{
   617  					AzureClients: AzureClients{
   618  						EnvironmentSettings: auth.EnvironmentSettings{
   619  							Environment: azureautorest.Environment{
   620  								Name: azureautorest.PublicCloud.Name,
   621  							},
   622  						},
   623  					},
   624  					AzureCluster: &infrav1.AzureCluster{
   625  						Spec: infrav1.AzureClusterSpec{
   626  							ResourceGroup: "my-rg",
   627  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   628  								Location: "westus",
   629  							},
   630  						},
   631  					},
   632  				},
   633  				cache: &MachineCache{
   634  					VMSKU: resourceskus.SKU{},
   635  				},
   636  			},
   637  			want: []azure.ResourceSpecGetter{},
   638  		},
   639  		{
   640  			name: "If OS type is Linux and cloud is not AzurePublicCloud, it returns empty",
   641  			machineScope: MachineScope{
   642  				Machine: &clusterv1.Machine{},
   643  				AzureMachine: &infrav1.AzureMachine{
   644  					ObjectMeta: metav1.ObjectMeta{
   645  						Name: "machine-name",
   646  					},
   647  					Spec: infrav1.AzureMachineSpec{
   648  						OSDisk: infrav1.OSDisk{
   649  							OSType: "Linux",
   650  						},
   651  					},
   652  				},
   653  				ClusterScoper: &ClusterScope{
   654  					AzureClients: AzureClients{
   655  						EnvironmentSettings: auth.EnvironmentSettings{
   656  							Environment: azureautorest.Environment{
   657  								Name: azureautorest.USGovernmentCloud.Name,
   658  							},
   659  						},
   660  					},
   661  					AzureCluster: &infrav1.AzureCluster{
   662  						Spec: infrav1.AzureClusterSpec{
   663  							ResourceGroup: "my-rg",
   664  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   665  								Location: "westus",
   666  							},
   667  						},
   668  					},
   669  				},
   670  				cache: &MachineCache{
   671  					VMSKU: resourceskus.SKU{},
   672  				},
   673  			},
   674  			want: []azure.ResourceSpecGetter{},
   675  		},
   676  		{
   677  			name: "If OS type is Windows and cloud is AzurePublicCloud, it returns ExtensionSpec",
   678  			machineScope: MachineScope{
   679  				Machine: &clusterv1.Machine{},
   680  				AzureMachine: &infrav1.AzureMachine{
   681  					ObjectMeta: metav1.ObjectMeta{
   682  						Name: "machine-name",
   683  					},
   684  					Spec: infrav1.AzureMachineSpec{
   685  						OSDisk: infrav1.OSDisk{
   686  							OSType: "Windows",
   687  						},
   688  					},
   689  				},
   690  				ClusterScoper: &ClusterScope{
   691  					AzureClients: AzureClients{
   692  						EnvironmentSettings: auth.EnvironmentSettings{
   693  							Environment: azureautorest.Environment{
   694  								Name: azureautorest.PublicCloud.Name,
   695  							},
   696  						},
   697  					},
   698  					AzureCluster: &infrav1.AzureCluster{
   699  						Spec: infrav1.AzureClusterSpec{
   700  							ResourceGroup: "my-rg",
   701  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   702  								Location: "westus",
   703  							},
   704  						},
   705  					},
   706  				},
   707  				cache: &MachineCache{
   708  					VMSKU: resourceskus.SKU{},
   709  				},
   710  			},
   711  			want: []azure.ResourceSpecGetter{
   712  				&vmextensions.VMExtensionSpec{
   713  					ExtensionSpec: azure.ExtensionSpec{
   714  						Name:      "CAPZ.Windows.Bootstrapping",
   715  						VMName:    "machine-name",
   716  						Publisher: "Microsoft.Azure.ContainerUpstream",
   717  						Version:   "1.0",
   718  						ProtectedSettings: map[string]string{
   719  							"commandToExecute": azure.WindowsBootstrapExtensionCommand,
   720  						},
   721  					},
   722  					ResourceGroup: "my-rg",
   723  					Location:      "westus",
   724  				},
   725  			},
   726  		},
   727  		{
   728  			name: "If OS type is Windows and cloud is not AzurePublicCloud, it returns empty",
   729  			machineScope: MachineScope{
   730  				Machine: &clusterv1.Machine{},
   731  				AzureMachine: &infrav1.AzureMachine{
   732  					ObjectMeta: metav1.ObjectMeta{
   733  						Name: "machine-name",
   734  					},
   735  					Spec: infrav1.AzureMachineSpec{
   736  						OSDisk: infrav1.OSDisk{
   737  							OSType: "Windows",
   738  						},
   739  					},
   740  				},
   741  				ClusterScoper: &ClusterScope{
   742  					AzureClients: AzureClients{
   743  						EnvironmentSettings: auth.EnvironmentSettings{
   744  							Environment: azureautorest.Environment{
   745  								Name: azureautorest.USGovernmentCloud.Name,
   746  							},
   747  						},
   748  					},
   749  					AzureCluster: &infrav1.AzureCluster{
   750  						Spec: infrav1.AzureClusterSpec{
   751  							ResourceGroup: "my-rg",
   752  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   753  								Location: "westus",
   754  							},
   755  						},
   756  					},
   757  				},
   758  				cache: &MachineCache{
   759  					VMSKU: resourceskus.SKU{},
   760  				},
   761  			},
   762  			want: []azure.ResourceSpecGetter{},
   763  		},
   764  		{
   765  			name: "If OS type is not Linux or Windows and cloud is AzurePublicCloud, it returns empty",
   766  			machineScope: MachineScope{
   767  				Machine: &clusterv1.Machine{},
   768  				AzureMachine: &infrav1.AzureMachine{
   769  					ObjectMeta: metav1.ObjectMeta{
   770  						Name: "machine-name",
   771  					},
   772  					Spec: infrav1.AzureMachineSpec{
   773  						OSDisk: infrav1.OSDisk{
   774  							OSType: "Other",
   775  						},
   776  					},
   777  				},
   778  				ClusterScoper: &ClusterScope{
   779  					AzureClients: AzureClients{
   780  						EnvironmentSettings: auth.EnvironmentSettings{
   781  							Environment: azureautorest.Environment{
   782  								Name: azureautorest.PublicCloud.Name,
   783  							},
   784  						},
   785  					},
   786  					AzureCluster: &infrav1.AzureCluster{
   787  						Spec: infrav1.AzureClusterSpec{
   788  							ResourceGroup: "my-rg",
   789  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   790  								Location: "westus",
   791  							},
   792  						},
   793  					},
   794  				},
   795  				cache: &MachineCache{
   796  					VMSKU: resourceskus.SKU{},
   797  				},
   798  			},
   799  			want: []azure.ResourceSpecGetter{},
   800  		},
   801  		{
   802  			name: "If OS type is not Windows or Linux and cloud is not AzurePublicCloud, it returns empty",
   803  			machineScope: MachineScope{
   804  				Machine: &clusterv1.Machine{},
   805  				AzureMachine: &infrav1.AzureMachine{
   806  					ObjectMeta: metav1.ObjectMeta{
   807  						Name: "machine-name",
   808  					},
   809  					Spec: infrav1.AzureMachineSpec{
   810  						OSDisk: infrav1.OSDisk{
   811  							OSType: "Other",
   812  						},
   813  					},
   814  				},
   815  				ClusterScoper: &ClusterScope{
   816  					AzureClients: AzureClients{
   817  						EnvironmentSettings: auth.EnvironmentSettings{
   818  							Environment: azureautorest.Environment{
   819  								Name: azureautorest.USGovernmentCloud.Name,
   820  							},
   821  						},
   822  					},
   823  					AzureCluster: &infrav1.AzureCluster{
   824  						Spec: infrav1.AzureClusterSpec{
   825  							ResourceGroup: "my-rg",
   826  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   827  								Location: "westus",
   828  							},
   829  						},
   830  					},
   831  				},
   832  				cache: &MachineCache{
   833  					VMSKU: resourceskus.SKU{},
   834  				},
   835  			},
   836  			want: []azure.ResourceSpecGetter{},
   837  		},
   838  		{
   839  			name: "If a custom VM extension is specified, it returns the custom VM extension",
   840  			machineScope: MachineScope{
   841  				Machine: &clusterv1.Machine{},
   842  				AzureMachine: &infrav1.AzureMachine{
   843  					ObjectMeta: metav1.ObjectMeta{
   844  						Name: "machine-name",
   845  					},
   846  					Spec: infrav1.AzureMachineSpec{
   847  						OSDisk: infrav1.OSDisk{
   848  							OSType: "Linux",
   849  						},
   850  						VMExtensions: []infrav1.VMExtension{
   851  							{
   852  								Name:      "custom-vm-extension",
   853  								Publisher: "Microsoft.Azure.Extensions",
   854  								Version:   "2.0",
   855  								Settings: map[string]string{
   856  									"timestamp": "1234567890",
   857  								},
   858  								ProtectedSettings: map[string]string{
   859  									"commandToExecute": "echo hello world",
   860  								},
   861  							},
   862  						},
   863  					},
   864  				},
   865  				ClusterScoper: &ClusterScope{
   866  					AzureClients: AzureClients{
   867  						EnvironmentSettings: auth.EnvironmentSettings{
   868  							Environment: azureautorest.Environment{
   869  								Name: azureautorest.PublicCloud.Name,
   870  							},
   871  						},
   872  					},
   873  					AzureCluster: &infrav1.AzureCluster{
   874  						Spec: infrav1.AzureClusterSpec{
   875  							ResourceGroup: "my-rg",
   876  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   877  								Location: "westus",
   878  							},
   879  						},
   880  					},
   881  				},
   882  				cache: &MachineCache{
   883  					VMSKU: resourceskus.SKU{},
   884  				},
   885  			},
   886  			want: []azure.ResourceSpecGetter{
   887  				&vmextensions.VMExtensionSpec{
   888  					ExtensionSpec: azure.ExtensionSpec{
   889  						Name:      "custom-vm-extension",
   890  						VMName:    "machine-name",
   891  						Publisher: "Microsoft.Azure.Extensions",
   892  						Version:   "2.0",
   893  						Settings: map[string]string{
   894  							"timestamp": "1234567890",
   895  						},
   896  						ProtectedSettings: map[string]string{
   897  							"commandToExecute": "echo hello world",
   898  						},
   899  					},
   900  					ResourceGroup: "my-rg",
   901  					Location:      "westus",
   902  				},
   903  				&vmextensions.VMExtensionSpec{
   904  					ExtensionSpec: azure.ExtensionSpec{
   905  						Name:      "CAPZ.Linux.Bootstrapping",
   906  						VMName:    "machine-name",
   907  						Publisher: "Microsoft.Azure.ContainerUpstream",
   908  						Version:   "1.0",
   909  						ProtectedSettings: map[string]string{
   910  							"commandToExecute": azure.LinuxBootstrapExtensionCommand,
   911  						},
   912  					},
   913  					ResourceGroup: "my-rg",
   914  					Location:      "westus",
   915  				},
   916  			},
   917  		},
   918  	}
   919  	for _, tt := range tests {
   920  		t.Run(tt.name, func(t *testing.T) {
   921  			if got := tt.machineScope.VMExtensionSpecs(); !reflect.DeepEqual(got, tt.want) {
   922  				t.Errorf("VMExtensionSpecs() = \n%s, want \n%s", specArrayToString(got), specArrayToString(tt.want))
   923  			}
   924  		})
   925  	}
   926  }
   927  
   928  func TestMachineScope_Subnet(t *testing.T) {
   929  	tests := []struct {
   930  		name         string
   931  		machineScope MachineScope
   932  		want         infrav1.SubnetSpec
   933  	}{
   934  		{
   935  			name: "returns empty if no subnet is found at cluster scope",
   936  			machineScope: MachineScope{
   937  				AzureMachine: &infrav1.AzureMachine{
   938  					ObjectMeta: metav1.ObjectMeta{
   939  						Name: "machine-name",
   940  					},
   941  					Spec: infrav1.AzureMachineSpec{
   942  						SubnetName: "machine-name-subnet",
   943  					},
   944  				},
   945  				ClusterScoper: &ClusterScope{
   946  					AzureCluster: &infrav1.AzureCluster{
   947  						Spec: infrav1.AzureClusterSpec{
   948  							NetworkSpec: infrav1.NetworkSpec{
   949  								Subnets: []infrav1.SubnetSpec{},
   950  							},
   951  						},
   952  					},
   953  				},
   954  			},
   955  			want: infrav1.SubnetSpec{},
   956  		},
   957  		{
   958  			name: "returns the machine subnet name if the same is present in the cluster scope",
   959  			machineScope: MachineScope{
   960  				AzureMachine: &infrav1.AzureMachine{
   961  					ObjectMeta: metav1.ObjectMeta{
   962  						Name: "machine-name",
   963  					},
   964  					Spec: infrav1.AzureMachineSpec{
   965  						NetworkInterfaces: []infrav1.NetworkInterface{{
   966  							SubnetName: "machine-name-subnet",
   967  						}},
   968  					},
   969  				},
   970  				ClusterScoper: &ClusterScope{
   971  					AzureCluster: &infrav1.AzureCluster{
   972  						Spec: infrav1.AzureClusterSpec{
   973  							NetworkSpec: infrav1.NetworkSpec{
   974  								Subnets: []infrav1.SubnetSpec{
   975  									{
   976  										SubnetClassSpec: infrav1.SubnetClassSpec{
   977  											Name: "machine-name-subnet",
   978  										},
   979  									},
   980  									{
   981  										SubnetClassSpec: infrav1.SubnetClassSpec{
   982  											Name: "another-machine-name-subnet",
   983  										},
   984  									},
   985  								},
   986  							},
   987  						},
   988  					},
   989  				},
   990  			},
   991  			want: infrav1.SubnetSpec{
   992  				SubnetClassSpec: infrav1.SubnetClassSpec{
   993  					Name: "machine-name-subnet",
   994  				},
   995  			},
   996  		},
   997  		{
   998  			name: "returns empty if machine subnet name is not present in the cluster scope",
   999  			machineScope: MachineScope{
  1000  				AzureMachine: &infrav1.AzureMachine{
  1001  					ObjectMeta: metav1.ObjectMeta{
  1002  						Name: "machine-name",
  1003  					},
  1004  					Spec: infrav1.AzureMachineSpec{
  1005  						SubnetName: "machine-name-subnet",
  1006  					},
  1007  				},
  1008  				ClusterScoper: &ClusterScope{
  1009  					AzureCluster: &infrav1.AzureCluster{
  1010  						Spec: infrav1.AzureClusterSpec{
  1011  							NetworkSpec: infrav1.NetworkSpec{},
  1012  						},
  1013  					},
  1014  				},
  1015  			},
  1016  			want: infrav1.SubnetSpec{},
  1017  		},
  1018  	}
  1019  	for _, tt := range tests {
  1020  		t.Run(tt.name, func(t *testing.T) {
  1021  			if got := tt.machineScope.Subnet(); !reflect.DeepEqual(got, tt.want) {
  1022  				t.Errorf("Subnet() = %v, want %v", got, tt.want)
  1023  			}
  1024  		})
  1025  	}
  1026  }
  1027  
  1028  func TestMachineScope_AvailabilityZone(t *testing.T) {
  1029  	tests := []struct {
  1030  		name         string
  1031  		machineScope MachineScope
  1032  		want         string
  1033  	}{
  1034  		{
  1035  			name: "returns empty if no failure domain is present",
  1036  			machineScope: MachineScope{
  1037  				Machine: &clusterv1.Machine{
  1038  					Spec: clusterv1.MachineSpec{},
  1039  				},
  1040  				AzureMachine: &infrav1.AzureMachine{
  1041  					ObjectMeta: metav1.ObjectMeta{
  1042  						Name: "machine-name",
  1043  					},
  1044  					Spec: infrav1.AzureMachineSpec{},
  1045  				},
  1046  			},
  1047  			want: "",
  1048  		},
  1049  		{
  1050  			name: "returns failure domain from the machine spec",
  1051  			machineScope: MachineScope{
  1052  				Machine: &clusterv1.Machine{
  1053  					Spec: clusterv1.MachineSpec{
  1054  						FailureDomain: ptr.To("dummy-failure-domain-from-machine-spec"),
  1055  					},
  1056  				},
  1057  				AzureMachine: &infrav1.AzureMachine{
  1058  					ObjectMeta: metav1.ObjectMeta{
  1059  						Name: "machine-name",
  1060  					},
  1061  					Spec: infrav1.AzureMachineSpec{
  1062  						FailureDomain: ptr.To("dummy-failure-domain-from-azuremachine-spec"),
  1063  					},
  1064  				},
  1065  			},
  1066  			want: "dummy-failure-domain-from-machine-spec",
  1067  		},
  1068  		{
  1069  			name: "returns failure domain from the azuremachine spec",
  1070  			machineScope: MachineScope{
  1071  				Machine: &clusterv1.Machine{
  1072  					Spec: clusterv1.MachineSpec{},
  1073  				},
  1074  				AzureMachine: &infrav1.AzureMachine{
  1075  					ObjectMeta: metav1.ObjectMeta{
  1076  						Name: "machine-name",
  1077  					},
  1078  					Spec: infrav1.AzureMachineSpec{
  1079  						FailureDomain: ptr.To("dummy-failure-domain-from-azuremachine-spec"),
  1080  					},
  1081  				},
  1082  			},
  1083  			want: "dummy-failure-domain-from-azuremachine-spec",
  1084  		},
  1085  	}
  1086  	for _, tt := range tests {
  1087  		t.Run(tt.name, func(t *testing.T) {
  1088  			if got := tt.machineScope.AvailabilityZone(); got != tt.want {
  1089  				t.Errorf("AvailabilityZone() = %v, want %v", got, tt.want)
  1090  			}
  1091  		})
  1092  	}
  1093  }
  1094  
  1095  func TestMachineScope_Namespace(t *testing.T) {
  1096  	tests := []struct {
  1097  		name         string
  1098  		machineScope MachineScope
  1099  		want         string
  1100  	}{
  1101  		{
  1102  			name: "returns azure machine namespace",
  1103  			machineScope: MachineScope{
  1104  				AzureMachine: &infrav1.AzureMachine{
  1105  					ObjectMeta: metav1.ObjectMeta{
  1106  						Name:      "machine-name",
  1107  						Namespace: "foo",
  1108  					},
  1109  				},
  1110  			},
  1111  			want: "foo",
  1112  		},
  1113  		{
  1114  			name: "returns azure machine namespace as empty if namespace is no specified",
  1115  			machineScope: MachineScope{
  1116  				AzureMachine: &infrav1.AzureMachine{
  1117  					ObjectMeta: metav1.ObjectMeta{
  1118  						Name: "machine-name",
  1119  					},
  1120  				},
  1121  			},
  1122  			want: "",
  1123  		},
  1124  	}
  1125  	for _, tt := range tests {
  1126  		t.Run(tt.name, func(t *testing.T) {
  1127  			if got := tt.machineScope.Namespace(); got != tt.want {
  1128  				t.Errorf("Namespace() = %v, want %v", got, tt.want)
  1129  			}
  1130  		})
  1131  	}
  1132  }
  1133  
  1134  func TestMachineScope_IsControlPlane(t *testing.T) {
  1135  	tests := []struct {
  1136  		name         string
  1137  		machineScope MachineScope
  1138  		want         bool
  1139  	}{
  1140  		{
  1141  			name: "returns false when machine is not control plane",
  1142  			machineScope: MachineScope{
  1143  				Machine: &clusterv1.Machine{},
  1144  				AzureMachine: &infrav1.AzureMachine{
  1145  					ObjectMeta: metav1.ObjectMeta{
  1146  						Name: "machine-name",
  1147  					},
  1148  				},
  1149  			},
  1150  			want: false,
  1151  		},
  1152  		{
  1153  			name: "returns true when machine is control plane",
  1154  			machineScope: MachineScope{
  1155  				Machine: &clusterv1.Machine{
  1156  					ObjectMeta: metav1.ObjectMeta{
  1157  						Labels: map[string]string{
  1158  							clusterv1.MachineControlPlaneLabel: "",
  1159  						},
  1160  					},
  1161  				},
  1162  				AzureMachine: &infrav1.AzureMachine{
  1163  					ObjectMeta: metav1.ObjectMeta{
  1164  						Name: "machine-name",
  1165  					},
  1166  				},
  1167  			},
  1168  			want: true,
  1169  		},
  1170  	}
  1171  	for _, tt := range tests {
  1172  		t.Run(tt.name, func(t *testing.T) {
  1173  			if got := tt.machineScope.IsControlPlane(); got != tt.want {
  1174  				t.Errorf("IsControlPlane() = %v, want %v", got, tt.want)
  1175  			}
  1176  		})
  1177  	}
  1178  }
  1179  
  1180  func TestMachineScope_Role(t *testing.T) {
  1181  	tests := []struct {
  1182  		name         string
  1183  		machineScope MachineScope
  1184  		want         string
  1185  	}{
  1186  		{
  1187  			name: "returns node when machine is worker",
  1188  			machineScope: MachineScope{
  1189  				Machine: &clusterv1.Machine{},
  1190  				AzureMachine: &infrav1.AzureMachine{
  1191  					ObjectMeta: metav1.ObjectMeta{
  1192  						Name: "machine-name",
  1193  					},
  1194  				},
  1195  			},
  1196  			want: infrav1.Node,
  1197  		},
  1198  		{
  1199  			name: "returns control-plane when machine is control plane",
  1200  			machineScope: MachineScope{
  1201  				Machine: &clusterv1.Machine{
  1202  					ObjectMeta: metav1.ObjectMeta{
  1203  						Labels: map[string]string{
  1204  							clusterv1.MachineControlPlaneLabel: "",
  1205  						},
  1206  					},
  1207  				},
  1208  				AzureMachine: &infrav1.AzureMachine{
  1209  					ObjectMeta: metav1.ObjectMeta{
  1210  						Name: "machine-name",
  1211  					},
  1212  				},
  1213  			},
  1214  			want: infrav1.ControlPlane,
  1215  		},
  1216  	}
  1217  	for _, tt := range tests {
  1218  		t.Run(tt.name, func(t *testing.T) {
  1219  			if got := tt.machineScope.Role(); got != tt.want {
  1220  				t.Errorf("Role() = %v, want %v", got, tt.want)
  1221  			}
  1222  		})
  1223  	}
  1224  }
  1225  
  1226  func TestMachineScope_AvailabilitySet(t *testing.T) {
  1227  	tests := []struct {
  1228  		name                         string
  1229  		machineScope                 MachineScope
  1230  		wantAvailabilitySetName      string
  1231  		wantAvailabilitySetExistence bool
  1232  	}{
  1233  		{
  1234  			name: "returns empty and false if availability set is not enabled",
  1235  			machineScope: MachineScope{
  1236  				ClusterScoper: &ClusterScope{
  1237  					Cluster: &clusterv1.Cluster{
  1238  						ObjectMeta: metav1.ObjectMeta{
  1239  							Name: "cluster",
  1240  						},
  1241  					},
  1242  					AzureCluster: &infrav1.AzureCluster{
  1243  						Status: infrav1.AzureClusterStatus{
  1244  							FailureDomains: clusterv1.FailureDomains{
  1245  								"foo-failure-domain": clusterv1.FailureDomainSpec{},
  1246  							},
  1247  						},
  1248  					},
  1249  				},
  1250  				Machine:      &clusterv1.Machine{},
  1251  				AzureMachine: &infrav1.AzureMachine{},
  1252  			},
  1253  			wantAvailabilitySetName:      "",
  1254  			wantAvailabilitySetExistence: false,
  1255  		},
  1256  		{
  1257  			name: "returns AvailabilitySet name and true if availability set is enabled and machine is control plane",
  1258  			machineScope: MachineScope{
  1259  
  1260  				ClusterScoper: &ClusterScope{
  1261  					Cluster: &clusterv1.Cluster{
  1262  						ObjectMeta: metav1.ObjectMeta{
  1263  							Name: "cluster",
  1264  						},
  1265  					},
  1266  					AzureCluster: &infrav1.AzureCluster{
  1267  						Status: infrav1.AzureClusterStatus{},
  1268  					},
  1269  				},
  1270  				Machine: &clusterv1.Machine{
  1271  					ObjectMeta: metav1.ObjectMeta{
  1272  						Labels: map[string]string{
  1273  							clusterv1.MachineControlPlaneLabel: "",
  1274  						},
  1275  					},
  1276  				},
  1277  				AzureMachine: &infrav1.AzureMachine{},
  1278  			},
  1279  			wantAvailabilitySetName:      "cluster_control-plane-as",
  1280  			wantAvailabilitySetExistence: true,
  1281  		},
  1282  		{
  1283  			name: "returns AvailabilitySet name and true if AvailabilitySet is enabled for worker machine which is part of machine deployment",
  1284  			machineScope: MachineScope{
  1285  
  1286  				ClusterScoper: &ClusterScope{
  1287  					Cluster: &clusterv1.Cluster{
  1288  						ObjectMeta: metav1.ObjectMeta{
  1289  							Name: "cluster",
  1290  						},
  1291  					},
  1292  					AzureCluster: &infrav1.AzureCluster{
  1293  						Status: infrav1.AzureClusterStatus{},
  1294  					},
  1295  				},
  1296  				Machine: &clusterv1.Machine{
  1297  					ObjectMeta: metav1.ObjectMeta{
  1298  						Labels: map[string]string{
  1299  							clusterv1.MachineDeploymentNameLabel: "foo-machine-deployment",
  1300  						},
  1301  					},
  1302  				},
  1303  				AzureMachine: &infrav1.AzureMachine{},
  1304  			},
  1305  			wantAvailabilitySetName:      "cluster_foo-machine-deployment-as",
  1306  			wantAvailabilitySetExistence: true,
  1307  		},
  1308  		{
  1309  			name: "returns empty and false if machine is using spot instances",
  1310  			machineScope: MachineScope{
  1311  				ClusterScoper: &ClusterScope{
  1312  					Cluster: &clusterv1.Cluster{
  1313  						ObjectMeta: metav1.ObjectMeta{
  1314  							Name: "cluster",
  1315  						},
  1316  					},
  1317  					AzureCluster: &infrav1.AzureCluster{
  1318  						Status: infrav1.AzureClusterStatus{},
  1319  					},
  1320  				},
  1321  				Machine: &clusterv1.Machine{
  1322  					ObjectMeta: metav1.ObjectMeta{
  1323  						Labels: map[string]string{
  1324  							clusterv1.MachineDeploymentNameLabel: "foo-machine-deployment",
  1325  						},
  1326  					},
  1327  				},
  1328  				AzureMachine: &infrav1.AzureMachine{
  1329  					Spec: infrav1.AzureMachineSpec{
  1330  						SpotVMOptions: &infrav1.SpotVMOptions{MaxPrice: resource.NewQuantity(-1, resource.DecimalSI)},
  1331  					},
  1332  				},
  1333  			},
  1334  			wantAvailabilitySetName:      "",
  1335  			wantAvailabilitySetExistence: false,
  1336  		},
  1337  		{
  1338  			name: "returns AvailabilitySet name and true if AvailabilitySet is enabled for worker machine which is part of machine set",
  1339  			machineScope: MachineScope{
  1340  
  1341  				ClusterScoper: &ClusterScope{
  1342  					Cluster: &clusterv1.Cluster{
  1343  						ObjectMeta: metav1.ObjectMeta{
  1344  							Name: "cluster",
  1345  						},
  1346  					},
  1347  					AzureCluster: &infrav1.AzureCluster{
  1348  						Status: infrav1.AzureClusterStatus{},
  1349  					},
  1350  				},
  1351  				Machine: &clusterv1.Machine{
  1352  					ObjectMeta: metav1.ObjectMeta{
  1353  						Labels: map[string]string{
  1354  							clusterv1.MachineSetNameLabel: "foo-machine-set",
  1355  						},
  1356  					},
  1357  				},
  1358  				AzureMachine: &infrav1.AzureMachine{},
  1359  			},
  1360  			wantAvailabilitySetName:      "cluster_foo-machine-set-as",
  1361  			wantAvailabilitySetExistence: true,
  1362  		},
  1363  		{
  1364  			name: "returns AvailabilitySet name and true if AvailabilitySet is enabled for worker machine and machine deployment name takes precedence over machine set name",
  1365  			machineScope: MachineScope{
  1366  
  1367  				ClusterScoper: &ClusterScope{
  1368  					Cluster: &clusterv1.Cluster{
  1369  						ObjectMeta: metav1.ObjectMeta{
  1370  							Name: "cluster",
  1371  						},
  1372  					},
  1373  					AzureCluster: &infrav1.AzureCluster{
  1374  						Status: infrav1.AzureClusterStatus{},
  1375  					},
  1376  				},
  1377  				Machine: &clusterv1.Machine{
  1378  					ObjectMeta: metav1.ObjectMeta{
  1379  						Labels: map[string]string{
  1380  							clusterv1.MachineDeploymentNameLabel: "foo-machine-deployment",
  1381  							clusterv1.MachineSetNameLabel:        "foo-machine-set",
  1382  						},
  1383  					},
  1384  				},
  1385  				AzureMachine: &infrav1.AzureMachine{},
  1386  			},
  1387  			wantAvailabilitySetName:      "cluster_foo-machine-deployment-as",
  1388  			wantAvailabilitySetExistence: true,
  1389  		},
  1390  		{
  1391  			name: "returns empty and false if AvailabilitySet is enabled but worker machine is not part of machine deployment or machine set",
  1392  			machineScope: MachineScope{
  1393  
  1394  				ClusterScoper: &ClusterScope{
  1395  					Cluster: &clusterv1.Cluster{
  1396  						ObjectMeta: metav1.ObjectMeta{
  1397  							Name: "cluster",
  1398  						},
  1399  					},
  1400  					AzureCluster: &infrav1.AzureCluster{
  1401  						Status: infrav1.AzureClusterStatus{},
  1402  					},
  1403  				},
  1404  				Machine: &clusterv1.Machine{
  1405  					ObjectMeta: metav1.ObjectMeta{
  1406  						Labels: map[string]string{},
  1407  					},
  1408  				},
  1409  				AzureMachine: &infrav1.AzureMachine{},
  1410  			},
  1411  			wantAvailabilitySetName:      "",
  1412  			wantAvailabilitySetExistence: false,
  1413  		},
  1414  		{
  1415  			name: "returns empty and false if machine has failureDomain set",
  1416  			machineScope: MachineScope{
  1417  				ClusterScoper: &ClusterScope{
  1418  					Cluster: &clusterv1.Cluster{
  1419  						ObjectMeta: metav1.ObjectMeta{
  1420  							Name: "cluster",
  1421  						},
  1422  					},
  1423  					AzureCluster: &infrav1.AzureCluster{
  1424  						Status: infrav1.AzureClusterStatus{},
  1425  					},
  1426  				},
  1427  				Machine: &clusterv1.Machine{
  1428  					ObjectMeta: metav1.ObjectMeta{
  1429  						Labels: map[string]string{
  1430  							clusterv1.MachineDeploymentNameLabel: "foo-machine-deployment",
  1431  						},
  1432  					},
  1433  					Spec: clusterv1.MachineSpec{
  1434  						FailureDomain: ptr.To("1"),
  1435  					},
  1436  				},
  1437  				AzureMachine: &infrav1.AzureMachine{
  1438  					Spec: infrav1.AzureMachineSpec{},
  1439  				},
  1440  			},
  1441  			wantAvailabilitySetName:      "",
  1442  			wantAvailabilitySetExistence: false,
  1443  		},
  1444  		{
  1445  			name: "returns empty and false if azureMachine has failureDomain set",
  1446  			machineScope: MachineScope{
  1447  				ClusterScoper: &ClusterScope{
  1448  					Cluster: &clusterv1.Cluster{
  1449  						ObjectMeta: metav1.ObjectMeta{
  1450  							Name: "cluster",
  1451  						},
  1452  					},
  1453  					AzureCluster: &infrav1.AzureCluster{
  1454  						Status: infrav1.AzureClusterStatus{},
  1455  					},
  1456  				},
  1457  				Machine: &clusterv1.Machine{
  1458  					ObjectMeta: metav1.ObjectMeta{
  1459  						Labels: map[string]string{
  1460  							clusterv1.MachineDeploymentNameLabel: "foo-machine-deployment",
  1461  						},
  1462  					},
  1463  				},
  1464  				AzureMachine: &infrav1.AzureMachine{
  1465  					Spec: infrav1.AzureMachineSpec{
  1466  						FailureDomain: ptr.To("1"),
  1467  					},
  1468  				},
  1469  			},
  1470  			wantAvailabilitySetName:      "",
  1471  			wantAvailabilitySetExistence: false,
  1472  		},
  1473  	}
  1474  	for _, tt := range tests {
  1475  		t.Run(tt.name, func(t *testing.T) {
  1476  			gotAvailabilitySetName, gotAvailabilitySetExistence := tt.machineScope.AvailabilitySet()
  1477  			if gotAvailabilitySetName != tt.wantAvailabilitySetName {
  1478  				t.Errorf("AvailabilitySet() gotAvailabilitySetName = %v, wantAvailabilitySetName %v", gotAvailabilitySetName, tt.wantAvailabilitySetName)
  1479  			}
  1480  			if gotAvailabilitySetExistence != tt.wantAvailabilitySetExistence {
  1481  				t.Errorf("AvailabilitySet() gotAvailabilitySetExistence = %v, wantAvailabilitySetExistence %v", gotAvailabilitySetExistence, tt.wantAvailabilitySetExistence)
  1482  			}
  1483  		})
  1484  	}
  1485  }
  1486  
  1487  func TestMachineScope_VMState(t *testing.T) {
  1488  	tests := []struct {
  1489  		name         string
  1490  		machineScope MachineScope
  1491  		want         infrav1.ProvisioningState
  1492  	}{
  1493  		{
  1494  			name: "returns the VMState if present in AzureMachine status",
  1495  			machineScope: MachineScope{
  1496  				AzureMachine: &infrav1.AzureMachine{
  1497  					ObjectMeta: metav1.ObjectMeta{
  1498  						Name: "machine-name",
  1499  					},
  1500  					Status: infrav1.AzureMachineStatus{
  1501  						VMState: func() *infrav1.ProvisioningState {
  1502  							provisioningState := infrav1.Creating
  1503  							return &provisioningState
  1504  						}(),
  1505  					},
  1506  				},
  1507  			},
  1508  			want: infrav1.Creating,
  1509  		},
  1510  		{
  1511  			name: "returns empty if VMState is not present in AzureMachine status",
  1512  			machineScope: MachineScope{
  1513  				AzureMachine: &infrav1.AzureMachine{
  1514  					ObjectMeta: metav1.ObjectMeta{
  1515  						Name: "machine-name",
  1516  					},
  1517  					Status: infrav1.AzureMachineStatus{},
  1518  				},
  1519  			},
  1520  			want: "",
  1521  		},
  1522  	}
  1523  	for _, tt := range tests {
  1524  		t.Run(tt.name, func(t *testing.T) {
  1525  			if got := tt.machineScope.VMState(); got != tt.want {
  1526  				t.Errorf("VMState() = %v, want %v", got, tt.want)
  1527  			}
  1528  		})
  1529  	}
  1530  }
  1531  
  1532  func TestMachineScope_GetVMImage(t *testing.T) {
  1533  	mockCtrl := gomock.NewController(t)
  1534  	defer mockCtrl.Finish()
  1535  
  1536  	clusterMock := mock_azure.NewMockClusterScoper(mockCtrl)
  1537  	clusterMock.EXPECT().Location().AnyTimes()
  1538  	clusterMock.EXPECT().SubscriptionID().AnyTimes()
  1539  	clusterMock.EXPECT().CloudEnvironment().AnyTimes()
  1540  	clusterMock.EXPECT().Token().Return(&azidentity.DefaultAzureCredential{}).AnyTimes()
  1541  	svc := virtualmachineimages.Service{Client: mock_virtualmachineimages.NewMockClient(mockCtrl)}
  1542  
  1543  	tests := []struct {
  1544  		name         string
  1545  		machineScope MachineScope
  1546  		want         *infrav1.Image
  1547  		expectedErr  string
  1548  	}{
  1549  		{
  1550  			name: "returns AzureMachine image is found if present in the AzureMachine spec",
  1551  			machineScope: MachineScope{
  1552  				AzureMachine: &infrav1.AzureMachine{
  1553  					ObjectMeta: metav1.ObjectMeta{
  1554  						Name: "machine-name",
  1555  					},
  1556  					Spec: infrav1.AzureMachineSpec{
  1557  						Image: &infrav1.Image{
  1558  							ID: ptr.To("1"),
  1559  						},
  1560  					},
  1561  				},
  1562  			},
  1563  			want: &infrav1.Image{
  1564  				ID: ptr.To("1"),
  1565  			},
  1566  			expectedErr: "",
  1567  		},
  1568  		{
  1569  			name: "if no image is specified and os specified is windows with version below 1.22, returns windows dockershim image",
  1570  			machineScope: MachineScope{
  1571  				Machine: &clusterv1.Machine{
  1572  					ObjectMeta: metav1.ObjectMeta{
  1573  						Name: "machine-name",
  1574  					},
  1575  					Spec: clusterv1.MachineSpec{
  1576  						Version: ptr.To("1.20.1"),
  1577  					},
  1578  				},
  1579  				AzureMachine: &infrav1.AzureMachine{
  1580  					ObjectMeta: metav1.ObjectMeta{
  1581  						Name: "machine-name",
  1582  					},
  1583  					Spec: infrav1.AzureMachineSpec{
  1584  						OSDisk: infrav1.OSDisk{
  1585  							OSType: azure.WindowsOS,
  1586  						},
  1587  					},
  1588  				},
  1589  				ClusterScoper: clusterMock,
  1590  			},
  1591  			want: func() *infrav1.Image {
  1592  				image, _ := svc.GetDefaultWindowsImage(context.TODO(), "", "1.20.1", "dockershim", "")
  1593  				return image
  1594  			}(),
  1595  			expectedErr: "",
  1596  		},
  1597  		{
  1598  			name: "if no image is specified and os specified is windows with version is 1.22+ with no annotation, returns windows containerd image",
  1599  			machineScope: MachineScope{
  1600  				Machine: &clusterv1.Machine{
  1601  					ObjectMeta: metav1.ObjectMeta{
  1602  						Name: "machine-name",
  1603  					},
  1604  					Spec: clusterv1.MachineSpec{
  1605  						Version: ptr.To("1.22.1"),
  1606  					},
  1607  				},
  1608  				AzureMachine: &infrav1.AzureMachine{
  1609  					ObjectMeta: metav1.ObjectMeta{
  1610  						Name: "machine-name",
  1611  					},
  1612  					Spec: infrav1.AzureMachineSpec{
  1613  						OSDisk: infrav1.OSDisk{
  1614  							OSType: azure.WindowsOS,
  1615  						},
  1616  					},
  1617  				},
  1618  				ClusterScoper: clusterMock,
  1619  			},
  1620  			want: func() *infrav1.Image {
  1621  				image, _ := svc.GetDefaultWindowsImage(context.TODO(), "", "1.22.1", "containerd", "")
  1622  				return image
  1623  			}(),
  1624  			expectedErr: "",
  1625  		},
  1626  		{
  1627  			name: "if no image is specified and os specified is windows with version is 1.22+ with annotation dockershim, returns windows dockershim image",
  1628  			machineScope: MachineScope{
  1629  				Machine: &clusterv1.Machine{
  1630  					ObjectMeta: metav1.ObjectMeta{
  1631  						Name: "machine-name",
  1632  					},
  1633  					Spec: clusterv1.MachineSpec{
  1634  						Version: ptr.To("1.22.1"),
  1635  					},
  1636  				},
  1637  				AzureMachine: &infrav1.AzureMachine{
  1638  					ObjectMeta: metav1.ObjectMeta{
  1639  						Name: "machine-name",
  1640  						Annotations: map[string]string{
  1641  							"runtime": "dockershim",
  1642  						},
  1643  					},
  1644  					Spec: infrav1.AzureMachineSpec{
  1645  						OSDisk: infrav1.OSDisk{
  1646  							OSType: azure.WindowsOS,
  1647  						},
  1648  					},
  1649  				},
  1650  				ClusterScoper: clusterMock,
  1651  			},
  1652  			want: func() *infrav1.Image {
  1653  				image, _ := svc.GetDefaultWindowsImage(context.TODO(), "", "1.22.1", "dockershim", "")
  1654  				return image
  1655  			}(),
  1656  			expectedErr: "",
  1657  		},
  1658  		{
  1659  			name: "if no image is specified and os specified is windows with version is less and 1.22 with annotation dockershim, returns windows dockershim image",
  1660  			machineScope: MachineScope{
  1661  				Machine: &clusterv1.Machine{
  1662  					ObjectMeta: metav1.ObjectMeta{
  1663  						Name: "machine-name",
  1664  					},
  1665  					Spec: clusterv1.MachineSpec{
  1666  						Version: ptr.To("1.21.1"),
  1667  					},
  1668  				},
  1669  				AzureMachine: &infrav1.AzureMachine{
  1670  					ObjectMeta: metav1.ObjectMeta{
  1671  						Name: "machine-name",
  1672  						Annotations: map[string]string{
  1673  							"runtime": "dockershim",
  1674  						},
  1675  					},
  1676  					Spec: infrav1.AzureMachineSpec{
  1677  						OSDisk: infrav1.OSDisk{
  1678  							OSType: azure.WindowsOS,
  1679  						},
  1680  					},
  1681  				},
  1682  				ClusterScoper: clusterMock,
  1683  			},
  1684  			want: func() *infrav1.Image {
  1685  				image, _ := svc.GetDefaultWindowsImage(context.TODO(), "", "1.21.1", "dockershim", "")
  1686  				return image
  1687  			}(),
  1688  			expectedErr: "",
  1689  		},
  1690  		{
  1691  			name: "if no image is specified and os specified is windows with version is less and 1.22 with annotation containerd, returns error",
  1692  			machineScope: MachineScope{
  1693  				Machine: &clusterv1.Machine{
  1694  					ObjectMeta: metav1.ObjectMeta{
  1695  						Name: "machine-name",
  1696  					},
  1697  					Spec: clusterv1.MachineSpec{
  1698  						Version: ptr.To("1.21.1"),
  1699  					},
  1700  				},
  1701  				AzureMachine: &infrav1.AzureMachine{
  1702  					ObjectMeta: metav1.ObjectMeta{
  1703  						Name: "machine-name",
  1704  						Annotations: map[string]string{
  1705  							"runtime": "containerd",
  1706  						},
  1707  					},
  1708  					Spec: infrav1.AzureMachineSpec{
  1709  						OSDisk: infrav1.OSDisk{
  1710  							OSType: azure.WindowsOS,
  1711  						},
  1712  					},
  1713  				},
  1714  				ClusterScoper: clusterMock,
  1715  			},
  1716  			want:        nil,
  1717  			expectedErr: "containerd image only supported in 1.22+",
  1718  		},
  1719  		{
  1720  			name: "if no image is specified and os specified is windows with windowsServerVersion annotation set to 2019, retrurns 2019 image",
  1721  			machineScope: MachineScope{
  1722  				Machine: &clusterv1.Machine{
  1723  					ObjectMeta: metav1.ObjectMeta{
  1724  						Name: "machine-name",
  1725  					},
  1726  					Spec: clusterv1.MachineSpec{
  1727  						Version: ptr.To("1.23.3"),
  1728  					},
  1729  				},
  1730  				AzureMachine: &infrav1.AzureMachine{
  1731  					ObjectMeta: metav1.ObjectMeta{
  1732  						Name: "machine-name",
  1733  						Annotations: map[string]string{
  1734  							"windowsServerVersion": "windows-2019",
  1735  						},
  1736  					},
  1737  					Spec: infrav1.AzureMachineSpec{
  1738  						OSDisk: infrav1.OSDisk{
  1739  							OSType: azure.WindowsOS,
  1740  						},
  1741  					},
  1742  				},
  1743  				ClusterScoper: clusterMock,
  1744  			},
  1745  			want: func() *infrav1.Image {
  1746  				image, _ := svc.GetDefaultWindowsImage(context.TODO(), "", "1.23.3", "", "windows-2019")
  1747  				return image
  1748  			}(),
  1749  			expectedErr: "",
  1750  		},
  1751  		{
  1752  			name: "if no image is specified and os specified is windows with windowsServerVersion annotation set to 2022, retrurns 2022 image",
  1753  			machineScope: MachineScope{
  1754  				Machine: &clusterv1.Machine{
  1755  					ObjectMeta: metav1.ObjectMeta{
  1756  						Name: "machine-name",
  1757  					},
  1758  					Spec: clusterv1.MachineSpec{
  1759  						Version: ptr.To("1.23.3"),
  1760  					},
  1761  				},
  1762  				AzureMachine: &infrav1.AzureMachine{
  1763  					ObjectMeta: metav1.ObjectMeta{
  1764  						Name: "machine-name",
  1765  						Annotations: map[string]string{
  1766  							"windowsServerVersion": "windows-2022",
  1767  						},
  1768  					},
  1769  					Spec: infrav1.AzureMachineSpec{
  1770  						OSDisk: infrav1.OSDisk{
  1771  							OSType: azure.WindowsOS,
  1772  						},
  1773  					},
  1774  				},
  1775  				ClusterScoper: clusterMock,
  1776  			},
  1777  			want: func() *infrav1.Image {
  1778  				image, _ := svc.GetDefaultWindowsImage(context.TODO(), "", "1.23.3", "", "windows-2022")
  1779  				return image
  1780  			}(),
  1781  			expectedErr: "",
  1782  		},
  1783  		{
  1784  			name: "if no image and OS is specified, returns linux image",
  1785  			machineScope: MachineScope{
  1786  				Machine: &clusterv1.Machine{
  1787  					ObjectMeta: metav1.ObjectMeta{
  1788  						Name: "machine-name",
  1789  					},
  1790  					Spec: clusterv1.MachineSpec{
  1791  						Version: ptr.To("1.20.1"),
  1792  					},
  1793  				},
  1794  				AzureMachine: &infrav1.AzureMachine{
  1795  					ObjectMeta: metav1.ObjectMeta{
  1796  						Name: "machine-name",
  1797  					},
  1798  				},
  1799  				ClusterScoper: clusterMock,
  1800  			},
  1801  			want: func() *infrav1.Image {
  1802  				image, _ := svc.GetDefaultUbuntuImage(context.TODO(), "", "1.20.1")
  1803  				return image
  1804  			}(),
  1805  			expectedErr: "",
  1806  		},
  1807  	}
  1808  	for _, tt := range tests {
  1809  		t.Run(tt.name, func(t *testing.T) {
  1810  			gotImage, err := tt.machineScope.GetVMImage(context.TODO())
  1811  			if (err == nil && tt.expectedErr != "") || (err != nil && tt.expectedErr != err.Error()) {
  1812  				t.Errorf("expected error %v, got %v", tt.expectedErr, err)
  1813  			}
  1814  			if !reflect.DeepEqual(gotImage, tt.want) {
  1815  				t.Errorf("GetVMImage(), gotImage = %v, wantImage %v", gotImage, tt.want)
  1816  			}
  1817  		})
  1818  	}
  1819  }
  1820  
  1821  func TestMachineScope_NICSpecs(t *testing.T) {
  1822  	tests := []struct {
  1823  		name         string
  1824  		machineScope MachineScope
  1825  		want         []azure.ResourceSpecGetter
  1826  	}{
  1827  		{
  1828  			name: "Node Machine with no NAT gateway and no public IP address",
  1829  			machineScope: MachineScope{
  1830  				ClusterScoper: &ClusterScope{
  1831  					AzureClients: AzureClients{
  1832  						EnvironmentSettings: auth.EnvironmentSettings{
  1833  							Values: map[string]string{
  1834  								auth.SubscriptionID: "123",
  1835  							},
  1836  						},
  1837  					},
  1838  					Cluster: &clusterv1.Cluster{
  1839  						ObjectMeta: metav1.ObjectMeta{
  1840  							Name:      "cluster",
  1841  							Namespace: "default",
  1842  						},
  1843  					},
  1844  					AzureCluster: &infrav1.AzureCluster{
  1845  						ObjectMeta: metav1.ObjectMeta{
  1846  							Name:      "cluster",
  1847  							Namespace: "default",
  1848  							OwnerReferences: []metav1.OwnerReference{
  1849  								{
  1850  									APIVersion: "cluster.x-k8s.io/v1beta1",
  1851  									Kind:       "Cluster",
  1852  									Name:       "cluster",
  1853  								},
  1854  							},
  1855  						},
  1856  						Spec: infrav1.AzureClusterSpec{
  1857  							ResourceGroup: "my-rg",
  1858  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1859  								Location: "westus",
  1860  							},
  1861  							NetworkSpec: infrav1.NetworkSpec{
  1862  								Vnet: infrav1.VnetSpec{
  1863  									Name:          "vnet1",
  1864  									ResourceGroup: "rg1",
  1865  								},
  1866  								Subnets: []infrav1.SubnetSpec{
  1867  									{
  1868  										SubnetClassSpec: infrav1.SubnetClassSpec{
  1869  											Role: infrav1.SubnetNode,
  1870  											Name: "subnet1",
  1871  										},
  1872  									},
  1873  								},
  1874  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  1875  									Name: "outbound-lb",
  1876  									BackendPool: infrav1.BackendPool{
  1877  										Name: "outbound-lb-outboundBackendPool",
  1878  									},
  1879  								},
  1880  							},
  1881  						},
  1882  					},
  1883  				},
  1884  				AzureMachine: &infrav1.AzureMachine{
  1885  					ObjectMeta: metav1.ObjectMeta{
  1886  						Name: "machine",
  1887  					},
  1888  					Spec: infrav1.AzureMachineSpec{
  1889  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  1890  						NetworkInterfaces: []infrav1.NetworkInterface{{
  1891  							SubnetName:       "subnet1",
  1892  							PrivateIPConfigs: 1,
  1893  						}},
  1894  					},
  1895  				},
  1896  				Machine: &clusterv1.Machine{
  1897  					ObjectMeta: metav1.ObjectMeta{
  1898  						Name:   "machine",
  1899  						Labels: map[string]string{
  1900  							// clusterv1.MachineControlPlaneLabel: "true",
  1901  						},
  1902  					},
  1903  				},
  1904  			},
  1905  			want: []azure.ResourceSpecGetter{
  1906  				&networkinterfaces.NICSpec{
  1907  					Name:                      "machine-name-nic",
  1908  					ResourceGroup:             "my-rg",
  1909  					Location:                  "westus",
  1910  					SubscriptionID:            "123",
  1911  					MachineName:               "machine-name",
  1912  					SubnetName:                "subnet1",
  1913  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  1914  					VNetName:                  "vnet1",
  1915  					VNetResourceGroup:         "rg1",
  1916  					PublicLBName:              "outbound-lb",
  1917  					PublicLBAddressPoolName:   "outbound-lb-outboundBackendPool",
  1918  					PublicLBNATRuleName:       "",
  1919  					InternalLBName:            "",
  1920  					InternalLBAddressPoolName: "",
  1921  					PublicIPName:              "",
  1922  					AcceleratedNetworking:     nil,
  1923  					DNSServers:                nil,
  1924  					IPv6Enabled:               false,
  1925  					EnableIPForwarding:        false,
  1926  					SKU:                       nil,
  1927  					ClusterName:               "cluster",
  1928  					AdditionalTags: infrav1.Tags{
  1929  						"kubernetes.io_cluster_cluster": "owned",
  1930  					},
  1931  				},
  1932  			},
  1933  		},
  1934  		{
  1935  			name: "Node Machine with no NAT gateway and no public IP address and SKU is in machine cache",
  1936  			machineScope: MachineScope{
  1937  				ClusterScoper: &ClusterScope{
  1938  					AzureClients: AzureClients{
  1939  						EnvironmentSettings: auth.EnvironmentSettings{
  1940  							Values: map[string]string{
  1941  								auth.SubscriptionID: "123",
  1942  							},
  1943  						},
  1944  					},
  1945  					Cluster: &clusterv1.Cluster{
  1946  						ObjectMeta: metav1.ObjectMeta{
  1947  							Name:      "cluster",
  1948  							Namespace: "default",
  1949  						},
  1950  					},
  1951  					AzureCluster: &infrav1.AzureCluster{
  1952  						ObjectMeta: metav1.ObjectMeta{
  1953  							Name:      "cluster",
  1954  							Namespace: "default",
  1955  							OwnerReferences: []metav1.OwnerReference{
  1956  								{
  1957  									APIVersion: "cluster.x-k8s.io/v1beta1",
  1958  									Kind:       "Cluster",
  1959  									Name:       "cluster",
  1960  								},
  1961  							},
  1962  						},
  1963  						Spec: infrav1.AzureClusterSpec{
  1964  							ResourceGroup: "my-rg",
  1965  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1966  								Location: "westus",
  1967  							},
  1968  							NetworkSpec: infrav1.NetworkSpec{
  1969  								Vnet: infrav1.VnetSpec{
  1970  									Name:          "vnet1",
  1971  									ResourceGroup: "rg1",
  1972  								},
  1973  								Subnets: []infrav1.SubnetSpec{
  1974  									{
  1975  										SubnetClassSpec: infrav1.SubnetClassSpec{
  1976  											Role: infrav1.SubnetNode,
  1977  											Name: "subnet1",
  1978  										},
  1979  									},
  1980  								},
  1981  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  1982  									Name: "outbound-lb",
  1983  									BackendPool: infrav1.BackendPool{
  1984  										Name: "outbound-lb-outboundBackendPool",
  1985  									},
  1986  								},
  1987  							},
  1988  						},
  1989  					},
  1990  				},
  1991  				AzureMachine: &infrav1.AzureMachine{
  1992  					ObjectMeta: metav1.ObjectMeta{
  1993  						Name: "machine",
  1994  					},
  1995  					Spec: infrav1.AzureMachineSpec{
  1996  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  1997  						NetworkInterfaces: []infrav1.NetworkInterface{{
  1998  							SubnetName:       "subnet1",
  1999  							PrivateIPConfigs: 1,
  2000  						}},
  2001  					},
  2002  				},
  2003  				Machine: &clusterv1.Machine{
  2004  					ObjectMeta: metav1.ObjectMeta{
  2005  						Name:   "machine",
  2006  						Labels: map[string]string{
  2007  							// clusterv1.MachineControlPlaneLabel: "true",
  2008  						},
  2009  					},
  2010  				},
  2011  				cache: &MachineCache{
  2012  					VMSKU: resourceskus.SKU{
  2013  						Name: ptr.To("Standard_D2v2"),
  2014  					},
  2015  				},
  2016  			},
  2017  			want: []azure.ResourceSpecGetter{
  2018  				&networkinterfaces.NICSpec{
  2019  					Name:                      "machine-name-nic",
  2020  					ResourceGroup:             "my-rg",
  2021  					Location:                  "westus",
  2022  					SubscriptionID:            "123",
  2023  					MachineName:               "machine-name",
  2024  					SubnetName:                "subnet1",
  2025  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2026  					VNetName:                  "vnet1",
  2027  					VNetResourceGroup:         "rg1",
  2028  					PublicLBName:              "outbound-lb",
  2029  					PublicLBAddressPoolName:   "outbound-lb-outboundBackendPool",
  2030  					PublicLBNATRuleName:       "",
  2031  					InternalLBName:            "",
  2032  					InternalLBAddressPoolName: "",
  2033  					PublicIPName:              "",
  2034  					AcceleratedNetworking:     nil,
  2035  					DNSServers:                nil,
  2036  					IPv6Enabled:               false,
  2037  					EnableIPForwarding:        false,
  2038  					SKU: &resourceskus.SKU{
  2039  						Name: ptr.To("Standard_D2v2"),
  2040  					},
  2041  					ClusterName: "cluster",
  2042  					AdditionalTags: infrav1.Tags{
  2043  						"kubernetes.io_cluster_cluster": "owned",
  2044  					},
  2045  				},
  2046  			},
  2047  		},
  2048  		{
  2049  			name: "Node Machine with NAT gateway",
  2050  			machineScope: MachineScope{
  2051  				ClusterScoper: &ClusterScope{
  2052  					AzureClients: AzureClients{
  2053  						EnvironmentSettings: auth.EnvironmentSettings{
  2054  							Values: map[string]string{
  2055  								auth.SubscriptionID: "123",
  2056  							},
  2057  						},
  2058  					},
  2059  					Cluster: &clusterv1.Cluster{
  2060  						ObjectMeta: metav1.ObjectMeta{
  2061  							Name:      "cluster",
  2062  							Namespace: "default",
  2063  						},
  2064  					},
  2065  					AzureCluster: &infrav1.AzureCluster{
  2066  						ObjectMeta: metav1.ObjectMeta{
  2067  							Name:      "cluster",
  2068  							Namespace: "default",
  2069  							OwnerReferences: []metav1.OwnerReference{
  2070  								{
  2071  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2072  									Kind:       "Cluster",
  2073  									Name:       "cluster",
  2074  								},
  2075  							},
  2076  						},
  2077  						Spec: infrav1.AzureClusterSpec{
  2078  							ResourceGroup: "my-rg",
  2079  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2080  								Location: "westus",
  2081  							},
  2082  							NetworkSpec: infrav1.NetworkSpec{
  2083  								Vnet: infrav1.VnetSpec{
  2084  									Name:          "vnet1",
  2085  									ResourceGroup: "rg1",
  2086  								},
  2087  								Subnets: []infrav1.SubnetSpec{
  2088  									{
  2089  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2090  											Role: infrav1.SubnetNode,
  2091  											Name: "subnet1",
  2092  										},
  2093  										NatGateway: infrav1.NatGateway{
  2094  											NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  2095  												Name: "natgw",
  2096  											},
  2097  										},
  2098  									},
  2099  								},
  2100  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2101  									Name: "outbound-lb",
  2102  								},
  2103  							},
  2104  						},
  2105  					},
  2106  				},
  2107  				AzureMachine: &infrav1.AzureMachine{
  2108  					ObjectMeta: metav1.ObjectMeta{
  2109  						Name: "machine",
  2110  					},
  2111  					Spec: infrav1.AzureMachineSpec{
  2112  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2113  						NetworkInterfaces: []infrav1.NetworkInterface{{
  2114  							SubnetName:       "subnet1",
  2115  							PrivateIPConfigs: 1,
  2116  						}},
  2117  					},
  2118  				},
  2119  				Machine: &clusterv1.Machine{
  2120  					ObjectMeta: metav1.ObjectMeta{
  2121  						Name:   "machine",
  2122  						Labels: map[string]string{
  2123  							// clusterv1.MachineControlPlaneLabel: "true",
  2124  						},
  2125  					},
  2126  				},
  2127  			},
  2128  			want: []azure.ResourceSpecGetter{
  2129  				&networkinterfaces.NICSpec{
  2130  					Name:                      "machine-name-nic",
  2131  					ResourceGroup:             "my-rg",
  2132  					Location:                  "westus",
  2133  					SubscriptionID:            "123",
  2134  					MachineName:               "machine-name",
  2135  					SubnetName:                "subnet1",
  2136  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2137  					VNetName:                  "vnet1",
  2138  					VNetResourceGroup:         "rg1",
  2139  					PublicLBName:              "",
  2140  					PublicLBAddressPoolName:   "",
  2141  					PublicLBNATRuleName:       "",
  2142  					InternalLBName:            "",
  2143  					InternalLBAddressPoolName: "",
  2144  					PublicIPName:              "",
  2145  					AcceleratedNetworking:     nil,
  2146  					DNSServers:                nil,
  2147  					IPv6Enabled:               false,
  2148  					EnableIPForwarding:        false,
  2149  					SKU:                       nil,
  2150  					ClusterName:               "cluster",
  2151  					AdditionalTags: infrav1.Tags{
  2152  						"kubernetes.io_cluster_cluster": "owned",
  2153  					},
  2154  				},
  2155  			},
  2156  		},
  2157  		{
  2158  			name: "Node Machine with public IP address",
  2159  			machineScope: MachineScope{
  2160  				ClusterScoper: &ClusterScope{
  2161  					AzureClients: AzureClients{
  2162  						EnvironmentSettings: auth.EnvironmentSettings{
  2163  							Values: map[string]string{
  2164  								auth.SubscriptionID: "123",
  2165  							},
  2166  						},
  2167  					},
  2168  					Cluster: &clusterv1.Cluster{
  2169  						ObjectMeta: metav1.ObjectMeta{
  2170  							Name:      "cluster",
  2171  							Namespace: "default",
  2172  						},
  2173  					},
  2174  					AzureCluster: &infrav1.AzureCluster{
  2175  						ObjectMeta: metav1.ObjectMeta{
  2176  							Name:      "cluster",
  2177  							Namespace: "default",
  2178  							OwnerReferences: []metav1.OwnerReference{
  2179  								{
  2180  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2181  									Kind:       "Cluster",
  2182  									Name:       "cluster",
  2183  								},
  2184  							},
  2185  						},
  2186  						Spec: infrav1.AzureClusterSpec{
  2187  							ResourceGroup: "my-rg",
  2188  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2189  								Location: "westus",
  2190  							},
  2191  							NetworkSpec: infrav1.NetworkSpec{
  2192  								Vnet: infrav1.VnetSpec{
  2193  									Name:          "vnet1",
  2194  									ResourceGroup: "rg1",
  2195  								},
  2196  								Subnets: []infrav1.SubnetSpec{
  2197  									{
  2198  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2199  											Role: infrav1.SubnetNode,
  2200  											Name: "subnet1",
  2201  										},
  2202  									},
  2203  								},
  2204  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2205  									Name: "outbound-lb",
  2206  								},
  2207  							},
  2208  						},
  2209  					},
  2210  				},
  2211  				AzureMachine: &infrav1.AzureMachine{
  2212  					ObjectMeta: metav1.ObjectMeta{
  2213  						Name: "machine",
  2214  					},
  2215  					Spec: infrav1.AzureMachineSpec{
  2216  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2217  						NetworkInterfaces: []infrav1.NetworkInterface{{
  2218  							SubnetName:       "subnet1",
  2219  							PrivateIPConfigs: 1,
  2220  						}},
  2221  						AllocatePublicIP: true,
  2222  					},
  2223  				},
  2224  				Machine: &clusterv1.Machine{
  2225  					ObjectMeta: metav1.ObjectMeta{
  2226  						Name:   "machine",
  2227  						Labels: map[string]string{
  2228  							// clusterv1.MachineControlPlaneLabel: "true",
  2229  						},
  2230  					},
  2231  				},
  2232  			},
  2233  			want: []azure.ResourceSpecGetter{
  2234  				&networkinterfaces.NICSpec{
  2235  					Name:                      "machine-name-nic",
  2236  					ResourceGroup:             "my-rg",
  2237  					Location:                  "westus",
  2238  					SubscriptionID:            "123",
  2239  					MachineName:               "machine-name",
  2240  					SubnetName:                "subnet1",
  2241  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2242  					VNetName:                  "vnet1",
  2243  					VNetResourceGroup:         "rg1",
  2244  					PublicLBName:              "",
  2245  					PublicLBAddressPoolName:   "",
  2246  					PublicLBNATRuleName:       "",
  2247  					InternalLBName:            "",
  2248  					InternalLBAddressPoolName: "",
  2249  					PublicIPName:              "pip-machine-name",
  2250  					AcceleratedNetworking:     nil,
  2251  					DNSServers:                nil,
  2252  					IPv6Enabled:               false,
  2253  					EnableIPForwarding:        false,
  2254  					SKU:                       nil,
  2255  					ClusterName:               "cluster",
  2256  					AdditionalTags: infrav1.Tags{
  2257  						"kubernetes.io_cluster_cluster": "owned",
  2258  					},
  2259  				},
  2260  			},
  2261  		},
  2262  		{
  2263  			name: "Control Plane Machine with private LB",
  2264  			machineScope: MachineScope{
  2265  				ClusterScoper: &ClusterScope{
  2266  					AzureClients: AzureClients{
  2267  						EnvironmentSettings: auth.EnvironmentSettings{
  2268  							Values: map[string]string{
  2269  								auth.SubscriptionID: "123",
  2270  							},
  2271  						},
  2272  					},
  2273  					Cluster: &clusterv1.Cluster{
  2274  						ObjectMeta: metav1.ObjectMeta{
  2275  							Name:      "cluster",
  2276  							Namespace: "default",
  2277  						},
  2278  					},
  2279  					AzureCluster: &infrav1.AzureCluster{
  2280  						ObjectMeta: metav1.ObjectMeta{
  2281  							Name:      "cluster",
  2282  							Namespace: "default",
  2283  							OwnerReferences: []metav1.OwnerReference{
  2284  								{
  2285  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2286  									Kind:       "Cluster",
  2287  									Name:       "cluster",
  2288  								},
  2289  							},
  2290  						},
  2291  						Spec: infrav1.AzureClusterSpec{
  2292  							ResourceGroup: "my-rg",
  2293  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2294  								Location: "westus",
  2295  							},
  2296  							NetworkSpec: infrav1.NetworkSpec{
  2297  								Vnet: infrav1.VnetSpec{
  2298  									Name:          "vnet1",
  2299  									ResourceGroup: "rg1",
  2300  								},
  2301  								Subnets: []infrav1.SubnetSpec{
  2302  									{
  2303  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2304  											Role: infrav1.SubnetNode,
  2305  											Name: "subnet1",
  2306  										},
  2307  									},
  2308  								},
  2309  								APIServerLB: infrav1.LoadBalancerSpec{
  2310  									Name: "api-lb",
  2311  									LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2312  										Type: infrav1.Internal,
  2313  									},
  2314  									BackendPool: infrav1.BackendPool{
  2315  										Name: "api-lb-backendPool",
  2316  									},
  2317  								},
  2318  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2319  									Name: "outbound-lb",
  2320  								},
  2321  							},
  2322  						},
  2323  					},
  2324  				},
  2325  				AzureMachine: &infrav1.AzureMachine{
  2326  					ObjectMeta: metav1.ObjectMeta{
  2327  						Name: "machine",
  2328  					},
  2329  					Spec: infrav1.AzureMachineSpec{
  2330  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2331  						NetworkInterfaces: []infrav1.NetworkInterface{{
  2332  							SubnetName:       "subnet1",
  2333  							PrivateIPConfigs: 1,
  2334  						}},
  2335  					},
  2336  				},
  2337  				Machine: &clusterv1.Machine{
  2338  					ObjectMeta: metav1.ObjectMeta{
  2339  						Name: "machine",
  2340  						Labels: map[string]string{
  2341  							clusterv1.MachineControlPlaneLabel: "true",
  2342  						},
  2343  					},
  2344  				},
  2345  			},
  2346  			want: []azure.ResourceSpecGetter{
  2347  				&networkinterfaces.NICSpec{
  2348  					Name:                      "machine-name-nic",
  2349  					ResourceGroup:             "my-rg",
  2350  					Location:                  "westus",
  2351  					SubscriptionID:            "123",
  2352  					MachineName:               "machine-name",
  2353  					SubnetName:                "subnet1",
  2354  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2355  					VNetName:                  "vnet1",
  2356  					VNetResourceGroup:         "rg1",
  2357  					PublicLBName:              "",
  2358  					PublicLBAddressPoolName:   "",
  2359  					PublicLBNATRuleName:       "",
  2360  					InternalLBName:            "api-lb",
  2361  					InternalLBAddressPoolName: "api-lb-backendPool",
  2362  					PublicIPName:              "",
  2363  					AcceleratedNetworking:     nil,
  2364  					DNSServers:                nil,
  2365  					IPv6Enabled:               false,
  2366  					EnableIPForwarding:        false,
  2367  					SKU:                       nil,
  2368  					ClusterName:               "cluster",
  2369  					AdditionalTags: infrav1.Tags{
  2370  						"kubernetes.io_cluster_cluster": "owned",
  2371  					},
  2372  				},
  2373  			},
  2374  		},
  2375  		{
  2376  			name: "Control Plane Machine with public LB",
  2377  			machineScope: MachineScope{
  2378  				ClusterScoper: &ClusterScope{
  2379  					AzureClients: AzureClients{
  2380  						EnvironmentSettings: auth.EnvironmentSettings{
  2381  							Values: map[string]string{
  2382  								auth.SubscriptionID: "123",
  2383  							},
  2384  						},
  2385  					},
  2386  					Cluster: &clusterv1.Cluster{
  2387  						ObjectMeta: metav1.ObjectMeta{
  2388  							Name:      "cluster",
  2389  							Namespace: "default",
  2390  						},
  2391  					},
  2392  					AzureCluster: &infrav1.AzureCluster{
  2393  						ObjectMeta: metav1.ObjectMeta{
  2394  							Name:      "cluster",
  2395  							Namespace: "default",
  2396  							OwnerReferences: []metav1.OwnerReference{
  2397  								{
  2398  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2399  									Kind:       "Cluster",
  2400  									Name:       "cluster",
  2401  								},
  2402  							},
  2403  						},
  2404  						Spec: infrav1.AzureClusterSpec{
  2405  							ResourceGroup: "my-rg",
  2406  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2407  								Location: "westus",
  2408  							},
  2409  							NetworkSpec: infrav1.NetworkSpec{
  2410  								Vnet: infrav1.VnetSpec{
  2411  									Name:          "vnet1",
  2412  									ResourceGroup: "rg1",
  2413  								},
  2414  								Subnets: []infrav1.SubnetSpec{
  2415  									{
  2416  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2417  											Role: infrav1.SubnetNode,
  2418  											Name: "subnet1",
  2419  										},
  2420  									},
  2421  								},
  2422  								APIServerLB: infrav1.LoadBalancerSpec{
  2423  									Name: "api-lb",
  2424  									BackendPool: infrav1.BackendPool{
  2425  										Name: "api-lb-backendPool",
  2426  									},
  2427  								},
  2428  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2429  									Name: "outbound-lb",
  2430  								},
  2431  							},
  2432  						},
  2433  					},
  2434  				},
  2435  				AzureMachine: &infrav1.AzureMachine{
  2436  					ObjectMeta: metav1.ObjectMeta{
  2437  						Name: "machine",
  2438  					},
  2439  					Spec: infrav1.AzureMachineSpec{
  2440  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2441  						NetworkInterfaces: []infrav1.NetworkInterface{{
  2442  							SubnetName:       "subnet1",
  2443  							PrivateIPConfigs: 1,
  2444  						}},
  2445  					},
  2446  				},
  2447  				Machine: &clusterv1.Machine{
  2448  					ObjectMeta: metav1.ObjectMeta{
  2449  						Name: "machine",
  2450  						Labels: map[string]string{
  2451  							clusterv1.MachineControlPlaneLabel: "true",
  2452  						},
  2453  					},
  2454  				},
  2455  			},
  2456  			want: []azure.ResourceSpecGetter{
  2457  				&networkinterfaces.NICSpec{
  2458  					Name:                      "machine-name-nic",
  2459  					ResourceGroup:             "my-rg",
  2460  					Location:                  "westus",
  2461  					SubscriptionID:            "123",
  2462  					MachineName:               "machine-name",
  2463  					SubnetName:                "subnet1",
  2464  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2465  					VNetName:                  "vnet1",
  2466  					VNetResourceGroup:         "rg1",
  2467  					PublicLBName:              "api-lb",
  2468  					PublicLBAddressPoolName:   "api-lb-backendPool",
  2469  					PublicLBNATRuleName:       "machine-name",
  2470  					InternalLBName:            "",
  2471  					InternalLBAddressPoolName: "",
  2472  					PublicIPName:              "",
  2473  					AcceleratedNetworking:     nil,
  2474  					DNSServers:                nil,
  2475  					IPv6Enabled:               false,
  2476  					EnableIPForwarding:        false,
  2477  					SKU:                       nil,
  2478  					ClusterName:               "cluster",
  2479  					AdditionalTags: infrav1.Tags{
  2480  						"kubernetes.io_cluster_cluster": "owned",
  2481  					},
  2482  				},
  2483  			},
  2484  		},
  2485  		{
  2486  			name: "Control Plane Machine with public LB and Custom DNS Servers",
  2487  			machineScope: MachineScope{
  2488  				ClusterScoper: &ClusterScope{
  2489  					AzureClients: AzureClients{
  2490  						EnvironmentSettings: auth.EnvironmentSettings{
  2491  							Values: map[string]string{
  2492  								auth.SubscriptionID: "123",
  2493  							},
  2494  						},
  2495  					},
  2496  					Cluster: &clusterv1.Cluster{
  2497  						ObjectMeta: metav1.ObjectMeta{
  2498  							Name:      "cluster",
  2499  							Namespace: "default",
  2500  						},
  2501  					},
  2502  					AzureCluster: &infrav1.AzureCluster{
  2503  						ObjectMeta: metav1.ObjectMeta{
  2504  							Name:      "cluster",
  2505  							Namespace: "default",
  2506  							OwnerReferences: []metav1.OwnerReference{
  2507  								{
  2508  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2509  									Kind:       "Cluster",
  2510  									Name:       "cluster",
  2511  								},
  2512  							},
  2513  						},
  2514  						Spec: infrav1.AzureClusterSpec{
  2515  							ResourceGroup: "my-rg",
  2516  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2517  								Location: "westus",
  2518  							},
  2519  							NetworkSpec: infrav1.NetworkSpec{
  2520  								Vnet: infrav1.VnetSpec{
  2521  									Name:          "vnet1",
  2522  									ResourceGroup: "rg1",
  2523  								},
  2524  								Subnets: []infrav1.SubnetSpec{
  2525  									{
  2526  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2527  											Role: infrav1.SubnetNode,
  2528  											Name: "subnet1",
  2529  										},
  2530  									},
  2531  								},
  2532  								APIServerLB: infrav1.LoadBalancerSpec{
  2533  									Name: "api-lb",
  2534  									BackendPool: infrav1.BackendPool{
  2535  										Name: "api-lb-backendPool",
  2536  									},
  2537  								},
  2538  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2539  									Name: "outbound-lb",
  2540  								},
  2541  							},
  2542  						},
  2543  					},
  2544  				},
  2545  				AzureMachine: &infrav1.AzureMachine{
  2546  					ObjectMeta: metav1.ObjectMeta{
  2547  						Name: "machine",
  2548  					},
  2549  					Spec: infrav1.AzureMachineSpec{
  2550  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2551  						NetworkInterfaces: []infrav1.NetworkInterface{{
  2552  							SubnetName:       "subnet1",
  2553  							PrivateIPConfigs: 1,
  2554  						}},
  2555  						DNSServers: []string{"123.123.123.123", "124.124.124.124"},
  2556  					},
  2557  				},
  2558  				Machine: &clusterv1.Machine{
  2559  					ObjectMeta: metav1.ObjectMeta{
  2560  						Name: "machine",
  2561  						Labels: map[string]string{
  2562  							clusterv1.MachineControlPlaneLabel: "true",
  2563  						},
  2564  					},
  2565  				},
  2566  			},
  2567  			want: []azure.ResourceSpecGetter{
  2568  				&networkinterfaces.NICSpec{
  2569  					Name:                      "machine-name-nic",
  2570  					ResourceGroup:             "my-rg",
  2571  					Location:                  "westus",
  2572  					SubscriptionID:            "123",
  2573  					MachineName:               "machine-name",
  2574  					SubnetName:                "subnet1",
  2575  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2576  					VNetName:                  "vnet1",
  2577  					VNetResourceGroup:         "rg1",
  2578  					PublicLBName:              "api-lb",
  2579  					PublicLBAddressPoolName:   "api-lb-backendPool",
  2580  					PublicLBNATRuleName:       "machine-name",
  2581  					InternalLBName:            "",
  2582  					InternalLBAddressPoolName: "",
  2583  					PublicIPName:              "",
  2584  					AcceleratedNetworking:     nil,
  2585  					DNSServers:                []string{"123.123.123.123", "124.124.124.124"},
  2586  					IPv6Enabled:               false,
  2587  					EnableIPForwarding:        false,
  2588  					SKU:                       nil,
  2589  					ClusterName:               "cluster",
  2590  					AdditionalTags: infrav1.Tags{
  2591  						"kubernetes.io_cluster_cluster": "owned",
  2592  					},
  2593  				},
  2594  			},
  2595  		},
  2596  		{
  2597  			name: "Node Machine with multiple Network Interfaces",
  2598  			machineScope: MachineScope{
  2599  				ClusterScoper: &ClusterScope{
  2600  					AzureClients: AzureClients{
  2601  						EnvironmentSettings: auth.EnvironmentSettings{
  2602  							Values: map[string]string{
  2603  								auth.SubscriptionID: "123",
  2604  							},
  2605  						},
  2606  					},
  2607  					Cluster: &clusterv1.Cluster{
  2608  						ObjectMeta: metav1.ObjectMeta{
  2609  							Name:      "cluster",
  2610  							Namespace: "default",
  2611  						},
  2612  					},
  2613  					AzureCluster: &infrav1.AzureCluster{
  2614  						ObjectMeta: metav1.ObjectMeta{
  2615  							Name:      "cluster",
  2616  							Namespace: "default",
  2617  							OwnerReferences: []metav1.OwnerReference{
  2618  								{
  2619  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2620  									Kind:       "Cluster",
  2621  									Name:       "cluster",
  2622  								},
  2623  							},
  2624  						},
  2625  						Spec: infrav1.AzureClusterSpec{
  2626  							ResourceGroup: "my-rg",
  2627  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2628  								Location: "westus",
  2629  							},
  2630  							NetworkSpec: infrav1.NetworkSpec{
  2631  								Vnet: infrav1.VnetSpec{
  2632  									Name:          "vnet1",
  2633  									ResourceGroup: "rg1",
  2634  								},
  2635  								Subnets: []infrav1.SubnetSpec{
  2636  									{
  2637  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2638  											Role: infrav1.SubnetNode,
  2639  											Name: "subnet1",
  2640  										},
  2641  									},
  2642  								},
  2643  								APIServerLB: infrav1.LoadBalancerSpec{
  2644  									Name: "api-lb",
  2645  								},
  2646  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2647  									Name: "outbound-lb",
  2648  									BackendPool: infrav1.BackendPool{
  2649  										Name: "outbound-lb-outboundBackendPool",
  2650  									},
  2651  								},
  2652  							},
  2653  						},
  2654  					},
  2655  				},
  2656  				AzureMachine: &infrav1.AzureMachine{
  2657  					ObjectMeta: metav1.ObjectMeta{
  2658  						Name: "machine",
  2659  					},
  2660  					Spec: infrav1.AzureMachineSpec{
  2661  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2662  						NetworkInterfaces: []infrav1.NetworkInterface{
  2663  							{
  2664  								SubnetName:            "subnet1",
  2665  								AcceleratedNetworking: ptr.To(true),
  2666  								PrivateIPConfigs:      1,
  2667  							},
  2668  							{
  2669  								SubnetName:            "subnet2",
  2670  								AcceleratedNetworking: ptr.To(true),
  2671  								PrivateIPConfigs:      2,
  2672  							},
  2673  						},
  2674  					},
  2675  				},
  2676  				Machine: &clusterv1.Machine{
  2677  					ObjectMeta: metav1.ObjectMeta{
  2678  						Name:   "machine",
  2679  						Labels: map[string]string{},
  2680  					},
  2681  				},
  2682  			},
  2683  			want: []azure.ResourceSpecGetter{
  2684  				&networkinterfaces.NICSpec{
  2685  					Name:                      "machine-name-nic-0",
  2686  					ResourceGroup:             "my-rg",
  2687  					Location:                  "westus",
  2688  					SubscriptionID:            "123",
  2689  					MachineName:               "machine-name",
  2690  					SubnetName:                "subnet1",
  2691  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2692  					VNetName:                  "vnet1",
  2693  					VNetResourceGroup:         "rg1",
  2694  					PublicLBName:              "outbound-lb",
  2695  					PublicLBAddressPoolName:   "outbound-lb-outboundBackendPool",
  2696  					PublicLBNATRuleName:       "",
  2697  					InternalLBName:            "",
  2698  					InternalLBAddressPoolName: "",
  2699  					PublicIPName:              "",
  2700  					AcceleratedNetworking:     ptr.To(true),
  2701  					IPv6Enabled:               false,
  2702  					EnableIPForwarding:        false,
  2703  					SKU:                       nil,
  2704  					ClusterName:               "cluster",
  2705  					AdditionalTags: map[string]string{
  2706  						"kubernetes.io_cluster_cluster": "owned",
  2707  					},
  2708  				},
  2709  				&networkinterfaces.NICSpec{
  2710  					Name:                      "machine-name-nic-1",
  2711  					ResourceGroup:             "my-rg",
  2712  					Location:                  "westus",
  2713  					SubscriptionID:            "123",
  2714  					MachineName:               "machine-name",
  2715  					SubnetName:                "subnet2",
  2716  					IPConfigs:                 []networkinterfaces.IPConfig{{}, {}},
  2717  					VNetName:                  "vnet1",
  2718  					VNetResourceGroup:         "rg1",
  2719  					PublicLBName:              "",
  2720  					PublicLBAddressPoolName:   "",
  2721  					PublicLBNATRuleName:       "",
  2722  					InternalLBName:            "",
  2723  					InternalLBAddressPoolName: "",
  2724  					PublicIPName:              "",
  2725  					AcceleratedNetworking:     ptr.To(true),
  2726  					IPv6Enabled:               false,
  2727  					EnableIPForwarding:        false,
  2728  					SKU:                       nil,
  2729  					ClusterName:               "cluster",
  2730  					AdditionalTags: map[string]string{
  2731  						"kubernetes.io_cluster_cluster": "owned",
  2732  					},
  2733  				},
  2734  			},
  2735  		},
  2736  		{
  2737  			name: "Node Machine with multiple Network Interfaces and Public IP Allocation enabled",
  2738  			machineScope: MachineScope{
  2739  				ClusterScoper: &ClusterScope{
  2740  					AzureClients: AzureClients{
  2741  						EnvironmentSettings: auth.EnvironmentSettings{
  2742  							Values: map[string]string{
  2743  								auth.SubscriptionID: "123",
  2744  							},
  2745  						},
  2746  					},
  2747  					Cluster: &clusterv1.Cluster{
  2748  						ObjectMeta: metav1.ObjectMeta{
  2749  							Name:      "cluster",
  2750  							Namespace: "default",
  2751  						},
  2752  					},
  2753  					AzureCluster: &infrav1.AzureCluster{
  2754  						ObjectMeta: metav1.ObjectMeta{
  2755  							Name:      "cluster",
  2756  							Namespace: "default",
  2757  							OwnerReferences: []metav1.OwnerReference{
  2758  								{
  2759  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2760  									Kind:       "Cluster",
  2761  									Name:       "cluster",
  2762  								},
  2763  							},
  2764  						},
  2765  						Spec: infrav1.AzureClusterSpec{
  2766  							ResourceGroup: "my-rg",
  2767  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2768  								Location: "westus",
  2769  							},
  2770  							NetworkSpec: infrav1.NetworkSpec{
  2771  								Vnet: infrav1.VnetSpec{
  2772  									Name:          "vnet1",
  2773  									ResourceGroup: "rg1",
  2774  								},
  2775  								Subnets: []infrav1.SubnetSpec{
  2776  									{
  2777  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2778  											Role: infrav1.SubnetNode,
  2779  											Name: "subnet1",
  2780  										},
  2781  									},
  2782  								},
  2783  								APIServerLB: infrav1.LoadBalancerSpec{
  2784  									Name: "api-lb",
  2785  								},
  2786  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2787  									Name: "outbound-lb",
  2788  								},
  2789  							},
  2790  						},
  2791  					},
  2792  				},
  2793  				AzureMachine: &infrav1.AzureMachine{
  2794  					ObjectMeta: metav1.ObjectMeta{
  2795  						Name: "machine",
  2796  					},
  2797  					Spec: infrav1.AzureMachineSpec{
  2798  						ProviderID:       ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2799  						AllocatePublicIP: true,
  2800  						NetworkInterfaces: []infrav1.NetworkInterface{
  2801  							{
  2802  								SubnetName:            "subnet1",
  2803  								AcceleratedNetworking: ptr.To(true),
  2804  								PrivateIPConfigs:      1,
  2805  							},
  2806  							{
  2807  								SubnetName:            "subnet2",
  2808  								AcceleratedNetworking: ptr.To(true),
  2809  								PrivateIPConfigs:      2,
  2810  							},
  2811  						},
  2812  					},
  2813  				},
  2814  				Machine: &clusterv1.Machine{
  2815  					ObjectMeta: metav1.ObjectMeta{
  2816  						Name:   "machine",
  2817  						Labels: map[string]string{},
  2818  					},
  2819  				},
  2820  			},
  2821  			want: []azure.ResourceSpecGetter{
  2822  				&networkinterfaces.NICSpec{
  2823  					Name:                      "machine-name-nic-0",
  2824  					ResourceGroup:             "my-rg",
  2825  					Location:                  "westus",
  2826  					SubscriptionID:            "123",
  2827  					MachineName:               "machine-name",
  2828  					SubnetName:                "subnet1",
  2829  					IPConfigs:                 []networkinterfaces.IPConfig{{}},
  2830  					VNetName:                  "vnet1",
  2831  					VNetResourceGroup:         "rg1",
  2832  					PublicLBName:              "",
  2833  					PublicLBAddressPoolName:   "",
  2834  					PublicLBNATRuleName:       "",
  2835  					InternalLBName:            "",
  2836  					InternalLBAddressPoolName: "",
  2837  					PublicIPName:              "pip-machine-name",
  2838  					AcceleratedNetworking:     ptr.To(true),
  2839  					IPv6Enabled:               false,
  2840  					EnableIPForwarding:        false,
  2841  					SKU:                       nil,
  2842  					ClusterName:               "cluster",
  2843  					AdditionalTags: map[string]string{
  2844  						"kubernetes.io_cluster_cluster": "owned",
  2845  					},
  2846  				},
  2847  				&networkinterfaces.NICSpec{
  2848  					Name:                      "machine-name-nic-1",
  2849  					ResourceGroup:             "my-rg",
  2850  					Location:                  "westus",
  2851  					SubscriptionID:            "123",
  2852  					MachineName:               "machine-name",
  2853  					SubnetName:                "subnet2",
  2854  					IPConfigs:                 []networkinterfaces.IPConfig{{}, {}},
  2855  					VNetName:                  "vnet1",
  2856  					VNetResourceGroup:         "rg1",
  2857  					PublicLBName:              "",
  2858  					PublicLBAddressPoolName:   "",
  2859  					PublicLBNATRuleName:       "",
  2860  					InternalLBName:            "",
  2861  					InternalLBAddressPoolName: "",
  2862  					PublicIPName:              "",
  2863  					AcceleratedNetworking:     ptr.To(true),
  2864  					IPv6Enabled:               false,
  2865  					EnableIPForwarding:        false,
  2866  					SKU:                       nil,
  2867  					ClusterName:               "cluster",
  2868  					AdditionalTags: map[string]string{
  2869  						"kubernetes.io_cluster_cluster": "owned",
  2870  					},
  2871  				},
  2872  			},
  2873  		},
  2874  		{
  2875  			name: "Node Machine with multiple IPConfigs",
  2876  			machineScope: MachineScope{
  2877  				ClusterScoper: &ClusterScope{
  2878  					AzureClients: AzureClients{
  2879  						EnvironmentSettings: auth.EnvironmentSettings{
  2880  							Values: map[string]string{
  2881  								auth.SubscriptionID: "123",
  2882  							},
  2883  						},
  2884  					},
  2885  					Cluster: &clusterv1.Cluster{
  2886  						ObjectMeta: metav1.ObjectMeta{
  2887  							Name:      "cluster",
  2888  							Namespace: "default",
  2889  						},
  2890  					},
  2891  					AzureCluster: &infrav1.AzureCluster{
  2892  						ObjectMeta: metav1.ObjectMeta{
  2893  							Name:      "cluster",
  2894  							Namespace: "default",
  2895  							OwnerReferences: []metav1.OwnerReference{
  2896  								{
  2897  									APIVersion: "cluster.x-k8s.io/v1beta1",
  2898  									Kind:       "Cluster",
  2899  									Name:       "cluster",
  2900  								},
  2901  							},
  2902  						},
  2903  						Spec: infrav1.AzureClusterSpec{
  2904  							ResourceGroup: "my-rg",
  2905  							AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2906  								Location: "westus",
  2907  							},
  2908  							NetworkSpec: infrav1.NetworkSpec{
  2909  								Vnet: infrav1.VnetSpec{
  2910  									Name:          "vnet1",
  2911  									ResourceGroup: "rg1",
  2912  								},
  2913  								Subnets: []infrav1.SubnetSpec{
  2914  									{
  2915  										SubnetClassSpec: infrav1.SubnetClassSpec{
  2916  											Role: infrav1.SubnetNode,
  2917  											Name: "subnet1",
  2918  										},
  2919  									},
  2920  								},
  2921  								APIServerLB: infrav1.LoadBalancerSpec{
  2922  									Name: "api-lb",
  2923  								},
  2924  								NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2925  									Name: "outbound-lb",
  2926  									BackendPool: infrav1.BackendPool{
  2927  										Name: "outbound-lb-outboundBackendPool",
  2928  									},
  2929  								},
  2930  							},
  2931  						},
  2932  					},
  2933  				},
  2934  				AzureMachine: &infrav1.AzureMachine{
  2935  					ObjectMeta: metav1.ObjectMeta{
  2936  						Name: "machine",
  2937  					},
  2938  					Spec: infrav1.AzureMachineSpec{
  2939  						ProviderID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/virtualMachines/machine-name"),
  2940  						NetworkInterfaces: []infrav1.NetworkInterface{
  2941  							{
  2942  								SubnetName:            "subnet1",
  2943  								AcceleratedNetworking: ptr.To(true),
  2944  								PrivateIPConfigs:      10,
  2945  							},
  2946  						},
  2947  					},
  2948  				},
  2949  				Machine: &clusterv1.Machine{
  2950  					ObjectMeta: metav1.ObjectMeta{
  2951  						Name:   "machine",
  2952  						Labels: map[string]string{},
  2953  					},
  2954  				},
  2955  			},
  2956  			want: []azure.ResourceSpecGetter{
  2957  				&networkinterfaces.NICSpec{
  2958  					Name:                      "machine-name-nic",
  2959  					ResourceGroup:             "my-rg",
  2960  					Location:                  "westus",
  2961  					SubscriptionID:            "123",
  2962  					MachineName:               "machine-name",
  2963  					SubnetName:                "subnet1",
  2964  					IPConfigs:                 []networkinterfaces.IPConfig{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
  2965  					VNetName:                  "vnet1",
  2966  					VNetResourceGroup:         "rg1",
  2967  					PublicLBName:              "outbound-lb",
  2968  					PublicLBAddressPoolName:   "outbound-lb-outboundBackendPool",
  2969  					PublicLBNATRuleName:       "",
  2970  					InternalLBName:            "",
  2971  					InternalLBAddressPoolName: "",
  2972  					PublicIPName:              "",
  2973  					AcceleratedNetworking:     ptr.To(true),
  2974  					IPv6Enabled:               false,
  2975  					EnableIPForwarding:        false,
  2976  					SKU:                       nil,
  2977  					ClusterName:               "cluster",
  2978  					AdditionalTags: map[string]string{
  2979  						"kubernetes.io_cluster_cluster": "owned",
  2980  					},
  2981  				},
  2982  			},
  2983  		},
  2984  	}
  2985  	for _, tt := range tests {
  2986  		t.Run(tt.name, func(t *testing.T) {
  2987  			g := NewWithT(t)
  2988  			gotNicSpecs := tt.machineScope.NICSpecs()
  2989  			if !reflect.DeepEqual(gotNicSpecs, tt.want) {
  2990  				g.Expect(gotNicSpecs).To(BeEquivalentTo(tt.want))
  2991  				t.Errorf("NICSpecs(), gotNicSpecs = %s, want %s", specArrayToString(gotNicSpecs), specArrayToString(tt.want))
  2992  			}
  2993  		})
  2994  	}
  2995  }
  2996  
  2997  func TestDiskSpecs(t *testing.T) {
  2998  	testcases := []struct {
  2999  		name         string
  3000  		machineScope MachineScope
  3001  		want         []azure.ResourceSpecGetter
  3002  	}{
  3003  		{
  3004  			name: "only os disk",
  3005  			machineScope: MachineScope{
  3006  				ClusterScoper: &ClusterScope{
  3007  					Cluster: &clusterv1.Cluster{
  3008  						ObjectMeta: metav1.ObjectMeta{
  3009  							Name: "cluster",
  3010  						},
  3011  					},
  3012  					AzureCluster: &infrav1.AzureCluster{
  3013  						ObjectMeta: metav1.ObjectMeta{
  3014  							Name: "cluster",
  3015  						},
  3016  						Spec: infrav1.AzureClusterSpec{
  3017  							ResourceGroup: "my-rg",
  3018  						},
  3019  					},
  3020  				},
  3021  				AzureMachine: &infrav1.AzureMachine{
  3022  					ObjectMeta: metav1.ObjectMeta{
  3023  						Name: "my-azure-machine",
  3024  					},
  3025  					Spec: infrav1.AzureMachineSpec{
  3026  						OSDisk: infrav1.OSDisk{
  3027  							DiskSizeGB: ptr.To[int32](30),
  3028  							OSType:     "Linux",
  3029  						},
  3030  					},
  3031  				},
  3032  				Machine: &clusterv1.Machine{
  3033  					ObjectMeta: metav1.ObjectMeta{
  3034  						Name: "machine",
  3035  					},
  3036  				},
  3037  			},
  3038  			want: []azure.ResourceSpecGetter{
  3039  				&disks.DiskSpec{
  3040  					Name:          "my-azure-machine_OSDisk",
  3041  					ResourceGroup: "my-rg",
  3042  				},
  3043  			},
  3044  		},
  3045  		{
  3046  			name: "os and data disks",
  3047  			machineScope: MachineScope{
  3048  				ClusterScoper: &ClusterScope{
  3049  					Cluster: &clusterv1.Cluster{
  3050  						ObjectMeta: metav1.ObjectMeta{
  3051  							Name: "cluster",
  3052  						},
  3053  					},
  3054  					AzureCluster: &infrav1.AzureCluster{
  3055  						ObjectMeta: metav1.ObjectMeta{
  3056  							Name: "cluster",
  3057  						},
  3058  						Spec: infrav1.AzureClusterSpec{
  3059  							ResourceGroup: "my-rg",
  3060  						},
  3061  					},
  3062  				},
  3063  				AzureMachine: &infrav1.AzureMachine{
  3064  					ObjectMeta: metav1.ObjectMeta{
  3065  						Name: "my-azure-machine",
  3066  					},
  3067  					Spec: infrav1.AzureMachineSpec{
  3068  						OSDisk: infrav1.OSDisk{
  3069  							DiskSizeGB: ptr.To[int32](30),
  3070  							OSType:     "Linux",
  3071  						},
  3072  						DataDisks: []infrav1.DataDisk{
  3073  							{
  3074  								NameSuffix: "etcddisk",
  3075  							},
  3076  						},
  3077  					},
  3078  				},
  3079  				Machine: &clusterv1.Machine{
  3080  					ObjectMeta: metav1.ObjectMeta{
  3081  						Name: "machine",
  3082  					},
  3083  				},
  3084  			},
  3085  			want: []azure.ResourceSpecGetter{
  3086  				&disks.DiskSpec{
  3087  					Name:          "my-azure-machine_OSDisk",
  3088  					ResourceGroup: "my-rg",
  3089  				},
  3090  				&disks.DiskSpec{
  3091  					Name:          "my-azure-machine_etcddisk",
  3092  					ResourceGroup: "my-rg",
  3093  				},
  3094  			},
  3095  		}, {
  3096  			name: "os and multiple data disks",
  3097  			machineScope: MachineScope{
  3098  				ClusterScoper: &ClusterScope{
  3099  					Cluster: &clusterv1.Cluster{
  3100  						ObjectMeta: metav1.ObjectMeta{
  3101  							Name: "cluster",
  3102  						},
  3103  					},
  3104  					AzureCluster: &infrav1.AzureCluster{
  3105  						ObjectMeta: metav1.ObjectMeta{
  3106  							Name: "cluster",
  3107  						},
  3108  						Spec: infrav1.AzureClusterSpec{
  3109  							ResourceGroup: "my-rg",
  3110  						},
  3111  					},
  3112  				},
  3113  				AzureMachine: &infrav1.AzureMachine{
  3114  					ObjectMeta: metav1.ObjectMeta{
  3115  						Name: "my-azure-machine",
  3116  					},
  3117  					Spec: infrav1.AzureMachineSpec{
  3118  						OSDisk: infrav1.OSDisk{
  3119  							DiskSizeGB: ptr.To[int32](30),
  3120  							OSType:     "Linux",
  3121  						},
  3122  						DataDisks: []infrav1.DataDisk{
  3123  							{
  3124  								NameSuffix: "etcddisk",
  3125  							},
  3126  							{
  3127  								NameSuffix: "otherdisk",
  3128  							},
  3129  						},
  3130  					},
  3131  				},
  3132  				Machine: &clusterv1.Machine{
  3133  					ObjectMeta: metav1.ObjectMeta{
  3134  						Name: "machine",
  3135  					},
  3136  				},
  3137  			},
  3138  			want: []azure.ResourceSpecGetter{
  3139  				&disks.DiskSpec{
  3140  					Name:          "my-azure-machine_OSDisk",
  3141  					ResourceGroup: "my-rg",
  3142  				},
  3143  				&disks.DiskSpec{
  3144  					Name:          "my-azure-machine_etcddisk",
  3145  					ResourceGroup: "my-rg",
  3146  				},
  3147  				&disks.DiskSpec{
  3148  					Name:          "my-azure-machine_otherdisk",
  3149  					ResourceGroup: "my-rg",
  3150  				},
  3151  			},
  3152  		},
  3153  	}
  3154  
  3155  	for _, tt := range testcases {
  3156  		tt := tt
  3157  		t.Run(tt.name, func(t *testing.T) {
  3158  			g := NewWithT(t)
  3159  
  3160  			t.Parallel()
  3161  			result := tt.machineScope.DiskSpecs()
  3162  			g.Expect(result).To(BeEquivalentTo(tt.want))
  3163  		})
  3164  	}
  3165  }
  3166  
  3167  func TestMachineScope_GetCapacityReservationGroupID(t *testing.T) {
  3168  	tests := []struct {
  3169  		name         string
  3170  		machineScope MachineScope
  3171  		want         string
  3172  	}{
  3173  		{
  3174  			name: "returns the entire capacity reservation group ID",
  3175  			machineScope: MachineScope{
  3176  				AzureMachine: &infrav1.AzureMachine{
  3177  					ObjectMeta: metav1.ObjectMeta{
  3178  						Name: "machine-name",
  3179  					},
  3180  					Spec: infrav1.AzureMachineSpec{
  3181  						CapacityReservationGroupID: ptr.To("azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/capacityReservationGroups/capacity-reservation-group-name"),
  3182  					},
  3183  				},
  3184  			},
  3185  			want: "azure:///subscriptions/1234-5678/resourceGroups/my-cluster/providers/Microsoft.Compute/capacityReservationGroups/capacity-reservation-group-name",
  3186  		},
  3187  		{
  3188  			name: "returns empty if capacity reservation group ID is empty",
  3189  			machineScope: MachineScope{
  3190  				AzureMachine: &infrav1.AzureMachine{
  3191  					ObjectMeta: metav1.ObjectMeta{
  3192  						Name: "machine-name",
  3193  					},
  3194  					Spec: infrav1.AzureMachineSpec{
  3195  						CapacityReservationGroupID: ptr.To(""),
  3196  					},
  3197  				},
  3198  			},
  3199  			want: "",
  3200  		},
  3201  	}
  3202  	for _, tt := range tests {
  3203  		t.Run(tt.name, func(t *testing.T) {
  3204  			got := tt.machineScope.GetCapacityReservationGroupID()
  3205  			if got != tt.want {
  3206  				t.Errorf("MachineScope.GetCapacityReservationGroupID() = %v, want %v", got, tt.want)
  3207  			}
  3208  		})
  3209  	}
  3210  }