sigs.k8s.io/cluster-api-provider-azure@v1.17.0/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  	scheme := runtime.NewScheme()
   934  	_ = asonetworkv1api20201101.AddToScheme(scheme)
   935  	_ = corev1.AddToScheme(scheme)
   936  	_ = infrav1.AddToScheme(scheme)
   937  
   938  	tests := []struct {
   939  		name         string
   940  		clusterScope ClusterScope
   941  		vnet         asonetworkv1api20201101.VirtualNetwork
   942  		want         []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]
   943  	}{
   944  		{
   945  			name: "returns nil if no subnets are specified",
   946  			clusterScope: ClusterScope{
   947  				AzureCluster: &infrav1.AzureCluster{
   948  					Spec: infrav1.AzureClusterSpec{
   949  						NetworkSpec: infrav1.NetworkSpec{
   950  							Subnets: infrav1.Subnets{},
   951  						},
   952  					},
   953  				},
   954  				cache: &ClusterCache{},
   955  			},
   956  			want: nil,
   957  		},
   958  		{
   959  			name: "returns specified node NAT gateway if present",
   960  			clusterScope: ClusterScope{
   961  				Cluster: &clusterv1.Cluster{
   962  					ObjectMeta: metav1.ObjectMeta{
   963  						Name: "my-cluster",
   964  					},
   965  				},
   966  				AzureClients: AzureClients{
   967  					EnvironmentSettings: auth.EnvironmentSettings{
   968  						Values: map[string]string{
   969  							auth.SubscriptionID: "123",
   970  						},
   971  					},
   972  				},
   973  				AzureCluster: &infrav1.AzureCluster{
   974  					Spec: infrav1.AzureClusterSpec{
   975  						ResourceGroup: "my-rg",
   976  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
   977  							Location: "centralIndia",
   978  							IdentityRef: &corev1.ObjectReference{
   979  								Kind: infrav1.AzureClusterIdentityKind,
   980  							},
   981  						},
   982  						NetworkSpec: infrav1.NetworkSpec{
   983  							Subnets: infrav1.Subnets{
   984  								{
   985  									SubnetClassSpec: infrav1.SubnetClassSpec{
   986  										Role: infrav1.SubnetNode,
   987  									},
   988  									RouteTable: infrav1.RouteTable{
   989  										ID:   "fake-route-table-id-1",
   990  										Name: "fake-route-table-1",
   991  									},
   992  									NatGateway: infrav1.NatGateway{
   993  										NatGatewayIP: infrav1.PublicIPSpec{
   994  											Name: "44.78.67.90",
   995  										},
   996  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
   997  											Name: "fake-nat-gateway-1",
   998  										},
   999  									},
  1000  								},
  1001  							},
  1002  							Vnet: infrav1.VnetSpec{
  1003  								Name: "fake-vnet-1",
  1004  							},
  1005  						},
  1006  					},
  1007  				},
  1008  				cache: &ClusterCache{},
  1009  			},
  1010  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1011  				ObjectMeta: metav1.ObjectMeta{
  1012  					Name: "fake-vnet-1",
  1013  				},
  1014  				Status: asonetworkv1api20201101.VirtualNetwork_STATUS{
  1015  					Tags: map[string]string{
  1016  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
  1017  					},
  1018  				},
  1019  			},
  1020  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{
  1021  				&natgateways.NatGatewaySpec{
  1022  					Name:           "fake-nat-gateway-1",
  1023  					ResourceGroup:  "my-rg",
  1024  					Location:       "centralIndia",
  1025  					SubscriptionID: "123",
  1026  					ClusterName:    "my-cluster",
  1027  					NatGatewayIP: infrav1.PublicIPSpec{
  1028  						Name: "44.78.67.90",
  1029  					},
  1030  					AdditionalTags: make(infrav1.Tags),
  1031  					IsVnetManaged:  true,
  1032  				},
  1033  			},
  1034  		},
  1035  		{
  1036  			name: "returns specified node NAT gateway if present and ignores duplicate",
  1037  			clusterScope: ClusterScope{
  1038  				Cluster: &clusterv1.Cluster{
  1039  					ObjectMeta: metav1.ObjectMeta{
  1040  						Name: "my-cluster",
  1041  					},
  1042  				},
  1043  				AzureClients: AzureClients{
  1044  					EnvironmentSettings: auth.EnvironmentSettings{
  1045  						Values: map[string]string{
  1046  							auth.SubscriptionID: "123",
  1047  						},
  1048  					},
  1049  				},
  1050  				AzureCluster: &infrav1.AzureCluster{
  1051  					Spec: infrav1.AzureClusterSpec{
  1052  						ResourceGroup: "my-rg",
  1053  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1054  							Location: "centralIndia",
  1055  							IdentityRef: &corev1.ObjectReference{
  1056  								Kind: infrav1.AzureClusterIdentityKind,
  1057  							},
  1058  						},
  1059  						NetworkSpec: infrav1.NetworkSpec{
  1060  							Subnets: infrav1.Subnets{
  1061  								{
  1062  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1063  										Role: infrav1.SubnetNode,
  1064  									},
  1065  									RouteTable: infrav1.RouteTable{
  1066  										ID:   "fake-route-table-id-1",
  1067  										Name: "fake-route-table-1",
  1068  									},
  1069  									NatGateway: infrav1.NatGateway{
  1070  										NatGatewayIP: infrav1.PublicIPSpec{
  1071  											Name: "44.78.67.90",
  1072  										},
  1073  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1074  											Name: "fake-nat-gateway-1",
  1075  										},
  1076  									},
  1077  								},
  1078  								// Duplicate Entry
  1079  								{
  1080  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1081  										Role: infrav1.SubnetNode,
  1082  									},
  1083  									RouteTable: infrav1.RouteTable{
  1084  										ID:   "fake-route-table-id-1",
  1085  										Name: "fake-route-table-1",
  1086  									},
  1087  									NatGateway: infrav1.NatGateway{
  1088  										NatGatewayIP: infrav1.PublicIPSpec{
  1089  											Name: "44.78.67.90",
  1090  										},
  1091  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1092  											Name: "fake-nat-gateway-1",
  1093  										},
  1094  									},
  1095  								},
  1096  							},
  1097  							Vnet: infrav1.VnetSpec{
  1098  								Name: "fake-vnet-1",
  1099  							},
  1100  						},
  1101  					},
  1102  				},
  1103  				cache: &ClusterCache{},
  1104  			},
  1105  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1106  				ObjectMeta: metav1.ObjectMeta{
  1107  					Name: "fake-vnet-1",
  1108  				},
  1109  				Status: asonetworkv1api20201101.VirtualNetwork_STATUS{
  1110  					Tags: map[string]string{
  1111  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
  1112  					},
  1113  				},
  1114  			},
  1115  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{
  1116  				&natgateways.NatGatewaySpec{
  1117  					Name:           "fake-nat-gateway-1",
  1118  					ResourceGroup:  "my-rg",
  1119  					Location:       "centralIndia",
  1120  					SubscriptionID: "123",
  1121  					ClusterName:    "my-cluster",
  1122  					NatGatewayIP: infrav1.PublicIPSpec{
  1123  						Name: "44.78.67.90",
  1124  					},
  1125  					AdditionalTags: make(infrav1.Tags),
  1126  					IsVnetManaged:  true,
  1127  				},
  1128  			},
  1129  		},
  1130  		{
  1131  			name: "returns specified node NAT gateway if present and ignores control plane nat gateway",
  1132  			clusterScope: ClusterScope{
  1133  				Cluster: &clusterv1.Cluster{
  1134  					ObjectMeta: metav1.ObjectMeta{
  1135  						Name: "my-cluster",
  1136  					},
  1137  				},
  1138  				AzureClients: AzureClients{
  1139  					EnvironmentSettings: auth.EnvironmentSettings{
  1140  						Values: map[string]string{
  1141  							auth.SubscriptionID: "123",
  1142  						},
  1143  					},
  1144  				},
  1145  				AzureCluster: &infrav1.AzureCluster{
  1146  					Spec: infrav1.AzureClusterSpec{
  1147  						ResourceGroup: "my-rg",
  1148  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1149  							Location: "centralIndia",
  1150  							IdentityRef: &corev1.ObjectReference{
  1151  								Kind: infrav1.AzureClusterIdentityKind,
  1152  							},
  1153  						},
  1154  						NetworkSpec: infrav1.NetworkSpec{
  1155  							Subnets: infrav1.Subnets{
  1156  								{
  1157  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1158  										Role: infrav1.SubnetNode,
  1159  									},
  1160  									RouteTable: infrav1.RouteTable{
  1161  										ID:   "fake-route-table-id-1",
  1162  										Name: "fake-route-table-1",
  1163  									},
  1164  									NatGateway: infrav1.NatGateway{
  1165  										NatGatewayIP: infrav1.PublicIPSpec{
  1166  											Name: "44.78.67.90",
  1167  										},
  1168  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1169  											Name: "fake-nat-gateway-1",
  1170  										},
  1171  									},
  1172  								},
  1173  								{
  1174  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1175  										Role: infrav1.SubnetControlPlane,
  1176  									},
  1177  									RouteTable: infrav1.RouteTable{
  1178  										ID:   "fake-route-table-id-2",
  1179  										Name: "fake-route-table-2",
  1180  									},
  1181  									NatGateway: infrav1.NatGateway{
  1182  										NatGatewayIP: infrav1.PublicIPSpec{
  1183  											Name: "44.78.67.91",
  1184  										},
  1185  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1186  											Name: "fake-nat-gateway-2",
  1187  										},
  1188  									},
  1189  								},
  1190  							},
  1191  							Vnet: infrav1.VnetSpec{
  1192  								Name: "fake-vnet-1",
  1193  							},
  1194  						},
  1195  					},
  1196  				},
  1197  				cache: &ClusterCache{},
  1198  			},
  1199  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1200  				ObjectMeta: metav1.ObjectMeta{
  1201  					Name: "fake-vnet-1",
  1202  				},
  1203  				Status: asonetworkv1api20201101.VirtualNetwork_STATUS{
  1204  					Tags: map[string]string{
  1205  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
  1206  					},
  1207  				},
  1208  			},
  1209  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.NatGateway]{
  1210  				&natgateways.NatGatewaySpec{
  1211  					Name:           "fake-nat-gateway-1",
  1212  					ResourceGroup:  "my-rg",
  1213  					Location:       "centralIndia",
  1214  					SubscriptionID: "123",
  1215  					ClusterName:    "my-cluster",
  1216  					NatGatewayIP: infrav1.PublicIPSpec{
  1217  						Name: "44.78.67.90",
  1218  					},
  1219  					AdditionalTags: make(infrav1.Tags),
  1220  					IsVnetManaged:  true,
  1221  				},
  1222  			},
  1223  		},
  1224  	}
  1225  
  1226  	for _, tt := range tests {
  1227  		tt := tt
  1228  		t.Run(tt.name, func(t *testing.T) {
  1229  			t.Parallel()
  1230  			fakeIdentity := &infrav1.AzureClusterIdentity{
  1231  				ObjectMeta: metav1.ObjectMeta{
  1232  					Name:      "fake-identity",
  1233  					Namespace: "default",
  1234  				},
  1235  				Spec: infrav1.AzureClusterIdentitySpec{
  1236  					Type:     infrav1.ServicePrincipal,
  1237  					ClientID: fakeClientID,
  1238  					TenantID: fakeTenantID,
  1239  				},
  1240  			}
  1241  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  1242  
  1243  			initObjects := []runtime.Object{&tt.vnet, fakeIdentity, fakeSecret}
  1244  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  1245  			tt.clusterScope.Client = fakeClient
  1246  
  1247  			if got := tt.clusterScope.NatGatewaySpecs(); !reflect.DeepEqual(got, tt.want) {
  1248  				t.Errorf("NatGatewaySpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
  1249  			}
  1250  		})
  1251  	}
  1252  }
  1253  
  1254  func TestSetNatGatewayIDInSubnets(t *testing.T) {
  1255  	tests := []struct {
  1256  		name          string
  1257  		clusterScope  ClusterScope
  1258  		asoNatgateway *asonetworkv1api20220701.NatGateway
  1259  	}{
  1260  		{
  1261  			name: "sets nat gateway id in the matching subnet",
  1262  			clusterScope: ClusterScope{
  1263  				Cluster: &clusterv1.Cluster{
  1264  					ObjectMeta: metav1.ObjectMeta{
  1265  						Name: "my-cluster",
  1266  					},
  1267  				},
  1268  				AzureCluster: &infrav1.AzureCluster{
  1269  					Spec: infrav1.AzureClusterSpec{
  1270  						NetworkSpec: infrav1.NetworkSpec{
  1271  							Subnets: infrav1.Subnets{
  1272  								{
  1273  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1274  										Name: "fake-subnet-1",
  1275  									},
  1276  									NatGateway: infrav1.NatGateway{
  1277  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1278  											Name: "fake-nat-gateway-1",
  1279  										},
  1280  									},
  1281  								},
  1282  								{
  1283  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1284  										Name: "fake-subnet-2",
  1285  									},
  1286  									NatGateway: infrav1.NatGateway{
  1287  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1288  											Name: "fake-nat-gateway-2",
  1289  										},
  1290  									},
  1291  								},
  1292  							},
  1293  						},
  1294  					},
  1295  				},
  1296  				cache: &ClusterCache{},
  1297  			},
  1298  			asoNatgateway: &asonetworkv1api20220701.NatGateway{
  1299  				ObjectMeta: metav1.ObjectMeta{
  1300  					Name: "fake-nat-gateway-1",
  1301  				},
  1302  				Status: asonetworkv1api20220701.NatGateway_STATUS{
  1303  					Id: ptr.To("dummy-id-1"),
  1304  				},
  1305  			},
  1306  		},
  1307  	}
  1308  
  1309  	for _, tt := range tests {
  1310  		tt := tt
  1311  		t.Run(tt.name, func(t *testing.T) {
  1312  			g := NewWithT(t)
  1313  			t.Parallel()
  1314  			tt.clusterScope.SetNatGatewayIDInSubnets(tt.asoNatgateway.Name, *tt.asoNatgateway.Status.Id)
  1315  			for _, subnet := range tt.clusterScope.AzureCluster.Spec.NetworkSpec.Subnets {
  1316  				if subnet.NatGateway.Name == tt.asoNatgateway.Name {
  1317  					g.Expect(subnet.NatGateway.ID).To(Equal(*tt.asoNatgateway.Status.Id))
  1318  				} else {
  1319  					g.Expect(subnet.NatGateway.ID).To(Equal(""))
  1320  				}
  1321  			}
  1322  		})
  1323  	}
  1324  }
  1325  
  1326  func TestNSGSpecs(t *testing.T) {
  1327  	tests := []struct {
  1328  		name         string
  1329  		clusterScope ClusterScope
  1330  		want         []azure.ResourceSpecGetter
  1331  	}{
  1332  		{
  1333  			name: "returns empty if no subnets are specified",
  1334  			clusterScope: ClusterScope{
  1335  				AzureCluster: &infrav1.AzureCluster{
  1336  					Spec: infrav1.AzureClusterSpec{
  1337  						NetworkSpec: infrav1.NetworkSpec{
  1338  							Subnets: infrav1.Subnets{},
  1339  						},
  1340  					},
  1341  				},
  1342  			},
  1343  			want: []azure.ResourceSpecGetter{},
  1344  		},
  1345  		{
  1346  			name: "returns specified security groups if present",
  1347  			clusterScope: ClusterScope{
  1348  				Cluster: &clusterv1.Cluster{
  1349  					ObjectMeta: metav1.ObjectMeta{
  1350  						Name: "my-cluster",
  1351  					},
  1352  				},
  1353  				AzureCluster: &infrav1.AzureCluster{
  1354  					Spec: infrav1.AzureClusterSpec{
  1355  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1356  							Location: "centralIndia",
  1357  							IdentityRef: &corev1.ObjectReference{
  1358  								Kind: infrav1.AzureClusterIdentityKind,
  1359  							},
  1360  						},
  1361  						NetworkSpec: infrav1.NetworkSpec{
  1362  							Vnet: infrav1.VnetSpec{
  1363  								ResourceGroup: "my-rg",
  1364  							},
  1365  							Subnets: infrav1.Subnets{
  1366  								{
  1367  									SecurityGroup: infrav1.SecurityGroup{
  1368  										Name: "fake-security-group-1",
  1369  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1370  											SecurityRules: infrav1.SecurityRules{
  1371  												{
  1372  													Name: "fake-rule-1",
  1373  												},
  1374  											},
  1375  										},
  1376  									},
  1377  								},
  1378  							},
  1379  						},
  1380  					},
  1381  				},
  1382  				cache: &ClusterCache{},
  1383  			},
  1384  			want: []azure.ResourceSpecGetter{
  1385  				&securitygroups.NSGSpec{
  1386  					Name: "fake-security-group-1",
  1387  					SecurityRules: infrav1.SecurityRules{
  1388  						{
  1389  							Name: "fake-rule-1",
  1390  						},
  1391  					},
  1392  					ResourceGroup:            "my-rg",
  1393  					Location:                 "centralIndia",
  1394  					ClusterName:              "my-cluster",
  1395  					AdditionalTags:           make(infrav1.Tags),
  1396  					LastAppliedSecurityRules: map[string]interface{}{},
  1397  				},
  1398  			},
  1399  		},
  1400  	}
  1401  
  1402  	for _, tt := range tests {
  1403  		tt := tt
  1404  		t.Run(tt.name, func(t *testing.T) {
  1405  			t.Parallel()
  1406  			if got := tt.clusterScope.NSGSpecs(); !reflect.DeepEqual(got, tt.want) {
  1407  				t.Errorf("RouteTableSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
  1408  			}
  1409  		})
  1410  	}
  1411  }
  1412  
  1413  func TestSubnetSpecs(t *testing.T) {
  1414  	scheme := runtime.NewScheme()
  1415  	_ = asonetworkv1api20201101.AddToScheme(scheme)
  1416  	_ = corev1.AddToScheme(scheme)
  1417  	_ = infrav1.AddToScheme(scheme)
  1418  
  1419  	tests := []struct {
  1420  		name         string
  1421  		clusterScope ClusterScope
  1422  		vnet         asonetworkv1api20201101.VirtualNetwork
  1423  		want         []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]
  1424  	}{
  1425  		{
  1426  			name: "returns empty if no subnets are specified",
  1427  			clusterScope: ClusterScope{
  1428  				AzureCluster: &infrav1.AzureCluster{
  1429  					Spec: infrav1.AzureClusterSpec{
  1430  						NetworkSpec: infrav1.NetworkSpec{
  1431  							Subnets: infrav1.Subnets{},
  1432  						},
  1433  					},
  1434  				},
  1435  				cache: &ClusterCache{},
  1436  			},
  1437  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{},
  1438  		},
  1439  		{
  1440  			name: "returns specified subnet spec",
  1441  			clusterScope: ClusterScope{
  1442  				Cluster: &clusterv1.Cluster{
  1443  					ObjectMeta: metav1.ObjectMeta{
  1444  						Name: "my-cluster",
  1445  					},
  1446  				},
  1447  				AzureClients: AzureClients{
  1448  					EnvironmentSettings: auth.EnvironmentSettings{
  1449  						Values: map[string]string{
  1450  							auth.SubscriptionID: "123",
  1451  						},
  1452  					},
  1453  				},
  1454  				AzureCluster: &infrav1.AzureCluster{
  1455  					Spec: infrav1.AzureClusterSpec{
  1456  						ResourceGroup: "my-rg",
  1457  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1458  							Location: "centralIndia",
  1459  							IdentityRef: &corev1.ObjectReference{
  1460  								Kind: infrav1.AzureClusterIdentityKind,
  1461  							},
  1462  						},
  1463  						NetworkSpec: infrav1.NetworkSpec{
  1464  							Vnet: infrav1.VnetSpec{
  1465  								ID:            "fake-vnet-id-1",
  1466  								Name:          "fake-vnet-1",
  1467  								ResourceGroup: "my-rg-vnet",
  1468  							},
  1469  							Subnets: infrav1.Subnets{
  1470  								{
  1471  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1472  										Role:       infrav1.SubnetNode,
  1473  										CIDRBlocks: []string{"192.168.1.1/16"},
  1474  										Name:       "fake-subnet-1",
  1475  									},
  1476  									NatGateway: infrav1.NatGateway{
  1477  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1478  											Name: "fake-natgateway-1",
  1479  										},
  1480  									},
  1481  									RouteTable: infrav1.RouteTable{
  1482  										ID:   "fake-route-table-id-1",
  1483  										Name: "fake-route-table-1",
  1484  									},
  1485  									SecurityGroup: infrav1.SecurityGroup{
  1486  										Name: "fake-security-group-1",
  1487  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1488  											SecurityRules: infrav1.SecurityRules{
  1489  												{
  1490  													Name: "fake-rule-1",
  1491  												},
  1492  											},
  1493  										},
  1494  									},
  1495  								},
  1496  							},
  1497  						},
  1498  					},
  1499  				},
  1500  				cache: &ClusterCache{},
  1501  			},
  1502  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1503  				ObjectMeta: metav1.ObjectMeta{
  1504  					Name: "fake-vnet-1",
  1505  				},
  1506  			},
  1507  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{
  1508  				&subnets.SubnetSpec{
  1509  					Name:              "fake-subnet-1",
  1510  					ResourceGroup:     "my-rg",
  1511  					SubscriptionID:    "123",
  1512  					CIDRs:             []string{"192.168.1.1/16"},
  1513  					VNetName:          "fake-vnet-1",
  1514  					VNetResourceGroup: "my-rg-vnet",
  1515  					IsVNetManaged:     false,
  1516  					RouteTableName:    "fake-route-table-1",
  1517  					SecurityGroupName: "fake-security-group-1",
  1518  					NatGatewayName:    "fake-natgateway-1",
  1519  				},
  1520  			},
  1521  		},
  1522  
  1523  		{
  1524  			name: "returns specified subnet spec and bastion spec if enabled",
  1525  			clusterScope: ClusterScope{
  1526  				Cluster: &clusterv1.Cluster{
  1527  					ObjectMeta: metav1.ObjectMeta{
  1528  						Name: "my-cluster",
  1529  					},
  1530  				},
  1531  				AzureClients: AzureClients{
  1532  					EnvironmentSettings: auth.EnvironmentSettings{
  1533  						Values: map[string]string{
  1534  							auth.SubscriptionID: "123",
  1535  						},
  1536  					},
  1537  				},
  1538  				AzureCluster: &infrav1.AzureCluster{
  1539  					Spec: infrav1.AzureClusterSpec{
  1540  						BastionSpec: infrav1.BastionSpec{
  1541  							AzureBastion: &infrav1.AzureBastion{
  1542  								Name: "fake-azure-bastion",
  1543  								Subnet: infrav1.SubnetSpec{
  1544  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1545  										Role:       infrav1.SubnetBastion,
  1546  										CIDRBlocks: []string{"172.122.1.1./16"},
  1547  										Name:       "fake-bastion-subnet-1",
  1548  									},
  1549  									RouteTable: infrav1.RouteTable{
  1550  										ID:   "fake-bastion-route-table-id-1",
  1551  										Name: "fake-bastion-route-table-1",
  1552  									},
  1553  									SecurityGroup: infrav1.SecurityGroup{
  1554  										Name: "fake-bastion-security-group-1",
  1555  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1556  											SecurityRules: infrav1.SecurityRules{
  1557  												{
  1558  													Name: "fake-rule-1",
  1559  												},
  1560  											},
  1561  										},
  1562  									},
  1563  								},
  1564  							},
  1565  						},
  1566  						ResourceGroup: "my-rg",
  1567  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1568  							Location: "centralIndia",
  1569  							IdentityRef: &corev1.ObjectReference{
  1570  								Kind: infrav1.AzureClusterIdentityKind,
  1571  							},
  1572  						},
  1573  						NetworkSpec: infrav1.NetworkSpec{
  1574  							Vnet: infrav1.VnetSpec{
  1575  								ID:            "fake-vnet-id-1",
  1576  								Name:          "fake-vnet-1",
  1577  								ResourceGroup: "my-rg-vnet",
  1578  							},
  1579  							Subnets: infrav1.Subnets{
  1580  								{
  1581  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1582  										Role:       infrav1.SubnetNode,
  1583  										CIDRBlocks: []string{"192.168.1.1/16"},
  1584  										Name:       "fake-subnet-1",
  1585  									},
  1586  									NatGateway: infrav1.NatGateway{
  1587  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1588  											Name: "fake-natgateway-1",
  1589  										},
  1590  									},
  1591  									RouteTable: infrav1.RouteTable{
  1592  										ID:   "fake-route-table-id-1",
  1593  										Name: "fake-route-table-1",
  1594  									},
  1595  									SecurityGroup: infrav1.SecurityGroup{
  1596  										Name: "fake-security-group-1",
  1597  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1598  											SecurityRules: infrav1.SecurityRules{
  1599  												{
  1600  													Name: "fake-rule-1",
  1601  												},
  1602  											},
  1603  										},
  1604  									},
  1605  								},
  1606  							},
  1607  						},
  1608  					},
  1609  				},
  1610  				cache: &ClusterCache{},
  1611  			},
  1612  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1613  				ObjectMeta: metav1.ObjectMeta{
  1614  					Name: "fake-vnet-1",
  1615  				},
  1616  			},
  1617  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20201101.VirtualNetworksSubnet]{
  1618  				&subnets.SubnetSpec{
  1619  					Name:              "fake-subnet-1",
  1620  					ResourceGroup:     "my-rg",
  1621  					SubscriptionID:    "123",
  1622  					CIDRs:             []string{"192.168.1.1/16"},
  1623  					VNetName:          "fake-vnet-1",
  1624  					VNetResourceGroup: "my-rg-vnet",
  1625  					IsVNetManaged:     false,
  1626  					RouteTableName:    "fake-route-table-1",
  1627  					SecurityGroupName: "fake-security-group-1",
  1628  					NatGatewayName:    "fake-natgateway-1",
  1629  				},
  1630  				&subnets.SubnetSpec{
  1631  					Name:              "fake-bastion-subnet-1",
  1632  					ResourceGroup:     "my-rg",
  1633  					SubscriptionID:    "123",
  1634  					CIDRs:             []string{"172.122.1.1./16"},
  1635  					VNetName:          "fake-vnet-1",
  1636  					VNetResourceGroup: "my-rg-vnet",
  1637  					IsVNetManaged:     false,
  1638  					SecurityGroupName: "fake-bastion-security-group-1",
  1639  					RouteTableName:    "fake-bastion-route-table-1",
  1640  				},
  1641  			},
  1642  		},
  1643  	}
  1644  
  1645  	for _, tt := range tests {
  1646  		tt := tt
  1647  		t.Run(tt.name, func(t *testing.T) {
  1648  			t.Parallel()
  1649  			fakeIdentity := &infrav1.AzureClusterIdentity{
  1650  				ObjectMeta: metav1.ObjectMeta{
  1651  					Name:      "fake-identity",
  1652  					Namespace: "default",
  1653  				},
  1654  				Spec: infrav1.AzureClusterIdentitySpec{
  1655  					Type:     infrav1.ServicePrincipal,
  1656  					ClientID: fakeClientID,
  1657  					TenantID: fakeTenantID,
  1658  				},
  1659  			}
  1660  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  1661  
  1662  			initObjects := []runtime.Object{&tt.vnet, fakeIdentity, fakeSecret}
  1663  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  1664  			tt.clusterScope.Client = fakeClient
  1665  
  1666  			if got := tt.clusterScope.SubnetSpecs(); !reflect.DeepEqual(got, tt.want) {
  1667  				t.Errorf("SubnetSpecs() = \n%s, want \n%s", specArrayToString(got), specArrayToString(tt.want))
  1668  			}
  1669  		})
  1670  	}
  1671  }
  1672  
  1673  func TestIsVnetManaged(t *testing.T) {
  1674  	scheme := runtime.NewScheme()
  1675  	_ = asonetworkv1api20201101.AddToScheme(scheme)
  1676  	_ = corev1.AddToScheme(scheme)
  1677  	_ = infrav1.AddToScheme(scheme)
  1678  
  1679  	tests := []struct {
  1680  		name         string
  1681  		clusterScope ClusterScope
  1682  		vnet         asonetworkv1api20201101.VirtualNetwork
  1683  		want         bool
  1684  	}{
  1685  		{
  1686  			name: "Wrong tags",
  1687  			clusterScope: ClusterScope{
  1688  				Cluster: &clusterv1.Cluster{
  1689  					ObjectMeta: metav1.ObjectMeta{
  1690  						Name: "my-cluster",
  1691  					},
  1692  				},
  1693  				AzureCluster: &infrav1.AzureCluster{
  1694  					Spec: infrav1.AzureClusterSpec{
  1695  						NetworkSpec: infrav1.NetworkSpec{
  1696  							Vnet: infrav1.VnetSpec{
  1697  								Name: "fake-vnet-1",
  1698  							},
  1699  						},
  1700  					},
  1701  				},
  1702  				cache: &ClusterCache{},
  1703  			},
  1704  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1705  				ObjectMeta: metav1.ObjectMeta{
  1706  					Name: "fake-vnet-1",
  1707  				},
  1708  				Status: asonetworkv1api20201101.VirtualNetwork_STATUS{
  1709  					Tags: map[string]string{
  1710  						"key": "value",
  1711  					},
  1712  				},
  1713  			},
  1714  			want: false,
  1715  		},
  1716  		{
  1717  			name: "Has owning tags",
  1718  			clusterScope: ClusterScope{
  1719  				Cluster: &clusterv1.Cluster{
  1720  					ObjectMeta: metav1.ObjectMeta{
  1721  						Name: "my-cluster",
  1722  					},
  1723  				},
  1724  				AzureCluster: &infrav1.AzureCluster{
  1725  					Spec: infrav1.AzureClusterSpec{
  1726  						NetworkSpec: infrav1.NetworkSpec{
  1727  							Vnet: infrav1.VnetSpec{
  1728  								Name: "fake-vnet-1",
  1729  							},
  1730  						},
  1731  					},
  1732  				},
  1733  				cache: &ClusterCache{},
  1734  			},
  1735  			vnet: asonetworkv1api20201101.VirtualNetwork{
  1736  				ObjectMeta: metav1.ObjectMeta{
  1737  					Name: "fake-vnet-1",
  1738  				},
  1739  				Status: asonetworkv1api20201101.VirtualNetwork_STATUS{
  1740  					Tags: map[string]string{
  1741  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": "owned",
  1742  					},
  1743  				},
  1744  			},
  1745  			want: true,
  1746  		},
  1747  		{
  1748  			name: "Has cached value of false",
  1749  			clusterScope: ClusterScope{
  1750  				AzureCluster: &infrav1.AzureCluster{
  1751  					Spec: infrav1.AzureClusterSpec{},
  1752  				},
  1753  				cache: &ClusterCache{
  1754  					isVnetManaged: ptr.To(false),
  1755  				},
  1756  			},
  1757  			want: false,
  1758  		},
  1759  		{
  1760  			name: "Has cached value of true",
  1761  			clusterScope: ClusterScope{
  1762  				AzureCluster: &infrav1.AzureCluster{
  1763  					Spec: infrav1.AzureClusterSpec{},
  1764  				},
  1765  				cache: &ClusterCache{
  1766  					isVnetManaged: ptr.To(true),
  1767  				},
  1768  			},
  1769  			want: true,
  1770  		},
  1771  	}
  1772  
  1773  	for _, tt := range tests {
  1774  		tt := tt
  1775  		t.Run(tt.name, func(t *testing.T) {
  1776  			t.Parallel()
  1777  			fakeIdentity := &infrav1.AzureClusterIdentity{
  1778  				ObjectMeta: metav1.ObjectMeta{
  1779  					Name:      "fake-identity",
  1780  					Namespace: "default",
  1781  				},
  1782  				Spec: infrav1.AzureClusterIdentitySpec{
  1783  					Type:     infrav1.ServicePrincipal,
  1784  					ClientID: fakeClientID,
  1785  					TenantID: fakeTenantID,
  1786  				},
  1787  			}
  1788  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  1789  
  1790  			initObjects := []runtime.Object{&tt.vnet, fakeIdentity, fakeSecret}
  1791  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  1792  			tt.clusterScope.Client = fakeClient
  1793  
  1794  			got := tt.clusterScope.IsVnetManaged()
  1795  			if !reflect.DeepEqual(got, tt.want) {
  1796  				t.Errorf("IsVnetManaged() = \n%t, want \n%t", got, tt.want)
  1797  			}
  1798  			if ptr.Deref(tt.clusterScope.cache.isVnetManaged, false) != got {
  1799  				t.Errorf("IsVnetManaged() = \n%t, cache = \n%t", got, ptr.Deref(tt.clusterScope.cache.isVnetManaged, false))
  1800  			}
  1801  		})
  1802  	}
  1803  }
  1804  
  1805  func TestAzureBastionSpec(t *testing.T) {
  1806  	tests := []struct {
  1807  		name         string
  1808  		clusterScope ClusterScope
  1809  		want         azure.ASOResourceSpecGetter[*asonetworkv1api20220701.BastionHost]
  1810  	}{
  1811  		{
  1812  			name: "returns nil if no subnets are specified",
  1813  			clusterScope: ClusterScope{
  1814  				AzureCluster: &infrav1.AzureCluster{
  1815  					Spec: infrav1.AzureClusterSpec{
  1816  						NetworkSpec: infrav1.NetworkSpec{
  1817  							Subnets: infrav1.Subnets{},
  1818  						},
  1819  					},
  1820  				},
  1821  			},
  1822  			want: nil,
  1823  		},
  1824  		{
  1825  			name: "returns bastion spec if enabled",
  1826  			clusterScope: ClusterScope{
  1827  				Cluster: &clusterv1.Cluster{
  1828  					ObjectMeta: metav1.ObjectMeta{
  1829  						Name: "my-cluster",
  1830  					},
  1831  				},
  1832  				AzureClients: AzureClients{
  1833  					EnvironmentSettings: auth.EnvironmentSettings{
  1834  						Values: map[string]string{
  1835  							auth.SubscriptionID: "123",
  1836  						},
  1837  					},
  1838  				},
  1839  				AzureCluster: &infrav1.AzureCluster{
  1840  					Spec: infrav1.AzureClusterSpec{
  1841  						BastionSpec: infrav1.BastionSpec{
  1842  							AzureBastion: &infrav1.AzureBastion{
  1843  								Name: "fake-azure-bastion-1",
  1844  								Subnet: infrav1.SubnetSpec{
  1845  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1846  										Role:       infrav1.SubnetBastion,
  1847  										CIDRBlocks: []string{"172.122.1.1./16"},
  1848  										Name:       "fake-bastion-subnet-1",
  1849  									},
  1850  									RouteTable: infrav1.RouteTable{
  1851  										ID:   "fake-bastion-route-table-id-1",
  1852  										Name: "fake-bastion-route-table-1",
  1853  									},
  1854  									SecurityGroup: infrav1.SecurityGroup{
  1855  										Name: "fake-bastion-security-group-1",
  1856  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1857  											SecurityRules: infrav1.SecurityRules{
  1858  												{
  1859  													Name: "fake-rule-1",
  1860  												},
  1861  											},
  1862  										},
  1863  									},
  1864  								},
  1865  								PublicIP: infrav1.PublicIPSpec{
  1866  									Name: "fake-public-ip-1",
  1867  								},
  1868  							},
  1869  						},
  1870  						ResourceGroup: "my-rg",
  1871  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  1872  							Location: "centralIndia",
  1873  							IdentityRef: &corev1.ObjectReference{
  1874  								Kind: infrav1.AzureClusterIdentityKind,
  1875  							},
  1876  						},
  1877  						NetworkSpec: infrav1.NetworkSpec{
  1878  							Vnet: infrav1.VnetSpec{
  1879  								ID:            "fake-vnet-id-1",
  1880  								Name:          "fake-vnet-1",
  1881  								ResourceGroup: "my-rg-vnet",
  1882  							},
  1883  							Subnets: infrav1.Subnets{
  1884  								{
  1885  									SubnetClassSpec: infrav1.SubnetClassSpec{
  1886  										Role:       infrav1.SubnetNode,
  1887  										CIDRBlocks: []string{"192.168.1.1/16"},
  1888  										Name:       "fake-subnet-1",
  1889  									},
  1890  									NatGateway: infrav1.NatGateway{
  1891  										NatGatewayClassSpec: infrav1.NatGatewayClassSpec{
  1892  											Name: "fake-natgateway-1",
  1893  										},
  1894  									},
  1895  									RouteTable: infrav1.RouteTable{
  1896  										ID:   "fake-route-table-id-1",
  1897  										Name: "fake-route-table-1",
  1898  									},
  1899  									SecurityGroup: infrav1.SecurityGroup{
  1900  										Name: "fake-security-group-1",
  1901  										SecurityGroupClass: infrav1.SecurityGroupClass{
  1902  											SecurityRules: infrav1.SecurityRules{
  1903  												{
  1904  													Name: "fake-rule-1",
  1905  												},
  1906  											},
  1907  										},
  1908  									},
  1909  								},
  1910  							},
  1911  						},
  1912  					},
  1913  				},
  1914  				cache: &ClusterCache{},
  1915  			},
  1916  			want: &bastionhosts.AzureBastionSpec{
  1917  				Name:          "fake-azure-bastion-1",
  1918  				ResourceGroup: "my-rg",
  1919  				Location:      "centralIndia",
  1920  				ClusterName:   "my-cluster",
  1921  				SubnetID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/"+
  1922  					"virtualNetworks/%s/subnets/%s", "123", "my-rg-vnet", "fake-vnet-1", "fake-bastion-subnet-1"),
  1923  				PublicIPID: fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/"+
  1924  					"publicIPAddresses/%s", "123", "my-rg", "fake-public-ip-1"),
  1925  			},
  1926  		},
  1927  	}
  1928  
  1929  	for _, tt := range tests {
  1930  		tt := tt
  1931  		t.Run(tt.name, func(t *testing.T) {
  1932  			t.Parallel()
  1933  			if got := tt.clusterScope.AzureBastionSpec(); !reflect.DeepEqual(got, tt.want) {
  1934  				t.Errorf("AzureBastionSpec() = \n%s, want \n%s", specToString(got), specToString(tt.want))
  1935  			}
  1936  		})
  1937  	}
  1938  }
  1939  
  1940  func TestSubnet(t *testing.T) {
  1941  	tests := []struct {
  1942  		clusterName             string
  1943  		subnetName              string
  1944  		azureClusterNetworkSpec infrav1.NetworkSpec
  1945  		expectSubnet            infrav1.SubnetSpec
  1946  	}{
  1947  		{
  1948  			clusterName:             "my-cluster-1",
  1949  			subnetName:              "subnet-1",
  1950  			azureClusterNetworkSpec: infrav1.NetworkSpec{},
  1951  			expectSubnet:            infrav1.SubnetSpec{},
  1952  		},
  1953  		{
  1954  			clusterName: "my-cluster-1",
  1955  			subnetName:  "subnet-1",
  1956  			azureClusterNetworkSpec: infrav1.NetworkSpec{
  1957  				Subnets: infrav1.Subnets{
  1958  					infrav1.SubnetSpec{
  1959  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1960  							Name: "subnet-1",
  1961  						},
  1962  						ID: "subnet-1-id",
  1963  					},
  1964  					infrav1.SubnetSpec{
  1965  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1966  							Name: "subnet-2",
  1967  						},
  1968  						ID: "subnet-1-id",
  1969  					},
  1970  					infrav1.SubnetSpec{
  1971  						SubnetClassSpec: infrav1.SubnetClassSpec{
  1972  							Name: "subnet-3",
  1973  						},
  1974  						ID: "subnet-2-id",
  1975  					},
  1976  				},
  1977  			},
  1978  			expectSubnet: infrav1.SubnetSpec{
  1979  				SubnetClassSpec: infrav1.SubnetClassSpec{
  1980  					Name: "subnet-1",
  1981  				},
  1982  				ID: "subnet-1-id",
  1983  			},
  1984  		},
  1985  	}
  1986  	for _, tc := range tests {
  1987  		t.Run(tc.clusterName, func(t *testing.T) {
  1988  			g := NewWithT(t)
  1989  			scheme := runtime.NewScheme()
  1990  			_ = infrav1.AddToScheme(scheme)
  1991  			_ = clusterv1.AddToScheme(scheme)
  1992  			_ = corev1.AddToScheme(scheme)
  1993  
  1994  			cluster := &clusterv1.Cluster{
  1995  				ObjectMeta: metav1.ObjectMeta{
  1996  					Name:      tc.clusterName,
  1997  					Namespace: "default",
  1998  				},
  1999  			}
  2000  			azureCluster := &infrav1.AzureCluster{
  2001  				ObjectMeta: metav1.ObjectMeta{
  2002  					Name: tc.clusterName,
  2003  					OwnerReferences: []metav1.OwnerReference{
  2004  						{
  2005  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2006  							Kind:       "Cluster",
  2007  							Name:       "my-cluster",
  2008  						},
  2009  					},
  2010  				},
  2011  				Spec: infrav1.AzureClusterSpec{
  2012  					NetworkSpec: tc.azureClusterNetworkSpec,
  2013  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2014  						SubscriptionID: "123",
  2015  						IdentityRef: &corev1.ObjectReference{
  2016  							Kind: infrav1.AzureClusterIdentityKind,
  2017  						},
  2018  					},
  2019  				},
  2020  			}
  2021  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2022  				Spec: infrav1.AzureClusterIdentitySpec{
  2023  					Type:     infrav1.ServicePrincipal,
  2024  					ClientID: fakeClientID,
  2025  					TenantID: fakeTenantID,
  2026  				},
  2027  			}
  2028  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2029  
  2030  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2031  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2032  
  2033  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2034  				Cluster:      cluster,
  2035  				AzureCluster: azureCluster,
  2036  				Client:       fakeClient,
  2037  			})
  2038  			g.Expect(err).NotTo(HaveOccurred())
  2039  			got := clusterScope.Subnet(tc.subnetName)
  2040  			g.Expect(tc.expectSubnet).Should(Equal(got))
  2041  		})
  2042  	}
  2043  }
  2044  
  2045  func TestControlPlaneRouteTable(t *testing.T) {
  2046  	tests := []struct {
  2047  		clusterName             string
  2048  		azureClusterNetworkSpec infrav1.NetworkSpec
  2049  		expectRouteTable        infrav1.RouteTable
  2050  	}{
  2051  		{
  2052  			clusterName:             "my-cluster-1",
  2053  			azureClusterNetworkSpec: infrav1.NetworkSpec{},
  2054  			expectRouteTable:        infrav1.RouteTable{},
  2055  		},
  2056  		{
  2057  			clusterName: "my-cluster-2",
  2058  			azureClusterNetworkSpec: infrav1.NetworkSpec{
  2059  				Subnets: infrav1.Subnets{
  2060  					infrav1.SubnetSpec{
  2061  						RouteTable: infrav1.RouteTable{
  2062  							ID:   "fake-id-1",
  2063  							Name: "route-tb-1",
  2064  						},
  2065  						SubnetClassSpec: infrav1.SubnetClassSpec{
  2066  							Role: infrav1.SubnetNode,
  2067  						},
  2068  					},
  2069  					infrav1.SubnetSpec{
  2070  						RouteTable: infrav1.RouteTable{
  2071  							ID:   "fake-id-2",
  2072  							Name: "route-tb-2",
  2073  						},
  2074  						SubnetClassSpec: infrav1.SubnetClassSpec{
  2075  							Role: infrav1.SubnetControlPlane,
  2076  						},
  2077  					},
  2078  					infrav1.SubnetSpec{
  2079  						RouteTable: infrav1.RouteTable{
  2080  							ID:   "fake-id-3",
  2081  							Name: "route-tb-3",
  2082  						},
  2083  						SubnetClassSpec: infrav1.SubnetClassSpec{
  2084  							Role: infrav1.SubnetBastion,
  2085  						},
  2086  					},
  2087  				},
  2088  			},
  2089  			expectRouteTable: infrav1.RouteTable{
  2090  				ID:   "fake-id-2",
  2091  				Name: "route-tb-2",
  2092  			},
  2093  		},
  2094  	}
  2095  	for _, tc := range tests {
  2096  		t.Run(tc.clusterName, func(t *testing.T) {
  2097  			g := NewWithT(t)
  2098  			scheme := runtime.NewScheme()
  2099  			_ = infrav1.AddToScheme(scheme)
  2100  			_ = clusterv1.AddToScheme(scheme)
  2101  			_ = corev1.AddToScheme(scheme)
  2102  
  2103  			cluster := &clusterv1.Cluster{
  2104  				ObjectMeta: metav1.ObjectMeta{
  2105  					Name:      tc.clusterName,
  2106  					Namespace: "default",
  2107  				},
  2108  			}
  2109  			azureCluster := &infrav1.AzureCluster{
  2110  				ObjectMeta: metav1.ObjectMeta{
  2111  					Name: tc.clusterName,
  2112  					OwnerReferences: []metav1.OwnerReference{
  2113  						{
  2114  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2115  							Kind:       "Cluster",
  2116  							Name:       "my-cluster",
  2117  						},
  2118  					},
  2119  				},
  2120  				Spec: infrav1.AzureClusterSpec{
  2121  					NetworkSpec: tc.azureClusterNetworkSpec,
  2122  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2123  						SubscriptionID: "123",
  2124  						IdentityRef: &corev1.ObjectReference{
  2125  							Kind: infrav1.AzureClusterIdentityKind,
  2126  						},
  2127  					},
  2128  				},
  2129  			}
  2130  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2131  				Spec: infrav1.AzureClusterIdentitySpec{
  2132  					Type:     infrav1.ServicePrincipal,
  2133  					ClientID: fakeClientID,
  2134  					TenantID: fakeTenantID,
  2135  				},
  2136  			}
  2137  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2138  
  2139  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2140  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2141  
  2142  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2143  				Cluster:      cluster,
  2144  				AzureCluster: azureCluster,
  2145  				Client:       fakeClient,
  2146  			})
  2147  			g.Expect(err).NotTo(HaveOccurred())
  2148  			got := clusterScope.ControlPlaneRouteTable()
  2149  			g.Expect(tc.expectRouteTable).Should(Equal(got))
  2150  		})
  2151  	}
  2152  }
  2153  
  2154  func TestGetPrivateDNSZoneName(t *testing.T) {
  2155  	tests := []struct {
  2156  		clusterName              string
  2157  		azureClusterNetworkSpec  infrav1.NetworkSpec
  2158  		expectPrivateDNSZoneName string
  2159  	}{
  2160  		{
  2161  			clusterName: "my-cluster-1",
  2162  			azureClusterNetworkSpec: infrav1.NetworkSpec{
  2163  				NetworkClassSpec: infrav1.NetworkClassSpec{
  2164  					PrivateDNSZoneName: "fake-privateDNSZoneName",
  2165  				},
  2166  			},
  2167  			expectPrivateDNSZoneName: "fake-privateDNSZoneName",
  2168  		},
  2169  		{
  2170  			clusterName:              "my-cluster-2",
  2171  			expectPrivateDNSZoneName: "my-cluster-2.capz.io",
  2172  		},
  2173  	}
  2174  	for _, tc := range tests {
  2175  		t.Run(tc.clusterName, func(t *testing.T) {
  2176  			g := NewWithT(t)
  2177  			scheme := runtime.NewScheme()
  2178  			_ = infrav1.AddToScheme(scheme)
  2179  			_ = clusterv1.AddToScheme(scheme)
  2180  			_ = corev1.AddToScheme(scheme)
  2181  
  2182  			cluster := &clusterv1.Cluster{
  2183  				ObjectMeta: metav1.ObjectMeta{
  2184  					Name:      tc.clusterName,
  2185  					Namespace: "default",
  2186  				},
  2187  			}
  2188  			azureCluster := &infrav1.AzureCluster{
  2189  				ObjectMeta: metav1.ObjectMeta{
  2190  					Name: tc.clusterName,
  2191  					OwnerReferences: []metav1.OwnerReference{
  2192  						{
  2193  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2194  							Kind:       "Cluster",
  2195  							Name:       "my-cluster",
  2196  						},
  2197  					},
  2198  				},
  2199  				Spec: infrav1.AzureClusterSpec{
  2200  					NetworkSpec: tc.azureClusterNetworkSpec,
  2201  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2202  						SubscriptionID: "123",
  2203  						IdentityRef: &corev1.ObjectReference{
  2204  							Kind: infrav1.AzureClusterIdentityKind,
  2205  						},
  2206  					},
  2207  				},
  2208  			}
  2209  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2210  				Spec: infrav1.AzureClusterIdentitySpec{
  2211  					Type:     infrav1.ServicePrincipal,
  2212  					ClientID: fakeClientID,
  2213  					TenantID: fakeTenantID,
  2214  				},
  2215  			}
  2216  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2217  
  2218  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2219  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2220  
  2221  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2222  				Cluster:      cluster,
  2223  				AzureCluster: azureCluster,
  2224  				Client:       fakeClient,
  2225  			})
  2226  			g.Expect(err).NotTo(HaveOccurred())
  2227  			got := clusterScope.GetPrivateDNSZoneName()
  2228  			g.Expect(tc.expectPrivateDNSZoneName).Should(Equal(got))
  2229  		})
  2230  	}
  2231  }
  2232  
  2233  func TestAPIServerLBPoolName(t *testing.T) {
  2234  	tests := []struct {
  2235  		lbName           string
  2236  		clusterName      string
  2237  		expectLBpoolName string
  2238  	}{
  2239  		{
  2240  			lbName:           "fake-lb-1",
  2241  			clusterName:      "my-cluster-1",
  2242  			expectLBpoolName: "fake-lb-1-backendPool",
  2243  		},
  2244  		{
  2245  			lbName:           "fake-lb-2",
  2246  			clusterName:      "my-cluster-2",
  2247  			expectLBpoolName: "fake-lb-2-backendPool",
  2248  		},
  2249  	}
  2250  	for _, tc := range tests {
  2251  		t.Run(tc.lbName, func(t *testing.T) {
  2252  			g := NewWithT(t)
  2253  			scheme := runtime.NewScheme()
  2254  			_ = infrav1.AddToScheme(scheme)
  2255  			_ = clusterv1.AddToScheme(scheme)
  2256  			_ = corev1.AddToScheme(scheme)
  2257  
  2258  			cluster := &clusterv1.Cluster{
  2259  				ObjectMeta: metav1.ObjectMeta{
  2260  					Name:      tc.clusterName,
  2261  					Namespace: "default",
  2262  				},
  2263  			}
  2264  			azureCluster := &infrav1.AzureCluster{
  2265  				ObjectMeta: metav1.ObjectMeta{
  2266  					Name: tc.clusterName,
  2267  					OwnerReferences: []metav1.OwnerReference{
  2268  						{
  2269  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2270  							Kind:       "Cluster",
  2271  							Name:       "my-cluster",
  2272  						},
  2273  					},
  2274  				},
  2275  				Spec: infrav1.AzureClusterSpec{
  2276  					NetworkSpec: infrav1.NetworkSpec{
  2277  						APIServerLB: infrav1.LoadBalancerSpec{
  2278  							Name: tc.lbName,
  2279  						},
  2280  					},
  2281  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2282  						SubscriptionID: "123",
  2283  						IdentityRef: &corev1.ObjectReference{
  2284  							Kind: infrav1.AzureClusterIdentityKind,
  2285  						},
  2286  					},
  2287  				},
  2288  			}
  2289  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2290  				Spec: infrav1.AzureClusterIdentitySpec{
  2291  					Type:     infrav1.ServicePrincipal,
  2292  					ClientID: fakeClientID,
  2293  					TenantID: fakeTenantID,
  2294  				},
  2295  			}
  2296  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2297  
  2298  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2299  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2300  
  2301  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2302  				Cluster:      cluster,
  2303  				AzureCluster: azureCluster,
  2304  				Client:       fakeClient,
  2305  			})
  2306  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2307  			g.Expect(err).NotTo(HaveOccurred())
  2308  			got := clusterScope.APIServerLBPoolName()
  2309  			g.Expect(tc.expectLBpoolName).Should(Equal(got))
  2310  		})
  2311  	}
  2312  }
  2313  
  2314  func TestOutboundLBName(t *testing.T) {
  2315  	tests := []struct {
  2316  		clusterName            string
  2317  		name                   string
  2318  		role                   string
  2319  		apiServerLB            *infrav1.LoadBalancerSpec
  2320  		controlPlaneOutboundLB *infrav1.LoadBalancerSpec
  2321  		nodeOutboundLB         *infrav1.LoadBalancerSpec
  2322  		expected               string
  2323  	}{
  2324  		{
  2325  			clusterName: "my-cluster",
  2326  			name:        "public cluster node outbound lb",
  2327  			role:        "node",
  2328  			expected:    "",
  2329  		},
  2330  		{
  2331  			clusterName: "my-cluster",
  2332  			name:        "public cluster control plane outbound lb",
  2333  			role:        "control-plane",
  2334  			expected:    "my-cluster-public-lb",
  2335  		},
  2336  		{
  2337  			clusterName:    "my-cluster",
  2338  			name:           "private cluster with node outbound lb",
  2339  			role:           "node",
  2340  			nodeOutboundLB: &infrav1.LoadBalancerSpec{},
  2341  			apiServerLB: &infrav1.LoadBalancerSpec{
  2342  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2343  					Type: "Internal",
  2344  				}},
  2345  			expected: "my-cluster",
  2346  		},
  2347  		{
  2348  			clusterName: "my-cluster",
  2349  			name:        "private cluster without node outbound lb",
  2350  			role:        "node",
  2351  			apiServerLB: &infrav1.LoadBalancerSpec{
  2352  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2353  					Type: "Internal",
  2354  				}},
  2355  			expected: "",
  2356  		},
  2357  		{
  2358  			clusterName:            "my-cluster",
  2359  			name:                   "private cluster with control plane outbound lb",
  2360  			role:                   "control-plane",
  2361  			controlPlaneOutboundLB: &infrav1.LoadBalancerSpec{Name: "cp-outbound-lb"},
  2362  			apiServerLB: &infrav1.LoadBalancerSpec{
  2363  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2364  					Type: "Internal",
  2365  				}},
  2366  			expected: "cp-outbound-lb",
  2367  		},
  2368  		{
  2369  			clusterName: "my-cluster",
  2370  			name:        "private cluster without control plane outbound lb",
  2371  			role:        "control-plane",
  2372  			apiServerLB: &infrav1.LoadBalancerSpec{
  2373  				LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  2374  					Type: "Internal",
  2375  				}},
  2376  			expected: "",
  2377  		},
  2378  	}
  2379  
  2380  	for _, tc := range tests {
  2381  		t.Run(tc.name, func(t *testing.T) {
  2382  			g := NewWithT(t)
  2383  			scheme := runtime.NewScheme()
  2384  			_ = infrav1.AddToScheme(scheme)
  2385  			_ = clusterv1.AddToScheme(scheme)
  2386  			_ = corev1.AddToScheme(scheme)
  2387  
  2388  			cluster := &clusterv1.Cluster{
  2389  				ObjectMeta: metav1.ObjectMeta{
  2390  					Name:      tc.clusterName,
  2391  					Namespace: "default",
  2392  				},
  2393  			}
  2394  
  2395  			azureCluster := &infrav1.AzureCluster{
  2396  				ObjectMeta: metav1.ObjectMeta{
  2397  					Name: tc.clusterName,
  2398  					OwnerReferences: []metav1.OwnerReference{
  2399  						{
  2400  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2401  							Kind:       "Cluster",
  2402  							Name:       "my-cluster",
  2403  						},
  2404  					},
  2405  				},
  2406  				Spec: infrav1.AzureClusterSpec{
  2407  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2408  						SubscriptionID: "123",
  2409  						IdentityRef: &corev1.ObjectReference{
  2410  							Kind: infrav1.AzureClusterIdentityKind,
  2411  						},
  2412  					},
  2413  					NetworkSpec: infrav1.NetworkSpec{
  2414  						Subnets: infrav1.Subnets{
  2415  							{
  2416  								SubnetClassSpec: infrav1.SubnetClassSpec{
  2417  									Name: "node",
  2418  									Role: infrav1.SubnetNode,
  2419  								},
  2420  							},
  2421  						},
  2422  					},
  2423  				},
  2424  			}
  2425  
  2426  			if tc.apiServerLB != nil {
  2427  				azureCluster.Spec.NetworkSpec.APIServerLB = *tc.apiServerLB
  2428  			}
  2429  
  2430  			if tc.controlPlaneOutboundLB != nil {
  2431  				azureCluster.Spec.NetworkSpec.ControlPlaneOutboundLB = tc.controlPlaneOutboundLB
  2432  			}
  2433  
  2434  			if tc.nodeOutboundLB != nil {
  2435  				azureCluster.Spec.NetworkSpec.NodeOutboundLB = tc.nodeOutboundLB
  2436  			}
  2437  
  2438  			azureCluster.Default()
  2439  
  2440  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2441  				Spec: infrav1.AzureClusterIdentitySpec{
  2442  					Type:     infrav1.ServicePrincipal,
  2443  					ClientID: fakeClientID,
  2444  					TenantID: fakeTenantID,
  2445  				},
  2446  			}
  2447  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2448  
  2449  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2450  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2451  
  2452  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2453  				Cluster:      cluster,
  2454  				AzureCluster: azureCluster,
  2455  				Client:       fakeClient,
  2456  			})
  2457  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2458  			g.Expect(err).NotTo(HaveOccurred())
  2459  			got := clusterScope.OutboundLBName(tc.role)
  2460  			g.Expect(tc.expected).Should(Equal(got))
  2461  		})
  2462  	}
  2463  }
  2464  
  2465  func TestBackendPoolName(t *testing.T) {
  2466  	tests := []struct {
  2467  		name        string
  2468  		clusterName string
  2469  
  2470  		customAPIServerBackendPoolName    string
  2471  		customNodeBackendPoolName         string
  2472  		customControlPlaneBackendPoolName string
  2473  
  2474  		expectedAPIServerBackendPoolName    string
  2475  		expectedNodeBackendPoolName         string
  2476  		expectedControlPlaneBackendPoolName string
  2477  	}{
  2478  		{
  2479  			name:                                "With default backend pool names",
  2480  			clusterName:                         "my-cluster",
  2481  			expectedAPIServerBackendPoolName:    "APIServerLBName-backendPool",
  2482  			expectedNodeBackendPoolName:         "NodeOutboundLBName-outboundBackendPool",
  2483  			expectedControlPlaneBackendPoolName: "my-cluster-outbound-lb-outboundBackendPool",
  2484  		},
  2485  		{
  2486  			name:        "With custom node backend pool name",
  2487  			clusterName: "my-cluster",
  2488  
  2489  			// setting custom name for node pool name only, others should stay the same
  2490  			customNodeBackendPoolName: "custom-node-poolname",
  2491  
  2492  			expectedAPIServerBackendPoolName:    "APIServerLBName-backendPool",
  2493  			expectedNodeBackendPoolName:         "custom-node-poolname",
  2494  			expectedControlPlaneBackendPoolName: "my-cluster-outbound-lb-outboundBackendPool",
  2495  		},
  2496  		{
  2497  			name:        "With custom backends pool name",
  2498  			clusterName: "my-cluster",
  2499  
  2500  			// setting custom names for all backends pools
  2501  			customAPIServerBackendPoolName:    "custom-api-server-poolname",
  2502  			customNodeBackendPoolName:         "custom-node-poolname",
  2503  			customControlPlaneBackendPoolName: "custom-control-plane-poolname",
  2504  
  2505  			expectedAPIServerBackendPoolName:    "custom-api-server-poolname",
  2506  			expectedNodeBackendPoolName:         "custom-node-poolname",
  2507  			expectedControlPlaneBackendPoolName: "custom-control-plane-poolname",
  2508  		},
  2509  	}
  2510  	for _, tc := range tests {
  2511  		t.Run(tc.name, func(t *testing.T) {
  2512  			g := NewWithT(t)
  2513  			scheme := runtime.NewScheme()
  2514  			_ = infrav1.AddToScheme(scheme)
  2515  			_ = clusterv1.AddToScheme(scheme)
  2516  			_ = corev1.AddToScheme(scheme)
  2517  
  2518  			cluster := &clusterv1.Cluster{
  2519  				ObjectMeta: metav1.ObjectMeta{
  2520  					Name:      tc.clusterName,
  2521  					Namespace: "default",
  2522  				},
  2523  			}
  2524  
  2525  			azureCluster := &infrav1.AzureCluster{
  2526  				ObjectMeta: metav1.ObjectMeta{
  2527  					Name: tc.clusterName,
  2528  					OwnerReferences: []metav1.OwnerReference{
  2529  						{
  2530  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2531  							Kind:       "Cluster",
  2532  							Name:       tc.clusterName,
  2533  						},
  2534  					},
  2535  				},
  2536  				Spec: infrav1.AzureClusterSpec{
  2537  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2538  						SubscriptionID: "123",
  2539  						IdentityRef: &corev1.ObjectReference{
  2540  							Kind: infrav1.AzureClusterIdentityKind,
  2541  						},
  2542  					},
  2543  					NetworkSpec: infrav1.NetworkSpec{
  2544  						Subnets: infrav1.Subnets{
  2545  							{
  2546  								SubnetClassSpec: infrav1.SubnetClassSpec{
  2547  									Role: infrav1.SubnetNode,
  2548  									Name: "node",
  2549  								},
  2550  							},
  2551  						},
  2552  						APIServerLB: infrav1.LoadBalancerSpec{
  2553  							Name: "APIServerLBName",
  2554  						},
  2555  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
  2556  							Name: "ControlPlaneOutboundLBName",
  2557  						},
  2558  						NodeOutboundLB: &infrav1.LoadBalancerSpec{
  2559  							Name: "NodeOutboundLBName",
  2560  						},
  2561  					},
  2562  				},
  2563  			}
  2564  
  2565  			azureCluster.Default()
  2566  
  2567  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2568  				Spec: infrav1.AzureClusterIdentitySpec{
  2569  					Type:     infrav1.ServicePrincipal,
  2570  					ClientID: fakeClientID,
  2571  					TenantID: fakeTenantID,
  2572  				},
  2573  			}
  2574  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2575  
  2576  			if tc.customAPIServerBackendPoolName != "" {
  2577  				azureCluster.Spec.NetworkSpec.APIServerLB.BackendPool.Name = tc.customAPIServerBackendPoolName
  2578  			}
  2579  
  2580  			if tc.customNodeBackendPoolName != "" {
  2581  				azureCluster.Spec.NetworkSpec.NodeOutboundLB.BackendPool.Name = tc.customNodeBackendPoolName
  2582  			}
  2583  
  2584  			if tc.customControlPlaneBackendPoolName != "" {
  2585  				azureCluster.Spec.NetworkSpec.ControlPlaneOutboundLB.BackendPool.Name = tc.customControlPlaneBackendPoolName
  2586  			}
  2587  
  2588  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2589  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2590  
  2591  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2592  				Cluster:      cluster,
  2593  				AzureCluster: azureCluster,
  2594  				Client:       fakeClient,
  2595  			})
  2596  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2597  			g.Expect(err).NotTo(HaveOccurred())
  2598  			got := clusterScope.LBSpecs()
  2599  			g.Expect(got).To(HaveLen(3))
  2600  
  2601  			// API server backend pool name
  2602  			apiServerLBSpec := got[0].(*loadbalancers.LBSpec)
  2603  			g.Expect(apiServerLBSpec.BackendPoolName).To(Equal(tc.expectedAPIServerBackendPoolName))
  2604  			g.Expect(apiServerLBSpec.Role).To(Equal(infrav1.APIServerRole))
  2605  
  2606  			// Node backend pool name
  2607  			NodeLBSpec := got[1].(*loadbalancers.LBSpec)
  2608  			g.Expect(NodeLBSpec.BackendPoolName).To(Equal(tc.expectedNodeBackendPoolName))
  2609  			g.Expect(NodeLBSpec.Role).To(Equal(infrav1.NodeOutboundRole))
  2610  
  2611  			// Control Plane backend pool name
  2612  			controlPlaneLBSpec := got[2].(*loadbalancers.LBSpec)
  2613  			g.Expect(controlPlaneLBSpec.BackendPoolName).To(Equal(tc.expectedControlPlaneBackendPoolName))
  2614  			g.Expect(controlPlaneLBSpec.Role).To(Equal(infrav1.ControlPlaneOutboundRole))
  2615  		})
  2616  	}
  2617  }
  2618  
  2619  func TestOutboundPoolName(t *testing.T) {
  2620  	tests := []struct {
  2621  		name                   string
  2622  		clusterName            string
  2623  		loadBalancerName       string
  2624  		expectOutboundPoolName string
  2625  	}{
  2626  		{
  2627  			name:                   "Empty loadBalancerName",
  2628  			clusterName:            "my-cluster",
  2629  			loadBalancerName:       "",
  2630  			expectOutboundPoolName: "",
  2631  		},
  2632  		{
  2633  			name:                   "Non empty loadBalancerName",
  2634  			clusterName:            "my-cluster",
  2635  			loadBalancerName:       "my-loadbalancer",
  2636  			expectOutboundPoolName: "my-loadbalancer-outboundBackendPool",
  2637  		},
  2638  	}
  2639  	for _, tc := range tests {
  2640  		t.Run(tc.name, func(t *testing.T) {
  2641  			g := NewWithT(t)
  2642  			scheme := runtime.NewScheme()
  2643  			_ = infrav1.AddToScheme(scheme)
  2644  			_ = clusterv1.AddToScheme(scheme)
  2645  			_ = corev1.AddToScheme(scheme)
  2646  
  2647  			cluster := &clusterv1.Cluster{
  2648  				ObjectMeta: metav1.ObjectMeta{
  2649  					Name:      tc.clusterName,
  2650  					Namespace: "default",
  2651  				},
  2652  			}
  2653  			azureCluster := &infrav1.AzureCluster{
  2654  				ObjectMeta: metav1.ObjectMeta{
  2655  					Name: tc.clusterName,
  2656  					OwnerReferences: []metav1.OwnerReference{
  2657  						{
  2658  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2659  							Kind:       "Cluster",
  2660  							Name:       "my-cluster",
  2661  						},
  2662  					},
  2663  				},
  2664  				Spec: infrav1.AzureClusterSpec{
  2665  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2666  						SubscriptionID: "123",
  2667  						IdentityRef: &corev1.ObjectReference{
  2668  							Kind: infrav1.AzureClusterIdentityKind,
  2669  						},
  2670  					},
  2671  				},
  2672  			}
  2673  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2674  				Spec: infrav1.AzureClusterIdentitySpec{
  2675  					Type:     infrav1.ServicePrincipal,
  2676  					ClientID: fakeClientID,
  2677  					TenantID: fakeTenantID,
  2678  				},
  2679  			}
  2680  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2681  
  2682  			if tc.loadBalancerName != "" {
  2683  				azureCluster.Spec.NetworkSpec.NodeOutboundLB = &infrav1.LoadBalancerSpec{
  2684  					Name: tc.loadBalancerName,
  2685  				}
  2686  			}
  2687  
  2688  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2689  			azureCluster.Default()
  2690  
  2691  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2692  
  2693  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2694  				Cluster:      cluster,
  2695  				AzureCluster: azureCluster,
  2696  				Client:       fakeClient,
  2697  			})
  2698  			clusterScope.AzureCluster.SetBackendPoolNameDefault()
  2699  			g.Expect(err).NotTo(HaveOccurred())
  2700  			got := clusterScope.OutboundPoolName(infrav1.Node)
  2701  			g.Expect(tc.expectOutboundPoolName).Should(Equal(got))
  2702  		})
  2703  	}
  2704  }
  2705  
  2706  func TestGenerateFQDN(t *testing.T) {
  2707  	tests := []struct {
  2708  		clusterName    string
  2709  		ipName         string
  2710  		subscriptionID string
  2711  		resourceGroup  string
  2712  		location       string
  2713  		expectFQDN     string
  2714  	}{
  2715  		{
  2716  			clusterName:    "my-cluster",
  2717  			ipName:         "172.123.45.78",
  2718  			subscriptionID: "123",
  2719  			resourceGroup:  "my-rg",
  2720  			location:       "eastus",
  2721  		},
  2722  		{
  2723  			clusterName:    "my-cluster-1",
  2724  			ipName:         "172.123.45.79",
  2725  			subscriptionID: "567",
  2726  			resourceGroup:  "my-rg-1",
  2727  			location:       "westus",
  2728  		},
  2729  		{
  2730  			clusterName:    "my-cluster-2",
  2731  			ipName:         "172.123.45.80",
  2732  			subscriptionID: "183",
  2733  			resourceGroup:  "my-rg-2",
  2734  			location:       "centralasia",
  2735  		},
  2736  	}
  2737  	for _, tc := range tests {
  2738  		t.Run(tc.clusterName, func(t *testing.T) {
  2739  			g := NewWithT(t)
  2740  			scheme := runtime.NewScheme()
  2741  			_ = infrav1.AddToScheme(scheme)
  2742  			_ = clusterv1.AddToScheme(scheme)
  2743  			_ = corev1.AddToScheme(scheme)
  2744  
  2745  			cluster := &clusterv1.Cluster{
  2746  				ObjectMeta: metav1.ObjectMeta{
  2747  					Name:      tc.clusterName,
  2748  					Namespace: "default",
  2749  				},
  2750  			}
  2751  			azureCluster := &infrav1.AzureCluster{
  2752  				ObjectMeta: metav1.ObjectMeta{
  2753  					Name: tc.clusterName,
  2754  					OwnerReferences: []metav1.OwnerReference{
  2755  						{
  2756  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2757  							Kind:       "Cluster",
  2758  							Name:       "my-cluster",
  2759  						},
  2760  					},
  2761  				},
  2762  				Spec: infrav1.AzureClusterSpec{
  2763  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2764  						SubscriptionID: "123",
  2765  						Location:       tc.location,
  2766  						IdentityRef: &corev1.ObjectReference{
  2767  							Kind: infrav1.AzureClusterIdentityKind,
  2768  						},
  2769  					},
  2770  					ResourceGroup: tc.resourceGroup,
  2771  				},
  2772  			}
  2773  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2774  				Spec: infrav1.AzureClusterIdentitySpec{
  2775  					Type:     infrav1.ServicePrincipal,
  2776  					ClientID: fakeClientID,
  2777  					TenantID: fakeTenantID,
  2778  				},
  2779  			}
  2780  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2781  
  2782  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2783  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2784  
  2785  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2786  				Cluster:      cluster,
  2787  				AzureCluster: azureCluster,
  2788  				Client:       fakeClient,
  2789  			})
  2790  			g.Expect(err).NotTo(HaveOccurred())
  2791  			got := clusterScope.GenerateFQDN(tc.ipName)
  2792  			g.Expect(got).Should(ContainSubstring(tc.clusterName))
  2793  			g.Expect(got).Should(ContainSubstring(tc.location))
  2794  		})
  2795  	}
  2796  }
  2797  
  2798  func TestAdditionalTags(t *testing.T) {
  2799  	tests := []struct {
  2800  		name                       string
  2801  		clusterName                string
  2802  		azureClusterAdditionalTags infrav1.Tags
  2803  		expectTags                 infrav1.Tags
  2804  	}{
  2805  		{
  2806  			name:        "Nil tags",
  2807  			clusterName: "my-cluster",
  2808  			expectTags:  infrav1.Tags{},
  2809  		},
  2810  
  2811  		{
  2812  			name:        "Single tag present in azure cluster spec",
  2813  			clusterName: "my-cluster",
  2814  			azureClusterAdditionalTags: infrav1.Tags{
  2815  				"fake-id-1": "fake-value-1",
  2816  			},
  2817  			expectTags: infrav1.Tags{
  2818  				"fake-id-1": "fake-value-1",
  2819  			},
  2820  		},
  2821  		{
  2822  			name:        "Multiple tags present in azure cluster spec",
  2823  			clusterName: "my-cluster",
  2824  			azureClusterAdditionalTags: infrav1.Tags{
  2825  				"fake-id-1": "fake-value-1",
  2826  				"fake-id-2": "fake-value-2",
  2827  				"fake-id-3": "fake-value-3",
  2828  			},
  2829  			expectTags: infrav1.Tags{
  2830  				"fake-id-1": "fake-value-1",
  2831  				"fake-id-2": "fake-value-2",
  2832  				"fake-id-3": "fake-value-3",
  2833  			},
  2834  		},
  2835  	}
  2836  	for _, tc := range tests {
  2837  		t.Run(tc.name, func(t *testing.T) {
  2838  			g := NewWithT(t)
  2839  			scheme := runtime.NewScheme()
  2840  			_ = infrav1.AddToScheme(scheme)
  2841  			_ = clusterv1.AddToScheme(scheme)
  2842  			_ = corev1.AddToScheme(scheme)
  2843  
  2844  			cluster := &clusterv1.Cluster{
  2845  				ObjectMeta: metav1.ObjectMeta{
  2846  					Name:      tc.clusterName,
  2847  					Namespace: "default",
  2848  				},
  2849  			}
  2850  			azureCluster := &infrav1.AzureCluster{
  2851  				ObjectMeta: metav1.ObjectMeta{
  2852  					Name: tc.clusterName,
  2853  					OwnerReferences: []metav1.OwnerReference{
  2854  						{
  2855  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2856  							Kind:       "Cluster",
  2857  							Name:       "my-cluster",
  2858  						},
  2859  					},
  2860  				},
  2861  				Spec: infrav1.AzureClusterSpec{
  2862  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2863  						SubscriptionID: "123",
  2864  						AdditionalTags: tc.azureClusterAdditionalTags,
  2865  						IdentityRef: &corev1.ObjectReference{
  2866  							Kind: infrav1.AzureClusterIdentityKind,
  2867  						},
  2868  					},
  2869  				},
  2870  			}
  2871  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2872  				Spec: infrav1.AzureClusterIdentitySpec{
  2873  					Type:     infrav1.ServicePrincipal,
  2874  					ClientID: fakeClientID,
  2875  					TenantID: fakeTenantID,
  2876  				},
  2877  			}
  2878  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2879  
  2880  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2881  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2882  
  2883  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2884  				Cluster:      cluster,
  2885  				AzureCluster: azureCluster,
  2886  				Client:       fakeClient,
  2887  			})
  2888  			g.Expect(err).NotTo(HaveOccurred())
  2889  			got := clusterScope.AdditionalTags()
  2890  			g.Expect(tc.expectTags).Should(Equal(got))
  2891  		})
  2892  	}
  2893  }
  2894  
  2895  func TestAPIServerPort(t *testing.T) {
  2896  	tests := []struct {
  2897  		name                string
  2898  		clusterName         string
  2899  		clusterNetowrk      *clusterv1.ClusterNetwork
  2900  		expectAPIServerPort int32
  2901  	}{
  2902  		{
  2903  			name:                "Nil cluster network",
  2904  			clusterName:         "my-cluster",
  2905  			expectAPIServerPort: 6443,
  2906  		},
  2907  
  2908  		{
  2909  			name:                "Non nil cluster network but nil apiserverport",
  2910  			clusterName:         "my-cluster",
  2911  			clusterNetowrk:      &clusterv1.ClusterNetwork{},
  2912  			expectAPIServerPort: 6443,
  2913  		},
  2914  		{
  2915  			name:        "Non nil cluster network and non nil apiserverport",
  2916  			clusterName: "my-cluster",
  2917  			clusterNetowrk: &clusterv1.ClusterNetwork{
  2918  				APIServerPort: ptr.To[int32](7000),
  2919  			},
  2920  			expectAPIServerPort: 7000,
  2921  		},
  2922  	}
  2923  	for _, tc := range tests {
  2924  		t.Run(tc.name, func(t *testing.T) {
  2925  			g := NewWithT(t)
  2926  			scheme := runtime.NewScheme()
  2927  			_ = infrav1.AddToScheme(scheme)
  2928  			_ = clusterv1.AddToScheme(scheme)
  2929  			_ = corev1.AddToScheme(scheme)
  2930  
  2931  			cluster := &clusterv1.Cluster{
  2932  				ObjectMeta: metav1.ObjectMeta{
  2933  					Name:      tc.clusterName,
  2934  					Namespace: "default",
  2935  				},
  2936  				Spec: clusterv1.ClusterSpec{
  2937  					ClusterNetwork: tc.clusterNetowrk,
  2938  				},
  2939  			}
  2940  			azureCluster := &infrav1.AzureCluster{
  2941  				ObjectMeta: metav1.ObjectMeta{
  2942  					Name: tc.clusterName,
  2943  					OwnerReferences: []metav1.OwnerReference{
  2944  						{
  2945  							APIVersion: "cluster.x-k8s.io/v1beta1",
  2946  							Kind:       "Cluster",
  2947  							Name:       "my-cluster",
  2948  						},
  2949  					},
  2950  				},
  2951  				Spec: infrav1.AzureClusterSpec{
  2952  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  2953  						SubscriptionID: "123",
  2954  						IdentityRef: &corev1.ObjectReference{
  2955  							Kind: infrav1.AzureClusterIdentityKind,
  2956  						},
  2957  					},
  2958  				},
  2959  			}
  2960  			fakeIdentity := &infrav1.AzureClusterIdentity{
  2961  				Spec: infrav1.AzureClusterIdentitySpec{
  2962  					Type:     infrav1.ServicePrincipal,
  2963  					ClientID: fakeClientID,
  2964  					TenantID: fakeTenantID,
  2965  				},
  2966  			}
  2967  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  2968  
  2969  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  2970  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  2971  
  2972  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  2973  				Cluster:      cluster,
  2974  				AzureCluster: azureCluster,
  2975  				Client:       fakeClient,
  2976  			})
  2977  			g.Expect(err).NotTo(HaveOccurred())
  2978  			got := clusterScope.APIServerPort()
  2979  			g.Expect(tc.expectAPIServerPort).Should(Equal(got))
  2980  		})
  2981  	}
  2982  }
  2983  
  2984  func TestFailureDomains(t *testing.T) {
  2985  	tests := []struct {
  2986  		name                 string
  2987  		expectFailureDomains []*string
  2988  		clusterName          string
  2989  		azureClusterStatus   infrav1.AzureClusterStatus
  2990  	}{
  2991  		{
  2992  			name:                 "Empty azure cluster status",
  2993  			expectFailureDomains: []*string{},
  2994  			clusterName:          "my-cluster",
  2995  		},
  2996  
  2997  		{
  2998  			name:                 "Single failure domain present in azure cluster status",
  2999  			expectFailureDomains: []*string{ptr.To("failure-domain-id")},
  3000  			clusterName:          "my-cluster",
  3001  			azureClusterStatus: infrav1.AzureClusterStatus{
  3002  				FailureDomains: map[string]clusterv1.FailureDomainSpec{
  3003  					"failure-domain-id": {},
  3004  				},
  3005  			},
  3006  		},
  3007  		{
  3008  			name:                 "Multiple failure domains present in azure cluster status",
  3009  			expectFailureDomains: []*string{ptr.To("failure-domain-id-1"), ptr.To("failure-domain-id-2"), ptr.To("failure-domain-id-3")},
  3010  			clusterName:          "my-cluster",
  3011  			azureClusterStatus: infrav1.AzureClusterStatus{
  3012  				FailureDomains: map[string]clusterv1.FailureDomainSpec{
  3013  					"failure-domain-id-1": {},
  3014  					"failure-domain-id-2": {},
  3015  					"failure-domain-id-3": {},
  3016  				},
  3017  			},
  3018  		},
  3019  	}
  3020  	for _, tc := range tests {
  3021  		t.Run(tc.name, func(t *testing.T) {
  3022  			g := NewWithT(t)
  3023  			scheme := runtime.NewScheme()
  3024  			_ = infrav1.AddToScheme(scheme)
  3025  			_ = clusterv1.AddToScheme(scheme)
  3026  			_ = corev1.AddToScheme(scheme)
  3027  
  3028  			cluster := &clusterv1.Cluster{
  3029  				ObjectMeta: metav1.ObjectMeta{
  3030  					Name:      tc.clusterName,
  3031  					Namespace: "default",
  3032  				},
  3033  			}
  3034  			azureCluster := &infrav1.AzureCluster{
  3035  				ObjectMeta: metav1.ObjectMeta{
  3036  					Name: tc.clusterName,
  3037  					OwnerReferences: []metav1.OwnerReference{
  3038  						{
  3039  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3040  							Kind:       "Cluster",
  3041  							Name:       "my-cluster",
  3042  						},
  3043  					},
  3044  				},
  3045  				Spec: infrav1.AzureClusterSpec{
  3046  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3047  						SubscriptionID: "123",
  3048  						IdentityRef: &corev1.ObjectReference{
  3049  							Kind: infrav1.AzureClusterIdentityKind,
  3050  						},
  3051  					},
  3052  				},
  3053  				Status: tc.azureClusterStatus,
  3054  			}
  3055  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3056  				Spec: infrav1.AzureClusterIdentitySpec{
  3057  					Type:     infrav1.ServicePrincipal,
  3058  					ClientID: fakeClientID,
  3059  					TenantID: fakeTenantID,
  3060  				},
  3061  			}
  3062  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3063  
  3064  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3065  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3066  
  3067  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3068  				Cluster:      cluster,
  3069  				AzureCluster: azureCluster,
  3070  				Client:       fakeClient,
  3071  			})
  3072  			g.Expect(err).NotTo(HaveOccurred())
  3073  			got := clusterScope.FailureDomains()
  3074  			g.Expect(tc.expectFailureDomains).Should(ConsistOf(got))
  3075  		})
  3076  	}
  3077  }
  3078  
  3079  func TestClusterScope_LBSpecs(t *testing.T) {
  3080  	tests := []struct {
  3081  		name         string
  3082  		azureCluster *infrav1.AzureCluster
  3083  		want         []azure.ResourceSpecGetter
  3084  	}{
  3085  		{
  3086  			name: "API Server LB, Control Plane Oubound LB, and Node Outbound LB",
  3087  			azureCluster: &infrav1.AzureCluster{
  3088  				ObjectMeta: metav1.ObjectMeta{
  3089  					Name: "my-cluster",
  3090  				},
  3091  				Spec: infrav1.AzureClusterSpec{
  3092  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3093  						AdditionalTags: infrav1.Tags{
  3094  							"foo": "bar",
  3095  						},
  3096  						SubscriptionID: "123",
  3097  						Location:       "westus2",
  3098  						IdentityRef: &corev1.ObjectReference{
  3099  							Kind: infrav1.AzureClusterIdentityKind,
  3100  						},
  3101  					},
  3102  					ResourceGroup: "my-rg",
  3103  					NetworkSpec: infrav1.NetworkSpec{
  3104  						Vnet: infrav1.VnetSpec{
  3105  							Name:          "my-vnet",
  3106  							ResourceGroup: "my-rg",
  3107  						},
  3108  						Subnets: []infrav1.SubnetSpec{
  3109  							{
  3110  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3111  									Name: "cp-subnet",
  3112  									Role: infrav1.SubnetControlPlane,
  3113  								},
  3114  							},
  3115  							{
  3116  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3117  									Name: "node-subnet",
  3118  									Role: infrav1.SubnetNode,
  3119  								},
  3120  							},
  3121  						},
  3122  						APIServerLB: infrav1.LoadBalancerSpec{
  3123  							Name: "api-server-lb",
  3124  							BackendPool: infrav1.BackendPool{
  3125  								Name: "api-server-lb-backend-pool",
  3126  							},
  3127  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3128  								Type:                 infrav1.Public,
  3129  								IdleTimeoutInMinutes: ptr.To[int32](30),
  3130  								SKU:                  infrav1.SKUStandard,
  3131  							},
  3132  							FrontendIPs: []infrav1.FrontendIP{
  3133  								{
  3134  									Name: "api-server-lb-frontend-ip",
  3135  									PublicIP: &infrav1.PublicIPSpec{
  3136  										Name: "api-server-lb-frontend-ip",
  3137  									},
  3138  								},
  3139  							},
  3140  						},
  3141  						ControlPlaneOutboundLB: &infrav1.LoadBalancerSpec{
  3142  							Name: "cp-outbound-lb",
  3143  							BackendPool: infrav1.BackendPool{
  3144  								Name: "cp-outbound-backend-pool",
  3145  							},
  3146  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3147  								Type:                 infrav1.Public,
  3148  								IdleTimeoutInMinutes: ptr.To[int32](15),
  3149  								SKU:                  infrav1.SKUStandard,
  3150  							},
  3151  							FrontendIPs: []infrav1.FrontendIP{
  3152  								{
  3153  									Name: "cp-outbound-lb-frontend-ip",
  3154  									PublicIP: &infrav1.PublicIPSpec{
  3155  										Name: "cp-outbound-lb-frontend-ip",
  3156  									},
  3157  								},
  3158  							},
  3159  						},
  3160  						NodeOutboundLB: &infrav1.LoadBalancerSpec{
  3161  							Name: "node-outbound-lb",
  3162  							BackendPool: infrav1.BackendPool{
  3163  								Name: "node-outbound-backend-pool",
  3164  							},
  3165  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3166  								Type:                 infrav1.Public,
  3167  								IdleTimeoutInMinutes: ptr.To[int32](50),
  3168  								SKU:                  infrav1.SKUStandard,
  3169  							},
  3170  							FrontendIPs: []infrav1.FrontendIP{
  3171  								{
  3172  									Name: "node-outbound-lb-frontend-ip",
  3173  									PublicIP: &infrav1.PublicIPSpec{
  3174  										Name: "node-outbound-lb-frontend-ip",
  3175  									},
  3176  								},
  3177  							},
  3178  						},
  3179  					},
  3180  				},
  3181  			},
  3182  			want: []azure.ResourceSpecGetter{
  3183  				&loadbalancers.LBSpec{
  3184  					Name:              "api-server-lb",
  3185  					ResourceGroup:     "my-rg",
  3186  					SubscriptionID:    "123",
  3187  					ClusterName:       "my-cluster",
  3188  					Location:          "westus2",
  3189  					VNetName:          "my-vnet",
  3190  					VNetResourceGroup: "my-rg",
  3191  					SubnetName:        "cp-subnet",
  3192  					FrontendIPConfigs: []infrav1.FrontendIP{
  3193  						{
  3194  							Name: "api-server-lb-frontend-ip",
  3195  							PublicIP: &infrav1.PublicIPSpec{
  3196  								Name: "api-server-lb-frontend-ip",
  3197  							},
  3198  						},
  3199  					},
  3200  					APIServerPort:        6443,
  3201  					Type:                 infrav1.Public,
  3202  					SKU:                  infrav1.SKUStandard,
  3203  					Role:                 infrav1.APIServerRole,
  3204  					BackendPoolName:      "api-server-lb-backend-pool",
  3205  					IdleTimeoutInMinutes: ptr.To[int32](30),
  3206  					AdditionalTags: infrav1.Tags{
  3207  						"foo": "bar",
  3208  					},
  3209  				},
  3210  				&loadbalancers.LBSpec{
  3211  					Name:              "node-outbound-lb",
  3212  					ResourceGroup:     "my-rg",
  3213  					SubscriptionID:    "123",
  3214  					ClusterName:       "my-cluster",
  3215  					Location:          "westus2",
  3216  					VNetName:          "my-vnet",
  3217  					VNetResourceGroup: "my-rg",
  3218  					FrontendIPConfigs: []infrav1.FrontendIP{
  3219  						{
  3220  							Name: "node-outbound-lb-frontend-ip",
  3221  							PublicIP: &infrav1.PublicIPSpec{
  3222  								Name: "node-outbound-lb-frontend-ip",
  3223  							},
  3224  						},
  3225  					},
  3226  					Type:                 infrav1.Public,
  3227  					SKU:                  infrav1.SKUStandard,
  3228  					Role:                 infrav1.NodeOutboundRole,
  3229  					BackendPoolName:      "node-outbound-backend-pool",
  3230  					IdleTimeoutInMinutes: ptr.To[int32](50),
  3231  					AdditionalTags: infrav1.Tags{
  3232  						"foo": "bar",
  3233  					},
  3234  				},
  3235  				&loadbalancers.LBSpec{
  3236  					Name:              "cp-outbound-lb",
  3237  					ResourceGroup:     "my-rg",
  3238  					SubscriptionID:    "123",
  3239  					ClusterName:       "my-cluster",
  3240  					Location:          "westus2",
  3241  					VNetName:          "my-vnet",
  3242  					VNetResourceGroup: "my-rg",
  3243  					FrontendIPConfigs: []infrav1.FrontendIP{
  3244  						{
  3245  							Name: "cp-outbound-lb-frontend-ip",
  3246  							PublicIP: &infrav1.PublicIPSpec{
  3247  								Name: "cp-outbound-lb-frontend-ip",
  3248  							},
  3249  						},
  3250  					},
  3251  					Type:                 infrav1.Public,
  3252  					SKU:                  infrav1.SKUStandard,
  3253  					BackendPoolName:      "cp-outbound-backend-pool",
  3254  					IdleTimeoutInMinutes: ptr.To[int32](15),
  3255  					Role:                 infrav1.ControlPlaneOutboundRole,
  3256  					AdditionalTags: infrav1.Tags{
  3257  						"foo": "bar",
  3258  					},
  3259  				},
  3260  			},
  3261  		},
  3262  		{
  3263  			name: "Private API Server LB",
  3264  			azureCluster: &infrav1.AzureCluster{
  3265  				ObjectMeta: metav1.ObjectMeta{
  3266  					Name: "my-cluster",
  3267  				},
  3268  				Spec: infrav1.AzureClusterSpec{
  3269  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3270  						SubscriptionID: "123",
  3271  						Location:       "westus2",
  3272  						IdentityRef: &corev1.ObjectReference{
  3273  							Kind: infrav1.AzureClusterIdentityKind,
  3274  						},
  3275  					},
  3276  					ResourceGroup: "my-rg",
  3277  					NetworkSpec: infrav1.NetworkSpec{
  3278  						Vnet: infrav1.VnetSpec{
  3279  							Name:          "my-vnet",
  3280  							ResourceGroup: "my-rg",
  3281  						},
  3282  						Subnets: []infrav1.SubnetSpec{
  3283  							{
  3284  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3285  									Name: "cp-subnet",
  3286  									Role: infrav1.SubnetControlPlane,
  3287  								},
  3288  							},
  3289  							{
  3290  								SubnetClassSpec: infrav1.SubnetClassSpec{
  3291  									Name: "node-subnet",
  3292  									Role: infrav1.SubnetNode,
  3293  								},
  3294  							},
  3295  						},
  3296  						APIServerLB: infrav1.LoadBalancerSpec{
  3297  							Name: "api-server-lb",
  3298  							BackendPool: infrav1.BackendPool{
  3299  								Name: "api-server-lb-backend-pool",
  3300  							},
  3301  							LoadBalancerClassSpec: infrav1.LoadBalancerClassSpec{
  3302  								Type:                 infrav1.Internal,
  3303  								IdleTimeoutInMinutes: ptr.To[int32](30),
  3304  								SKU:                  infrav1.SKUStandard,
  3305  							},
  3306  						},
  3307  					},
  3308  				},
  3309  			},
  3310  			want: []azure.ResourceSpecGetter{
  3311  				&loadbalancers.LBSpec{
  3312  					Name:                 "api-server-lb",
  3313  					ResourceGroup:        "my-rg",
  3314  					SubscriptionID:       "123",
  3315  					ClusterName:          "my-cluster",
  3316  					Location:             "westus2",
  3317  					VNetName:             "my-vnet",
  3318  					VNetResourceGroup:    "my-rg",
  3319  					SubnetName:           "cp-subnet",
  3320  					APIServerPort:        6443,
  3321  					Type:                 infrav1.Internal,
  3322  					SKU:                  infrav1.SKUStandard,
  3323  					Role:                 infrav1.APIServerRole,
  3324  					BackendPoolName:      "api-server-lb-backend-pool",
  3325  					IdleTimeoutInMinutes: ptr.To[int32](30),
  3326  					AdditionalTags:       infrav1.Tags{},
  3327  				},
  3328  			},
  3329  		},
  3330  	}
  3331  	for _, tc := range tests {
  3332  		tc := tc
  3333  		t.Run(tc.name, func(t *testing.T) {
  3334  			t.Parallel()
  3335  			g := NewWithT(t)
  3336  			scheme := runtime.NewScheme()
  3337  			_ = infrav1.AddToScheme(scheme)
  3338  			_ = clusterv1.AddToScheme(scheme)
  3339  			_ = corev1.AddToScheme(scheme)
  3340  
  3341  			cluster := &clusterv1.Cluster{
  3342  				ObjectMeta: metav1.ObjectMeta{
  3343  					Name:      tc.azureCluster.Name,
  3344  					Namespace: "default",
  3345  				},
  3346  			}
  3347  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3348  				Spec: infrav1.AzureClusterIdentitySpec{
  3349  					Type:     infrav1.ServicePrincipal,
  3350  					ClientID: fakeClientID,
  3351  					TenantID: fakeTenantID,
  3352  				},
  3353  			}
  3354  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3355  
  3356  			initObjects := []runtime.Object{cluster, tc.azureCluster, fakeIdentity, fakeSecret}
  3357  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3358  
  3359  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3360  				Cluster:      cluster,
  3361  				AzureCluster: tc.azureCluster,
  3362  				Client:       fakeClient,
  3363  			})
  3364  			g.Expect(err).NotTo(HaveOccurred())
  3365  			if got := clusterScope.LBSpecs(); !reflect.DeepEqual(got, tc.want) {
  3366  				t.Errorf("LBSpecs() diff between expected result and actual result (%v): %s", got, cmp.Diff(tc.want, got))
  3367  			}
  3368  		})
  3369  	}
  3370  }
  3371  
  3372  func TestExtendedLocationName(t *testing.T) {
  3373  	tests := []struct {
  3374  		name             string
  3375  		clusterName      string
  3376  		extendedLocation infrav1.ExtendedLocationSpec
  3377  	}{
  3378  		{
  3379  			name:        "Empty extendedLocatioName",
  3380  			clusterName: "my-cluster",
  3381  			extendedLocation: infrav1.ExtendedLocationSpec{
  3382  				Name: "",
  3383  				Type: "",
  3384  			},
  3385  		},
  3386  		{
  3387  			name:        "Non empty extendedLocationName",
  3388  			clusterName: "my-cluster",
  3389  			extendedLocation: infrav1.ExtendedLocationSpec{
  3390  				Name: "ex-loc-name",
  3391  				Type: "ex-loc-type",
  3392  			},
  3393  		},
  3394  	}
  3395  	for _, tc := range tests {
  3396  		t.Run(tc.name, func(t *testing.T) {
  3397  			g := NewWithT(t)
  3398  			scheme := runtime.NewScheme()
  3399  			_ = infrav1.AddToScheme(scheme)
  3400  			_ = clusterv1.AddToScheme(scheme)
  3401  			_ = corev1.AddToScheme(scheme)
  3402  
  3403  			cluster := &clusterv1.Cluster{
  3404  				ObjectMeta: metav1.ObjectMeta{
  3405  					Name:      tc.clusterName,
  3406  					Namespace: "default",
  3407  				},
  3408  			}
  3409  
  3410  			azureCluster := &infrav1.AzureCluster{
  3411  				ObjectMeta: metav1.ObjectMeta{
  3412  					Name: tc.clusterName,
  3413  					OwnerReferences: []metav1.OwnerReference{
  3414  						{
  3415  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3416  							Kind:       "Cluster",
  3417  							Name:       "my-cluster",
  3418  						},
  3419  					},
  3420  				},
  3421  				Spec: infrav1.AzureClusterSpec{
  3422  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3423  						SubscriptionID: "123",
  3424  						ExtendedLocation: &infrav1.ExtendedLocationSpec{
  3425  							Name: tc.extendedLocation.Name,
  3426  							Type: tc.extendedLocation.Type,
  3427  						},
  3428  						IdentityRef: &corev1.ObjectReference{
  3429  							Kind: infrav1.AzureClusterIdentityKind,
  3430  						},
  3431  					},
  3432  				},
  3433  			}
  3434  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3435  				Spec: infrav1.AzureClusterIdentitySpec{
  3436  					Type:     infrav1.ServicePrincipal,
  3437  					ClientID: fakeClientID,
  3438  					TenantID: fakeTenantID,
  3439  				},
  3440  			}
  3441  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3442  
  3443  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3444  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3445  
  3446  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3447  				Cluster:      cluster,
  3448  				AzureCluster: azureCluster,
  3449  				Client:       fakeClient,
  3450  			})
  3451  
  3452  			g.Expect(err).NotTo(HaveOccurred())
  3453  			got := clusterScope.ExtendedLocationName()
  3454  			g.Expect(tc.extendedLocation.Name).Should(Equal(got))
  3455  		})
  3456  	}
  3457  }
  3458  
  3459  func TestExtendedLocationType(t *testing.T) {
  3460  	tests := []struct {
  3461  		name             string
  3462  		clusterName      string
  3463  		extendedLocation infrav1.ExtendedLocationSpec
  3464  	}{
  3465  		{
  3466  			name:        "Empty extendedLocatioType",
  3467  			clusterName: "my-cluster",
  3468  			extendedLocation: infrav1.ExtendedLocationSpec{
  3469  				Name: "",
  3470  				Type: "",
  3471  			},
  3472  		},
  3473  		{
  3474  			name:        "Non empty extendedLocationType",
  3475  			clusterName: "my-cluster",
  3476  			extendedLocation: infrav1.ExtendedLocationSpec{
  3477  				Name: "ex-loc-name",
  3478  				Type: "ex-loc-type",
  3479  			},
  3480  		},
  3481  	}
  3482  	for _, tc := range tests {
  3483  		t.Run(tc.name, func(t *testing.T) {
  3484  			g := NewWithT(t)
  3485  			scheme := runtime.NewScheme()
  3486  			_ = infrav1.AddToScheme(scheme)
  3487  			_ = clusterv1.AddToScheme(scheme)
  3488  			_ = corev1.AddToScheme(scheme)
  3489  
  3490  			cluster := &clusterv1.Cluster{
  3491  				ObjectMeta: metav1.ObjectMeta{
  3492  					Name:      tc.clusterName,
  3493  					Namespace: "default",
  3494  				},
  3495  			}
  3496  
  3497  			azureCluster := &infrav1.AzureCluster{
  3498  				ObjectMeta: metav1.ObjectMeta{
  3499  					Name: tc.clusterName,
  3500  					OwnerReferences: []metav1.OwnerReference{
  3501  						{
  3502  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3503  							Kind:       "Cluster",
  3504  							Name:       "my-cluster",
  3505  						},
  3506  					},
  3507  				},
  3508  				Spec: infrav1.AzureClusterSpec{
  3509  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3510  						SubscriptionID: "123",
  3511  						ExtendedLocation: &infrav1.ExtendedLocationSpec{
  3512  							Name: tc.extendedLocation.Name,
  3513  							Type: tc.extendedLocation.Type,
  3514  						},
  3515  						IdentityRef: &corev1.ObjectReference{
  3516  							Kind: infrav1.AzureClusterIdentityKind,
  3517  						},
  3518  					},
  3519  				},
  3520  			}
  3521  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3522  				Spec: infrav1.AzureClusterIdentitySpec{
  3523  					Type:     infrav1.ServicePrincipal,
  3524  					ClientID: fakeClientID,
  3525  					TenantID: fakeTenantID,
  3526  				},
  3527  			}
  3528  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3529  
  3530  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3531  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3532  
  3533  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3534  				Cluster:      cluster,
  3535  				AzureCluster: azureCluster,
  3536  				Client:       fakeClient,
  3537  			})
  3538  
  3539  			g.Expect(err).NotTo(HaveOccurred())
  3540  			got := clusterScope.ExtendedLocationType()
  3541  			g.Expect(tc.extendedLocation.Type).Should(Equal(got))
  3542  		})
  3543  	}
  3544  }
  3545  
  3546  func TestVNetPeerings(t *testing.T) {
  3547  	fakeSubscriptionID := "123"
  3548  
  3549  	tests := []struct {
  3550  		name                 string
  3551  		subscriptionID       string
  3552  		azureClusterVNetSpec infrav1.VnetSpec
  3553  		want                 []azure.ResourceSpecGetter
  3554  	}{
  3555  		{
  3556  			name:           "VNet peerings are not specified",
  3557  			subscriptionID: fakeSubscriptionID,
  3558  			azureClusterVNetSpec: infrav1.VnetSpec{
  3559  				ResourceGroup: "rg1",
  3560  				Name:          "vnet1",
  3561  			},
  3562  			want: []azure.ResourceSpecGetter{},
  3563  		},
  3564  		{
  3565  			name:           "One VNet peering is specified",
  3566  			subscriptionID: fakeSubscriptionID,
  3567  			azureClusterVNetSpec: infrav1.VnetSpec{
  3568  				ResourceGroup: "rg1",
  3569  				Name:          "vnet1",
  3570  				Peerings: infrav1.VnetPeerings{
  3571  					{
  3572  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3573  							ResourceGroup:  "rg2",
  3574  							RemoteVnetName: "vnet2",
  3575  						},
  3576  					},
  3577  				},
  3578  			},
  3579  			want: []azure.ResourceSpecGetter{
  3580  				&vnetpeerings.VnetPeeringSpec{
  3581  					PeeringName:         "vnet1-To-vnet2",
  3582  					SourceResourceGroup: "rg1",
  3583  					SourceVnetName:      "vnet1",
  3584  					RemoteResourceGroup: "rg2",
  3585  					RemoteVnetName:      "vnet2",
  3586  					SubscriptionID:      fakeSubscriptionID,
  3587  				},
  3588  				&vnetpeerings.VnetPeeringSpec{
  3589  					PeeringName:         "vnet2-To-vnet1",
  3590  					SourceResourceGroup: "rg2",
  3591  					SourceVnetName:      "vnet2",
  3592  					RemoteResourceGroup: "rg1",
  3593  					RemoteVnetName:      "vnet1",
  3594  					SubscriptionID:      fakeSubscriptionID,
  3595  				},
  3596  			},
  3597  		},
  3598  		{
  3599  			name:           "One VNet peering with optional properties is specified",
  3600  			subscriptionID: fakeSubscriptionID,
  3601  			azureClusterVNetSpec: infrav1.VnetSpec{
  3602  				ResourceGroup: "rg1",
  3603  				Name:          "vnet1",
  3604  				Peerings: infrav1.VnetPeerings{
  3605  					{
  3606  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3607  							ResourceGroup:  "rg2",
  3608  							RemoteVnetName: "vnet2",
  3609  							ForwardPeeringProperties: infrav1.VnetPeeringProperties{
  3610  								AllowForwardedTraffic: ptr.To(true),
  3611  								AllowGatewayTransit:   ptr.To(false),
  3612  								UseRemoteGateways:     ptr.To(true),
  3613  							},
  3614  							ReversePeeringProperties: infrav1.VnetPeeringProperties{
  3615  								AllowForwardedTraffic: ptr.To(true),
  3616  								AllowGatewayTransit:   ptr.To(true),
  3617  								UseRemoteGateways:     ptr.To(false),
  3618  							},
  3619  						},
  3620  					},
  3621  				},
  3622  			},
  3623  			want: []azure.ResourceSpecGetter{
  3624  				&vnetpeerings.VnetPeeringSpec{
  3625  					PeeringName:           "vnet1-To-vnet2",
  3626  					SourceResourceGroup:   "rg1",
  3627  					SourceVnetName:        "vnet1",
  3628  					RemoteResourceGroup:   "rg2",
  3629  					RemoteVnetName:        "vnet2",
  3630  					SubscriptionID:        fakeSubscriptionID,
  3631  					AllowForwardedTraffic: ptr.To(true),
  3632  					AllowGatewayTransit:   ptr.To(false),
  3633  					UseRemoteGateways:     ptr.To(true),
  3634  				},
  3635  				&vnetpeerings.VnetPeeringSpec{
  3636  					PeeringName:           "vnet2-To-vnet1",
  3637  					SourceResourceGroup:   "rg2",
  3638  					SourceVnetName:        "vnet2",
  3639  					RemoteResourceGroup:   "rg1",
  3640  					RemoteVnetName:        "vnet1",
  3641  					SubscriptionID:        fakeSubscriptionID,
  3642  					AllowForwardedTraffic: ptr.To(true),
  3643  					AllowGatewayTransit:   ptr.To(true),
  3644  					UseRemoteGateways:     ptr.To(false),
  3645  				},
  3646  			},
  3647  		},
  3648  		{
  3649  			name:           "Two VNet peerings are specified",
  3650  			subscriptionID: fakeSubscriptionID,
  3651  			azureClusterVNetSpec: infrav1.VnetSpec{
  3652  				ResourceGroup: "rg1",
  3653  				Name:          "vnet1",
  3654  				Peerings: infrav1.VnetPeerings{
  3655  					{
  3656  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3657  							ResourceGroup:  "rg2",
  3658  							RemoteVnetName: "vnet2",
  3659  							ForwardPeeringProperties: infrav1.VnetPeeringProperties{
  3660  								AllowForwardedTraffic: ptr.To(true),
  3661  								AllowGatewayTransit:   ptr.To(false),
  3662  								UseRemoteGateways:     ptr.To(true),
  3663  							},
  3664  							ReversePeeringProperties: infrav1.VnetPeeringProperties{
  3665  								AllowForwardedTraffic: ptr.To(true),
  3666  								AllowGatewayTransit:   ptr.To(true),
  3667  								UseRemoteGateways:     ptr.To(false),
  3668  							},
  3669  						},
  3670  					},
  3671  					{
  3672  						VnetPeeringClassSpec: infrav1.VnetPeeringClassSpec{
  3673  							ResourceGroup:  "rg3",
  3674  							RemoteVnetName: "vnet3",
  3675  						},
  3676  					},
  3677  				},
  3678  			},
  3679  			want: []azure.ResourceSpecGetter{
  3680  				&vnetpeerings.VnetPeeringSpec{
  3681  					PeeringName:           "vnet1-To-vnet2",
  3682  					SourceResourceGroup:   "rg1",
  3683  					SourceVnetName:        "vnet1",
  3684  					RemoteResourceGroup:   "rg2",
  3685  					RemoteVnetName:        "vnet2",
  3686  					SubscriptionID:        fakeSubscriptionID,
  3687  					AllowForwardedTraffic: ptr.To(true),
  3688  					AllowGatewayTransit:   ptr.To(false),
  3689  					UseRemoteGateways:     ptr.To(true),
  3690  				},
  3691  				&vnetpeerings.VnetPeeringSpec{
  3692  					PeeringName:           "vnet2-To-vnet1",
  3693  					SourceResourceGroup:   "rg2",
  3694  					SourceVnetName:        "vnet2",
  3695  					RemoteResourceGroup:   "rg1",
  3696  					RemoteVnetName:        "vnet1",
  3697  					SubscriptionID:        fakeSubscriptionID,
  3698  					AllowForwardedTraffic: ptr.To(true),
  3699  					AllowGatewayTransit:   ptr.To(true),
  3700  					UseRemoteGateways:     ptr.To(false),
  3701  				},
  3702  				&vnetpeerings.VnetPeeringSpec{
  3703  					PeeringName:         "vnet1-To-vnet3",
  3704  					SourceResourceGroup: "rg1",
  3705  					SourceVnetName:      "vnet1",
  3706  					RemoteResourceGroup: "rg3",
  3707  					RemoteVnetName:      "vnet3",
  3708  					SubscriptionID:      fakeSubscriptionID,
  3709  				},
  3710  				&vnetpeerings.VnetPeeringSpec{
  3711  					PeeringName:         "vnet3-To-vnet1",
  3712  					SourceResourceGroup: "rg3",
  3713  					SourceVnetName:      "vnet3",
  3714  					RemoteResourceGroup: "rg1",
  3715  					RemoteVnetName:      "vnet1",
  3716  					SubscriptionID:      fakeSubscriptionID,
  3717  				},
  3718  			},
  3719  		},
  3720  	}
  3721  
  3722  	for _, tc := range tests {
  3723  		t.Run(tc.name, func(t *testing.T) {
  3724  			g := NewWithT(t)
  3725  			scheme := runtime.NewScheme()
  3726  			_ = infrav1.AddToScheme(scheme)
  3727  			_ = clusterv1.AddToScheme(scheme)
  3728  			_ = corev1.AddToScheme(scheme)
  3729  
  3730  			clusterName := "my-cluster"
  3731  			clusterNamespace := "default"
  3732  
  3733  			cluster := &clusterv1.Cluster{
  3734  				ObjectMeta: metav1.ObjectMeta{
  3735  					Name:      clusterName,
  3736  					Namespace: clusterNamespace,
  3737  				},
  3738  			}
  3739  			azureCluster := &infrav1.AzureCluster{
  3740  				ObjectMeta: metav1.ObjectMeta{
  3741  					Name:      clusterName,
  3742  					Namespace: clusterNamespace,
  3743  					OwnerReferences: []metav1.OwnerReference{
  3744  						{
  3745  							APIVersion: "cluster.x-k8s.io/v1beta1",
  3746  							Kind:       "Cluster",
  3747  							Name:       clusterName,
  3748  						},
  3749  					},
  3750  				},
  3751  				Spec: infrav1.AzureClusterSpec{
  3752  					ResourceGroup: "rg1",
  3753  					AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  3754  						SubscriptionID: tc.subscriptionID,
  3755  						IdentityRef: &corev1.ObjectReference{
  3756  							Kind:      infrav1.AzureClusterIdentityKind,
  3757  							Namespace: clusterNamespace,
  3758  						},
  3759  					},
  3760  					NetworkSpec: infrav1.NetworkSpec{
  3761  						Vnet: tc.azureClusterVNetSpec,
  3762  					},
  3763  				},
  3764  			}
  3765  			fakeIdentity := &infrav1.AzureClusterIdentity{
  3766  				ObjectMeta: metav1.ObjectMeta{
  3767  					Namespace: clusterNamespace,
  3768  				},
  3769  				Spec: infrav1.AzureClusterIdentitySpec{
  3770  					Type:     infrav1.ServicePrincipal,
  3771  					ClientID: fakeClientID,
  3772  					TenantID: fakeTenantID,
  3773  				},
  3774  			}
  3775  			fakeSecret := &corev1.Secret{Data: map[string][]byte{"clientSecret": []byte("fooSecret")}}
  3776  
  3777  			initObjects := []runtime.Object{cluster, azureCluster, fakeIdentity, fakeSecret}
  3778  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initObjects...).Build()
  3779  
  3780  			clusterScope, err := NewClusterScope(context.TODO(), ClusterScopeParams{
  3781  				Cluster:      cluster,
  3782  				AzureCluster: azureCluster,
  3783  				Client:       fakeClient,
  3784  			})
  3785  			g.Expect(err).NotTo(HaveOccurred())
  3786  			got := clusterScope.VnetPeeringSpecs()
  3787  			g.Expect(tc.want).To(Equal(got))
  3788  		})
  3789  	}
  3790  }
  3791  
  3792  func TestPrivateEndpointSpecs(t *testing.T) {
  3793  	tests := []struct {
  3794  		name         string
  3795  		clusterScope ClusterScope
  3796  		want         []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint]
  3797  	}{
  3798  		{
  3799  			name: "returns empty private endpoints list if no subnets are specified",
  3800  			clusterScope: ClusterScope{
  3801  				AzureCluster: &infrav1.AzureCluster{
  3802  					Spec: infrav1.AzureClusterSpec{
  3803  						NetworkSpec: infrav1.NetworkSpec{
  3804  							Subnets: infrav1.Subnets{},
  3805  						},
  3806  					},
  3807  				},
  3808  				cache: &ClusterCache{},
  3809  			},
  3810  			want: make([]azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint], 0),
  3811  		},
  3812  		{
  3813  			name: "returns empty private endpoints list if no private endpoints are specified",
  3814  			clusterScope: ClusterScope{
  3815  				AzureCluster: &infrav1.AzureCluster{
  3816  					Spec: infrav1.AzureClusterSpec{
  3817  						NetworkSpec: infrav1.NetworkSpec{
  3818  							Subnets: []infrav1.SubnetSpec{
  3819  								{
  3820  									SubnetClassSpec: infrav1.SubnetClassSpec{
  3821  										PrivateEndpoints: infrav1.PrivateEndpoints{},
  3822  									},
  3823  								},
  3824  							},
  3825  						},
  3826  					},
  3827  				},
  3828  				cache: &ClusterCache{},
  3829  			},
  3830  			want: make([]azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint], 0),
  3831  		},
  3832  		{
  3833  			name: "returns list of private endpoint specs if private endpoints are specified",
  3834  			clusterScope: ClusterScope{
  3835  				Cluster: &clusterv1.Cluster{
  3836  					ObjectMeta: metav1.ObjectMeta{
  3837  						Name:      "my-cluster",
  3838  						Namespace: "dummy-ns",
  3839  					},
  3840  				},
  3841  				AzureCluster: &infrav1.AzureCluster{
  3842  					Spec: infrav1.AzureClusterSpec{
  3843  						ResourceGroup: "dummy-rg",
  3844  						NetworkSpec: infrav1.NetworkSpec{
  3845  							Subnets: []infrav1.SubnetSpec{
  3846  								{
  3847  									ID: "dummy-subnet-id",
  3848  									SubnetClassSpec: infrav1.SubnetClassSpec{
  3849  										PrivateEndpoints: []infrav1.PrivateEndpointSpec{
  3850  											{
  3851  												Name:                       "my-private-endpoint",
  3852  												Location:                   "westus2",
  3853  												CustomNetworkInterfaceName: "my-custom-nic",
  3854  												PrivateIPAddresses: []string{
  3855  													"IP1",
  3856  													"IP2",
  3857  												},
  3858  												ApplicationSecurityGroups: []string{
  3859  													"ASG1",
  3860  													"ASG2",
  3861  												},
  3862  												PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{
  3863  													{
  3864  														Name:                 "my-pls-connection",
  3865  														RequestMessage:       "my-request-message",
  3866  														PrivateLinkServiceID: "my-pls-id",
  3867  														GroupIDs: []string{
  3868  															"my-group-id-1",
  3869  														},
  3870  													},
  3871  												},
  3872  											},
  3873  											{
  3874  												Name:                       "my-private-endpoint-2",
  3875  												Location:                   "westus2",
  3876  												CustomNetworkInterfaceName: "my-custom-nic-2",
  3877  												PrivateIPAddresses: []string{
  3878  													"IP3",
  3879  													"IP4",
  3880  												},
  3881  												ApplicationSecurityGroups: []string{
  3882  													"ASG3",
  3883  													"ASG4",
  3884  												},
  3885  												PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{
  3886  													{
  3887  														Name:                 "my-pls-connection",
  3888  														RequestMessage:       "my-request-message",
  3889  														PrivateLinkServiceID: "my-pls-id",
  3890  														GroupIDs: []string{
  3891  															"my-group-id-1",
  3892  														},
  3893  													},
  3894  												},
  3895  											},
  3896  										},
  3897  									},
  3898  								},
  3899  								{
  3900  									ID: "dummy-subnet-id-2",
  3901  									SubnetClassSpec: infrav1.SubnetClassSpec{
  3902  										PrivateEndpoints: []infrav1.PrivateEndpointSpec{
  3903  											{
  3904  												Name:                       "my-private-endpoint-3",
  3905  												Location:                   "westus2",
  3906  												CustomNetworkInterfaceName: "my-custom-nic-3",
  3907  												PrivateIPAddresses: []string{
  3908  													"IP5",
  3909  													"IP6",
  3910  												},
  3911  												ApplicationSecurityGroups: []string{
  3912  													"ASG5",
  3913  													"ASG6",
  3914  												},
  3915  												PrivateLinkServiceConnections: []infrav1.PrivateLinkServiceConnection{
  3916  													{
  3917  														Name:                 "my-pls-connection",
  3918  														RequestMessage:       "my-request-message",
  3919  														PrivateLinkServiceID: "my-pls-id",
  3920  														GroupIDs: []string{
  3921  															"my-group-id-1",
  3922  														},
  3923  													},
  3924  												},
  3925  											},
  3926  										},
  3927  									},
  3928  								},
  3929  							},
  3930  						},
  3931  					},
  3932  				},
  3933  				cache: &ClusterCache{},
  3934  			},
  3935  			want: []azure.ASOResourceSpecGetter[*asonetworkv1api20220701.PrivateEndpoint]{
  3936  				&privateendpoints.PrivateEndpointSpec{
  3937  					Name:                       "my-private-endpoint",
  3938  					ResourceGroup:              "dummy-rg",
  3939  					Location:                   "westus2",
  3940  					CustomNetworkInterfaceName: "my-custom-nic",
  3941  					PrivateIPAddresses: []string{
  3942  						"IP1",
  3943  						"IP2",
  3944  					},
  3945  					SubnetID: "dummy-subnet-id",
  3946  					ApplicationSecurityGroups: []string{
  3947  						"ASG1",
  3948  						"ASG2",
  3949  					},
  3950  					ClusterName: "my-cluster",
  3951  					PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{
  3952  						{
  3953  							Name:                 "my-pls-connection",
  3954  							RequestMessage:       "my-request-message",
  3955  							PrivateLinkServiceID: "my-pls-id",
  3956  							GroupIDs: []string{
  3957  								"my-group-id-1",
  3958  							},
  3959  						},
  3960  					},
  3961  					AdditionalTags: make(infrav1.Tags, 0),
  3962  				},
  3963  				&privateendpoints.PrivateEndpointSpec{
  3964  					Name:                       "my-private-endpoint-2",
  3965  					ResourceGroup:              "dummy-rg",
  3966  					Location:                   "westus2",
  3967  					CustomNetworkInterfaceName: "my-custom-nic-2",
  3968  					PrivateIPAddresses: []string{
  3969  						"IP3",
  3970  						"IP4",
  3971  					},
  3972  					SubnetID: "dummy-subnet-id",
  3973  					ApplicationSecurityGroups: []string{
  3974  						"ASG3",
  3975  						"ASG4",
  3976  					},
  3977  					ClusterName: "my-cluster",
  3978  					PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{
  3979  						{
  3980  							Name:                 "my-pls-connection",
  3981  							RequestMessage:       "my-request-message",
  3982  							PrivateLinkServiceID: "my-pls-id",
  3983  							GroupIDs: []string{
  3984  								"my-group-id-1",
  3985  							},
  3986  						},
  3987  					},
  3988  					AdditionalTags: make(infrav1.Tags, 0),
  3989  				},
  3990  				&privateendpoints.PrivateEndpointSpec{
  3991  					Name:                       "my-private-endpoint-3",
  3992  					ResourceGroup:              "dummy-rg",
  3993  					Location:                   "westus2",
  3994  					CustomNetworkInterfaceName: "my-custom-nic-3",
  3995  					PrivateIPAddresses: []string{
  3996  						"IP5",
  3997  						"IP6",
  3998  					},
  3999  					SubnetID: "dummy-subnet-id-2",
  4000  					ApplicationSecurityGroups: []string{
  4001  						"ASG5",
  4002  						"ASG6",
  4003  					},
  4004  					ClusterName: "my-cluster",
  4005  					PrivateLinkServiceConnections: []privateendpoints.PrivateLinkServiceConnection{
  4006  						{
  4007  							Name:                 "my-pls-connection",
  4008  							RequestMessage:       "my-request-message",
  4009  							PrivateLinkServiceID: "my-pls-id",
  4010  							GroupIDs: []string{
  4011  								"my-group-id-1",
  4012  							},
  4013  						},
  4014  					},
  4015  					AdditionalTags: make(infrav1.Tags, 0),
  4016  				},
  4017  			},
  4018  		},
  4019  	}
  4020  
  4021  	for _, tt := range tests {
  4022  		tt := tt
  4023  		t.Run(tt.name, func(t *testing.T) {
  4024  			t.Parallel()
  4025  			if got := tt.clusterScope.PrivateEndpointSpecs(); !reflect.DeepEqual(got, tt.want) {
  4026  				t.Errorf("PrivateEndpointSpecs() = %s, want %s", specArrayToString(got), specArrayToString(tt.want))
  4027  			}
  4028  		})
  4029  	}
  4030  }
  4031  
  4032  func TestSetFailureDomain(t *testing.T) {
  4033  	t.Parallel()
  4034  
  4035  	cases := map[string]struct {
  4036  		discoveredFDs clusterv1.FailureDomains
  4037  		specifiedFDs  clusterv1.FailureDomains
  4038  		expectedFDs   clusterv1.FailureDomains
  4039  	}{
  4040  		"no failure domains specified": {
  4041  			discoveredFDs: clusterv1.FailureDomains{
  4042  				"fd1": clusterv1.FailureDomainSpec{ControlPlane: true},
  4043  				"fd2": clusterv1.FailureDomainSpec{ControlPlane: false},
  4044  			},
  4045  			expectedFDs: clusterv1.FailureDomains{
  4046  				"fd1": clusterv1.FailureDomainSpec{ControlPlane: true},
  4047  				"fd2": clusterv1.FailureDomainSpec{ControlPlane: false},
  4048  			},
  4049  		},
  4050  		"no failure domains discovered": {
  4051  			specifiedFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  4052  		},
  4053  		"failure domain specified without intersection": {
  4054  			discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  4055  			specifiedFDs:  clusterv1.FailureDomains{"fd2": clusterv1.FailureDomainSpec{ControlPlane: false}},
  4056  			expectedFDs:   clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  4057  		},
  4058  		"failure domain override to false succeeds": {
  4059  			discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  4060  			specifiedFDs:  clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  4061  			expectedFDs:   clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  4062  		},
  4063  		"failure domain override to true fails": {
  4064  			discoveredFDs: clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  4065  			specifiedFDs:  clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: true}},
  4066  			expectedFDs:   clusterv1.FailureDomains{"fd1": clusterv1.FailureDomainSpec{ControlPlane: false}},
  4067  		},
  4068  	}
  4069  
  4070  	for name, tc := range cases {
  4071  		tc := tc
  4072  		t.Run(name, func(t *testing.T) {
  4073  			t.Parallel()
  4074  			g := NewWithT(t)
  4075  
  4076  			c := ClusterScope{
  4077  				AzureCluster: &infrav1.AzureCluster{
  4078  					Spec: infrav1.AzureClusterSpec{
  4079  						AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
  4080  							FailureDomains: tc.specifiedFDs,
  4081  							IdentityRef: &corev1.ObjectReference{
  4082  								Kind: infrav1.AzureClusterIdentityKind,
  4083  							},
  4084  						},
  4085  					},
  4086  				},
  4087  			}
  4088  
  4089  			for fdName, fd := range tc.discoveredFDs {
  4090  				c.SetFailureDomain(fdName, fd)
  4091  			}
  4092  
  4093  			for fdName, fd := range tc.expectedFDs {
  4094  				g.Expect(fdName).Should(BeKeyOf(c.AzureCluster.Status.FailureDomains))
  4095  				g.Expect(c.AzureCluster.Status.FailureDomains[fdName].ControlPlane).To(Equal(fd.ControlPlane))
  4096  
  4097  				delete(c.AzureCluster.Status.FailureDomains, fdName)
  4098  			}
  4099  
  4100  			g.Expect(c.AzureCluster.Status.FailureDomains).To(BeEmpty())
  4101  		})
  4102  	}
  4103  }
  4104  
  4105  func TestGroupSpecs(t *testing.T) {
  4106  	cases := []struct {
  4107  		name     string
  4108  		input    ClusterScope
  4109  		expected []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]
  4110  	}{
  4111  		{
  4112  			name: "virtualNetwork belongs to a different resource group",
  4113  			input: ClusterScope{
  4114  				Cluster: &clusterv1.Cluster{
  4115  					ObjectMeta: metav1.ObjectMeta{
  4116  						Name: "cluster1",
  4117  					},
  4118  				},
  4119  				AzureCluster: &infrav1.AzureCluster{
  4120  					Spec: infrav1.AzureClusterSpec{
  4121  						ResourceGroup: "dummy-rg",
  4122  						NetworkSpec: infrav1.NetworkSpec{
  4123  							Vnet: infrav1.VnetSpec{
  4124  								ResourceGroup: "different-rg",
  4125  							},
  4126  						},
  4127  					},
  4128  				},
  4129  			},
  4130  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4131  				&groups.GroupSpec{
  4132  					Name:           "dummy-rg",
  4133  					AzureName:      "dummy-rg",
  4134  					ClusterName:    "cluster1",
  4135  					Location:       "",
  4136  					AdditionalTags: make(infrav1.Tags, 0),
  4137  				},
  4138  				&groups.GroupSpec{
  4139  					Name:           "different-rg",
  4140  					AzureName:      "different-rg",
  4141  					ClusterName:    "cluster1",
  4142  					Location:       "",
  4143  					AdditionalTags: make(infrav1.Tags, 0),
  4144  				},
  4145  			},
  4146  		},
  4147  		{
  4148  			name: "virtualNetwork belongs to a same resource group",
  4149  			input: ClusterScope{
  4150  				Cluster: &clusterv1.Cluster{
  4151  					ObjectMeta: metav1.ObjectMeta{
  4152  						Name: "cluster1",
  4153  					},
  4154  				},
  4155  				AzureCluster: &infrav1.AzureCluster{
  4156  					Spec: infrav1.AzureClusterSpec{
  4157  						ResourceGroup: "dummy-rg",
  4158  						NetworkSpec: infrav1.NetworkSpec{
  4159  							Vnet: infrav1.VnetSpec{
  4160  								ResourceGroup: "dummy-rg",
  4161  							},
  4162  						},
  4163  					},
  4164  				},
  4165  			},
  4166  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4167  				&groups.GroupSpec{
  4168  					Name:           "dummy-rg",
  4169  					AzureName:      "dummy-rg",
  4170  					ClusterName:    "cluster1",
  4171  					Location:       "",
  4172  					AdditionalTags: make(infrav1.Tags, 0),
  4173  				},
  4174  			},
  4175  		},
  4176  		{
  4177  			name: "virtualNetwork resource group not specified",
  4178  			input: ClusterScope{
  4179  				Cluster: &clusterv1.Cluster{
  4180  					ObjectMeta: metav1.ObjectMeta{
  4181  						Name:      "cluster1",
  4182  						Namespace: "default",
  4183  					},
  4184  				},
  4185  				AzureCluster: &infrav1.AzureCluster{
  4186  					Spec: infrav1.AzureClusterSpec{
  4187  						ResourceGroup: "dummy-rg",
  4188  						NetworkSpec: infrav1.NetworkSpec{
  4189  							Vnet: infrav1.VnetSpec{
  4190  								Name: "vnet1",
  4191  							},
  4192  						},
  4193  					},
  4194  				},
  4195  			},
  4196  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4197  				&groups.GroupSpec{
  4198  					Name:           "dummy-rg",
  4199  					AzureName:      "dummy-rg",
  4200  					ClusterName:    "cluster1",
  4201  					Location:       "",
  4202  					AdditionalTags: make(infrav1.Tags, 0),
  4203  				},
  4204  			},
  4205  		},
  4206  		{
  4207  			name: "virtualNetwork belongs to different resource group with non-k8s name",
  4208  			input: ClusterScope{
  4209  				Cluster: &clusterv1.Cluster{
  4210  					ObjectMeta: metav1.ObjectMeta{
  4211  						Name:      "cluster1",
  4212  						Namespace: "default",
  4213  					},
  4214  				},
  4215  				AzureCluster: &infrav1.AzureCluster{
  4216  					Spec: infrav1.AzureClusterSpec{
  4217  						ResourceGroup: "dummy-rg",
  4218  						NetworkSpec: infrav1.NetworkSpec{
  4219  							Vnet: infrav1.VnetSpec{
  4220  								ResourceGroup: "my_custom_rg",
  4221  								Name:          "vnet1",
  4222  							},
  4223  						},
  4224  					},
  4225  				},
  4226  			},
  4227  			expected: []azure.ASOResourceSpecGetter[*asoresourcesv1.ResourceGroup]{
  4228  				&groups.GroupSpec{
  4229  					Name:           "dummy-rg",
  4230  					AzureName:      "dummy-rg",
  4231  					ClusterName:    "cluster1",
  4232  					Location:       "",
  4233  					AdditionalTags: make(infrav1.Tags, 0),
  4234  				},
  4235  				&groups.GroupSpec{
  4236  					Name:           "my-custom-rg",
  4237  					AzureName:      "my_custom_rg",
  4238  					ClusterName:    "cluster1",
  4239  					Location:       "",
  4240  					AdditionalTags: make(infrav1.Tags, 0),
  4241  				},
  4242  			},
  4243  		},
  4244  	}
  4245  
  4246  	for _, c := range cases {
  4247  		c := c
  4248  		t.Run(c.name, func(t *testing.T) {
  4249  			s := &ClusterScope{
  4250  				AzureCluster: c.input.AzureCluster,
  4251  				Cluster:      c.input.Cluster,
  4252  			}
  4253  			if got := s.GroupSpecs(); !reflect.DeepEqual(got, c.expected) {
  4254  				t.Errorf("GroupSpecs() = %s, want %s", specArrayToString(got), specArrayToString(c.expected))
  4255  			}
  4256  		})
  4257  	}
  4258  }