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

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package scope
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"strings"
    24  	"testing"
    25  
    26  	asonetworkv1api20201101 "github.com/Azure/azure-service-operator/v2/api/network/v1api20201101"
    27  	asonetworkv1api20220701 "github.com/Azure/azure-service-operator/v2/api/network/v1api20220701"
    28  	asoresourcesv1 "github.com/Azure/azure-service-operator/v2/api/resources/v1api20200601"
    29  	"github.com/Azure/go-autorest/autorest/azure/auth"
    30  	"github.com/google/go-cmp/cmp"
    31  	. "github.com/onsi/gomega"
    32  	corev1 "k8s.io/api/core/v1"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/runtime"
    35  	"k8s.io/utils/ptr"
    36  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    37  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    38  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/bastionhosts"
    39  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/groups"
    40  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/loadbalancers"
    41  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/natgateways"
    42  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/privateendpoints"
    43  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/publicips"
    44  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/routetables"
    45  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/securitygroups"
    46  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/subnets"
    47  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/vnetpeerings"
    48  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    49  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    50  )
    51  
    52  const fakeClientID = "fake-client-id"
    53  const fakeTenantID = "fake-tenant-id"
    54  
    55  func specToString(spec any) string {
    56  	var sb strings.Builder
    57  	sb.WriteString("{ ")
    58  	sb.WriteString(fmt.Sprintf("%+v ", spec))
    59  	sb.WriteString("}")
    60  	return sb.String()
    61  }
    62  
    63  func specArrayToString[T any](specs []T) string {
    64  	var sb strings.Builder
    65  	sb.WriteString("[\n")
    66  	for _, spec := range specs {
    67  		sb.WriteString(fmt.Sprintf("\t%+v\n", specToString(spec)))
    68  	}
    69  	sb.WriteString("]")
    70  
    71  	return sb.String()
    72  }
    73  
    74  func TestAPIServerHost(t *testing.T) {
    75  	fakeSubscriptionID := "123"
    76  
    77  	tests := []struct {
    78  		name         string
    79  		azureCluster infrav1.AzureCluster
    80  		want         string
    81  	}{
    82  		{
    83  			name: "public apiserver lb (user-defined dns)",
    84  			azureCluster: infrav1.AzureCluster{
    85  				Spec: infrav1.AzureClusterSpec{
    86  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
    87  						SubscriptionID: fakeSubscriptionID,
    88  						IdentityRef: &corev1.ObjectReference{
    89  							Kind: infrav1.AzureClusterIdentityKind,
    90  						},
    91  					},
    92  					NetworkSpec: infrav1.NetworkSpec{
    93  						APIServerLB: infrav1.LoadBalancerSpec{
    94  							FrontendIPs: []infrav1.FrontendIP{
    95  								{
    96  									PublicIP: &infrav1.PublicIPSpec{
    97  										DNSName: "my-cluster-apiserver.example.com",
    98  									},
    99  								},
   100  							},
   101  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   102  								Type: infrav1.Public,
   103  							},
   104  						},
   105  					},
   106  				},
   107  			},
   108  			want: "my-cluster-apiserver.example.com",
   109  		},
   110  		{
   111  			name: "private apiserver lb (default private dns zone)",
   112  			azureCluster: infrav1.AzureCluster{
   113  				Spec: infrav1.AzureClusterSpec{
   114  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   115  						SubscriptionID: fakeSubscriptionID,
   116  						IdentityRef: &corev1.ObjectReference{
   117  							Kind: infrav1.AzureClusterIdentityKind,
   118  						},
   119  					},
   120  					NetworkSpec: infrav1.NetworkSpec{
   121  						APIServerLB: infrav1.LoadBalancerSpec{
   122  							FrontendIPs: []infrav1.FrontendIP{
   123  								{
   124  									PublicIP: &infrav1.PublicIPSpec{
   125  										DNSName: "my-cluster-apiserver.capz.io",
   126  									},
   127  								},
   128  							},
   129  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   130  								Type: infrav1.Public,
   131  							},
   132  						},
   133  					},
   134  				},
   135  			},
   136  			want: "my-cluster-apiserver.capz.io",
   137  		},
   138  		{
   139  			name: "private apiserver (user-defined private dns zone)",
   140  			azureCluster: infrav1.AzureCluster{
   141  				Spec: infrav1.AzureClusterSpec{
   142  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   143  						SubscriptionID: fakeSubscriptionID,
   144  						IdentityRef: &corev1.ObjectReference{
   145  							Kind: infrav1.AzureClusterIdentityKind,
   146  						},
   147  					},
   148  					NetworkSpec: infrav1.NetworkSpec{
   149  						NetworkClassSpec: infrav1.NetworkClassSpec{
   150  							PrivateDNSZoneName: "example.private",
   151  						},
   152  						APIServerLB: infrav1.LoadBalancerSpec{
   153  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   154  								Type: infrav1.Internal,
   155  							},
   156  						},
   157  					},
   158  				},
   159  			},
   160  			want: "apiserver.example.private",
   161  		},
   162  	}
   163  
   164  	for _, tc := range tests {
   165  		tc := tc
   166  		g := NewWithT(t)
   167  		scheme := runtime.NewScheme()
   168  		_ = clusterv1.AddToScheme(scheme)
   169  		_ = infrav1.AddToScheme(scheme)
   170  		_ = corev1.AddToScheme(scheme)
   171  
   172  		cluster := &clusterv1.Cluster{
   173  			ObjectMeta: metav1.ObjectMeta{
   174  				Name:      "my-cluster",
   175  				Namespace: "default",
   176  			},
   177  		}
   178  
   179  		tc.azureCluster.ObjectMeta = metav1.ObjectMeta{
   180  			Name: cluster.Name,
   181  			OwnerReferences: []metav1.OwnerReference{
   182  				{
   183  					APIVersion: "cluster.x-k8s.io/v1beta1",
   184  					Kind:       "Cluster",
   185  					Name:       "my-cluster",
   186  				},
   187  			},
   188  		}
   189  		tc.azureCluster.Default()
   190  
   191  		fakeIdentity := &infrav1.AzureClusterIdentity{
   192  			Spec: infrav1.AzureClusterIdentitySpec{
   193  				Type:     infrav1.ServicePrincipal,
   194  				ClientID: fakeClientID,
   195  				TenantID: fakeTenantID,
   196  			},
   197  		}
   198  		fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
   199  
   200  		initObjects := []runtime.Object{cluster, &tc.azureCluster, fakeIdentity, fakeSecret}
   201  		fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
   202  
   203  		clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
   204  			Cluster:      cluster,
   205  			AzureCluster: &tc.azureCluster,
   206  			Client:       fakeClient,
   207  		})
   208  		g.Expect(err).NotTo(HaveOccurred())
   209  
   210  		g.Expect(clusterScope.APIServerHost()).Should(Equal(tc.want))
   211  	}
   212  }
   213  
   214  func TestGettingSecurityRules(t *testing.T) {
   215  	g := NewWithT(t)
   216  	scheme := runtime.NewScheme()
   217  	_ = clusterv1.AddToScheme(scheme)
   218  	_ = infrav1.AddToScheme(scheme)
   219  	_ = corev1.AddToScheme(scheme)
   220  
   221  	cluster := &clusterv1.Cluster{
   222  		ObjectMeta: metav1.ObjectMeta{
   223  			Name:      "my-cluster",
   224  			Namespace: "default",
   225  		},
   226  	}
   227  
   228  	azureCluster := &infrav1.AzureCluster{
   229  		ObjectMeta: metav1.ObjectMeta{
   230  			Name: "my-azure-cluster",
   231  			OwnerReferences: []metav1.OwnerReference{
   232  				{
   233  					APIVersion: "cluster.x-k8s.io/v1beta1",
   234  					Kind:       "Cluster",
   235  					Name:       "my-cluster",
   236  				},
   237  			},
   238  		},
   239  		Spec: infrav1.AzureClusterSpec{
   240  			AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   241  				SubscriptionID: "123",
   242  				IdentityRef: &corev1.ObjectReference{
   243  					Kind: infrav1.AzureClusterIdentityKind,
   244  				},
   245  			},
   246  			NetworkSpec: infrav1.NetworkSpec{
   247  				Subnets: infrav1.Subnets{
   248  					{
   249  						SubnetClassSpec: infrav1.SubnetClassSpec{
   250  							Role: infrav1.SubnetNode,
   251  							Name: "node",
   252  						},
   253  					},
   254  				},
   255  			},
   256  		},
   257  	}
   258  	azureCluster.Default()
   259  
   260  	fakeIdentity := &infrav1.AzureClusterIdentity{
   261  		Spec: infrav1.AzureClusterIdentitySpec{
   262  			Type:     infrav1.ServicePrincipal,
   263  			ClientID: fakeClientID,
   264  			TenantID: fakeTenantID,
   265  		},
   266  	}
   267  	fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
   268  
   269  	initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
   270  	fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
   271  
   272  	clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
   273  		Cluster:      cluster,
   274  		AzureCluster: azureCluster,
   275  		Client:       fakeClient,
   276  	})
   277  	g.Expect(err).NotTo(HaveOccurred())
   278  
   279  	clusterScope.SetControlPlaneSecurityRules()
   280  
   281  	subnet, err := clusterScope.AzureCluster.Spec.NetworkSpec.GetControlPlaneSubnet()
   282  	g.Expect(err).NotTo(HaveOccurred())
   283  	g.Expect(subnet.SecurityGroup.SecurityRules).To(HaveLen(2))
   284  }
   285  
   286  func TestPublicIPSpecs(t *testing.T) {
   287  	tests := []struct {
   288  		name                 string
   289  		azureCluster         *infrav1.AzureCluster
   290  		expectedPublicIPSpec []azure.ResourceSpecGetter
   291  	}{
   292  		{
   293  			name: "Azure cluster with internal type LB and nil frontend IP count",
   294  			azureCluster: &infrav1.AzureCluster{
   295  				ObjectMeta: metav1.ObjectMeta{
   296  					Name: "my-cluster",
   297  					OwnerReferences: []metav1.OwnerReference{
   298  						{
   299  							APIVersion: "cluster.x-k8s.io/v1beta1",
   300  							Kind:       "Cluster",
   301  							Name:       "my-cluster",
   302  						},
   303  					},
   304  				},
   305  				Status: infrav1.AzureClusterStatus{
   306  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   307  						"failure-domain-id-1": {},
   308  						"failure-domain-id-2": {},
   309  						"failure-domain-id-3": {},
   310  					},
   311  				},
   312  				Spec: infrav1.AzureClusterSpec{
   313  					ResourceGroup: "my-rg",
   314  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   315  						SubscriptionID: "123",
   316  						Location:       "centralIndia",
   317  						AdditionalTags: infrav1.Tags{
   318  							"Name": "my-publicip-ipv6",
   319  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   320  						},
   321  						IdentityRef: &corev1.ObjectReference{
   322  							Kind: infrav1.AzureClusterIdentityKind,
   323  						},
   324  					},
   325  					NetworkSpec: infrav1.NetworkSpec{
   326  						APIServerLB: infrav1.LoadBalancerSpec{
   327  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   328  								Type: infrav1.Internal,
   329  							},
   330  						},
   331  					},
   332  				},
   333  			},
   334  			expectedPublicIPSpec: nil,
   335  		},
   336  		{
   337  			name: "Azure cluster with internal type LB and 0 frontend IP count",
   338  			azureCluster: &infrav1.AzureCluster{
   339  				ObjectMeta: metav1.ObjectMeta{
   340  					Name: "my-cluster",
   341  					OwnerReferences: []metav1.OwnerReference{
   342  						{
   343  							APIVersion: "cluster.x-k8s.io/v1beta1",
   344  							Kind:       "Cluster",
   345  							Name:       "my-cluster",
   346  						},
   347  					},
   348  				},
   349  				Status: infrav1.AzureClusterStatus{
   350  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   351  						"failure-domain-id-1": {},
   352  						"failure-domain-id-2": {},
   353  						"failure-domain-id-3": {},
   354  					},
   355  				},
   356  				Spec: infrav1.AzureClusterSpec{
   357  					ResourceGroup: "my-rg",
   358  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   359  						SubscriptionID: "123",
   360  						Location:       "centralIndia",
   361  						AdditionalTags: infrav1.Tags{
   362  							"Name": "my-publicip-ipv6",
   363  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   364  						},
   365  						IdentityRef: &corev1.ObjectReference{
   366  							Kind: infrav1.AzureClusterIdentityKind,
   367  						},
   368  					},
   369  					NetworkSpec: infrav1.NetworkSpec{
   370  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
   371  							FrontendIPsCount: ptr.To[int32](0),
   372  						},
   373  						APIServerLB: infrav1.LoadBalancerSpec{
   374  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   375  								Type: infrav1.Internal,
   376  							},
   377  						},
   378  					},
   379  				},
   380  			},
   381  			expectedPublicIPSpec: nil,
   382  		},
   383  		{
   384  			name: "Azure cluster with internal type apiserver LB and 1 frontend IP count",
   385  			azureCluster: &infrav1.AzureCluster{
   386  				ObjectMeta: metav1.ObjectMeta{
   387  					Name: "my-cluster",
   388  					OwnerReferences: []metav1.OwnerReference{
   389  						{
   390  							APIVersion: "cluster.x-k8s.io/v1beta1",
   391  							Kind:       "Cluster",
   392  							Name:       "my-cluster",
   393  						},
   394  					},
   395  				},
   396  				Status: infrav1.AzureClusterStatus{
   397  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   398  						"failure-domain-id-1": {},
   399  						"failure-domain-id-2": {},
   400  						"failure-domain-id-3": {},
   401  					},
   402  				},
   403  				Spec: infrav1.AzureClusterSpec{
   404  					ResourceGroup: "my-rg",
   405  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   406  						SubscriptionID: "123",
   407  						Location:       "centralIndia",
   408  						AdditionalTags: infrav1.Tags{
   409  							"Name": "my-publicip-ipv6",
   410  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   411  						},
   412  						IdentityRef: &corev1.ObjectReference{
   413  							Kind: infrav1.AzureClusterIdentityKind,
   414  						},
   415  					},
   416  					NetworkSpec: infrav1.NetworkSpec{
   417  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
   418  							FrontendIPsCount: ptr.To[int32](1),
   419  							FrontendIPs: []infrav1.FrontendIP{
   420  								{
   421  									Name: "my-frontend-ip",
   422  									PublicIP: &infrav1.PublicIPSpec{
   423  										Name: "pip-my-cluster-controlplane-outbound",
   424  									},
   425  								},
   426  							},
   427  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   428  						},
   429  						APIServerLB: infrav1.LoadBalancerSpec{
   430  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   431  								Type: infrav1.Internal,
   432  							},
   433  						},
   434  					},
   435  				},
   436  			},
   437  			expectedPublicIPSpec: []azure.ResourceSpecGetter{
   438  				&publicips.PublicIPSpec{
   439  					Name:           "pip-my-cluster-controlplane-outbound",
   440  					ResourceGroup:  "my-rg",
   441  					DNSName:        "",
   442  					IsIPv6:         false,
   443  					ClusterName:    "my-cluster",
   444  					Location:       "centralIndia",
   445  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   446  					AdditionalTags: infrav1.Tags{
   447  						"Name": "my-publicip-ipv6",
   448  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   449  					},
   450  				},
   451  			},
   452  		},
   453  		{
   454  			name: "Azure cluster with internal type apiserver LB and many frontend IP count",
   455  			azureCluster: &infrav1.AzureCluster{
   456  				ObjectMeta: metav1.ObjectMeta{
   457  					Name: "my-cluster",
   458  					OwnerReferences: []metav1.OwnerReference{
   459  						{
   460  							APIVersion: "cluster.x-k8s.io/v1beta1",
   461  							Kind:       "Cluster",
   462  							Name:       "my-cluster",
   463  						},
   464  					},
   465  				},
   466  				Status: infrav1.AzureClusterStatus{
   467  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   468  						"failure-domain-id-1": {},
   469  						"failure-domain-id-2": {},
   470  						"failure-domain-id-3": {},
   471  					},
   472  				},
   473  				Spec: infrav1.AzureClusterSpec{
   474  					ResourceGroup: "my-rg",
   475  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   476  						SubscriptionID: "123",
   477  						Location:       "centralIndia",
   478  						AdditionalTags: infrav1.Tags{
   479  							"Name": "my-publicip-ipv6",
   480  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   481  						},
   482  						IdentityRef: &corev1.ObjectReference{
   483  							Kind: infrav1.AzureClusterIdentityKind,
   484  						},
   485  					},
   486  					NetworkSpec: infrav1.NetworkSpec{
   487  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
   488  							FrontendIPsCount: ptr.To[int32](3),
   489  							FrontendIPs: []infrav1.FrontendIP{
   490  								{
   491  									Name: "my-frontend-ip-1",
   492  									PublicIP: &infrav1.PublicIPSpec{
   493  										Name: "pip-my-cluster-controlplane-outbound-1",
   494  									},
   495  								},
   496  								{
   497  									Name: "my-frontend-ip-2",
   498  									PublicIP: &infrav1.PublicIPSpec{
   499  										Name: "pip-my-cluster-controlplane-outbound-2",
   500  									},
   501  								},
   502  								{
   503  									Name: "my-frontend-ip-3",
   504  									PublicIP: &infrav1.PublicIPSpec{
   505  										Name: "pip-my-cluster-controlplane-outbound-3",
   506  									},
   507  								},
   508  							},
   509  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   510  						},
   511  						APIServerLB: infrav1.LoadBalancerSpec{
   512  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
   513  								Type: infrav1.Internal,
   514  							},
   515  						},
   516  					},
   517  				},
   518  			},
   519  			expectedPublicIPSpec: []azure.ResourceSpecGetter{
   520  				&publicips.PublicIPSpec{
   521  					Name:           "pip-my-cluster-controlplane-outbound-1",
   522  					ResourceGroup:  "my-rg",
   523  					DNSName:        "",
   524  					IsIPv6:         false,
   525  					ClusterName:    "my-cluster",
   526  					Location:       "centralIndia",
   527  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   528  					AdditionalTags: infrav1.Tags{
   529  						"Name": "my-publicip-ipv6",
   530  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   531  					},
   532  				},
   533  				&publicips.PublicIPSpec{
   534  					Name:           "pip-my-cluster-controlplane-outbound-2",
   535  					ResourceGroup:  "my-rg",
   536  					DNSName:        "",
   537  					IsIPv6:         false,
   538  					ClusterName:    "my-cluster",
   539  					Location:       "centralIndia",
   540  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   541  					AdditionalTags: infrav1.Tags{
   542  						"Name": "my-publicip-ipv6",
   543  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   544  					},
   545  				},
   546  				&publicips.PublicIPSpec{
   547  					Name:           "pip-my-cluster-controlplane-outbound-3",
   548  					ResourceGroup:  "my-rg",
   549  					DNSName:        "",
   550  					IsIPv6:         false,
   551  					ClusterName:    "my-cluster",
   552  					Location:       "centralIndia",
   553  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   554  					AdditionalTags: infrav1.Tags{
   555  						"Name": "my-publicip-ipv6",
   556  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   557  					},
   558  				},
   559  			},
   560  		},
   561  		{
   562  			name: "Azure cluster with public type apiserver LB",
   563  			azureCluster: &infrav1.AzureCluster{
   564  				ObjectMeta: metav1.ObjectMeta{
   565  					Name: "my-cluster",
   566  					OwnerReferences: []metav1.OwnerReference{
   567  						{
   568  							APIVersion: "cluster.x-k8s.io/v1beta1",
   569  							Kind:       "Cluster",
   570  							Name:       "my-cluster",
   571  						},
   572  					},
   573  				},
   574  				Status: infrav1.AzureClusterStatus{
   575  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   576  						"failure-domain-id-1": {},
   577  						"failure-domain-id-2": {},
   578  						"failure-domain-id-3": {},
   579  					},
   580  				},
   581  				Spec: infrav1.AzureClusterSpec{
   582  					ResourceGroup: "my-rg",
   583  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   584  						SubscriptionID: "123",
   585  						Location:       "centralIndia",
   586  						AdditionalTags: infrav1.Tags{
   587  							"Name": "my-publicip-ipv6",
   588  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   589  						},
   590  						IdentityRef: &corev1.ObjectReference{
   591  							Kind: infrav1.AzureClusterIdentityKind,
   592  						},
   593  					},
   594  					NetworkSpec: infrav1.NetworkSpec{
   595  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
   596  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   597  						},
   598  						APIServerLB: infrav1.LoadBalancerSpec{
   599  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   600  							FrontendIPs: []infrav1.FrontendIP{
   601  								{
   602  									PublicIP: &infrav1.PublicIPSpec{
   603  										Name:    "40.60.89.22",
   604  										DNSName: "fake-dns",
   605  									},
   606  								},
   607  							},
   608  						},
   609  					},
   610  				},
   611  			},
   612  			expectedPublicIPSpec: []azure.ResourceSpecGetter{
   613  				&publicips.PublicIPSpec{
   614  					Name:           "40.60.89.22",
   615  					ResourceGroup:  "my-rg",
   616  					DNSName:        "fake-dns",
   617  					IsIPv6:         false,
   618  					ClusterName:    "my-cluster",
   619  					Location:       "centralIndia",
   620  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   621  					AdditionalTags: infrav1.Tags{
   622  						"Name": "my-publicip-ipv6",
   623  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   624  					},
   625  				},
   626  			},
   627  		},
   628  		{
   629  			name: "Azure cluster with public type apiserver LB and public node outbound lb",
   630  			azureCluster: &infrav1.AzureCluster{
   631  				ObjectMeta: metav1.ObjectMeta{
   632  					Name: "my-cluster",
   633  					OwnerReferences: []metav1.OwnerReference{
   634  						{
   635  							APIVersion: "cluster.x-k8s.io/v1beta1",
   636  							Kind:       "Cluster",
   637  							Name:       "my-cluster",
   638  						},
   639  					},
   640  				},
   641  				Status: infrav1.AzureClusterStatus{
   642  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   643  						"failure-domain-id-1": {},
   644  						"failure-domain-id-2": {},
   645  						"failure-domain-id-3": {},
   646  					},
   647  				},
   648  				Spec: infrav1.AzureClusterSpec{
   649  					ResourceGroup: "my-rg",
   650  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   651  						SubscriptionID: "123",
   652  						Location:       "centralIndia",
   653  						AdditionalTags: infrav1.Tags{
   654  							"Name": "my-publicip-ipv6",
   655  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   656  						},
   657  						IdentityRef: &corev1.ObjectReference{
   658  							Kind: infrav1.AzureClusterIdentityKind,
   659  						},
   660  					},
   661  					NetworkSpec: infrav1.NetworkSpec{
   662  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
   663  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   664  						},
   665  						NodeOutboundLB: &infrav1.LoadBalancerSpec{
   666  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   667  						},
   668  						APIServerLB: infrav1.LoadBalancerSpec{
   669  							FrontendIPs: []infrav1.FrontendIP{
   670  								{
   671  									PublicIP: &infrav1.PublicIPSpec{
   672  										Name:    "40.60.89.22",
   673  										DNSName: "fake-dns",
   674  									},
   675  								},
   676  							},
   677  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   678  						},
   679  					},
   680  				},
   681  			},
   682  			expectedPublicIPSpec: []azure.ResourceSpecGetter{
   683  				&publicips.PublicIPSpec{
   684  					Name:           "40.60.89.22",
   685  					ResourceGroup:  "my-rg",
   686  					DNSName:        "fake-dns",
   687  					IsIPv6:         false,
   688  					ClusterName:    "my-cluster",
   689  					Location:       "centralIndia",
   690  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   691  					AdditionalTags: infrav1.Tags{
   692  						"Name": "my-publicip-ipv6",
   693  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   694  					},
   695  				},
   696  			},
   697  		},
   698  		{
   699  			name: "Azure cluster with public type apiserver LB and public node outbound lb, NAT gateways and bastions",
   700  			azureCluster: &infrav1.AzureCluster{
   701  				ObjectMeta: metav1.ObjectMeta{
   702  					Name: "my-cluster",
   703  					OwnerReferences: []metav1.OwnerReference{
   704  						{
   705  							APIVersion: "cluster.x-k8s.io/v1beta1",
   706  							Kind:       "Cluster",
   707  							Name:       "my-cluster",
   708  						},
   709  					},
   710  				},
   711  				Status: infrav1.AzureClusterStatus{
   712  					FailureDomains: map[string]clusterv1.FailureDomainSpec{
   713  						"failure-domain-id-1": {},
   714  						"failure-domain-id-2": {},
   715  						"failure-domain-id-3": {},
   716  					},
   717  				},
   718  				Spec: infrav1.AzureClusterSpec{
   719  					ResourceGroup: "my-rg",
   720  					BastionSpec: infrav1.BastionSpec{
   721  						AzureBastion: &infrav1.AzureBastion{
   722  							PublicIP: infrav1.PublicIPSpec{
   723  								Name:    "fake-bastion-public-ip",
   724  								DNSName: "fake-bastion-dns-name",
   725  							},
   726  						},
   727  					},
   728  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   729  						SubscriptionID: "123",
   730  						Location:       "centralIndia",
   731  						AdditionalTags: infrav1.Tags{
   732  							"Name": "my-publicip-ipv6",
   733  							"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   734  						},
   735  						IdentityRef: &corev1.ObjectReference{
   736  							Kind: infrav1.AzureClusterIdentityKind,
   737  						},
   738  					},
   739  					NetworkSpec: infrav1.NetworkSpec{
   740  						Subnets: infrav1.Subnets{
   741  							infrav1.SubnetSpec{
   742  								SubnetClassSpec: infrav1.SubnetClassSpec{
   743  									Role: infrav1.SubnetNode,
   744  								},
   745  								NatGateway: infrav1.NatGateway{
   746  									NatGatewayIP: infrav1.PublicIPSpec{
   747  										Name:    "fake-public-ip",
   748  										DNSName: "fake-dns-name",
   749  									},
   750  								},
   751  							},
   752  						},
   753  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
   754  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   755  						},
   756  						NodeOutboundLB: &infrav1.LoadBalancerSpec{
   757  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   758  						},
   759  						APIServerLB: infrav1.LoadBalancerSpec{
   760  							FrontendIPs: []infrav1.FrontendIP{
   761  								{
   762  									PublicIP: &infrav1.PublicIPSpec{
   763  										Name:    "40.60.89.22",
   764  										DNSName: "fake-dns",
   765  									},
   766  								},
   767  							},
   768  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{},
   769  						},
   770  					},
   771  				},
   772  			},
   773  			expectedPublicIPSpec: []azure.ResourceSpecGetter{
   774  				&publicips.PublicIPSpec{
   775  					Name:           "40.60.89.22",
   776  					ResourceGroup:  "my-rg",
   777  					DNSName:        "fake-dns",
   778  					IsIPv6:         false,
   779  					ClusterName:    "my-cluster",
   780  					Location:       "centralIndia",
   781  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   782  					AdditionalTags: infrav1.Tags{
   783  						"Name": "my-publicip-ipv6",
   784  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   785  					},
   786  				},
   787  				&publicips.PublicIPSpec{
   788  					Name:           "fake-bastion-public-ip",
   789  					ResourceGroup:  "my-rg",
   790  					DNSName:        "fake-bastion-dns-name",
   791  					IsIPv6:         false,
   792  					ClusterName:    "my-cluster",
   793  					Location:       "centralIndia",
   794  					FailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
   795  					AdditionalTags: infrav1.Tags{
   796  						"Name": "my-publicip-ipv6",
   797  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
   798  					},
   799  				},
   800  			},
   801  		},
   802  	}
   803  
   804  	for _, tc := range tests {
   805  		t.Run(tc.name, func(t *testing.T) {
   806  			g := NewWithT(t)
   807  			scheme := runtime.NewScheme()
   808  			_ = infrav1.AddToScheme(scheme)
   809  			_ = clusterv1.AddToScheme(scheme)
   810  			_ = corev1.AddToScheme(scheme)
   811  
   812  			cluster := &clusterv1.Cluster{
   813  				ObjectMeta: metav1.ObjectMeta{
   814  					Name:      tc.azureCluster.Name,
   815  					Namespace: "default",
   816  				},
   817  			}
   818  			fakeIdentity := &infrav1.AzureClusterIdentity{
   819  				Spec: infrav1.AzureClusterIdentitySpec{
   820  					Type:     infrav1.ServicePrincipal,
   821  					ClientID: fakeClientID,
   822  					TenantID: fakeTenantID,
   823  				},
   824  			}
   825  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
   826  
   827  			initObjects := []runtime.Object{cluster, tc.azureCluster, fakeIdentity, fakeSecret}
   828  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
   829  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
   830  				Cluster:      cluster,
   831  				AzureCluster: tc.azureCluster,
   832  				Client:       fakeClient,
   833  			})
   834  			g.Expect(err).NotTo(HaveOccurred())
   835  
   836  			if got := clusterScope.PublicIPSpecs(); !reflect.DeepEqual(got, tc.expectedPublicIPSpec) {
   837  				t.Errorf("PublicIPSpecs() diff between expected result and actual result (%v): %s", got, cmp.Diff(tc.expectedPublicIPSpec, got))
   838  			}
   839  		})
   840  	}
   841  }
   842  
   843  func TestRouteTableSpecs(t *testing.T) {
   844  	tests := []struct {
   845  		name         string
   846  		clusterScope ClusterScope
   847  		want         []azure.ResourceSpecGetter
   848  	}{
   849  		{
   850  			name: "returns nil if no subnets are specified",
   851  			clusterScope: ClusterScope{
   852  				AzureCluster: &infrav1.AzureCluster{
   853  					Spec: infrav1.AzureClusterSpec{
   854  						NetworkSpec: infrav1.NetworkSpec{
   855  							Subnets: infrav1.Subnets{},
   856  						},
   857  					},
   858  				},
   859  				cache: &ClusterCache{},
   860  			},
   861  			want: nil,
   862  		},
   863  		{
   864  			name: "returns specified route tables if present",
   865  			clusterScope: ClusterScope{
   866  				Cluster: &clusterv1.Cluster{
   867  					ObjectMeta: metav1.ObjectMeta{
   868  						Name: "my-cluster",
   869  					},
   870  				},
   871  				AzureCluster: &infrav1.AzureCluster{
   872  					Spec: infrav1.AzureClusterSpec{
   873  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   874  							Location: "centralIndia",
   875  							IdentityRef: &corev1.ObjectReference{
   876  								Kind: infrav1.AzureClusterIdentityKind,
   877  							},
   878  						},
   879  						NetworkSpec: infrav1.NetworkSpec{
   880  							Vnet: infrav1.VnetSpec{
   881  								ResourceGroup: "my-rg",
   882  							},
   883  							Subnets: infrav1.Subnets{
   884  								{
   885  									RouteTable: infrav1.RouteTable{
   886  										ID:   "fake-route-table-id-1",
   887  										Name: "fake-route-table-1",
   888  									},
   889  								},
   890  								{
   891  									RouteTable: infrav1.RouteTable{
   892  										ID:   "fake-route-table-id-2",
   893  										Name: "fake-route-table-2",
   894  									},
   895  								},
   896  							},
   897  						},
   898  					},
   899  				},
   900  				cache: &ClusterCache{},
   901  			},
   902  			want: []azure.ResourceSpecGetter{
   903  				&routetables.RouteTableSpec{
   904  					Name:           "fake-route-table-1",
   905  					ResourceGroup:  "my-rg",
   906  					Location:       "centralIndia",
   907  					ClusterName:    "my-cluster",
   908  					AdditionalTags: make(infrav1.Tags),
   909  				},
   910  				&routetables.RouteTableSpec{
   911  					Name:           "fake-route-table-2",
   912  					ResourceGroup:  "my-rg",
   913  					Location:       "centralIndia",
   914  					ClusterName:    "my-cluster",
   915  					AdditionalTags: make(infrav1.Tags),
   916  				},
   917  			},
   918  		},
   919  	}
   920  
   921  	for _, tt := range tests {
   922  		tt := tt
   923  		t.Run(tt.name, func(t *testing.T) {
   924  			t.Parallel()
   925  			if got := tt.clusterScope.RouteTableSpecs(); !reflect.DeepEqual(got, tt.want) {
   926  				t.Errorf("RouteTableSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
   927  			}
   928  		})
   929  	}
   930  }
   931  
   932  func TestNatGatewaySpecs(t *testing.T) {
   933  	tests := []struct {
   934  		name         string
   935  		clusterScope ClusterScope
   936  		want         []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]
   937  	}{
   938  		{
   939  			name: "returns nil if no subnets are specified",
   940  			clusterScope: ClusterScope{
   941  				AzureCluster: &infrav1.AzureCluster{
   942  					Spec: infrav1.AzureClusterSpec{
   943  						NetworkSpec: infrav1.NetworkSpec{
   944  							Subnets: infrav1.Subnets{},
   945  						},
   946  					},
   947  				},
   948  				cache: &ClusterCache{},
   949  			},
   950  			want: nil,
   951  		},
   952  		{
   953  			name: "returns specified node NAT gateway if present",
   954  			clusterScope: ClusterScope{
   955  				Cluster: &clusterv1.Cluster{
   956  					ObjectMeta: metav1.ObjectMeta{
   957  						Name: "my-cluster",
   958  					},
   959  				},
   960  				AzureClients: AzureClients{
   961  					EnvironmentSettings: auth.EnvironmentSettings{
   962  						Values: map[string]string{
   963  							auth.SubscriptionID: "123",
   964  						},
   965  					},
   966  				},
   967  				AzureCluster: &infrav1.AzureCluster{
   968  					Spec: infrav1.AzureClusterSpec{
   969  						ResourceGroup: "my-rg",
   970  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   971  							Location: "centralIndia",
   972  							IdentityRef: &corev1.ObjectReference{
   973  								Kind: infrav1.AzureClusterIdentityKind,
   974  							},
   975  						},
   976  						NetworkSpec: infrav1.NetworkSpec{
   977  							Subnets: infrav1.Subnets{
   978  								{
   979  									SubnetClassSpec: infrav1.SubnetClassSpec{
   980  										Role: infrav1.SubnetNode,
   981  									},
   982  									RouteTable: infrav1.RouteTable{
   983  										ID:   "fake-route-table-id-1",
   984  										Name: "fake-route-table-1",
   985  									},
   986  									NatGateway: infrav1.NatGateway{
   987  										NatGatewayIP: infrav1.PublicIPSpec{
   988  											Name: "44.78.67.90",
   989  										},
   990  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
   991  											Name: "fake-nat-gateway-1",
   992  										},
   993  									},
   994  								},
   995  							},
   996  						},
   997  					},
   998  				},
   999  				cache: &ClusterCache{},
  1000  			},
  1001  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{
  1002  				&natgateways.NatGatewaySpec{
  1003  					Name:           "fake-nat-gateway-1",
  1004  					ResourceGroup:  "my-rg",
  1005  					Location:       "centralIndia",
  1006  					SubscriptionID: "123",
  1007  					ClusterName:    "my-cluster",
  1008  					NatGatewayIP: infrav1.PublicIPSpec{
  1009  						Name: "44.78.67.90",
  1010  					},
  1011  					AdditionalTags: make(infrav1.Tags),
  1012  					IsVnetManaged:  true,
  1013  				},
  1014  			},
  1015  		},
  1016  		{
  1017  			name: "returns specified node NAT gateway if present and ignores duplicate",
  1018  			clusterScope: ClusterScope{
  1019  				Cluster: &clusterv1.Cluster{
  1020  					ObjectMeta: metav1.ObjectMeta{
  1021  						Name: "my-cluster",
  1022  					},
  1023  				},
  1024  				AzureClients: AzureClients{
  1025  					EnvironmentSettings: auth.EnvironmentSettings{
  1026  						Values: map[string]string{
  1027  							auth.SubscriptionID: "123",
  1028  						},
  1029  					},
  1030  				},
  1031  				AzureCluster: &infrav1.AzureCluster{
  1032  					Spec: infrav1.AzureClusterSpec{
  1033  						ResourceGroup: "my-rg",
  1034  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1035  							Location: "centralIndia",
  1036  							IdentityRef: &corev1.ObjectReference{
  1037  								Kind: infrav1.AzureClusterIdentityKind,
  1038  							},
  1039  						},
  1040  						NetworkSpec: infrav1.NetworkSpec{
  1041  							Subnets: infrav1.Subnets{
  1042  								{
  1043  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1044  										Role: infrav1.SubnetNode,
  1045  									},
  1046  									RouteTable: infrav1.RouteTable{
  1047  										ID:   "fake-route-table-id-1",
  1048  										Name: "fake-route-table-1",
  1049  									},
  1050  									NatGateway: infrav1.NatGateway{
  1051  										NatGatewayIP: infrav1.PublicIPSpec{
  1052  											Name: "44.78.67.90",
  1053  										},
  1054  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1055  											Name: "fake-nat-gateway-1",
  1056  										},
  1057  									},
  1058  								},
  1059  								// Duplicate Entry
  1060  								{
  1061  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1062  										Role: infrav1.SubnetNode,
  1063  									},
  1064  									RouteTable: infrav1.RouteTable{
  1065  										ID:   "fake-route-table-id-1",
  1066  										Name: "fake-route-table-1",
  1067  									},
  1068  									NatGateway: infrav1.NatGateway{
  1069  										NatGatewayIP: infrav1.PublicIPSpec{
  1070  											Name: "44.78.67.90",
  1071  										},
  1072  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1073  											Name: "fake-nat-gateway-1",
  1074  										},
  1075  									},
  1076  								},
  1077  							},
  1078  						},
  1079  					},
  1080  				},
  1081  				cache: &ClusterCache{},
  1082  			},
  1083  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{
  1084  				&natgateways.NatGatewaySpec{
  1085  					Name:           "fake-nat-gateway-1",
  1086  					ResourceGroup:  "my-rg",
  1087  					Location:       "centralIndia",
  1088  					SubscriptionID: "123",
  1089  					ClusterName:    "my-cluster",
  1090  					NatGatewayIP: infrav1.PublicIPSpec{
  1091  						Name: "44.78.67.90",
  1092  					},
  1093  					AdditionalTags: make(infrav1.Tags),
  1094  					IsVnetManaged:  true,
  1095  				},
  1096  			},
  1097  		},
  1098  		{
  1099  			name: "returns specified node NAT gateway if present and ignores control plane nat gateway",
  1100  			clusterScope: ClusterScope{
  1101  				Cluster: &clusterv1.Cluster{
  1102  					ObjectMeta: metav1.ObjectMeta{
  1103  						Name: "my-cluster",
  1104  					},
  1105  				},
  1106  				AzureClients: AzureClients{
  1107  					EnvironmentSettings: auth.EnvironmentSettings{
  1108  						Values: map[string]string{
  1109  							auth.SubscriptionID: "123",
  1110  						},
  1111  					},
  1112  				},
  1113  				AzureCluster: &infrav1.AzureCluster{
  1114  					Spec: infrav1.AzureClusterSpec{
  1115  						ResourceGroup: "my-rg",
  1116  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1117  							Location: "centralIndia",
  1118  							IdentityRef: &corev1.ObjectReference{
  1119  								Kind: infrav1.AzureClusterIdentityKind,
  1120  							},
  1121  						},
  1122  						NetworkSpec: infrav1.NetworkSpec{
  1123  							Subnets: infrav1.Subnets{
  1124  								{
  1125  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1126  										Role: infrav1.SubnetNode,
  1127  									},
  1128  									RouteTable: infrav1.RouteTable{
  1129  										ID:   "fake-route-table-id-1",
  1130  										Name: "fake-route-table-1",
  1131  									},
  1132  									NatGateway: infrav1.NatGateway{
  1133  										NatGatewayIP: infrav1.PublicIPSpec{
  1134  											Name: "44.78.67.90",
  1135  										},
  1136  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1137  											Name: "fake-nat-gateway-1",
  1138  										},
  1139  									},
  1140  								},
  1141  								{
  1142  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1143  										Role: infrav1.SubnetControlPlane,
  1144  									},
  1145  									RouteTable: infrav1.RouteTable{
  1146  										ID:   "fake-route-table-id-2",
  1147  										Name: "fake-route-table-2",
  1148  									},
  1149  									NatGateway: infrav1.NatGateway{
  1150  										NatGatewayIP: infrav1.PublicIPSpec{
  1151  											Name: "44.78.67.91",
  1152  										},
  1153  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1154  											Name: "fake-nat-gateway-2",
  1155  										},
  1156  									},
  1157  								},
  1158  							},
  1159  						},
  1160  					},
  1161  				},
  1162  				cache: &ClusterCache{},
  1163  			},
  1164  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{
  1165  				&natgateways.NatGatewaySpec{
  1166  					Name:           "fake-nat-gateway-1",
  1167  					ResourceGroup:  "my-rg",
  1168  					Location:       "centralIndia",
  1169  					SubscriptionID: "123",
  1170  					ClusterName:    "my-cluster",
  1171  					NatGatewayIP: infrav1.PublicIPSpec{
  1172  						Name: "44.78.67.90",
  1173  					},
  1174  					AdditionalTags: make(infrav1.Tags),
  1175  					IsVnetManaged:  true,
  1176  				},
  1177  			},
  1178  		},
  1179  	}
  1180  
  1181  	for _, tt := range tests {
  1182  		tt := tt
  1183  		t.Run(tt.name, func(t *testing.T) {
  1184  			t.Parallel()
  1185  			if got := tt.clusterScope.NatGatewaySpecs(); !reflect.DeepEqual(got, tt.want) {
  1186  				t.Errorf("NatGatewaySpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
  1187  			}
  1188  		})
  1189  	}
  1190  }
  1191  
  1192  func TestSetNatGatewayIDInSubnets(t *testing.T) {
  1193  	tests := []struct {
  1194  		name          string
  1195  		clusterScope  ClusterScope
  1196  		asoNatgateway *asonetworkv1api20220701.NatGateway
  1197  	}{
  1198  		{
  1199  			name: "sets nat gateway id in the matching subnet",
  1200  			clusterScope: ClusterScope{
  1201  				Cluster: &clusterv1.Cluster{
  1202  					ObjectMeta: metav1.ObjectMeta{
  1203  						Name: "my-cluster",
  1204  					},
  1205  				},
  1206  				AzureCluster: &infrav1.AzureCluster{
  1207  					Spec: infrav1.AzureClusterSpec{
  1208  						NetworkSpec: infrav1.NetworkSpec{
  1209  							Subnets: infrav1.Subnets{
  1210  								{
  1211  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1212  										Name: "fake-subnet-1",
  1213  									},
  1214  									NatGateway: infrav1.NatGateway{
  1215  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1216  											Name: "fake-nat-gateway-1",
  1217  										},
  1218  									},
  1219  								},
  1220  								{
  1221  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1222  										Name: "fake-subnet-2",
  1223  									},
  1224  									NatGateway: infrav1.NatGateway{
  1225  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1226  											Name: "fake-nat-gateway-2",
  1227  										},
  1228  									},
  1229  								},
  1230  							},
  1231  						},
  1232  					},
  1233  				},
  1234  				cache: &ClusterCache{},
  1235  			},
  1236  			asoNatgateway: &asonetworkv1api20220701.NatGateway{
  1237  				ObjectMeta: metav1.ObjectMeta{
  1238  					Name: "fake-nat-gateway-1",
  1239  				},
  1240  				Status: asonetworkv1api20220701.NatGateway_STATUS{
  1241  					Id: ptr.To("dummy-id-1"),
  1242  				},
  1243  			},
  1244  		},
  1245  	}
  1246  
  1247  	for _, tt := range tests {
  1248  		tt := tt
  1249  		t.Run(tt.name, func(t *testing.T) {
  1250  			g := NewWithT(t)
  1251  			t.Parallel()
  1252  			tt.clusterScope.SetNatGatewayIDInSubnets(tt.asoNatgateway.Name, *tt.asoNatgateway.Status.Id)
  1253  			for _, subnet := range tt.clusterScope.AzureCluster.Spec.NetworkSpec.Subnets {
  1254  				if subnet.NatGateway.Name == tt.asoNatgateway.Name {
  1255  					g.Expect(subnet.NatGateway.ID).To(Equal(*tt.asoNatgateway.Status.Id))
  1256  				} else {
  1257  					g.Expect(subnet.NatGateway.ID).To(Equal(""))
  1258  				}
  1259  			}
  1260  		})
  1261  	}
  1262  }
  1263  
  1264  func TestNSGSpecs(t *testing.T) {
  1265  	tests := []struct {
  1266  		name         string
  1267  		clusterScope ClusterScope
  1268  		want         []azure.ResourceSpecGetter
  1269  	}{
  1270  		{
  1271  			name: "returns empty if no subnets are specified",
  1272  			clusterScope: ClusterScope{
  1273  				AzureCluster: &infrav1.AzureCluster{
  1274  					Spec: infrav1.AzureClusterSpec{
  1275  						NetworkSpec: infrav1.NetworkSpec{
  1276  							Subnets: infrav1.Subnets{},
  1277  						},
  1278  					},
  1279  				},
  1280  			},
  1281  			want: []azure.ResourceSpecGetter{},
  1282  		},
  1283  		{
  1284  			name: "returns specified security groups if present",
  1285  			clusterScope: ClusterScope{
  1286  				Cluster: &clusterv1.Cluster{
  1287  					ObjectMeta: metav1.ObjectMeta{
  1288  						Name: "my-cluster",
  1289  					},
  1290  				},
  1291  				AzureCluster: &infrav1.AzureCluster{
  1292  					Spec: infrav1.AzureClusterSpec{
  1293  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1294  							Location: "centralIndia",
  1295  							IdentityRef: &corev1.ObjectReference{
  1296  								Kind: infrav1.AzureClusterIdentityKind,
  1297  							},
  1298  						},
  1299  						NetworkSpec: infrav1.NetworkSpec{
  1300  							Vnet: infrav1.VnetSpec{
  1301  								ResourceGroup: "my-rg",
  1302  							},
  1303  							Subnets: infrav1.Subnets{
  1304  								{
  1305  									SecurityGroup: infrav1.SecurityGroup{
  1306  										Name: "fake-security-group-1",
  1307  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1308  											SecurityRules: infrav1.SecurityRules{
  1309  												{
  1310  													Name: "fake-rule-1",
  1311  												},
  1312  											},
  1313  										},
  1314  									},
  1315  								},
  1316  							},
  1317  						},
  1318  					},
  1319  				},
  1320  				cache: &ClusterCache{},
  1321  			},
  1322  			want: []azure.ResourceSpecGetter{
  1323  				&securitygroups.NSGSpec{
  1324  					Name: "fake-security-group-1",
  1325  					SecurityRules: infrav1.SecurityRules{
  1326  						{
  1327  							Name: "fake-rule-1",
  1328  						},
  1329  					},
  1330  					ResourceGroup:            "my-rg",
  1331  					Location:                 "centralIndia",
  1332  					ClusterName:              "my-cluster",
  1333  					AdditionalTags:           make(infrav1.Tags),
  1334  					LastAppliedSecurityRules: map[string]interface{}{},
  1335  				},
  1336  			},
  1337  		},
  1338  	}
  1339  
  1340  	for _, tt := range tests {
  1341  		tt := tt
  1342  		t.Run(tt.name, func(t *testing.T) {
  1343  			t.Parallel()
  1344  			if got := tt.clusterScope.NSGSpecs(); !reflect.DeepEqual(got, tt.want) {
  1345  				t.Errorf("RouteTableSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
  1346  			}
  1347  		})
  1348  	}
  1349  }
  1350  
  1351  func TestSubnetSpecs(t *testing.T) {
  1352  	tests := []struct {
  1353  		name         string
  1354  		clusterScope ClusterScope
  1355  		want         []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]
  1356  	}{
  1357  		{
  1358  			name: "returns empty if no subnets are specified",
  1359  			clusterScope: ClusterScope{
  1360  				AzureCluster: &infrav1.AzureCluster{
  1361  					Spec: infrav1.AzureClusterSpec{
  1362  						NetworkSpec: infrav1.NetworkSpec{
  1363  							Subnets: infrav1.Subnets{},
  1364  						},
  1365  					},
  1366  				},
  1367  				cache: &ClusterCache{},
  1368  			},
  1369  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{},
  1370  		},
  1371  		{
  1372  			name: "returns specified subnet spec",
  1373  			clusterScope: ClusterScope{
  1374  				Cluster: &clusterv1.Cluster{
  1375  					ObjectMeta: metav1.ObjectMeta{
  1376  						Name: "my-cluster",
  1377  					},
  1378  				},
  1379  				AzureClients: AzureClients{
  1380  					EnvironmentSettings: auth.EnvironmentSettings{
  1381  						Values: map[string]string{
  1382  							auth.SubscriptionID: "123",
  1383  						},
  1384  					},
  1385  				},
  1386  				AzureCluster: &infrav1.AzureCluster{
  1387  					Spec: infrav1.AzureClusterSpec{
  1388  						ResourceGroup: "my-rg",
  1389  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1390  							Location: "centralIndia",
  1391  							IdentityRef: &corev1.ObjectReference{
  1392  								Kind: infrav1.AzureClusterIdentityKind,
  1393  							},
  1394  						},
  1395  						NetworkSpec: infrav1.NetworkSpec{
  1396  							Vnet: infrav1.VnetSpec{
  1397  								ID:            "fake-vnet-id-1",
  1398  								Name:          "fake-vnet-1",
  1399  								ResourceGroup: "my-rg-vnet",
  1400  							},
  1401  							Subnets: infrav1.Subnets{
  1402  								{
  1403  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1404  										Role:       infrav1.SubnetNode,
  1405  										CIDRBlocks: []string{"192.168.1.1/16"},
  1406  										Name:       "fake-subnet-1",
  1407  									},
  1408  									NatGateway: infrav1.NatGateway{
  1409  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1410  											Name: "fake-natgateway-1",
  1411  										},
  1412  									},
  1413  									RouteTable: infrav1.RouteTable{
  1414  										ID:   "fake-route-table-id-1",
  1415  										Name: "fake-route-table-1",
  1416  									},
  1417  									SecurityGroup: infrav1.SecurityGroup{
  1418  										Name: "fake-security-group-1",
  1419  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1420  											SecurityRules: infrav1.SecurityRules{
  1421  												{
  1422  													Name: "fake-rule-1",
  1423  												},
  1424  											},
  1425  										},
  1426  									},
  1427  								},
  1428  							},
  1429  						},
  1430  					},
  1431  				},
  1432  				cache: &ClusterCache{},
  1433  			},
  1434  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{
  1435  				&subnets.SubnetSpec{
  1436  					Name:              "fake-subnet-1",
  1437  					ResourceGroup:     "my-rg",
  1438  					SubscriptionID:    "123",
  1439  					CIDRs:             []string{"192.168.1.1/16"},
  1440  					VNetName:          "fake-vnet-1",
  1441  					VNetResourceGroup: "my-rg-vnet",
  1442  					IsVNetManaged:     false,
  1443  					RouteTableName:    "fake-route-table-1",
  1444  					SecurityGroupName: "fake-security-group-1",
  1445  					NatGatewayName:    "fake-natgateway-1",
  1446  				},
  1447  			},
  1448  		},
  1449  
  1450  		{
  1451  			name: "returns specified subnet spec and bastion spec if enabled",
  1452  			clusterScope: ClusterScope{
  1453  				Cluster: &clusterv1.Cluster{
  1454  					ObjectMeta: metav1.ObjectMeta{
  1455  						Name: "my-cluster",
  1456  					},
  1457  				},
  1458  				AzureClients: AzureClients{
  1459  					EnvironmentSettings: auth.EnvironmentSettings{
  1460  						Values: map[string]string{
  1461  							auth.SubscriptionID: "123",
  1462  						},
  1463  					},
  1464  				},
  1465  				AzureCluster: &infrav1.AzureCluster{
  1466  					Spec: infrav1.AzureClusterSpec{
  1467  						BastionSpec: infrav1.BastionSpec{
  1468  							AzureBastion: &infrav1.AzureBastion{
  1469  								Name: "fake-azure-bastion",
  1470  								Subnet: infrav1.SubnetSpec{
  1471  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1472  										Role:       infrav1.SubnetBastion,
  1473  										CIDRBlocks: []string{"172.122.1.1./16"},
  1474  										Name:       "fake-bastion-subnet-1",
  1475  									},
  1476  									RouteTable: infrav1.RouteTable{
  1477  										ID:   "fake-bastion-route-table-id-1",
  1478  										Name: "fake-bastion-route-table-1",
  1479  									},
  1480  									SecurityGroup: infrav1.SecurityGroup{
  1481  										Name: "fake-bastion-security-group-1",
  1482  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1483  											SecurityRules: infrav1.SecurityRules{
  1484  												{
  1485  													Name: "fake-rule-1",
  1486  												},
  1487  											},
  1488  										},
  1489  									},
  1490  								},
  1491  							},
  1492  						},
  1493  						ResourceGroup: "my-rg",
  1494  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1495  							Location: "centralIndia",
  1496  							IdentityRef: &corev1.ObjectReference{
  1497  								Kind: infrav1.AzureClusterIdentityKind,
  1498  							},
  1499  						},
  1500  						NetworkSpec: infrav1.NetworkSpec{
  1501  							Vnet: infrav1.VnetSpec{
  1502  								ID:            "fake-vnet-id-1",
  1503  								Name:          "fake-vnet-1",
  1504  								ResourceGroup: "my-rg-vnet",
  1505  							},
  1506  							Subnets: infrav1.Subnets{
  1507  								{
  1508  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1509  										Role:       infrav1.SubnetNode,
  1510  										CIDRBlocks: []string{"192.168.1.1/16"},
  1511  										Name:       "fake-subnet-1",
  1512  									},
  1513  									NatGateway: infrav1.NatGateway{
  1514  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1515  											Name: "fake-natgateway-1",
  1516  										},
  1517  									},
  1518  									RouteTable: infrav1.RouteTable{
  1519  										ID:   "fake-route-table-id-1",
  1520  										Name: "fake-route-table-1",
  1521  									},
  1522  									SecurityGroup: infrav1.SecurityGroup{
  1523  										Name: "fake-security-group-1",
  1524  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1525  											SecurityRules: infrav1.SecurityRules{
  1526  												{
  1527  													Name: "fake-rule-1",
  1528  												},
  1529  											},
  1530  										},
  1531  									},
  1532  								},
  1533  							},
  1534  						},
  1535  					},
  1536  				},
  1537  				cache: &ClusterCache{},
  1538  			},
  1539  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{
  1540  				&subnets.SubnetSpec{
  1541  					Name:              "fake-subnet-1",
  1542  					ResourceGroup:     "my-rg",
  1543  					SubscriptionID:    "123",
  1544  					CIDRs:             []string{"192.168.1.1/16"},
  1545  					VNetName:          "fake-vnet-1",
  1546  					VNetResourceGroup: "my-rg-vnet",
  1547  					IsVNetManaged:     false,
  1548  					RouteTableName:    "fake-route-table-1",
  1549  					SecurityGroupName: "fake-security-group-1",
  1550  					NatGatewayName:    "fake-natgateway-1",
  1551  				},
  1552  				&subnets.SubnetSpec{
  1553  					Name:              "fake-bastion-subnet-1",
  1554  					ResourceGroup:     "my-rg",
  1555  					SubscriptionID:    "123",
  1556  					CIDRs:             []string{"172.122.1.1./16"},
  1557  					VNetName:          "fake-vnet-1",
  1558  					VNetResourceGroup: "my-rg-vnet",
  1559  					IsVNetManaged:     false,
  1560  					SecurityGroupName: "fake-bastion-security-group-1",
  1561  					RouteTableName:    "fake-bastion-route-table-1",
  1562  				},
  1563  			},
  1564  		},
  1565  	}
  1566  
  1567  	for _, tt := range tests {
  1568  		tt := tt
  1569  		t.Run(tt.name, func(t *testing.T) {
  1570  			t.Parallel()
  1571  			if got := tt.clusterScope.SubnetSpecs(); !reflect.DeepEqual(got, tt.want) {
  1572  				t.Errorf("SubnetSpecs() = \n%s, want \n%s", specArrayToString(got), specArrayToString(tt.want))
  1573  			}
  1574  		})
  1575  	}
  1576  }
  1577  
  1578  func TestIsVnetManaged(t *testing.T) {
  1579  	tests := []struct {
  1580  		name         string
  1581  		clusterScope ClusterScope
  1582  		want         bool
  1583  	}{
  1584  		{
  1585  			name: "VNET ID is empty",
  1586  			clusterScope: ClusterScope{
  1587  				Cluster: &clusterv1.Cluster{
  1588  					ObjectMeta: metav1.ObjectMeta{
  1589  						Name: "my-cluster",
  1590  					},
  1591  				},
  1592  				AzureCluster: &infrav1.AzureCluster{
  1593  					Spec: infrav1.AzureClusterSpec{
  1594  						NetworkSpec: infrav1.NetworkSpec{
  1595  							Vnet: infrav1.VnetSpec{
  1596  								ID: "",
  1597  							},
  1598  						},
  1599  					},
  1600  				},
  1601  				cache: &ClusterCache{},
  1602  			},
  1603  			want: true,
  1604  		},
  1605  		{
  1606  			name: "Wrong tags",
  1607  			clusterScope: ClusterScope{
  1608  				Cluster: &clusterv1.Cluster{
  1609  					ObjectMeta: metav1.ObjectMeta{
  1610  						Name: "my-cluster",
  1611  					},
  1612  				},
  1613  				AzureCluster: &infrav1.AzureCluster{
  1614  					Spec: infrav1.AzureClusterSpec{
  1615  						NetworkSpec: infrav1.NetworkSpec{
  1616  							Vnet: infrav1.VnetSpec{
  1617  								ID: "my-id",
  1618  								VnetClassSpec: infrav1.VnetClassSpec{Tags: map[string]string{
  1619  									"key": "value",
  1620  								}},
  1621  							},
  1622  						},
  1623  					},
  1624  				},
  1625  				cache: &ClusterCache{},
  1626  			},
  1627  			want: false,
  1628  		},
  1629  		{
  1630  			name: "Has owning tags",
  1631  			clusterScope: ClusterScope{
  1632  				Cluster: &clusterv1.Cluster{
  1633  					ObjectMeta: metav1.ObjectMeta{
  1634  						Name: "my-cluster",
  1635  					},
  1636  				},
  1637  				AzureCluster: &infrav1.AzureCluster{
  1638  					Spec: infrav1.AzureClusterSpec{
  1639  						NetworkSpec: infrav1.NetworkSpec{
  1640  							Vnet: infrav1.VnetSpec{
  1641  								ID: "my-id",
  1642  								VnetClassSpec: infrav1.VnetClassSpec{Tags: map[string]string{
  1643  									"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
  1644  								}},
  1645  							},
  1646  						},
  1647  					},
  1648  				},
  1649  				cache: &ClusterCache{},
  1650  			},
  1651  			want: true,
  1652  		},
  1653  		{
  1654  			name: "Has cached value of false",
  1655  			clusterScope: ClusterScope{
  1656  				AzureCluster: &infrav1.AzureCluster{
  1657  					Spec: infrav1.AzureClusterSpec{},
  1658  				},
  1659  				cache: &ClusterCache{
  1660  					isVnetManaged: ptr.To(false),
  1661  				},
  1662  			},
  1663  			want: false,
  1664  		},
  1665  		{
  1666  			name: "Has cached value of true",
  1667  			clusterScope: ClusterScope{
  1668  				AzureCluster: &infrav1.AzureCluster{
  1669  					Spec: infrav1.AzureClusterSpec{},
  1670  				},
  1671  				cache: &ClusterCache{
  1672  					isVnetManaged: ptr.To(true),
  1673  				},
  1674  			},
  1675  			want: true,
  1676  		},
  1677  	}
  1678  
  1679  	for _, tt := range tests {
  1680  		tt := tt
  1681  		t.Run(tt.name, func(t *testing.T) {
  1682  			t.Parallel()
  1683  			got := tt.clusterScope.IsVnetManaged()
  1684  			if !reflect.DeepEqual(got, tt.want) {
  1685  				t.Errorf("IsVnetManaged() = \n%t, want \n%t", got, tt.want)
  1686  			}
  1687  			if ptr.Deref(tt.clusterScope.cache.isVnetManaged, false) != got {
  1688  				t.Errorf("IsVnetManaged() = \n%t, cache = \n%t", got, ptr.Deref(tt.clusterScope.cache.isVnetManaged, false))
  1689  			}
  1690  		})
  1691  	}
  1692  }
  1693  
  1694  func TestAzureBastionSpec(t *testing.T) {
  1695  	tests := []struct {
  1696  		name         string
  1697  		clusterScope ClusterScope
  1698  		want         azure.ASOResourceSpecGetter[*asonetworkv1api20220701.BastionHost]
  1699  	}{
  1700  		{
  1701  			name: "returns nil if no subnets are specified",
  1702  			clusterScope: ClusterScope{
  1703  				AzureCluster: &infrav1.AzureCluster{
  1704  					Spec: infrav1.AzureClusterSpec{
  1705  						NetworkSpec: infrav1.NetworkSpec{
  1706  							Subnets: infrav1.Subnets{},
  1707  						},
  1708  					},
  1709  				},
  1710  			},
  1711  			want: nil,
  1712  		},
  1713  		{
  1714  			name: "returns bastion spec if enabled",
  1715  			clusterScope: ClusterScope{
  1716  				Cluster: &clusterv1.Cluster{
  1717  					ObjectMeta: metav1.ObjectMeta{
  1718  						Name: "my-cluster",
  1719  					},
  1720  				},
  1721  				AzureClients: AzureClients{
  1722  					EnvironmentSettings: auth.EnvironmentSettings{
  1723  						Values: map[string]string{
  1724  							auth.SubscriptionID: "123",
  1725  						},
  1726  					},
  1727  				},
  1728  				AzureCluster: &infrav1.AzureCluster{
  1729  					Spec: infrav1.AzureClusterSpec{
  1730  						BastionSpec: infrav1.BastionSpec{
  1731  							AzureBastion: &infrav1.AzureBastion{
  1732  								Name: "fake-azure-bastion-1",
  1733  								Subnet: infrav1.SubnetSpec{
  1734  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1735  										Role:       infrav1.SubnetBastion,
  1736  										CIDRBlocks: []string{"172.122.1.1./16"},
  1737  										Name:       "fake-bastion-subnet-1",
  1738  									},
  1739  									RouteTable: infrav1.RouteTable{
  1740  										ID:   "fake-bastion-route-table-id-1",
  1741  										Name: "fake-bastion-route-table-1",
  1742  									},
  1743  									SecurityGroup: infrav1.SecurityGroup{
  1744  										Name: "fake-bastion-security-group-1",
  1745  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1746  											SecurityRules: infrav1.SecurityRules{
  1747  												{
  1748  													Name: "fake-rule-1",
  1749  												},
  1750  											},
  1751  										},
  1752  									},
  1753  								},
  1754  								PublicIP: infrav1.PublicIPSpec{
  1755  									Name: "fake-public-ip-1",
  1756  								},
  1757  							},
  1758  						},
  1759  						ResourceGroup: "my-rg",
  1760  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1761  							Location: "centralIndia",
  1762  							IdentityRef: &corev1.ObjectReference{
  1763  								Kind: infrav1.AzureClusterIdentityKind,
  1764  							},
  1765  						},
  1766  						NetworkSpec: infrav1.NetworkSpec{
  1767  							Vnet: infrav1.VnetSpec{
  1768  								ID:            "fake-vnet-id-1",
  1769  								Name:          "fake-vnet-1",
  1770  								ResourceGroup: "my-rg-vnet",
  1771  							},
  1772  							Subnets: infrav1.Subnets{
  1773  								{
  1774  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1775  										Role:       infrav1.SubnetNode,
  1776  										CIDRBlocks: []string{"192.168.1.1/16"},
  1777  										Name:       "fake-subnet-1",
  1778  									},
  1779  									NatGateway: infrav1.NatGateway{
  1780  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1781  											Name: "fake-natgateway-1",
  1782  										},
  1783  									},
  1784  									RouteTable: infrav1.RouteTable{
  1785  										ID:   "fake-route-table-id-1",
  1786  										Name: "fake-route-table-1",
  1787  									},
  1788  									SecurityGroup: infrav1.SecurityGroup{
  1789  										Name: "fake-security-group-1",
  1790  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1791  											SecurityRules: infrav1.SecurityRules{
  1792  												{
  1793  													Name: "fake-rule-1",
  1794  												},
  1795  											},
  1796  										},
  1797  									},
  1798  								},
  1799  							},
  1800  						},
  1801  					},
  1802  				},
  1803  				cache: &ClusterCache{},
  1804  			},
  1805  			want: &bastionhosts.AzureBastionSpec{
  1806  				Name:          "fake-azure-bastion-1",
  1807  				ResourceGroup: "my-rg",
  1808  				Location:      "centralIndia",
  1809  				ClusterName:   "my-cluster",
  1810  				SubnetID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/"+
  1811  					"virtualNetworks/%s/subnets/%s", "123", "my-rg", "fake-vnet-1", "fake-bastion-subnet-1"),
  1812  				PublicIPID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/"+
  1813  					"publicIPAddresses/%s", "123", "my-rg", "fake-public-ip-1"),
  1814  			},
  1815  		},
  1816  	}
  1817  
  1818  	for _, tt := range tests {
  1819  		tt := tt
  1820  		t.Run(tt.name, func(t *testing.T) {
  1821  			t.Parallel()
  1822  			if got := tt.clusterScope.AzureBastionSpec(); !reflect.DeepEqual(got, tt.want) {
  1823  				t.Errorf("AzureBastionSpec() = \n%s, want \n%s", specToString(got), specToString(tt.want))
  1824  			}
  1825  		})
  1826  	}
  1827  }
  1828  
  1829  func TestSubnet(t *testing.T) {
  1830  	tests := []struct {
  1831  		clusterName             string
  1832  		subnetName              string
  1833  		azureClusterNetworkSpec infrav1.NetworkSpec
  1834  		expectSubnet            infrav1.SubnetSpec
  1835  	}{
  1836  		{
  1837  			clusterName:             "my-cluster-1",
  1838  			subnetName:              "subnet-1",
  1839  			azureClusterNetworkSpec: infrav1.NetworkSpec{},
  1840  			expectSubnet:            infrav1.SubnetSpec{},
  1841  		},
  1842  		{
  1843  			clusterName: "my-cluster-1",
  1844  			subnetName:  "subnet-1",
  1845  			azureClusterNetworkSpec: infrav1.NetworkSpec{
  1846  				Subnets: infrav1.Subnets{
  1847  					infrav1.SubnetSpec{
  1848  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1849  							Name: "subnet-1",
  1850  						},
  1851  						ID: "subnet-1-id",
  1852  					},
  1853  					infrav1.SubnetSpec{
  1854  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1855  							Name: "subnet-2",
  1856  						},
  1857  						ID: "subnet-1-id",
  1858  					},
  1859  					infrav1.SubnetSpec{
  1860  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1861  							Name: "subnet-3",
  1862  						},
  1863  						ID: "subnet-2-id",
  1864  					},
  1865  				},
  1866  			},
  1867  			expectSubnet: infrav1.SubnetSpec{
  1868  				SubnetClassSpec: infrav1.SubnetClassSpec{
  1869  					Name: "subnet-1",
  1870  				},
  1871  				ID: "subnet-1-id",
  1872  			},
  1873  		},
  1874  	}
  1875  	for _, tc := range tests {
  1876  		t.Run(tc.clusterName, func(t *testing.T) {
  1877  			g := NewWithT(t)
  1878  			scheme := runtime.NewScheme()
  1879  			_ = infrav1.AddToScheme(scheme)
  1880  			_ = clusterv1.AddToScheme(scheme)
  1881  			_ = corev1.AddToScheme(scheme)
  1882  
  1883  			cluster := &clusterv1.Cluster{
  1884  				ObjectMeta: metav1.ObjectMeta{
  1885  					Name:      tc.clusterName,
  1886  					Namespace: "default",
  1887  				},
  1888  			}
  1889  			azureCluster := &infrav1.AzureCluster{
  1890  				ObjectMeta: metav1.ObjectMeta{
  1891  					Name: tc.clusterName,
  1892  					OwnerReferences: []metav1.OwnerReference{
  1893  						{
  1894  							APIVersion: "cluster.x-k8s.io/v1beta1",
  1895  							Kind:       "Cluster",
  1896  							Name:       "my-cluster",
  1897  						},
  1898  					},
  1899  				},
  1900  				Spec: infrav1.AzureClusterSpec{
  1901  					NetworkSpec: tc.azureClusterNetworkSpec,
  1902  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1903  						SubscriptionID: "123",
  1904  						IdentityRef: &corev1.ObjectReference{
  1905  							Kind: infrav1.AzureClusterIdentityKind,
  1906  						},
  1907  					},
  1908  				},
  1909  			}
  1910  			fakeIdentity := &infrav1.AzureClusterIdentity{
  1911  				Spec: infrav1.AzureClusterIdentitySpec{
  1912  					Type:     infrav1.ServicePrincipal,
  1913  					ClientID: fakeClientID,
  1914  					TenantID: fakeTenantID,
  1915  				},
  1916  			}
  1917  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  1918  
  1919  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  1920  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  1921  
  1922  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  1923  				Cluster:      cluster,
  1924  				AzureCluster: azureCluster,
  1925  				Client:       fakeClient,
  1926  			})
  1927  			g.Expect(err).NotTo(HaveOccurred())
  1928  			got := clusterScope.Subnet(tc.subnetName)
  1929  			g.Expect(tc.expectSubnet).Should(Equal(got))
  1930  		})
  1931  	}
  1932  }
  1933  
  1934  func TestControlPlaneRouteTable(t *testing.T) {
  1935  	tests := []struct {
  1936  		clusterName             string
  1937  		azureClusterNetworkSpec infrav1.NetworkSpec
  1938  		expectRouteTable        infrav1.RouteTable
  1939  	}{
  1940  		{
  1941  			clusterName:             "my-cluster-1",
  1942  			azureClusterNetworkSpec: infrav1.NetworkSpec{},
  1943  			expectRouteTable:        infrav1.RouteTable{},
  1944  		},
  1945  		{
  1946  			clusterName: "my-cluster-2",
  1947  			azureClusterNetworkSpec: infrav1.NetworkSpec{
  1948  				Subnets: infrav1.Subnets{
  1949  					infrav1.SubnetSpec{
  1950  						RouteTable: infrav1.RouteTable{
  1951  							ID:   "fake-id-1",
  1952  							Name: "route-tb-1",
  1953  						},
  1954  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1955  							Role: infrav1.SubnetNode,
  1956  						},
  1957  					},
  1958  					infrav1.SubnetSpec{
  1959  						RouteTable: infrav1.RouteTable{
  1960  							ID:   "fake-id-2",
  1961  							Name: "route-tb-2",
  1962  						},
  1963  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1964  							Role: infrav1.SubnetControlPlane,
  1965  						},
  1966  					},
  1967  					infrav1.SubnetSpec{
  1968  						RouteTable: infrav1.RouteTable{
  1969  							ID:   "fake-id-3",
  1970  							Name: "route-tb-3",
  1971  						},
  1972  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1973  							Role: infrav1.SubnetBastion,
  1974  						},
  1975  					},
  1976  				},
  1977  			},
  1978  			expectRouteTable: infrav1.RouteTable{
  1979  				ID:   "fake-id-2",
  1980  				Name: "route-tb-2",
  1981  			},
  1982  		},
  1983  	}
  1984  	for _, tc := range tests {
  1985  		t.Run(tc.clusterName, func(t *testing.T) {
  1986  			g := NewWithT(t)
  1987  			scheme := runtime.NewScheme()
  1988  			_ = infrav1.AddToScheme(scheme)
  1989  			_ = clusterv1.AddToScheme(scheme)
  1990  			_ = corev1.AddToScheme(scheme)
  1991  
  1992  			cluster := &clusterv1.Cluster{
  1993  				ObjectMeta: metav1.ObjectMeta{
  1994  					Name:      tc.clusterName,
  1995  					Namespace: "default",
  1996  				},
  1997  			}
  1998  			azureCluster := &infrav1.AzureCluster{
  1999  				ObjectMeta: metav1.ObjectMeta{
  2000  					Name: tc.clusterName,
  2001  					OwnerReferences: []metav1.OwnerReference{
  2002  						{
  2003  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2004  							Kind:       "Cluster",
  2005  							Name:       "my-cluster",
  2006  						},
  2007  					},
  2008  				},
  2009  				Spec: infrav1.AzureClusterSpec{
  2010  					NetworkSpec: tc.azureClusterNetworkSpec,
  2011  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2012  						SubscriptionID: "123",
  2013  						IdentityRef: &corev1.ObjectReference{
  2014  							Kind: infrav1.AzureClusterIdentityKind,
  2015  						},
  2016  					},
  2017  				},
  2018  			}
  2019  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2020  				Spec: infrav1.AzureClusterIdentitySpec{
  2021  					Type:     infrav1.ServicePrincipal,
  2022  					ClientID: fakeClientID,
  2023  					TenantID: fakeTenantID,
  2024  				},
  2025  			}
  2026  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2027  
  2028  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2029  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2030  
  2031  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2032  				Cluster:      cluster,
  2033  				AzureCluster: azureCluster,
  2034  				Client:       fakeClient,
  2035  			})
  2036  			g.Expect(err).NotTo(HaveOccurred())
  2037  			got := clusterScope.ControlPlaneRouteTable()
  2038  			g.Expect(tc.expectRouteTable).Should(Equal(got))
  2039  		})
  2040  	}
  2041  }
  2042  
  2043  func TestGetPrivateDNSZoneName(t *testing.T) {
  2044  	tests := []struct {
  2045  		clusterName              string
  2046  		azureClusterNetworkSpec  infrav1.NetworkSpec
  2047  		expectPrivateDNSZoneName string
  2048  	}{
  2049  		{
  2050  			clusterName: "my-cluster-1",
  2051  			azureClusterNetworkSpec: infrav1.NetworkSpec{
  2052  				NetworkClassSpec: infrav1.NetworkClassSpec{
  2053  					PrivateDNSZoneName: "fake-privateDNSZoneName",
  2054  				},
  2055  			},
  2056  			expectPrivateDNSZoneName: "fake-privateDNSZoneName",
  2057  		},
  2058  		{
  2059  			clusterName:              "my-cluster-2",
  2060  			expectPrivateDNSZoneName: "my-cluster-2.capz.io",
  2061  		},
  2062  	}
  2063  	for _, tc := range tests {
  2064  		t.Run(tc.clusterName, func(t *testing.T) {
  2065  			g := NewWithT(t)
  2066  			scheme := runtime.NewScheme()
  2067  			_ = infrav1.AddToScheme(scheme)
  2068  			_ = clusterv1.AddToScheme(scheme)
  2069  			_ = corev1.AddToScheme(scheme)
  2070  
  2071  			cluster := &clusterv1.Cluster{
  2072  				ObjectMeta: metav1.ObjectMeta{
  2073  					Name:      tc.clusterName,
  2074  					Namespace: "default",
  2075  				},
  2076  			}
  2077  			azureCluster := &infrav1.AzureCluster{
  2078  				ObjectMeta: metav1.ObjectMeta{
  2079  					Name: tc.clusterName,
  2080  					OwnerReferences: []metav1.OwnerReference{
  2081  						{
  2082  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2083  							Kind:       "Cluster",
  2084  							Name:       "my-cluster",
  2085  						},
  2086  					},
  2087  				},
  2088  				Spec: infrav1.AzureClusterSpec{
  2089  					NetworkSpec: tc.azureClusterNetworkSpec,
  2090  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2091  						SubscriptionID: "123",
  2092  						IdentityRef: &corev1.ObjectReference{
  2093  							Kind: infrav1.AzureClusterIdentityKind,
  2094  						},
  2095  					},
  2096  				},
  2097  			}
  2098  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2099  				Spec: infrav1.AzureClusterIdentitySpec{
  2100  					Type:     infrav1.ServicePrincipal,
  2101  					ClientID: fakeClientID,
  2102  					TenantID: fakeTenantID,
  2103  				},
  2104  			}
  2105  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2106  
  2107  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2108  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2109  
  2110  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2111  				Cluster:      cluster,
  2112  				AzureCluster: azureCluster,
  2113  				Client:       fakeClient,
  2114  			})
  2115  			g.Expect(err).NotTo(HaveOccurred())
  2116  			got := clusterScope.GetPrivateDNSZoneName()
  2117  			g.Expect(tc.expectPrivateDNSZoneName).Should(Equal(got))
  2118  		})
  2119  	}
  2120  }
  2121  
  2122  func TestAPIServerLBPoolName(t *testing.T) {
  2123  	tests := []struct {
  2124  		lbName           string
  2125  		clusterName      string
  2126  		expectLBpoolName string
  2127  	}{
  2128  		{
  2129  			lbName:           "fake-lb-1",
  2130  			clusterName:      "my-cluster-1",
  2131  			expectLBpoolName: "fake-lb-1-backendPool",
  2132  		},
  2133  		{
  2134  			lbName:           "fake-lb-2",
  2135  			clusterName:      "my-cluster-2",
  2136  			expectLBpoolName: "fake-lb-2-backendPool",
  2137  		},
  2138  	}
  2139  	for _, tc := range tests {
  2140  		t.Run(tc.lbName, func(t *testing.T) {
  2141  			g := NewWithT(t)
  2142  			scheme := runtime.NewScheme()
  2143  			_ = infrav1.AddToScheme(scheme)
  2144  			_ = clusterv1.AddToScheme(scheme)
  2145  			_ = corev1.AddToScheme(scheme)
  2146  
  2147  			cluster := &clusterv1.Cluster{
  2148  				ObjectMeta: metav1.ObjectMeta{
  2149  					Name:      tc.clusterName,
  2150  					Namespace: "default",
  2151  				},
  2152  			}
  2153  			azureCluster := &infrav1.AzureCluster{
  2154  				ObjectMeta: metav1.ObjectMeta{
  2155  					Name: tc.clusterName,
  2156  					OwnerReferences: []metav1.OwnerReference{
  2157  						{
  2158  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2159  							Kind:       "Cluster",
  2160  							Name:       "my-cluster",
  2161  						},
  2162  					},
  2163  				},
  2164  				Spec: infrav1.AzureClusterSpec{
  2165  					NetworkSpec: infrav1.NetworkSpec{
  2166  						APIServerLB: infrav1.LoadBalancerSpec{
  2167  							Name: tc.lbName,
  2168  						},
  2169  					},
  2170  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2171  						SubscriptionID: "123",
  2172  						IdentityRef: &corev1.ObjectReference{
  2173  							Kind: infrav1.AzureClusterIdentityKind,
  2174  						},
  2175  					},
  2176  				},
  2177  			}
  2178  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2179  				Spec: infrav1.AzureClusterIdentitySpec{
  2180  					Type:     infrav1.ServicePrincipal,
  2181  					ClientID: fakeClientID,
  2182  					TenantID: fakeTenantID,
  2183  				},
  2184  			}
  2185  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2186  
  2187  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2188  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2189  
  2190  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2191  				Cluster:      cluster,
  2192  				AzureCluster: azureCluster,
  2193  				Client:       fakeClient,
  2194  			})
  2195  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2196  			g.Expect(err).NotTo(HaveOccurred())
  2197  			got := clusterScope.APIServerLBPoolName()
  2198  			g.Expect(tc.expectLBpoolName).Should(Equal(got))
  2199  		})
  2200  	}
  2201  }
  2202  
  2203  func TestOutboundLBName(t *testing.T) {
  2204  	tests := []struct {
  2205  		clusterName            string
  2206  		name                   string
  2207  		role                   string
  2208  		apiServerLB            *infrav1.LoadBalancerSpec
  2209  		controlPlaneOutboundLB *infrav1.LoadBalancerSpec
  2210  		nodeOutboundLB         *infrav1.LoadBalancerSpec
  2211  		expected               string
  2212  	}{
  2213  		{
  2214  			clusterName: "my-cluster",
  2215  			name:        "public cluster node outbound lb",
  2216  			role:        "node",
  2217  			expected:    "",
  2218  		},
  2219  		{
  2220  			clusterName: "my-cluster",
  2221  			name:        "public cluster control plane outbound lb",
  2222  			role:        "control-plane",
  2223  			expected:    "my-cluster-public-lb",
  2224  		},
  2225  		{
  2226  			clusterName:    "my-cluster",
  2227  			name:           "private cluster with node outbound lb",
  2228  			role:           "node",
  2229  			nodeOutboundLB: &infrav1.LoadBalancerSpec{},
  2230  			apiServerLB: &infrav1.LoadBalancerSpec{
  2231  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2232  					Type: "Internal",
  2233  				}},
  2234  			expected: "my-cluster",
  2235  		},
  2236  		{
  2237  			clusterName: "my-cluster",
  2238  			name:        "private cluster without node outbound lb",
  2239  			role:        "node",
  2240  			apiServerLB: &infrav1.LoadBalancerSpec{
  2241  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2242  					Type: "Internal",
  2243  				}},
  2244  			expected: "",
  2245  		},
  2246  		{
  2247  			clusterName:            "my-cluster",
  2248  			name:                   "private cluster with control plane outbound lb",
  2249  			role:                   "control-plane",
  2250  			controlPlaneOutboundLB: &infrav1.LoadBalancerSpec{Name: "cp-outbound-lb"},
  2251  			apiServerLB: &infrav1.LoadBalancerSpec{
  2252  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2253  					Type: "Internal",
  2254  				}},
  2255  			expected: "cp-outbound-lb",
  2256  		},
  2257  		{
  2258  			clusterName: "my-cluster",
  2259  			name:        "private cluster without control plane outbound lb",
  2260  			role:        "control-plane",
  2261  			apiServerLB: &infrav1.LoadBalancerSpec{
  2262  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2263  					Type: "Internal",
  2264  				}},
  2265  			expected: "",
  2266  		},
  2267  	}
  2268  
  2269  	for _, tc := range tests {
  2270  		t.Run(tc.name, func(t *testing.T) {
  2271  			g := NewWithT(t)
  2272  			scheme := runtime.NewScheme()
  2273  			_ = infrav1.AddToScheme(scheme)
  2274  			_ = clusterv1.AddToScheme(scheme)
  2275  			_ = corev1.AddToScheme(scheme)
  2276  
  2277  			cluster := &clusterv1.Cluster{
  2278  				ObjectMeta: metav1.ObjectMeta{
  2279  					Name:      tc.clusterName,
  2280  					Namespace: "default",
  2281  				},
  2282  			}
  2283  
  2284  			azureCluster := &infrav1.AzureCluster{
  2285  				ObjectMeta: metav1.ObjectMeta{
  2286  					Name: tc.clusterName,
  2287  					OwnerReferences: []metav1.OwnerReference{
  2288  						{
  2289  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2290  							Kind:       "Cluster",
  2291  							Name:       "my-cluster",
  2292  						},
  2293  					},
  2294  				},
  2295  				Spec: infrav1.AzureClusterSpec{
  2296  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2297  						SubscriptionID: "123",
  2298  						IdentityRef: &corev1.ObjectReference{
  2299  							Kind: infrav1.AzureClusterIdentityKind,
  2300  						},
  2301  					},
  2302  					NetworkSpec: infrav1.NetworkSpec{
  2303  						Subnets: infrav1.Subnets{
  2304  							{
  2305  								SubnetClassSpec: infrav1.SubnetClassSpec{
  2306  									Name: "node",
  2307  									Role: infrav1.SubnetNode,
  2308  								},
  2309  							},
  2310  						},
  2311  					},
  2312  				},
  2313  			}
  2314  
  2315  			if tc.apiServerLB != nil {
  2316  				azureCluster.Spec.NetworkSpec.APIServerLB = *tc.apiServerLB
  2317  			}
  2318  
  2319  			if tc.controlPlaneOutboundLB != nil {
  2320  				azureCluster.Spec.NetworkSpec.ControlPlaneOutboundLB = tc.controlPlaneOutboundLB
  2321  			}
  2322  
  2323  			if tc.nodeOutboundLB != nil {
  2324  				azureCluster.Spec.NetworkSpec.NodeOutboundLB = tc.nodeOutboundLB
  2325  			}
  2326  
  2327  			azureCluster.Default()
  2328  
  2329  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2330  				Spec: infrav1.AzureClusterIdentitySpec{
  2331  					Type:     infrav1.ServicePrincipal,
  2332  					ClientID: fakeClientID,
  2333  					TenantID: fakeTenantID,
  2334  				},
  2335  			}
  2336  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2337  
  2338  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2339  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2340  
  2341  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2342  				Cluster:      cluster,
  2343  				AzureCluster: azureCluster,
  2344  				Client:       fakeClient,
  2345  			})
  2346  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2347  			g.Expect(err).NotTo(HaveOccurred())
  2348  			got := clusterScope.OutboundLBName(tc.role)
  2349  			g.Expect(tc.expected).Should(Equal(got))
  2350  		})
  2351  	}
  2352  }
  2353  
  2354  func TestBackendPoolName(t *testing.T) {
  2355  	tests := []struct {
  2356  		name        string
  2357  		clusterName string
  2358  
  2359  		customAPIServerBackendPoolName    string
  2360  		customNodeBackendPoolName         string
  2361  		customControlPlaneBackendPoolName string
  2362  
  2363  		expectedAPIServerBackendPoolName    string
  2364  		expectedNodeBackendPoolName         string
  2365  		expectedControlPlaneBackendPoolName string
  2366  	}{
  2367  		{
  2368  			name:                                "With default backend pool names",
  2369  			clusterName:                         "my-cluster",
  2370  			expectedAPIServerBackendPoolName:    "APIServerLBName-backendPool",
  2371  			expectedNodeBackendPoolName:         "NodeOutboundLBName-outboundBackendPool",
  2372  			expectedControlPlaneBackendPoolName: "my-cluster-outbound-lb-outboundBackendPool",
  2373  		},
  2374  		{
  2375  			name:        "With custom node backend pool name",
  2376  			clusterName: "my-cluster",
  2377  
  2378  			// setting custom name for node pool name only, others should stay the same
  2379  			customNodeBackendPoolName: "custom-node-poolname",
  2380  
  2381  			expectedAPIServerBackendPoolName:    "APIServerLBName-backendPool",
  2382  			expectedNodeBackendPoolName:         "custom-node-poolname",
  2383  			expectedControlPlaneBackendPoolName: "my-cluster-outbound-lb-outboundBackendPool",
  2384  		},
  2385  		{
  2386  			name:        "With custom backends pool name",
  2387  			clusterName: "my-cluster",
  2388  
  2389  			// setting custom names for all backends pools
  2390  			customAPIServerBackendPoolName:    "custom-api-server-poolname",
  2391  			customNodeBackendPoolName:         "custom-node-poolname",
  2392  			customControlPlaneBackendPoolName: "custom-control-plane-poolname",
  2393  
  2394  			expectedAPIServerBackendPoolName:    "custom-api-server-poolname",
  2395  			expectedNodeBackendPoolName:         "custom-node-poolname",
  2396  			expectedControlPlaneBackendPoolName: "custom-control-plane-poolname",
  2397  		},
  2398  	}
  2399  	for _, tc := range tests {
  2400  		t.Run(tc.name, func(t *testing.T) {
  2401  			g := NewWithT(t)
  2402  			scheme := runtime.NewScheme()
  2403  			_ = infrav1.AddToScheme(scheme)
  2404  			_ = clusterv1.AddToScheme(scheme)
  2405  			_ = corev1.AddToScheme(scheme)
  2406  
  2407  			cluster := &clusterv1.Cluster{
  2408  				ObjectMeta: metav1.ObjectMeta{
  2409  					Name:      tc.clusterName,
  2410  					Namespace: "default",
  2411  				},
  2412  			}
  2413  
  2414  			azureCluster := &infrav1.AzureCluster{
  2415  				ObjectMeta: metav1.ObjectMeta{
  2416  					Name: tc.clusterName,
  2417  					OwnerReferences: []metav1.OwnerReference{
  2418  						{
  2419  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2420  							Kind:       "Cluster",
  2421  							Name:       tc.clusterName,
  2422  						},
  2423  					},
  2424  				},
  2425  				Spec: infrav1.AzureClusterSpec{
  2426  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2427  						SubscriptionID: "123",
  2428  						IdentityRef: &corev1.ObjectReference{
  2429  							Kind: infrav1.AzureClusterIdentityKind,
  2430  						},
  2431  					},
  2432  					NetworkSpec: infrav1.NetworkSpec{
  2433  						Subnets: infrav1.Subnets{
  2434  							{
  2435  								SubnetClassSpec: infrav1.SubnetClassSpec{
  2436  									Role: infrav1.SubnetNode,
  2437  									Name: "node",
  2438  								},
  2439  							},
  2440  						},
  2441  						APIServerLB: infrav1.LoadBalancerSpec{
  2442  							Name: "APIServerLBName",
  2443  						},
  2444  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
  2445  							Name: "ControlPlaneOutboundLBName",
  2446  						},
  2447  						NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2448  							Name: "NodeOutboundLBName",
  2449  						},
  2450  					},
  2451  				},
  2452  			}
  2453  
  2454  			azureCluster.Default()
  2455  
  2456  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2457  				Spec: infrav1.AzureClusterIdentitySpec{
  2458  					Type:     infrav1.ServicePrincipal,
  2459  					ClientID: fakeClientID,
  2460  					TenantID: fakeTenantID,
  2461  				},
  2462  			}
  2463  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2464  
  2465  			if tc.customAPIServerBackendPoolName != "" {
  2466  				azureCluster.Spec.NetworkSpec.APIServerLB.BackendPool.Name = tc.customAPIServerBackendPoolName
  2467  			}
  2468  
  2469  			if tc.customNodeBackendPoolName != "" {
  2470  				azureCluster.Spec.NetworkSpec.NodeOutboundLB.BackendPool.Name = tc.customNodeBackendPoolName
  2471  			}
  2472  
  2473  			if tc.customControlPlaneBackendPoolName != "" {
  2474  				azureCluster.Spec.NetworkSpec.ControlPlaneOutboundLB.BackendPool.Name = tc.customControlPlaneBackendPoolName
  2475  			}
  2476  
  2477  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2478  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2479  
  2480  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2481  				Cluster:      cluster,
  2482  				AzureCluster: azureCluster,
  2483  				Client:       fakeClient,
  2484  			})
  2485  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2486  			g.Expect(err).NotTo(HaveOccurred())
  2487  			got := clusterScope.LBSpecs()
  2488  			g.Expect(got).To(HaveLen(3))
  2489  
  2490  			// API server backend pool name
  2491  			apiServerLBSpec := got[0].(*loadbalancers.LBSpec)
  2492  			g.Expect(apiServerLBSpec.BackendPoolName).To(Equal(tc.expectedAPIServerBackendPoolName))
  2493  			g.Expect(apiServerLBSpec.Role).To(Equal(infrav1.APIServerRole))
  2494  
  2495  			// Node backend pool name
  2496  			NodeLBSpec := got[1].(*loadbalancers.LBSpec)
  2497  			g.Expect(NodeLBSpec.BackendPoolName).To(Equal(tc.expectedNodeBackendPoolName))
  2498  			g.Expect(NodeLBSpec.Role).To(Equal(infrav1.NodeOutboundRole))
  2499  
  2500  			// Control Plane backend pool name
  2501  			controlPlaneLBSpec := got[2].(*loadbalancers.LBSpec)
  2502  			g.Expect(controlPlaneLBSpec.BackendPoolName).To(Equal(tc.expectedControlPlaneBackendPoolName))
  2503  			g.Expect(controlPlaneLBSpec.Role).To(Equal(infrav1.ControlPlaneOutboundRole))
  2504  		})
  2505  	}
  2506  }
  2507  
  2508  func TestOutboundPoolName(t *testing.T) {
  2509  	tests := []struct {
  2510  		name                   string
  2511  		clusterName            string
  2512  		loadBalancerName       string
  2513  		expectOutboundPoolName string
  2514  	}{
  2515  		{
  2516  			name:                   "Empty loadBalancerName",
  2517  			clusterName:            "my-cluster",
  2518  			loadBalancerName:       "",
  2519  			expectOutboundPoolName: "",
  2520  		},
  2521  		{
  2522  			name:                   "Non empty loadBalancerName",
  2523  			clusterName:            "my-cluster",
  2524  			loadBalancerName:       "my-loadbalancer",
  2525  			expectOutboundPoolName: "my-loadbalancer-outboundBackendPool",
  2526  		},
  2527  	}
  2528  	for _, tc := range tests {
  2529  		t.Run(tc.name, func(t *testing.T) {
  2530  			g := NewWithT(t)
  2531  			scheme := runtime.NewScheme()
  2532  			_ = infrav1.AddToScheme(scheme)
  2533  			_ = clusterv1.AddToScheme(scheme)
  2534  			_ = corev1.AddToScheme(scheme)
  2535  
  2536  			cluster := &clusterv1.Cluster{
  2537  				ObjectMeta: metav1.ObjectMeta{
  2538  					Name:      tc.clusterName,
  2539  					Namespace: "default",
  2540  				},
  2541  			}
  2542  			azureCluster := &infrav1.AzureCluster{
  2543  				ObjectMeta: metav1.ObjectMeta{
  2544  					Name: tc.clusterName,
  2545  					OwnerReferences: []metav1.OwnerReference{
  2546  						{
  2547  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2548  							Kind:       "Cluster",
  2549  							Name:       "my-cluster",
  2550  						},
  2551  					},
  2552  				},
  2553  				Spec: infrav1.AzureClusterSpec{
  2554  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2555  						SubscriptionID: "123",
  2556  						IdentityRef: &corev1.ObjectReference{
  2557  							Kind: infrav1.AzureClusterIdentityKind,
  2558  						},
  2559  					},
  2560  				},
  2561  			}
  2562  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2563  				Spec: infrav1.AzureClusterIdentitySpec{
  2564  					Type:     infrav1.ServicePrincipal,
  2565  					ClientID: fakeClientID,
  2566  					TenantID: fakeTenantID,
  2567  				},
  2568  			}
  2569  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2570  
  2571  			if tc.loadBalancerName != "" {
  2572  				azureCluster.Spec.NetworkSpec.NodeOutboundLB = &infrav1.LoadBalancerSpec{
  2573  					Name: tc.loadBalancerName,
  2574  				}
  2575  			}
  2576  
  2577  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2578  			azureCluster.Default()
  2579  
  2580  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2581  
  2582  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2583  				Cluster:      cluster,
  2584  				AzureCluster: azureCluster,
  2585  				Client:       fakeClient,
  2586  			})
  2587  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2588  			g.Expect(err).NotTo(HaveOccurred())
  2589  			got := clusterScope.OutboundPoolName(infrav1.Node)
  2590  			g.Expect(tc.expectOutboundPoolName).Should(Equal(got))
  2591  		})
  2592  	}
  2593  }
  2594  
  2595  func TestGenerateFQDN(t *testing.T) {
  2596  	tests := []struct {
  2597  		clusterName    string
  2598  		ipName         string
  2599  		subscriptionID string
  2600  		resourceGroup  string
  2601  		location       string
  2602  		expectFQDN     string
  2603  	}{
  2604  		{
  2605  			clusterName:    "my-cluster",
  2606  			ipName:         "172.123.45.78",
  2607  			subscriptionID: "123",
  2608  			resourceGroup:  "my-rg",
  2609  			location:       "eastus",
  2610  		},
  2611  		{
  2612  			clusterName:    "my-cluster-1",
  2613  			ipName:         "172.123.45.79",
  2614  			subscriptionID: "567",
  2615  			resourceGroup:  "my-rg-1",
  2616  			location:       "westus",
  2617  		},
  2618  		{
  2619  			clusterName:    "my-cluster-2",
  2620  			ipName:         "172.123.45.80",
  2621  			subscriptionID: "183",
  2622  			resourceGroup:  "my-rg-2",
  2623  			location:       "centralasia",
  2624  		},
  2625  	}
  2626  	for _, tc := range tests {
  2627  		t.Run(tc.clusterName, func(t *testing.T) {
  2628  			g := NewWithT(t)
  2629  			scheme := runtime.NewScheme()
  2630  			_ = infrav1.AddToScheme(scheme)
  2631  			_ = clusterv1.AddToScheme(scheme)
  2632  			_ = corev1.AddToScheme(scheme)
  2633  
  2634  			cluster := &clusterv1.Cluster{
  2635  				ObjectMeta: metav1.ObjectMeta{
  2636  					Name:      tc.clusterName,
  2637  					Namespace: "default",
  2638  				},
  2639  			}
  2640  			azureCluster := &infrav1.AzureCluster{
  2641  				ObjectMeta: metav1.ObjectMeta{
  2642  					Name: tc.clusterName,
  2643  					OwnerReferences: []metav1.OwnerReference{
  2644  						{
  2645  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2646  							Kind:       "Cluster",
  2647  							Name:       "my-cluster",
  2648  						},
  2649  					},
  2650  				},
  2651  				Spec: infrav1.AzureClusterSpec{
  2652  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2653  						SubscriptionID: "123",
  2654  						Location:       tc.location,
  2655  						IdentityRef: &corev1.ObjectReference{
  2656  							Kind: infrav1.AzureClusterIdentityKind,
  2657  						},
  2658  					},
  2659  					ResourceGroup: tc.resourceGroup,
  2660  				},
  2661  			}
  2662  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2663  				Spec: infrav1.AzureClusterIdentitySpec{
  2664  					Type:     infrav1.ServicePrincipal,
  2665  					ClientID: fakeClientID,
  2666  					TenantID: fakeTenantID,
  2667  				},
  2668  			}
  2669  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2670  
  2671  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2672  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2673  
  2674  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2675  				Cluster:      cluster,
  2676  				AzureCluster: azureCluster,
  2677  				Client:       fakeClient,
  2678  			})
  2679  			g.Expect(err).NotTo(HaveOccurred())
  2680  			got := clusterScope.GenerateFQDN(tc.ipName)
  2681  			g.Expect(got).Should(ContainSubstring(tc.clusterName))
  2682  			g.Expect(got).Should(ContainSubstring(tc.location))
  2683  		})
  2684  	}
  2685  }
  2686  
  2687  func TestAdditionalTags(t *testing.T) {
  2688  	tests := []struct {
  2689  		name                       string
  2690  		clusterName                string
  2691  		azureClusterAdditionalTags infrav1.Tags
  2692  		expectTags                 infrav1.Tags
  2693  	}{
  2694  		{
  2695  			name:        "Nil tags",
  2696  			clusterName: "my-cluster",
  2697  			expectTags:  infrav1.Tags{},
  2698  		},
  2699  
  2700  		{
  2701  			name:        "Single tag present in azure cluster spec",
  2702  			clusterName: "my-cluster",
  2703  			azureClusterAdditionalTags: infrav1.Tags{
  2704  				"fake-id-1": "fake-value-1",
  2705  			},
  2706  			expectTags: infrav1.Tags{
  2707  				"fake-id-1": "fake-value-1",
  2708  			},
  2709  		},
  2710  		{
  2711  			name:        "Multiple tags present in azure cluster spec",
  2712  			clusterName: "my-cluster",
  2713  			azureClusterAdditionalTags: infrav1.Tags{
  2714  				"fake-id-1": "fake-value-1",
  2715  				"fake-id-2": "fake-value-2",
  2716  				"fake-id-3": "fake-value-3",
  2717  			},
  2718  			expectTags: infrav1.Tags{
  2719  				"fake-id-1": "fake-value-1",
  2720  				"fake-id-2": "fake-value-2",
  2721  				"fake-id-3": "fake-value-3",
  2722  			},
  2723  		},
  2724  	}
  2725  	for _, tc := range tests {
  2726  		t.Run(tc.name, func(t *testing.T) {
  2727  			g := NewWithT(t)
  2728  			scheme := runtime.NewScheme()
  2729  			_ = infrav1.AddToScheme(scheme)
  2730  			_ = clusterv1.AddToScheme(scheme)
  2731  			_ = corev1.AddToScheme(scheme)
  2732  
  2733  			cluster := &clusterv1.Cluster{
  2734  				ObjectMeta: metav1.ObjectMeta{
  2735  					Name:      tc.clusterName,
  2736  					Namespace: "default",
  2737  				},
  2738  			}
  2739  			azureCluster := &infrav1.AzureCluster{
  2740  				ObjectMeta: metav1.ObjectMeta{
  2741  					Name: tc.clusterName,
  2742  					OwnerReferences: []metav1.OwnerReference{
  2743  						{
  2744  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2745  							Kind:       "Cluster",
  2746  							Name:       "my-cluster",
  2747  						},
  2748  					},
  2749  				},
  2750  				Spec: infrav1.AzureClusterSpec{
  2751  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2752  						SubscriptionID: "123",
  2753  						AdditionalTags: tc.azureClusterAdditionalTags,
  2754  						IdentityRef: &corev1.ObjectReference{
  2755  							Kind: infrav1.AzureClusterIdentityKind,
  2756  						},
  2757  					},
  2758  				},
  2759  			}
  2760  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2761  				Spec: infrav1.AzureClusterIdentitySpec{
  2762  					Type:     infrav1.ServicePrincipal,
  2763  					ClientID: fakeClientID,
  2764  					TenantID: fakeTenantID,
  2765  				},
  2766  			}
  2767  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2768  
  2769  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2770  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2771  
  2772  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2773  				Cluster:      cluster,
  2774  				AzureCluster: azureCluster,
  2775  				Client:       fakeClient,
  2776  			})
  2777  			g.Expect(err).NotTo(HaveOccurred())
  2778  			got := clusterScope.AdditionalTags()
  2779  			g.Expect(tc.expectTags).Should(Equal(got))
  2780  		})
  2781  	}
  2782  }
  2783  
  2784  func TestAPIServerPort(t *testing.T) {
  2785  	tests := []struct {
  2786  		name                string
  2787  		clusterName         string
  2788  		clusterNetowrk      *clusterv1.ClusterNetwork
  2789  		expectAPIServerPort int32
  2790  	}{
  2791  		{
  2792  			name:                "Nil cluster network",
  2793  			clusterName:         "my-cluster",
  2794  			expectAPIServerPort: 6443,
  2795  		},
  2796  
  2797  		{
  2798  			name:                "Non nil cluster network but nil apiserverport",
  2799  			clusterName:         "my-cluster",
  2800  			clusterNetowrk:      &clusterv1.ClusterNetwork{},
  2801  			expectAPIServerPort: 6443,
  2802  		},
  2803  		{
  2804  			name:        "Non nil cluster network and non nil apiserverport",
  2805  			clusterName: "my-cluster",
  2806  			clusterNetowrk: &clusterv1.ClusterNetwork{
  2807  				APIServerPort: ptr.To[int32](7000),
  2808  			},
  2809  			expectAPIServerPort: 7000,
  2810  		},
  2811  	}
  2812  	for _, tc := range tests {
  2813  		t.Run(tc.name, func(t *testing.T) {
  2814  			g := NewWithT(t)
  2815  			scheme := runtime.NewScheme()
  2816  			_ = infrav1.AddToScheme(scheme)
  2817  			_ = clusterv1.AddToScheme(scheme)
  2818  			_ = corev1.AddToScheme(scheme)
  2819  
  2820  			cluster := &clusterv1.Cluster{
  2821  				ObjectMeta: metav1.ObjectMeta{
  2822  					Name:      tc.clusterName,
  2823  					Namespace: "default",
  2824  				},
  2825  				Spec: clusterv1.ClusterSpec{
  2826  					ClusterNetwork: tc.clusterNetowrk,
  2827  				},
  2828  			}
  2829  			azureCluster := &infrav1.AzureCluster{
  2830  				ObjectMeta: metav1.ObjectMeta{
  2831  					Name: tc.clusterName,
  2832  					OwnerReferences: []metav1.OwnerReference{
  2833  						{
  2834  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2835  							Kind:       "Cluster",
  2836  							Name:       "my-cluster",
  2837  						},
  2838  					},
  2839  				},
  2840  				Spec: infrav1.AzureClusterSpec{
  2841  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2842  						SubscriptionID: "123",
  2843  						IdentityRef: &corev1.ObjectReference{
  2844  							Kind: infrav1.AzureClusterIdentityKind,
  2845  						},
  2846  					},
  2847  				},
  2848  			}
  2849  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2850  				Spec: infrav1.AzureClusterIdentitySpec{
  2851  					Type:     infrav1.ServicePrincipal,
  2852  					ClientID: fakeClientID,
  2853  					TenantID: fakeTenantID,
  2854  				},
  2855  			}
  2856  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2857  
  2858  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2859  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2860  
  2861  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2862  				Cluster:      cluster,
  2863  				AzureCluster: azureCluster,
  2864  				Client:       fakeClient,
  2865  			})
  2866  			g.Expect(err).NotTo(HaveOccurred())
  2867  			got := clusterScope.APIServerPort()
  2868  			g.Expect(tc.expectAPIServerPort).Should(Equal(got))
  2869  		})
  2870  	}
  2871  }
  2872  
  2873  func TestFailureDomains(t *testing.T) {
  2874  	tests := []struct {
  2875  		name                 string
  2876  		expectFailureDomains []*string
  2877  		clusterName          string
  2878  		azureClusterStatus   infrav1.AzureClusterStatus
  2879  	}{
  2880  		{
  2881  			name:                 "Empty azure cluster status",
  2882  			expectFailureDomains: []*string{},
  2883  			clusterName:          "my-cluster",
  2884  		},
  2885  
  2886  		{
  2887  			name:                 "Single failure domain present in azure cluster status",
  2888  			expectFailureDomains: []*string{ptr.To("failure-domain-id")},
  2889  			clusterName:          "my-cluster",
  2890  			azureClusterStatus: infrav1.AzureClusterStatus{
  2891  				FailureDomains: map[string]clusterv1.FailureDomainSpec{
  2892  					"failure-domain-id": {},
  2893  				},
  2894  			},
  2895  		},
  2896  		{
  2897  			name:                 "Multiple failure domains present in azure cluster status",
  2898  			expectFailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
  2899  			clusterName:          "my-cluster",
  2900  			azureClusterStatus: infrav1.AzureClusterStatus{
  2901  				FailureDomains: map[string]clusterv1.FailureDomainSpec{
  2902  					"failure-domain-id-1": {},
  2903  					"failure-domain-id-2": {},
  2904  					"failure-domain-id-3": {},
  2905  				},
  2906  			},
  2907  		},
  2908  	}
  2909  	for _, tc := range tests {
  2910  		t.Run(tc.name, func(t *testing.T) {
  2911  			g := NewWithT(t)
  2912  			scheme := runtime.NewScheme()
  2913  			_ = infrav1.AddToScheme(scheme)
  2914  			_ = clusterv1.AddToScheme(scheme)
  2915  			_ = corev1.AddToScheme(scheme)
  2916  
  2917  			cluster := &clusterv1.Cluster{
  2918  				ObjectMeta: metav1.ObjectMeta{
  2919  					Name:      tc.clusterName,
  2920  					Namespace: "default",
  2921  				},
  2922  			}
  2923  			azureCluster := &infrav1.AzureCluster{
  2924  				ObjectMeta: metav1.ObjectMeta{
  2925  					Name: tc.clusterName,
  2926  					OwnerReferences: []metav1.OwnerReference{
  2927  						{
  2928  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2929  							Kind:       "Cluster",
  2930  							Name:       "my-cluster",
  2931  						},
  2932  					},
  2933  				},
  2934  				Spec: infrav1.AzureClusterSpec{
  2935  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2936  						SubscriptionID: "123",
  2937  						IdentityRef: &corev1.ObjectReference{
  2938  							Kind: infrav1.AzureClusterIdentityKind,
  2939  						},
  2940  					},
  2941  				},
  2942  				Status: tc.azureClusterStatus,
  2943  			}
  2944  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2945  				Spec: infrav1.AzureClusterIdentitySpec{
  2946  					Type:     infrav1.ServicePrincipal,
  2947  					ClientID: fakeClientID,
  2948  					TenantID: fakeTenantID,
  2949  				},
  2950  			}
  2951  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2952  
  2953  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2954  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2955  
  2956  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2957  				Cluster:      cluster,
  2958  				AzureCluster: azureCluster,
  2959  				Client:       fakeClient,
  2960  			})
  2961  			g.Expect(err).NotTo(HaveOccurred())
  2962  			got := clusterScope.FailureDomains()
  2963  			g.Expect(tc.expectFailureDomains).Should(ConsistOf(got))
  2964  		})
  2965  	}
  2966  }
  2967  
  2968  func TestClusterScope_LBSpecs(t *testing.T) {
  2969  	tests := []struct {
  2970  		name         string
  2971  		azureCluster *infrav1.AzureCluster
  2972  		want         []azure.ResourceSpecGetter
  2973  	}{
  2974  		{
  2975  			name: "API Server LB, Control Plane Oubound LB, and Node Outbound LB",
  2976  			azureCluster: &infrav1.AzureCluster{
  2977  				ObjectMeta: metav1.ObjectMeta{
  2978  					Name: "my-cluster",
  2979  				},
  2980  				Spec: infrav1.AzureClusterSpec{
  2981  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2982  						AdditionalTags: infrav1.Tags{
  2983  							"foo": "bar",
  2984  						},
  2985  						SubscriptionID: "123",
  2986  						Location:       "westus2",
  2987  						IdentityRef: &corev1.ObjectReference{
  2988  							Kind: infrav1.AzureClusterIdentityKind,
  2989  						},
  2990  					},
  2991  					ResourceGroup: "my-rg",
  2992  					NetworkSpec: infrav1.NetworkSpec{
  2993  						Vnet: infrav1.VnetSpec{
  2994  							Name:          "my-vnet",
  2995  							ResourceGroup: "my-rg",
  2996  						},
  2997  						Subnets: []infrav1.SubnetSpec{
  2998  							{
  2999  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3000  									Name: "cp-subnet",
  3001  									Role: infrav1.SubnetControlPlane,
  3002  								},
  3003  							},
  3004  							{
  3005  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3006  									Name: "node-subnet",
  3007  									Role: infrav1.SubnetNode,
  3008  								},
  3009  							},
  3010  						},
  3011  						APIServerLB: infrav1.LoadBalancerSpec{
  3012  							Name: "api-server-lb",
  3013  							BackendPool: infrav1.BackendPool{
  3014  								Name: "api-server-lb-backend-pool",
  3015  							},
  3016  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3017  								Type:                 infrav1.Public,
  3018  								IdleTimeoutInMinutes: ptr.To[int32](30),
  3019  								SKU:                  infrav1.SKUStandard,
  3020  							},
  3021  							FrontendIPs: []infrav1.FrontendIP{
  3022  								{
  3023  									Name: "api-server-lb-frontend-ip",
  3024  									PublicIP: &infrav1.PublicIPSpec{
  3025  										Name: "api-server-lb-frontend-ip",
  3026  									},
  3027  								},
  3028  							},
  3029  						},
  3030  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
  3031  							Name: "cp-outbound-lb",
  3032  							BackendPool: infrav1.BackendPool{
  3033  								Name: "cp-outbound-backend-pool",
  3034  							},
  3035  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3036  								Type:                 infrav1.Public,
  3037  								IdleTimeoutInMinutes: ptr.To[int32](15),
  3038  								SKU:                  infrav1.SKUStandard,
  3039  							},
  3040  							FrontendIPs: []infrav1.FrontendIP{
  3041  								{
  3042  									Name: "cp-outbound-lb-frontend-ip",
  3043  									PublicIP: &infrav1.PublicIPSpec{
  3044  										Name: "cp-outbound-lb-frontend-ip",
  3045  									},
  3046  								},
  3047  							},
  3048  						},
  3049  						NodeOutboundLB: &infrav1.LoadBalancerSpec{
  3050  							Name: "node-outbound-lb",
  3051  							BackendPool: infrav1.BackendPool{
  3052  								Name: "node-outbound-backend-pool",
  3053  							},
  3054  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3055  								Type:                 infrav1.Public,
  3056  								IdleTimeoutInMinutes: ptr.To[int32](50),
  3057  								SKU:                  infrav1.SKUStandard,
  3058  							},
  3059  							FrontendIPs: []infrav1.FrontendIP{
  3060  								{
  3061  									Name: "node-outbound-lb-frontend-ip",
  3062  									PublicIP: &infrav1.PublicIPSpec{
  3063  										Name: "node-outbound-lb-frontend-ip",
  3064  									},
  3065  								},
  3066  							},
  3067  						},
  3068  					},
  3069  				},
  3070  			},
  3071  			want: []azure.ResourceSpecGetter{
  3072  				&loadbalancers.LBSpec{
  3073  					Name:              "api-server-lb",
  3074  					ResourceGroup:     "my-rg",
  3075  					SubscriptionID:    "123",
  3076  					ClusterName:       "my-cluster",
  3077  					Location:          "westus2",
  3078  					VNetName:          "my-vnet",
  3079  					VNetResourceGroup: "my-rg",
  3080  					SubnetName:        "cp-subnet",
  3081  					FrontendIPConfigs: []infrav1.FrontendIP{
  3082  						{
  3083  							Name: "api-server-lb-frontend-ip",
  3084  							PublicIP: &infrav1.PublicIPSpec{
  3085  								Name: "api-server-lb-frontend-ip",
  3086  							},
  3087  						},
  3088  					},
  3089  					APIServerPort:        6443,
  3090  					Type:                 infrav1.Public,
  3091  					SKU:                  infrav1.SKUStandard,
  3092  					Role:                 infrav1.APIServerRole,
  3093  					BackendPoolName:      "api-server-lb-backend-pool",
  3094  					IdleTimeoutInMinutes: ptr.To[int32](30),
  3095  					AdditionalTags: infrav1.Tags{
  3096  						"foo": "bar",
  3097  					},
  3098  				},
  3099  				&loadbalancers.LBSpec{
  3100  					Name:              "node-outbound-lb",
  3101  					ResourceGroup:     "my-rg",
  3102  					SubscriptionID:    "123",
  3103  					ClusterName:       "my-cluster",
  3104  					Location:          "westus2",
  3105  					VNetName:          "my-vnet",
  3106  					VNetResourceGroup: "my-rg",
  3107  					FrontendIPConfigs: []infrav1.FrontendIP{
  3108  						{
  3109  							Name: "node-outbound-lb-frontend-ip",
  3110  							PublicIP: &infrav1.PublicIPSpec{
  3111  								Name: "node-outbound-lb-frontend-ip",
  3112  							},
  3113  						},
  3114  					},
  3115  					Type:                 infrav1.Public,
  3116  					SKU:                  infrav1.SKUStandard,
  3117  					Role:                 infrav1.NodeOutboundRole,
  3118  					BackendPoolName:      "node-outbound-backend-pool",
  3119  					IdleTimeoutInMinutes: ptr.To[int32](50),
  3120  					AdditionalTags: infrav1.Tags{
  3121  						"foo": "bar",
  3122  					},
  3123  				},
  3124  				&loadbalancers.LBSpec{
  3125  					Name:              "cp-outbound-lb",
  3126  					ResourceGroup:     "my-rg",
  3127  					SubscriptionID:    "123",
  3128  					ClusterName:       "my-cluster",
  3129  					Location:          "westus2",
  3130  					VNetName:          "my-vnet",
  3131  					VNetResourceGroup: "my-rg",
  3132  					FrontendIPConfigs: []infrav1.FrontendIP{
  3133  						{
  3134  							Name: "cp-outbound-lb-frontend-ip",
  3135  							PublicIP: &infrav1.PublicIPSpec{
  3136  								Name: "cp-outbound-lb-frontend-ip",
  3137  							},
  3138  						},
  3139  					},
  3140  					Type:                 infrav1.Public,
  3141  					SKU:                  infrav1.SKUStandard,
  3142  					BackendPoolName:      "cp-outbound-backend-pool",
  3143  					IdleTimeoutInMinutes: ptr.To[int32](15),
  3144  					Role:                 infrav1.ControlPlaneOutboundRole,
  3145  					AdditionalTags: infrav1.Tags{
  3146  						"foo": "bar",
  3147  					},
  3148  				},
  3149  			},
  3150  		},
  3151  		{
  3152  			name: "Private API Server LB",
  3153  			azureCluster: &infrav1.AzureCluster{
  3154  				ObjectMeta: metav1.ObjectMeta{
  3155  					Name: "my-cluster",
  3156  				},
  3157  				Spec: infrav1.AzureClusterSpec{
  3158  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3159  						SubscriptionID: "123",
  3160  						Location:       "westus2",
  3161  						IdentityRef: &corev1.ObjectReference{
  3162  							Kind: infrav1.AzureClusterIdentityKind,
  3163  						},
  3164  					},
  3165  					ResourceGroup: "my-rg",
  3166  					NetworkSpec: infrav1.NetworkSpec{
  3167  						Vnet: infrav1.VnetSpec{
  3168  							Name:          "my-vnet",
  3169  							ResourceGroup: "my-rg",
  3170  						},
  3171  						Subnets: []infrav1.SubnetSpec{
  3172  							{
  3173  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3174  									Name: "cp-subnet",
  3175  									Role: infrav1.SubnetControlPlane,
  3176  								},
  3177  							},
  3178  							{
  3179  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3180  									Name: "node-subnet",
  3181  									Role: infrav1.SubnetNode,
  3182  								},
  3183  							},
  3184  						},
  3185  						APIServerLB: infrav1.LoadBalancerSpec{
  3186  							Name: "api-server-lb",
  3187  							BackendPool: infrav1.BackendPool{
  3188  								Name: "api-server-lb-backend-pool",
  3189  							},
  3190  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3191  								Type:                 infrav1.Internal,
  3192  								IdleTimeoutInMinutes: ptr.To[int32](30),
  3193  								SKU:                  infrav1.SKUStandard,
  3194  							},
  3195  						},
  3196  					},
  3197  				},
  3198  			},
  3199  			want: []azure.ResourceSpecGetter{
  3200  				&loadbalancers.LBSpec{
  3201  					Name:                 "api-server-lb",
  3202  					ResourceGroup:        "my-rg",
  3203  					SubscriptionID:       "123",
  3204  					ClusterName:          "my-cluster",
  3205  					Location:             "westus2",
  3206  					VNetName:             "my-vnet",
  3207  					VNetResourceGroup:    "my-rg",
  3208  					SubnetName:           "cp-subnet",
  3209  					APIServerPort:        6443,
  3210  					Type:                 infrav1.Internal,
  3211  					SKU:                  infrav1.SKUStandard,
  3212  					Role:                 infrav1.APIServerRole,
  3213  					BackendPoolName:      "api-server-lb-backend-pool",
  3214  					IdleTimeoutInMinutes: ptr.To[int32](30),
  3215  					AdditionalTags:       infrav1.Tags{},
  3216  				},
  3217  			},
  3218  		},
  3219  	}
  3220  	for _, tc := range tests {
  3221  		tc := tc
  3222  		t.Run(tc.name, func(t *testing.T) {
  3223  			t.Parallel()
  3224  			g := NewWithT(t)
  3225  			scheme := runtime.NewScheme()
  3226  			_ = infrav1.AddToScheme(scheme)
  3227  			_ = clusterv1.AddToScheme(scheme)
  3228  			_ = corev1.AddToScheme(scheme)
  3229  
  3230  			cluster := &clusterv1.Cluster{
  3231  				ObjectMeta: metav1.ObjectMeta{
  3232  					Name:      tc.azureCluster.Name,
  3233  					Namespace: "default",
  3234  				},
  3235  			}
  3236  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3237  				Spec: infrav1.AzureClusterIdentitySpec{
  3238  					Type:     infrav1.ServicePrincipal,
  3239  					ClientID: fakeClientID,
  3240  					TenantID: fakeTenantID,
  3241  				},
  3242  			}
  3243  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3244  
  3245  			initObjects := []runtime.Object{cluster, tc.azureCluster, fakeIdentity, fakeSecret}
  3246  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3247  
  3248  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3249  				Cluster:      cluster,
  3250  				AzureCluster: tc.azureCluster,
  3251  				Client:       fakeClient,
  3252  			})
  3253  			g.Expect(err).NotTo(HaveOccurred())
  3254  			if got := clusterScope.LBSpecs(); !reflect.DeepEqual(got, tc.want) {
  3255  				t.Errorf("LBSpecs() diff between expected result and actual result (%v): %s", got, cmp.Diff(tc.want, got))
  3256  			}
  3257  		})
  3258  	}
  3259  }
  3260  
  3261  func TestExtendedLocationName(t *testing.T) {
  3262  	tests := []struct {
  3263  		name             string
  3264  		clusterName      string
  3265  		extendedLocation infrav1.ExtendedLocationSpec
  3266  	}{
  3267  		{
  3268  			name:        "Empty extendedLocatioName",
  3269  			clusterName: "my-cluster",
  3270  			extendedLocation: infrav1.ExtendedLocationSpec{
  3271  				Name: "",
  3272  				Type: "",
  3273  			},
  3274  		},
  3275  		{
  3276  			name:        "Non empty extendedLocationName",
  3277  			clusterName: "my-cluster",
  3278  			extendedLocation: infrav1.ExtendedLocationSpec{
  3279  				Name: "ex-loc-name",
  3280  				Type: "ex-loc-type",
  3281  			},
  3282  		},
  3283  	}
  3284  	for _, tc := range tests {
  3285  		t.Run(tc.name, func(t *testing.T) {
  3286  			g := NewWithT(t)
  3287  			scheme := runtime.NewScheme()
  3288  			_ = infrav1.AddToScheme(scheme)
  3289  			_ = clusterv1.AddToScheme(scheme)
  3290  			_ = corev1.AddToScheme(scheme)
  3291  
  3292  			cluster := &clusterv1.Cluster{
  3293  				ObjectMeta: metav1.ObjectMeta{
  3294  					Name:      tc.clusterName,
  3295  					Namespace: "default",
  3296  				},
  3297  			}
  3298  
  3299  			azureCluster := &infrav1.AzureCluster{
  3300  				ObjectMeta: metav1.ObjectMeta{
  3301  					Name: tc.clusterName,
  3302  					OwnerReferences: []metav1.OwnerReference{
  3303  						{
  3304  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3305  							Kind:       "Cluster",
  3306  							Name:       "my-cluster",
  3307  						},
  3308  					},
  3309  				},
  3310  				Spec: infrav1.AzureClusterSpec{
  3311  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3312  						SubscriptionID: "123",
  3313  						ExtendedLocation: &infrav1.ExtendedLocationSpec{
  3314  							Name: tc.extendedLocation.Name,
  3315  							Type: tc.extendedLocation.Type,
  3316  						},
  3317  						IdentityRef: &corev1.ObjectReference{
  3318  							Kind: infrav1.AzureClusterIdentityKind,
  3319  						},
  3320  					},
  3321  				},
  3322  			}
  3323  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3324  				Spec: infrav1.AzureClusterIdentitySpec{
  3325  					Type:     infrav1.ServicePrincipal,
  3326  					ClientID: fakeClientID,
  3327  					TenantID: fakeTenantID,
  3328  				},
  3329  			}
  3330  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3331  
  3332  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3333  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3334  
  3335  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3336  				Cluster:      cluster,
  3337  				AzureCluster: azureCluster,
  3338  				Client:       fakeClient,
  3339  			})
  3340  
  3341  			g.Expect(err).NotTo(HaveOccurred())
  3342  			got := clusterScope.ExtendedLocationName()
  3343  			g.Expect(tc.extendedLocation.Name).Should(Equal(got))
  3344  		})
  3345  	}
  3346  }
  3347  
  3348  func TestExtendedLocationType(t *testing.T) {
  3349  	tests := []struct {
  3350  		name             string
  3351  		clusterName      string
  3352  		extendedLocation infrav1.ExtendedLocationSpec
  3353  	}{
  3354  		{
  3355  			name:        "Empty extendedLocatioType",
  3356  			clusterName: "my-cluster",
  3357  			extendedLocation: infrav1.ExtendedLocationSpec{
  3358  				Name: "",
  3359  				Type: "",
  3360  			},
  3361  		},
  3362  		{
  3363  			name:        "Non empty extendedLocationType",
  3364  			clusterName: "my-cluster",
  3365  			extendedLocation: infrav1.ExtendedLocationSpec{
  3366  				Name: "ex-loc-name",
  3367  				Type: "ex-loc-type",
  3368  			},
  3369  		},
  3370  	}
  3371  	for _, tc := range tests {
  3372  		t.Run(tc.name, func(t *testing.T) {
  3373  			g := NewWithT(t)
  3374  			scheme := runtime.NewScheme()
  3375  			_ = infrav1.AddToScheme(scheme)
  3376  			_ = clusterv1.AddToScheme(scheme)
  3377  			_ = corev1.AddToScheme(scheme)
  3378  
  3379  			cluster := &clusterv1.Cluster{
  3380  				ObjectMeta: metav1.ObjectMeta{
  3381  					Name:      tc.clusterName,
  3382  					Namespace: "default",
  3383  				},
  3384  			}
  3385  
  3386  			azureCluster := &infrav1.AzureCluster{
  3387  				ObjectMeta: metav1.ObjectMeta{
  3388  					Name: tc.clusterName,
  3389  					OwnerReferences: []metav1.OwnerReference{
  3390  						{
  3391  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3392  							Kind:       "Cluster",
  3393  							Name:       "my-cluster",
  3394  						},
  3395  					},
  3396  				},
  3397  				Spec: infrav1.AzureClusterSpec{
  3398  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3399  						SubscriptionID: "123",
  3400  						ExtendedLocation: &infrav1.ExtendedLocationSpec{
  3401  							Name: tc.extendedLocation.Name,
  3402  							Type: tc.extendedLocation.Type,
  3403  						},
  3404  						IdentityRef: &corev1.ObjectReference{
  3405  							Kind: infrav1.AzureClusterIdentityKind,
  3406  						},
  3407  					},
  3408  				},
  3409  			}
  3410  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3411  				Spec: infrav1.AzureClusterIdentitySpec{
  3412  					Type:     infrav1.ServicePrincipal,
  3413  					ClientID: fakeClientID,
  3414  					TenantID: fakeTenantID,
  3415  				},
  3416  			}
  3417  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3418  
  3419  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3420  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3421  
  3422  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3423  				Cluster:      cluster,
  3424  				AzureCluster: azureCluster,
  3425  				Client:       fakeClient,
  3426  			})
  3427  
  3428  			g.Expect(err).NotTo(HaveOccurred())
  3429  			got := clusterScope.ExtendedLocationType()
  3430  			g.Expect(tc.extendedLocation.Type).Should(Equal(got))
  3431  		})
  3432  	}
  3433  }
  3434  
  3435  func TestVNetPeerings(t *testing.T) {
  3436  	fakeSubscriptionID := "123"
  3437  
  3438  	tests := []struct {
  3439  		name                 string
  3440  		subscriptionID       string
  3441  		azureClusterVNetSpec infrav1.VnetSpec
  3442  		want                 []azure.ResourceSpecGetter
  3443  	}{
  3444  		{
  3445  			name:           "VNet peerings are not specified",
  3446  			subscriptionID: fakeSubscriptionID,
  3447  			azureClusterVNetSpec: infrav1.VnetSpec{
  3448  				ResourceGroup: "rg1",
  3449  				Name:          "vnet1",
  3450  			},
  3451  			want: []azure.ResourceSpecGetter{},
  3452  		},
  3453  		{
  3454  			name:           "One VNet peering is specified",
  3455  			subscriptionID: fakeSubscriptionID,
  3456  			azureClusterVNetSpec: infrav1.VnetSpec{
  3457  				ResourceGroup: "rg1",
  3458  				Name:          "vnet1",
  3459  				Peerings: infrav1.VnetPeerings{
  3460  					{
  3461  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3462  							ResourceGroup:  "rg2",
  3463  							RemoteVnetName: "vnet2",
  3464  						},
  3465  					},
  3466  				},
  3467  			},
  3468  			want: []azure.ResourceSpecGetter{
  3469  				&vnetpeerings.VnetPeeringSpec{
  3470  					PeeringName:         "vnet1-To-vnet2",
  3471  					SourceResourceGroup: "rg1",
  3472  					SourceVnetName:      "vnet1",
  3473  					RemoteResourceGroup: "rg2",
  3474  					RemoteVnetName:      "vnet2",
  3475  					SubscriptionID:      fakeSubscriptionID,
  3476  				},
  3477  				&vnetpeerings.VnetPeeringSpec{
  3478  					PeeringName:         "vnet2-To-vnet1",
  3479  					SourceResourceGroup: "rg2",
  3480  					SourceVnetName:      "vnet2",
  3481  					RemoteResourceGroup: "rg1",
  3482  					RemoteVnetName:      "vnet1",
  3483  					SubscriptionID:      fakeSubscriptionID,
  3484  				},
  3485  			},
  3486  		},
  3487  		{
  3488  			name:           "One VNet peering with optional properties is specified",
  3489  			subscriptionID: fakeSubscriptionID,
  3490  			azureClusterVNetSpec: infrav1.VnetSpec{
  3491  				ResourceGroup: "rg1",
  3492  				Name:          "vnet1",
  3493  				Peerings: infrav1.VnetPeerings{
  3494  					{
  3495  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3496  							ResourceGroup:  "rg2",
  3497  							RemoteVnetName: "vnet2",
  3498  							ForwardPeeringProperties: infrav1.VnetPeeringProperties{
  3499  								AllowForwardedTraffic: ptr.To(true),
  3500  								AllowGatewayTransit:   ptr.To(false),
  3501  								UseRemoteGateways:     ptr.To(true),
  3502  							},
  3503  							ReversePeeringProperties: infrav1.VnetPeeringProperties{
  3504  								AllowForwardedTraffic: ptr.To(true),
  3505  								AllowGatewayTransit:   ptr.To(true),
  3506  								UseRemoteGateways:     ptr.To(false),
  3507  							},
  3508  						},
  3509  					},
  3510  				},
  3511  			},
  3512  			want: []azure.ResourceSpecGetter{
  3513  				&vnetpeerings.VnetPeeringSpec{
  3514  					PeeringName:           "vnet1-To-vnet2",
  3515  					SourceResourceGroup:   "rg1",
  3516  					SourceVnetName:        "vnet1",
  3517  					RemoteResourceGroup:   "rg2",
  3518  					RemoteVnetName:        "vnet2",
  3519  					SubscriptionID:        fakeSubscriptionID,
  3520  					AllowForwardedTraffic: ptr.To(true),
  3521  					AllowGatewayTransit:   ptr.To(false),
  3522  					UseRemoteGateways:     ptr.To(true),
  3523  				},
  3524  				&vnetpeerings.VnetPeeringSpec{
  3525  					PeeringName:           "vnet2-To-vnet1",
  3526  					SourceResourceGroup:   "rg2",
  3527  					SourceVnetName:        "vnet2",
  3528  					RemoteResourceGroup:   "rg1",
  3529  					RemoteVnetName:        "vnet1",
  3530  					SubscriptionID:        fakeSubscriptionID,
  3531  					AllowForwardedTraffic: ptr.To(true),
  3532  					AllowGatewayTransit:   ptr.To(true),
  3533  					UseRemoteGateways:     ptr.To(false),
  3534  				},
  3535  			},
  3536  		},
  3537  		{
  3538  			name:           "Two VNet peerings are specified",
  3539  			subscriptionID: fakeSubscriptionID,
  3540  			azureClusterVNetSpec: infrav1.VnetSpec{
  3541  				ResourceGroup: "rg1",
  3542  				Name:          "vnet1",
  3543  				Peerings: infrav1.VnetPeerings{
  3544  					{
  3545  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3546  							ResourceGroup:  "rg2",
  3547  							RemoteVnetName: "vnet2",
  3548  							ForwardPeeringProperties: infrav1.VnetPeeringProperties{
  3549  								AllowForwardedTraffic: ptr.To(true),
  3550  								AllowGatewayTransit:   ptr.To(false),
  3551  								UseRemoteGateways:     ptr.To(true),
  3552  							},
  3553  							ReversePeeringProperties: infrav1.VnetPeeringProperties{
  3554  								AllowForwardedTraffic: ptr.To(true),
  3555  								AllowGatewayTransit:   ptr.To(true),
  3556  								UseRemoteGateways:     ptr.To(false),
  3557  							},
  3558  						},
  3559  					},
  3560  					{
  3561  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3562  							ResourceGroup:  "rg3",
  3563  							RemoteVnetName: "vnet3",
  3564  						},
  3565  					},
  3566  				},
  3567  			},
  3568  			want: []azure.ResourceSpecGetter{
  3569  				&vnetpeerings.VnetPeeringSpec{
  3570  					PeeringName:           "vnet1-To-vnet2",
  3571  					SourceResourceGroup:   "rg1",
  3572  					SourceVnetName:        "vnet1",
  3573  					RemoteResourceGroup:   "rg2",
  3574  					RemoteVnetName:        "vnet2",
  3575  					SubscriptionID:        fakeSubscriptionID,
  3576  					AllowForwardedTraffic: ptr.To(true),
  3577  					AllowGatewayTransit:   ptr.To(false),
  3578  					UseRemoteGateways:     ptr.To(true),
  3579  				},
  3580  				&vnetpeerings.VnetPeeringSpec{
  3581  					PeeringName:           "vnet2-To-vnet1",
  3582  					SourceResourceGroup:   "rg2",
  3583  					SourceVnetName:        "vnet2",
  3584  					RemoteResourceGroup:   "rg1",
  3585  					RemoteVnetName:        "vnet1",
  3586  					SubscriptionID:        fakeSubscriptionID,
  3587  					AllowForwardedTraffic: ptr.To(true),
  3588  					AllowGatewayTransit:   ptr.To(true),
  3589  					UseRemoteGateways:     ptr.To(false),
  3590  				},
  3591  				&vnetpeerings.VnetPeeringSpec{
  3592  					PeeringName:         "vnet1-To-vnet3",
  3593  					SourceResourceGroup: "rg1",
  3594  					SourceVnetName:      "vnet1",
  3595  					RemoteResourceGroup: "rg3",
  3596  					RemoteVnetName:      "vnet3",
  3597  					SubscriptionID:      fakeSubscriptionID,
  3598  				},
  3599  				&vnetpeerings.VnetPeeringSpec{
  3600  					PeeringName:         "vnet3-To-vnet1",
  3601  					SourceResourceGroup: "rg3",
  3602  					SourceVnetName:      "vnet3",
  3603  					RemoteResourceGroup: "rg1",
  3604  					RemoteVnetName:      "vnet1",
  3605  					SubscriptionID:      fakeSubscriptionID,
  3606  				},
  3607  			},
  3608  		},
  3609  	}
  3610  
  3611  	for _, tc := range tests {
  3612  		t.Run(tc.name, func(t *testing.T) {
  3613  			g := NewWithT(t)
  3614  			scheme := runtime.NewScheme()
  3615  			_ = infrav1.AddToScheme(scheme)
  3616  			_ = clusterv1.AddToScheme(scheme)
  3617  			_ = corev1.AddToScheme(scheme)
  3618  
  3619  			clusterName := "my-cluster"
  3620  			clusterNamespace := "default"
  3621  
  3622  			cluster := &clusterv1.Cluster{
  3623  				ObjectMeta: metav1.ObjectMeta{
  3624  					Name:      clusterName,
  3625  					Namespace: clusterNamespace,
  3626  				},
  3627  			}
  3628  			azureCluster := &infrav1.AzureCluster{
  3629  				ObjectMeta: metav1.ObjectMeta{
  3630  					Name:      clusterName,
  3631  					Namespace: clusterNamespace,
  3632  					OwnerReferences: []metav1.OwnerReference{
  3633  						{
  3634  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3635  							Kind:       "Cluster",
  3636  							Name:       clusterName,
  3637  						},
  3638  					},
  3639  				},
  3640  				Spec: infrav1.AzureClusterSpec{
  3641  					ResourceGroup: "rg1",
  3642  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3643  						SubscriptionID: tc.subscriptionID,
  3644  						IdentityRef: &corev1.ObjectReference{
  3645  							Kind: infrav1.AzureClusterIdentityKind,
  3646  						},
  3647  					},
  3648  					NetworkSpec: infrav1.NetworkSpec{
  3649  						Vnet: tc.azureClusterVNetSpec,
  3650  					},
  3651  				},
  3652  			}
  3653  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3654  				ObjectMeta: metav1.ObjectMeta{
  3655  					Namespace: clusterNamespace,
  3656  				},
  3657  				Spec: infrav1.AzureClusterIdentitySpec{
  3658  					Type:     infrav1.ServicePrincipal,
  3659  					ClientID: fakeClientID,
  3660  					TenantID: fakeTenantID,
  3661  				},
  3662  			}
  3663  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3664  
  3665  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3666  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3667  
  3668  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3669  				Cluster:      cluster,
  3670  				AzureCluster: azureCluster,
  3671  				Client:       fakeClient,
  3672  			})
  3673  			g.Expect(err).NotTo(HaveOccurred())
  3674  			got := clusterScope.VnetPeeringSpecs()
  3675  			g.Expect(tc.want).To(Equal(got))
  3676  		})
  3677  	}
  3678  }
  3679  
  3680  func TestPrivateEndpointSpecs(t *testing.T) {
  3681  	tests := []struct {
  3682  		name         string
  3683  		clusterScope ClusterScope
  3684  		want         []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint]
  3685  	}{
  3686  		{
  3687  			name: "returns empty private endpoints list if no subnets are specified",
  3688  			clusterScope: ClusterScope{
  3689  				AzureCluster: &infrav1.AzureCluster{
  3690  					Spec: infrav1.AzureClusterSpec{
  3691  						NetworkSpec: infrav1.NetworkSpec{
  3692  							Subnets: infrav1.Subnets{},
  3693  						},
  3694  					},
  3695  				},
  3696  				cache: &ClusterCache{},
  3697  			},
  3698  			want: make([]azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint], 0),
  3699  		},
  3700  		{
  3701  			name: "returns empty private endpoints list if no private endpoints are specified",
  3702  			clusterScope: ClusterScope{
  3703  				AzureCluster: &infrav1.AzureCluster{
  3704  					Spec: infrav1.AzureClusterSpec{
  3705  						NetworkSpec: infrav1.NetworkSpec{
  3706  							Subnets: []infrav1.SubnetSpec{
  3707  								{
  3708  									SubnetClassSpec: infrav1.SubnetClassSpec{
  3709  										PrivateEndpoints: infrav1.PrivateEndpoints{},
  3710  									},
  3711  								},
  3712  							},
  3713  						},
  3714  					},
  3715  				},
  3716  				cache: &ClusterCache{},
  3717  			},
  3718  			want: make([]azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint], 0),
  3719  		},
  3720  		{
  3721  			name: "returns list of private endpoint specs if private endpoints are specified",
  3722  			clusterScope: ClusterScope{
  3723  				Cluster: &clusterv1.Cluster{
  3724  					ObjectMeta: metav1.ObjectMeta{
  3725  						Name:      "my-cluster",
  3726  						Namespace: "dummy-ns",
  3727  					},
  3728  				},
  3729  				AzureCluster: &infrav1.AzureCluster{
  3730  					Spec: infrav1.AzureClusterSpec{
  3731  						ResourceGroup: "dummy-rg",
  3732  						NetworkSpec: infrav1.NetworkSpec{
  3733  							Subnets: []infrav1.SubnetSpec{
  3734  								{
  3735  									ID: "dummy-subnet-id",
  3736  									SubnetClassSpec: infrav1.SubnetClassSpec{
  3737  										PrivateEndpoints: []infrav1.PrivateEndpointSpec{
  3738  											{
  3739  												Name:                       "my-private-endpoint",
  3740  												Location:                   "westus2",
  3741  												CustomNetworkInterfaceName: "my-custom-nic",
  3742  												PrivateIPAddresses: []string{
  3743  													"IP1",
  3744  													"IP2",
  3745  												},
  3746  												ApplicationSecurityGroups: []string{
  3747  													"ASG1",
  3748  													"ASG2",
  3749  												},
  3750  												PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{
  3751  													{
  3752  														Name:                 "my-pls-connection",
  3753  														RequestMessage:       "my-request-message",
  3754  														PrivateLinkServiceID: "my-pls-id",
  3755  														GroupIDs: []string{
  3756  															"my-group-id-1",
  3757  														},
  3758  													},
  3759  												},
  3760  											},
  3761  											{
  3762  												Name:                       "my-private-endpoint-2",
  3763  												Location:                   "westus2",
  3764  												CustomNetworkInterfaceName: "my-custom-nic-2",
  3765  												PrivateIPAddresses: []string{
  3766  													"IP3",
  3767  													"IP4",
  3768  												},
  3769  												ApplicationSecurityGroups: []string{
  3770  													"ASG3",
  3771  													"ASG4",
  3772  												},
  3773  												PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{
  3774  													{
  3775  														Name:                 "my-pls-connection",
  3776  														RequestMessage:       "my-request-message",
  3777  														PrivateLinkServiceID: "my-pls-id",
  3778  														GroupIDs: []string{
  3779  															"my-group-id-1",
  3780  														},
  3781  													},
  3782  												},
  3783  											},
  3784  										},
  3785  									},
  3786  								},
  3787  								{
  3788  									ID: "dummy-subnet-id-2",
  3789  									SubnetClassSpec: infrav1.SubnetClassSpec{
  3790  										PrivateEndpoints: []infrav1.PrivateEndpointSpec{
  3791  											{
  3792  												Name:                       "my-private-endpoint-3",
  3793  												Location:                   "westus2",
  3794  												CustomNetworkInterfaceName: "my-custom-nic-3",
  3795  												PrivateIPAddresses: []string{
  3796  													"IP5",
  3797  													"IP6",
  3798  												},
  3799  												ApplicationSecurityGroups: []string{
  3800  													"ASG5",
  3801  													"ASG6",
  3802  												},
  3803  												PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{
  3804  													{
  3805  														Name:                 "my-pls-connection",
  3806  														RequestMessage:       "my-request-message",
  3807  														PrivateLinkServiceID: "my-pls-id",
  3808  														GroupIDs: []string{
  3809  															"my-group-id-1",
  3810  														},
  3811  													},
  3812  												},
  3813  											},
  3814  										},
  3815  									},
  3816  								},
  3817  							},
  3818  						},
  3819  					},
  3820  				},
  3821  				cache: &ClusterCache{},
  3822  			},
  3823  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint]{
  3824  				&privateendpoints.PrivateEndpointSpec{
  3825  					Name:                       "my-private-endpoint",
  3826  					ResourceGroup:              "dummy-rg",
  3827  					Location:                   "westus2",
  3828  					CustomNetworkInterfaceName: "my-custom-nic",
  3829  					PrivateIPAddresses: []string{
  3830  						"IP1",
  3831  						"IP2",
  3832  					},
  3833  					SubnetID: "dummy-subnet-id",
  3834  					ApplicationSecurityGroups: []string{
  3835  						"ASG1",
  3836  						"ASG2",
  3837  					},
  3838  					ClusterName: "my-cluster",
  3839  					PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{
  3840  						{
  3841  							Name:                 "my-pls-connection",
  3842  							RequestMessage:       "my-request-message",
  3843  							PrivateLinkServiceID: "my-pls-id",
  3844  							GroupIDs: []string{
  3845  								"my-group-id-1",
  3846  							},
  3847  						},
  3848  					},
  3849  					AdditionalTags: make(infrav1.Tags, 0),
  3850  				},
  3851  				&privateendpoints.PrivateEndpointSpec{
  3852  					Name:                       "my-private-endpoint-2",
  3853  					ResourceGroup:              "dummy-rg",
  3854  					Location:                   "westus2",
  3855  					CustomNetworkInterfaceName: "my-custom-nic-2",
  3856  					PrivateIPAddresses: []string{
  3857  						"IP3",
  3858  						"IP4",
  3859  					},
  3860  					SubnetID: "dummy-subnet-id",
  3861  					ApplicationSecurityGroups: []string{
  3862  						"ASG3",
  3863  						"ASG4",
  3864  					},
  3865  					ClusterName: "my-cluster",
  3866  					PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{
  3867  						{
  3868  							Name:                 "my-pls-connection",
  3869  							RequestMessage:       "my-request-message",
  3870  							PrivateLinkServiceID: "my-pls-id",
  3871  							GroupIDs: []string{
  3872  								"my-group-id-1",
  3873  							},
  3874  						},
  3875  					},
  3876  					AdditionalTags: make(infrav1.Tags, 0),
  3877  				},
  3878  				&privateendpoints.PrivateEndpointSpec{
  3879  					Name:                       "my-private-endpoint-3",
  3880  					ResourceGroup:              "dummy-rg",
  3881  					Location:                   "westus2",
  3882  					CustomNetworkInterfaceName: "my-custom-nic-3",
  3883  					PrivateIPAddresses: []string{
  3884  						"IP5",
  3885  						"IP6",
  3886  					},
  3887  					SubnetID: "dummy-subnet-id-2",
  3888  					ApplicationSecurityGroups: []string{
  3889  						"ASG5",
  3890  						"ASG6",
  3891  					},
  3892  					ClusterName: "my-cluster",
  3893  					PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{
  3894  						{
  3895  							Name:                 "my-pls-connection",
  3896  							RequestMessage:       "my-request-message",
  3897  							PrivateLinkServiceID: "my-pls-id",
  3898  							GroupIDs: []string{
  3899  								"my-group-id-1",
  3900  							},
  3901  						},
  3902  					},
  3903  					AdditionalTags: make(infrav1.Tags, 0),
  3904  				},
  3905  			},
  3906  		},
  3907  	}
  3908  
  3909  	for _, tt := range tests {
  3910  		tt := tt
  3911  		t.Run(tt.name, func(t *testing.T) {
  3912  			t.Parallel()
  3913  			if got := tt.clusterScope.PrivateEndpointSpecs(); !reflect.DeepEqual(got, tt.want) {
  3914  				t.Errorf("PrivateEndpointSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
  3915  			}
  3916  		})
  3917  	}
  3918  }
  3919  
  3920  func TestSetFailureDomain(t *testing.T) {
  3921  	t.Parallel()
  3922  
  3923  	cases := map[string]struct {
  3924  		discoveredFDs clusterv1.FailureDomains
  3925  		specifiedFDs  clusterv1.FailureDomains
  3926  		expectedFDs   clusterv1.FailureDomains
  3927  	}{
  3928  		"no failure domains specified": {
  3929  			discoveredFDs: clusterv1.FailureDomains{
  3930  				"fd1": clusterv1.FailureDomainSpec{ControlPlane: true},
  3931  				"fd2": clusterv1.FailureDomainSpec{ControlPlane: false},
  3932  			},
  3933  			expectedFDs: clusterv1.FailureDomains{
  3934  				"fd1": clusterv1.FailureDomainSpec{ControlPlane: true},
  3935  				"fd2": clusterv1.FailureDomainSpec{ControlPlane: false},
  3936  			},
  3937  		},
  3938  		"no failure domains discovered": {
  3939  			specifiedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  3940  		},
  3941  		"failure domain specified without intersection": {
  3942  			discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  3943  			specifiedFDs:  clusterv1.FailureDomains{"fd2": clusterv1.FailureDomainSpec{ControlPlane: false}},
  3944  			expectedFDs:   clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  3945  		},
  3946  		"failure domain override to false succeeds": {
  3947  			discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  3948  			specifiedFDs:  clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  3949  			expectedFDs:   clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  3950  		},
  3951  		"failure domain override to true fails": {
  3952  			discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  3953  			specifiedFDs:  clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  3954  			expectedFDs:   clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  3955  		},
  3956  	}
  3957  
  3958  	for name, tc := range cases {
  3959  		tc := tc
  3960  		t.Run(name, func(t *testing.T) {
  3961  			t.Parallel()
  3962  			g := NewWithT(t)
  3963  
  3964  			c := ClusterScope{
  3965  				AzureCluster: &infrav1.AzureCluster{
  3966  					Spec: infrav1.AzureClusterSpec{
  3967  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3968  							FailureDomains: tc.specifiedFDs,
  3969  							IdentityRef: &corev1.ObjectReference{
  3970  								Kind: infrav1.AzureClusterIdentityKind,
  3971  							},
  3972  						},
  3973  					},
  3974  				},
  3975  			}
  3976  
  3977  			for fdName, fd := range tc.discoveredFDs {
  3978  				c.SetFailureDomain(fdName, fd)
  3979  			}
  3980  
  3981  			for fdName, fd := range tc.expectedFDs {
  3982  				g.Expect(fdName).Should(BeKeyOf(c.AzureCluster.Status.FailureDomains))
  3983  				g.Expect(c.AzureCluster.Status.FailureDomains[fdName].ControlPlane).To(Equal(fd.ControlPlane))
  3984  
  3985  				delete(c.AzureCluster.Status.FailureDomains, fdName)
  3986  			}
  3987  
  3988  			g.Expect(c.AzureCluster.Status.FailureDomains).To(BeEmpty())
  3989  		})
  3990  	}
  3991  }
  3992  
  3993  func TestGroupSpecs(t *testing.T) {
  3994  	cases := []struct {
  3995  		name     string
  3996  		input    ClusterScope
  3997  		expected []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]
  3998  	}{
  3999  		{
  4000  			name: "virtualNetwork belongs to a different resource group",
  4001  			input: ClusterScope{
  4002  				Cluster: &clusterv1.Cluster{
  4003  					ObjectMeta: metav1.ObjectMeta{
  4004  						Name: "cluster1",
  4005  					},
  4006  				},
  4007  				AzureCluster: &infrav1.AzureCluster{
  4008  					Spec: infrav1.AzureClusterSpec{
  4009  						ResourceGroup: "dummy-rg",
  4010  						NetworkSpec: infrav1.NetworkSpec{
  4011  							Vnet: infrav1.VnetSpec{
  4012  								ResourceGroup: "different-rg",
  4013  							},
  4014  						},
  4015  					},
  4016  				},
  4017  			},
  4018  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4019  				&groups.GroupSpec{
  4020  					Name:           "dummy-rg",
  4021  					AzureName:      "dummy-rg",
  4022  					ClusterName:    "cluster1",
  4023  					Location:       "",
  4024  					AdditionalTags: make(infrav1.Tags, 0),
  4025  				},
  4026  				&groups.GroupSpec{
  4027  					Name:           "different-rg",
  4028  					AzureName:      "different-rg",
  4029  					ClusterName:    "cluster1",
  4030  					Location:       "",
  4031  					AdditionalTags: make(infrav1.Tags, 0),
  4032  				},
  4033  			},
  4034  		},
  4035  		{
  4036  			name: "virtualNetwork belongs to a same resource group",
  4037  			input: ClusterScope{
  4038  				Cluster: &clusterv1.Cluster{
  4039  					ObjectMeta: metav1.ObjectMeta{
  4040  						Name: "cluster1",
  4041  					},
  4042  				},
  4043  				AzureCluster: &infrav1.AzureCluster{
  4044  					Spec: infrav1.AzureClusterSpec{
  4045  						ResourceGroup: "dummy-rg",
  4046  						NetworkSpec: infrav1.NetworkSpec{
  4047  							Vnet: infrav1.VnetSpec{
  4048  								ResourceGroup: "dummy-rg",
  4049  							},
  4050  						},
  4051  					},
  4052  				},
  4053  			},
  4054  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4055  				&groups.GroupSpec{
  4056  					Name:           "dummy-rg",
  4057  					AzureName:      "dummy-rg",
  4058  					ClusterName:    "cluster1",
  4059  					Location:       "",
  4060  					AdditionalTags: make(infrav1.Tags, 0),
  4061  				},
  4062  			},
  4063  		},
  4064  		{
  4065  			name: "virtualNetwork resource group not specified",
  4066  			input: ClusterScope{
  4067  				Cluster: &clusterv1.Cluster{
  4068  					ObjectMeta: metav1.ObjectMeta{
  4069  						Name:      "cluster1",
  4070  						Namespace: "default",
  4071  					},
  4072  				},
  4073  				AzureCluster: &infrav1.AzureCluster{
  4074  					Spec: infrav1.AzureClusterSpec{
  4075  						ResourceGroup: "dummy-rg",
  4076  						NetworkSpec: infrav1.NetworkSpec{
  4077  							Vnet: infrav1.VnetSpec{
  4078  								Name: "vnet1",
  4079  							},
  4080  						},
  4081  					},
  4082  				},
  4083  			},
  4084  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4085  				&groups.GroupSpec{
  4086  					Name:           "dummy-rg",
  4087  					AzureName:      "dummy-rg",
  4088  					ClusterName:    "cluster1",
  4089  					Location:       "",
  4090  					AdditionalTags: make(infrav1.Tags, 0),
  4091  				},
  4092  			},
  4093  		},
  4094  		{
  4095  			name: "virtualNetwork belongs to different resource group with non-k8s name",
  4096  			input: ClusterScope{
  4097  				Cluster: &clusterv1.Cluster{
  4098  					ObjectMeta: metav1.ObjectMeta{
  4099  						Name:      "cluster1",
  4100  						Namespace: "default",
  4101  					},
  4102  				},
  4103  				AzureCluster: &infrav1.AzureCluster{
  4104  					Spec: infrav1.AzureClusterSpec{
  4105  						ResourceGroup: "dummy-rg",
  4106  						NetworkSpec: infrav1.NetworkSpec{
  4107  							Vnet: infrav1.VnetSpec{
  4108  								ResourceGroup: "my_custom_rg",
  4109  								Name:          "vnet1",
  4110  							},
  4111  						},
  4112  					},
  4113  				},
  4114  			},
  4115  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4116  				&groups.GroupSpec{
  4117  					Name:           "dummy-rg",
  4118  					AzureName:      "dummy-rg",
  4119  					ClusterName:    "cluster1",
  4120  					Location:       "",
  4121  					AdditionalTags: make(infrav1.Tags, 0),
  4122  				},
  4123  				&groups.GroupSpec{
  4124  					Name:           "my-custom-rg",
  4125  					AzureName:      "my_custom_rg",
  4126  					ClusterName:    "cluster1",
  4127  					Location:       "",
  4128  					AdditionalTags: make(infrav1.Tags, 0),
  4129  				},
  4130  			},
  4131  		},
  4132  	}
  4133  
  4134  	for _, c := range cases {
  4135  		c := c
  4136  		t.Run(c.name, func(t *testing.T) {
  4137  			s := &ClusterScope{
  4138  				AzureCluster: c.input.AzureCluster,
  4139  				Cluster:      c.input.Cluster,
  4140  			}
  4141  			if got := s.GroupSpecs(); !reflect.DeepEqual(got, c.expected) {
  4142  				t.Errorf("GroupSpecs() = %s, want %s", specArrayToString(got), specArrayToString(c.expected))
  4143  			}
  4144  		})
  4145  	}
  4146  }