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

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1beta1
    18  
    19  import (
    20  	"encoding/json"
    21  	"reflect"
    22  	"testing"
    23  
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/utils/ptr"
    27  )
    28  
    29  func TestResourceGroupDefault(t *testing.T) {
    30  	cases := map[string]struct {
    31  		cluster *AzureCluster
    32  		output  *AzureCluster
    33  	}{
    34  		"default empty rg": {
    35  			cluster: &AzureCluster{
    36  				ObjectMeta: metav1.ObjectMeta{
    37  					Name: "foo",
    38  				},
    39  				Spec: AzureClusterSpec{},
    40  			},
    41  			output: &AzureCluster{
    42  				ObjectMeta: metav1.ObjectMeta{
    43  					Name: "foo",
    44  				},
    45  				Spec: AzureClusterSpec{
    46  					ResourceGroup: "foo",
    47  				},
    48  			},
    49  		},
    50  		"don't change if mismatched": {
    51  			cluster: &AzureCluster{
    52  				ObjectMeta: metav1.ObjectMeta{
    53  					Name: "foo",
    54  				},
    55  				Spec: AzureClusterSpec{
    56  					ResourceGroup: "bar",
    57  				},
    58  			},
    59  			output: &AzureCluster{
    60  				ObjectMeta: metav1.ObjectMeta{
    61  					Name: "foo",
    62  				},
    63  				Spec: AzureClusterSpec{
    64  					ResourceGroup: "bar",
    65  				},
    66  			},
    67  		},
    68  	}
    69  
    70  	for name := range cases {
    71  		c := cases[name]
    72  		t.Run(name, func(t *testing.T) {
    73  			t.Parallel()
    74  			c.cluster.setResourceGroupDefault()
    75  			if !reflect.DeepEqual(c.cluster, c.output) {
    76  				expected, _ := json.MarshalIndent(c.output, "", "\t")
    77  				actual, _ := json.MarshalIndent(c.cluster, "", "\t")
    78  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
    79  			}
    80  		})
    81  	}
    82  }
    83  
    84  func TestVnetDefaults(t *testing.T) {
    85  	cases := []struct {
    86  		name    string
    87  		cluster *AzureCluster
    88  		output  *AzureCluster
    89  	}{
    90  		{
    91  			name:    "resource group vnet specified",
    92  			cluster: createValidCluster(),
    93  			output: &AzureCluster{
    94  				ObjectMeta: metav1.ObjectMeta{
    95  					Name: "test-cluster",
    96  				},
    97  				Spec: AzureClusterSpec{
    98  					NetworkSpec: NetworkSpec{
    99  						Vnet: VnetSpec{
   100  							ResourceGroup: "custom-vnet",
   101  							Name:          "my-vnet",
   102  							VnetClassSpec: VnetClassSpec{
   103  								CIDRBlocks: []string{DefaultVnetCIDR},
   104  							},
   105  						},
   106  						Subnets: Subnets{
   107  							{
   108  								SubnetClassSpec: SubnetClassSpec{
   109  									Role: SubnetControlPlane,
   110  									Name: "control-plane-subnet",
   111  								},
   112  
   113  								SecurityGroup: SecurityGroup{},
   114  								RouteTable:    RouteTable{},
   115  							},
   116  							{
   117  								SubnetClassSpec: SubnetClassSpec{
   118  									Role: SubnetNode,
   119  									Name: "node-subnet",
   120  								},
   121  								SecurityGroup: SecurityGroup{},
   122  								RouteTable:    RouteTable{},
   123  							},
   124  						},
   125  						APIServerLB: LoadBalancerSpec{
   126  							Name: "my-lb",
   127  							FrontendIPs: []FrontendIP{
   128  								{
   129  									Name: "ip-config",
   130  									PublicIP: &PublicIPSpec{
   131  										Name:    "public-ip",
   132  										DNSName: "myfqdn.azure.com",
   133  									},
   134  								},
   135  							},
   136  							LoadBalancerClassSpec: LoadBalancerClassSpec{
   137  								SKU: SKUStandard,
   138  
   139  								Type: Public,
   140  							},
   141  						},
   142  						NodeOutboundLB: &LoadBalancerSpec{
   143  							FrontendIPsCount: ptr.To[int32](1),
   144  						},
   145  					},
   146  					AzureClusterClassSpec: AzureClusterClassSpec{
   147  						IdentityRef: &corev1.ObjectReference{
   148  							Kind: AzureClusterIdentityKind,
   149  						},
   150  					},
   151  				},
   152  			},
   153  		},
   154  		{
   155  			name: "vnet not specified",
   156  			cluster: &AzureCluster{
   157  				ObjectMeta: metav1.ObjectMeta{
   158  					Name: "cluster-test",
   159  				},
   160  				Spec: AzureClusterSpec{
   161  					ResourceGroup: "cluster-test",
   162  					AzureClusterClassSpec: AzureClusterClassSpec{
   163  						IdentityRef: &corev1.ObjectReference{
   164  							Kind: AzureClusterIdentityKind,
   165  						},
   166  					},
   167  				},
   168  			},
   169  			output: &AzureCluster{
   170  				ObjectMeta: metav1.ObjectMeta{
   171  					Name: "cluster-test",
   172  				},
   173  				Spec: AzureClusterSpec{
   174  					ResourceGroup: "cluster-test",
   175  					NetworkSpec: NetworkSpec{
   176  						Vnet: VnetSpec{
   177  							ResourceGroup: "cluster-test",
   178  							Name:          "cluster-test-vnet",
   179  							VnetClassSpec: VnetClassSpec{
   180  								CIDRBlocks: []string{DefaultVnetCIDR},
   181  							},
   182  						},
   183  					},
   184  					AzureClusterClassSpec: AzureClusterClassSpec{
   185  						IdentityRef: &corev1.ObjectReference{
   186  							Kind: AzureClusterIdentityKind,
   187  						},
   188  					},
   189  				},
   190  			},
   191  		},
   192  		{
   193  			name: "custom CIDR",
   194  			cluster: &AzureCluster{
   195  				ObjectMeta: metav1.ObjectMeta{
   196  					Name: "cluster-test",
   197  				},
   198  				Spec: AzureClusterSpec{
   199  					ResourceGroup: "cluster-test",
   200  					NetworkSpec: NetworkSpec{
   201  						Vnet: VnetSpec{
   202  							VnetClassSpec: VnetClassSpec{
   203  								CIDRBlocks: []string{"10.0.0.0/16"},
   204  							},
   205  						},
   206  					},
   207  					AzureClusterClassSpec: AzureClusterClassSpec{
   208  						IdentityRef: &corev1.ObjectReference{
   209  							Kind: AzureClusterIdentityKind,
   210  						},
   211  					},
   212  				},
   213  			},
   214  			output: &AzureCluster{
   215  				ObjectMeta: metav1.ObjectMeta{
   216  					Name: "cluster-test",
   217  				},
   218  				Spec: AzureClusterSpec{
   219  					ResourceGroup: "cluster-test",
   220  					NetworkSpec: NetworkSpec{
   221  						Vnet: VnetSpec{
   222  							ResourceGroup: "cluster-test",
   223  							Name:          "cluster-test-vnet",
   224  							VnetClassSpec: VnetClassSpec{
   225  								CIDRBlocks: []string{"10.0.0.0/16"},
   226  							},
   227  						},
   228  					},
   229  					AzureClusterClassSpec: AzureClusterClassSpec{
   230  						IdentityRef: &corev1.ObjectReference{
   231  							Kind: AzureClusterIdentityKind,
   232  						},
   233  					},
   234  				},
   235  			},
   236  		},
   237  		{
   238  			name: "IPv6 enabled",
   239  			cluster: &AzureCluster{
   240  				ObjectMeta: metav1.ObjectMeta{
   241  					Name: "cluster-test",
   242  				},
   243  				Spec: AzureClusterSpec{
   244  					ResourceGroup: "cluster-test",
   245  					NetworkSpec: NetworkSpec{
   246  						Vnet: VnetSpec{
   247  							VnetClassSpec: VnetClassSpec{
   248  								CIDRBlocks: []string{DefaultVnetCIDR, "2001:1234:5678:9a00::/56"},
   249  							},
   250  						},
   251  					},
   252  					AzureClusterClassSpec: AzureClusterClassSpec{
   253  						IdentityRef: &corev1.ObjectReference{
   254  							Kind: AzureClusterIdentityKind,
   255  						},
   256  					},
   257  				},
   258  			},
   259  			output: &AzureCluster{
   260  				ObjectMeta: metav1.ObjectMeta{
   261  					Name: "cluster-test",
   262  				},
   263  				Spec: AzureClusterSpec{
   264  					ResourceGroup: "cluster-test",
   265  					NetworkSpec: NetworkSpec{
   266  						Vnet: VnetSpec{
   267  							ResourceGroup: "cluster-test",
   268  							Name:          "cluster-test-vnet",
   269  							VnetClassSpec: VnetClassSpec{
   270  								CIDRBlocks: []string{DefaultVnetCIDR, "2001:1234:5678:9a00::/56"},
   271  							},
   272  						},
   273  					},
   274  					AzureClusterClassSpec: AzureClusterClassSpec{
   275  						IdentityRef: &corev1.ObjectReference{
   276  							Kind: AzureClusterIdentityKind,
   277  						},
   278  					},
   279  				},
   280  			},
   281  		},
   282  	}
   283  
   284  	for _, c := range cases {
   285  		tc := c
   286  		t.Run(tc.name, func(t *testing.T) {
   287  			t.Parallel()
   288  			tc.cluster.setVnetDefaults()
   289  			if !reflect.DeepEqual(tc.cluster, tc.output) {
   290  				expected, _ := json.MarshalIndent(tc.output, "", "\t")
   291  				actual, _ := json.MarshalIndent(tc.cluster, "", "\t")
   292  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
   293  			}
   294  		})
   295  	}
   296  }
   297  
   298  func TestSubnetDefaults(t *testing.T) {
   299  	cases := []struct {
   300  		name    string
   301  		cluster *AzureCluster
   302  		output  *AzureCluster
   303  	}{
   304  		{
   305  			name: "no subnets",
   306  			cluster: &AzureCluster{
   307  				ObjectMeta: metav1.ObjectMeta{
   308  					Name: "cluster-test",
   309  				},
   310  				Spec: AzureClusterSpec{
   311  					NetworkSpec: NetworkSpec{},
   312  				},
   313  			},
   314  			output: &AzureCluster{
   315  				ObjectMeta: metav1.ObjectMeta{
   316  					Name: "cluster-test",
   317  				},
   318  				Spec: AzureClusterSpec{
   319  					NetworkSpec: NetworkSpec{
   320  						Subnets: Subnets{
   321  							{
   322  								SubnetClassSpec: SubnetClassSpec{
   323  									Role:       SubnetControlPlane,
   324  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
   325  									Name:       "cluster-test-controlplane-subnet",
   326  								},
   327  
   328  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
   329  								RouteTable:    RouteTable{},
   330  							},
   331  							{
   332  								SubnetClassSpec: SubnetClassSpec{
   333  									Role:       SubnetNode,
   334  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
   335  									Name:       "cluster-test-node-subnet",
   336  								},
   337  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   338  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   339  								NatGateway: NatGateway{NatGatewayClassSpec: NatGatewayClassSpec{
   340  									Name: "cluster-test-node-natgw",
   341  								}},
   342  							},
   343  						},
   344  					},
   345  				},
   346  			},
   347  		},
   348  		{
   349  			name: "subnets with custom attributes",
   350  			cluster: &AzureCluster{
   351  				ObjectMeta: metav1.ObjectMeta{
   352  					Name: "cluster-test",
   353  				},
   354  				Spec: AzureClusterSpec{
   355  					NetworkSpec: NetworkSpec{
   356  						Subnets: Subnets{
   357  							{
   358  								SubnetClassSpec: SubnetClassSpec{
   359  									Role:       SubnetControlPlane,
   360  									CIDRBlocks: []string{"10.0.0.16/24"},
   361  									Name:       "my-controlplane-subnet",
   362  								},
   363  							},
   364  							{
   365  								SubnetClassSpec: SubnetClassSpec{
   366  									Role:       SubnetNode,
   367  									CIDRBlocks: []string{"10.1.0.16/24"},
   368  									Name:       "my-node-subnet",
   369  								},
   370  								NatGateway: NatGateway{
   371  									NatGatewayClassSpec: NatGatewayClassSpec{
   372  										Name: "foo-natgw",
   373  									},
   374  								},
   375  							},
   376  						},
   377  					},
   378  				},
   379  			},
   380  			output: &AzureCluster{
   381  				ObjectMeta: metav1.ObjectMeta{
   382  					Name: "cluster-test",
   383  				},
   384  				Spec: AzureClusterSpec{
   385  					NetworkSpec: NetworkSpec{
   386  						Subnets: Subnets{
   387  							{
   388  								SubnetClassSpec: SubnetClassSpec{
   389  									Role:       SubnetControlPlane,
   390  									CIDRBlocks: []string{"10.0.0.16/24"},
   391  									Name:       "my-controlplane-subnet",
   392  								},
   393  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
   394  								RouteTable:    RouteTable{},
   395  							},
   396  							{
   397  								SubnetClassSpec: SubnetClassSpec{
   398  									Role:       SubnetNode,
   399  									CIDRBlocks: []string{"10.1.0.16/24"},
   400  									Name:       "my-node-subnet",
   401  								},
   402  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   403  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   404  								NatGateway: NatGateway{
   405  									NatGatewayClassSpec: NatGatewayClassSpec{
   406  										Name: "foo-natgw",
   407  									},
   408  									NatGatewayIP: PublicIPSpec{
   409  										Name: "pip-foo-natgw",
   410  									},
   411  								},
   412  							},
   413  						},
   414  					},
   415  				},
   416  			},
   417  		},
   418  		{
   419  			name: "subnets specified",
   420  			cluster: &AzureCluster{
   421  				ObjectMeta: metav1.ObjectMeta{
   422  					Name: "cluster-test",
   423  				},
   424  				Spec: AzureClusterSpec{
   425  					NetworkSpec: NetworkSpec{
   426  						Subnets: Subnets{
   427  							{
   428  								SubnetClassSpec: SubnetClassSpec{
   429  									Role: SubnetControlPlane,
   430  									Name: "cluster-test-controlplane-subnet",
   431  								},
   432  							},
   433  							{
   434  								SubnetClassSpec: SubnetClassSpec{
   435  									Role: SubnetNode,
   436  									Name: "cluster-test-node-subnet",
   437  								},
   438  							},
   439  						},
   440  					},
   441  				},
   442  			},
   443  			output: &AzureCluster{
   444  				ObjectMeta: metav1.ObjectMeta{
   445  					Name: "cluster-test",
   446  				},
   447  				Spec: AzureClusterSpec{
   448  					NetworkSpec: NetworkSpec{
   449  						Subnets: Subnets{
   450  							{
   451  								SubnetClassSpec: SubnetClassSpec{
   452  									Role:       SubnetControlPlane,
   453  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
   454  									Name:       "cluster-test-controlplane-subnet",
   455  								},
   456  
   457  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
   458  								RouteTable:    RouteTable{},
   459  							},
   460  							{
   461  								SubnetClassSpec: SubnetClassSpec{
   462  									Role:       SubnetNode,
   463  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
   464  									Name:       "cluster-test-node-subnet",
   465  								},
   466  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   467  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   468  								NatGateway: NatGateway{
   469  									NatGatewayClassSpec: NatGatewayClassSpec{
   470  										Name: "cluster-test-node-natgw-1",
   471  									},
   472  									NatGatewayIP: PublicIPSpec{
   473  										Name: "pip-cluster-test-node-natgw-1",
   474  									},
   475  								},
   476  							},
   477  						},
   478  					},
   479  				},
   480  			},
   481  		},
   482  		{
   483  			name: "cluster subnet with custom attributes",
   484  			cluster: &AzureCluster{
   485  				ObjectMeta: metav1.ObjectMeta{
   486  					Name: "cluster-test",
   487  				},
   488  				Spec: AzureClusterSpec{
   489  					NetworkSpec: NetworkSpec{
   490  						Subnets: Subnets{
   491  							{
   492  								SubnetClassSpec: SubnetClassSpec{
   493  									Role:       SubnetCluster,
   494  									CIDRBlocks: []string{"10.0.0.16/24"},
   495  									Name:       "my-subnet",
   496  								},
   497  								NatGateway: NatGateway{
   498  									NatGatewayClassSpec: NatGatewayClassSpec{
   499  										Name: "foo-natgw",
   500  									},
   501  								},
   502  							},
   503  						},
   504  					},
   505  				},
   506  			},
   507  			output: &AzureCluster{
   508  				ObjectMeta: metav1.ObjectMeta{
   509  					Name: "cluster-test",
   510  				},
   511  				Spec: AzureClusterSpec{
   512  					NetworkSpec: NetworkSpec{
   513  						Subnets: Subnets{
   514  							{
   515  								SubnetClassSpec: SubnetClassSpec{
   516  									Role:       SubnetCluster,
   517  									CIDRBlocks: []string{"10.0.0.16/24"},
   518  									Name:       "my-subnet",
   519  								},
   520  								SecurityGroup: SecurityGroup{Name: "cluster-test-nsg"},
   521  								RouteTable:    RouteTable{Name: "cluster-test-routetable"},
   522  								NatGateway: NatGateway{
   523  									NatGatewayClassSpec: NatGatewayClassSpec{
   524  										Name: "foo-natgw",
   525  									},
   526  									NatGatewayIP: PublicIPSpec{
   527  										Name: "pip-foo-natgw",
   528  									},
   529  								},
   530  							},
   531  						},
   532  					},
   533  				},
   534  			},
   535  		},
   536  		{
   537  			name: "cluster subnet with subnets specified",
   538  			cluster: &AzureCluster{
   539  				ObjectMeta: metav1.ObjectMeta{
   540  					Name: "cluster-test",
   541  				},
   542  				Spec: AzureClusterSpec{
   543  					NetworkSpec: NetworkSpec{
   544  						Subnets: Subnets{
   545  							{
   546  								SubnetClassSpec: SubnetClassSpec{
   547  									Role: SubnetCluster,
   548  									Name: "cluster-test-subnet",
   549  								},
   550  							},
   551  						},
   552  					},
   553  				},
   554  			},
   555  			output: &AzureCluster{
   556  				ObjectMeta: metav1.ObjectMeta{
   557  					Name: "cluster-test",
   558  				},
   559  				Spec: AzureClusterSpec{
   560  					NetworkSpec: NetworkSpec{
   561  						Subnets: Subnets{
   562  							{
   563  								SubnetClassSpec: SubnetClassSpec{
   564  									Role:       SubnetCluster,
   565  									CIDRBlocks: []string{DefaultClusterSubnetCIDR},
   566  									Name:       "cluster-test-subnet",
   567  								},
   568  								SecurityGroup: SecurityGroup{Name: "cluster-test-nsg"},
   569  								RouteTable:    RouteTable{Name: "cluster-test-routetable"},
   570  								NatGateway: NatGateway{
   571  									NatGatewayClassSpec: NatGatewayClassSpec{
   572  										Name: "cluster-test-natgw",
   573  									},
   574  									NatGatewayIP: PublicIPSpec{
   575  										Name: "pip-cluster-test-natgw",
   576  									},
   577  								},
   578  							},
   579  						},
   580  					},
   581  				},
   582  			},
   583  		},
   584  		{
   585  			name: "subnets route tables specified",
   586  			cluster: &AzureCluster{
   587  				ObjectMeta: metav1.ObjectMeta{
   588  					Name: "cluster-test",
   589  				},
   590  				Spec: AzureClusterSpec{
   591  					NetworkSpec: NetworkSpec{
   592  						Subnets: Subnets{
   593  							{
   594  								SubnetClassSpec: SubnetClassSpec{
   595  									Role: SubnetControlPlane,
   596  									Name: "cluster-test-controlplane-subnet",
   597  								},
   598  								RouteTable: RouteTable{
   599  									Name: "control-plane-custom-route-table",
   600  								},
   601  							},
   602  							{
   603  								SubnetClassSpec: SubnetClassSpec{
   604  									Role: SubnetNode,
   605  									Name: "cluster-test-node-subnet",
   606  								},
   607  							},
   608  						},
   609  					},
   610  				},
   611  			},
   612  			output: &AzureCluster{
   613  				ObjectMeta: metav1.ObjectMeta{
   614  					Name: "cluster-test",
   615  				},
   616  				Spec: AzureClusterSpec{
   617  					NetworkSpec: NetworkSpec{
   618  						Subnets: Subnets{
   619  							{
   620  								SubnetClassSpec: SubnetClassSpec{
   621  									Role:       SubnetControlPlane,
   622  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
   623  									Name:       "cluster-test-controlplane-subnet",
   624  								},
   625  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
   626  								RouteTable:    RouteTable{Name: "control-plane-custom-route-table"},
   627  							},
   628  							{
   629  								SubnetClassSpec: SubnetClassSpec{
   630  									Role:       SubnetNode,
   631  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
   632  									Name:       "cluster-test-node-subnet",
   633  								},
   634  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   635  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   636  								NatGateway: NatGateway{
   637  									NatGatewayClassSpec: NatGatewayClassSpec{
   638  										Name: "cluster-test-node-natgw-1",
   639  									},
   640  									NatGatewayIP: PublicIPSpec{
   641  										Name: "pip-cluster-test-node-natgw-1",
   642  									},
   643  								},
   644  							},
   645  						},
   646  					},
   647  				},
   648  			},
   649  		},
   650  		{
   651  			name: "only node subnet specified",
   652  			cluster: &AzureCluster{
   653  				ObjectMeta: metav1.ObjectMeta{
   654  					Name: "cluster-test",
   655  				},
   656  				Spec: AzureClusterSpec{
   657  					NetworkSpec: NetworkSpec{
   658  						Subnets: Subnets{
   659  							{
   660  								SubnetClassSpec: SubnetClassSpec{
   661  									Role: SubnetNode,
   662  									Name: "my-node-subnet",
   663  								},
   664  							},
   665  						},
   666  					},
   667  				},
   668  			},
   669  			output: &AzureCluster{
   670  				ObjectMeta: metav1.ObjectMeta{
   671  					Name: "cluster-test",
   672  				},
   673  				Spec: AzureClusterSpec{
   674  					NetworkSpec: NetworkSpec{
   675  						Subnets: Subnets{
   676  							{
   677  								SubnetClassSpec: SubnetClassSpec{
   678  									Role:       SubnetNode,
   679  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
   680  									Name:       "my-node-subnet",
   681  								},
   682  
   683  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   684  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   685  								NatGateway: NatGateway{
   686  									NatGatewayClassSpec: NatGatewayClassSpec{
   687  										Name: "cluster-test-node-natgw-1",
   688  									},
   689  									NatGatewayIP: PublicIPSpec{
   690  										Name: "pip-cluster-test-node-natgw-1",
   691  									},
   692  								},
   693  							},
   694  							{
   695  								SubnetClassSpec: SubnetClassSpec{
   696  									Role:       SubnetControlPlane,
   697  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
   698  									Name:       "cluster-test-controlplane-subnet",
   699  								},
   700  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
   701  								RouteTable:    RouteTable{},
   702  							},
   703  						},
   704  					},
   705  				},
   706  			},
   707  		},
   708  		{
   709  			name: "subnets specified with IPv6 enabled",
   710  			cluster: &AzureCluster{
   711  				ObjectMeta: metav1.ObjectMeta{
   712  					Name: "cluster-test",
   713  				},
   714  				Spec: AzureClusterSpec{
   715  					NetworkSpec: NetworkSpec{
   716  						Vnet: VnetSpec{
   717  							VnetClassSpec: VnetClassSpec{
   718  								CIDRBlocks: []string{"2001:be00::1/56"},
   719  							},
   720  						},
   721  						Subnets: Subnets{
   722  							{
   723  								SubnetClassSpec: SubnetClassSpec{
   724  									Role:       "control-plane",
   725  									CIDRBlocks: []string{"2001:beef::1/64"},
   726  									Name:       "cluster-test-controlplane-subnet",
   727  								},
   728  							},
   729  							{
   730  								SubnetClassSpec: SubnetClassSpec{
   731  									Role:       "node",
   732  									CIDRBlocks: []string{"2001:beea::1/64"},
   733  									Name:       "cluster-test-node-subnet",
   734  								},
   735  							},
   736  						},
   737  					},
   738  				},
   739  			},
   740  			output: &AzureCluster{
   741  				ObjectMeta: metav1.ObjectMeta{
   742  					Name: "cluster-test",
   743  				},
   744  				Spec: AzureClusterSpec{
   745  					NetworkSpec: NetworkSpec{
   746  						Vnet: VnetSpec{
   747  							VnetClassSpec: VnetClassSpec{
   748  								CIDRBlocks: []string{"2001:be00::1/56"},
   749  							},
   750  						},
   751  						Subnets: Subnets{
   752  							{
   753  								SubnetClassSpec: SubnetClassSpec{
   754  									Role:       SubnetControlPlane,
   755  									CIDRBlocks: []string{"2001:beef::1/64"},
   756  									Name:       "cluster-test-controlplane-subnet",
   757  								},
   758  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
   759  								RouteTable:    RouteTable{},
   760  							},
   761  							{
   762  								SubnetClassSpec: SubnetClassSpec{
   763  									Role:       SubnetNode,
   764  									CIDRBlocks: []string{"2001:beea::1/64"},
   765  									Name:       "cluster-test-node-subnet",
   766  								},
   767  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   768  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   769  							},
   770  						},
   771  					},
   772  				},
   773  			},
   774  		},
   775  		{
   776  			name: "subnets with custom security group",
   777  			cluster: &AzureCluster{
   778  				ObjectMeta: metav1.ObjectMeta{
   779  					Name: "cluster-test",
   780  				},
   781  				Spec: AzureClusterSpec{
   782  					NetworkSpec: NetworkSpec{
   783  						Subnets: Subnets{
   784  							{
   785  								SubnetClassSpec: SubnetClassSpec{
   786  									Role: "control-plane",
   787  									Name: "cluster-test-controlplane-subnet",
   788  								},
   789  								SecurityGroup: SecurityGroup{
   790  									SecurityGroupClass: SecurityGroupClass{
   791  										SecurityRules: []SecurityRule{
   792  											{
   793  												Name:             "allow_port_50000",
   794  												Description:      "allow port 50000",
   795  												Protocol:         "*",
   796  												Priority:         2202,
   797  												SourcePorts:      ptr.To("*"),
   798  												DestinationPorts: ptr.To("*"),
   799  												Source:           ptr.To("*"),
   800  												Destination:      ptr.To("*"),
   801  												Action:           SecurityRuleActionAllow,
   802  											},
   803  										},
   804  									},
   805  									Name: "my-custom-sg",
   806  								},
   807  							},
   808  							{
   809  								SubnetClassSpec: SubnetClassSpec{
   810  									Role: "node",
   811  									Name: "cluster-test-node-subnet",
   812  								},
   813  								SecurityGroup: SecurityGroup{
   814  									SecurityGroupClass: SecurityGroupClass{
   815  										SecurityRules: []SecurityRule{
   816  											{
   817  												Name:             "allow_port_50000",
   818  												Description:      "allow port 50000",
   819  												Protocol:         "*",
   820  												Priority:         2202,
   821  												SourcePorts:      ptr.To("*"),
   822  												DestinationPorts: ptr.To("*"),
   823  												Source:           ptr.To("*"),
   824  												Destination:      ptr.To("*"),
   825  												Action:           SecurityRuleActionAllow,
   826  											},
   827  										},
   828  									},
   829  									Name: "my-custom-node-sg",
   830  								},
   831  							},
   832  						},
   833  					},
   834  				},
   835  			},
   836  			output: &AzureCluster{
   837  				ObjectMeta: metav1.ObjectMeta{
   838  					Name: "cluster-test",
   839  				},
   840  				Spec: AzureClusterSpec{
   841  					NetworkSpec: NetworkSpec{
   842  						Subnets: Subnets{
   843  							{
   844  								SubnetClassSpec: SubnetClassSpec{
   845  									Role:       "control-plane",
   846  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
   847  									Name:       "cluster-test-controlplane-subnet",
   848  								},
   849  								SecurityGroup: SecurityGroup{
   850  									SecurityGroupClass: SecurityGroupClass{
   851  										SecurityRules: []SecurityRule{
   852  											{
   853  												Name:             "allow_port_50000",
   854  												Description:      "allow port 50000",
   855  												Protocol:         "*",
   856  												Priority:         2202,
   857  												SourcePorts:      ptr.To("*"),
   858  												DestinationPorts: ptr.To("*"),
   859  												Source:           ptr.To("*"),
   860  												Destination:      ptr.To("*"),
   861  												Direction:        SecurityRuleDirectionInbound,
   862  												Action:           SecurityRuleActionAllow,
   863  											},
   864  										},
   865  									},
   866  									Name: "my-custom-sg",
   867  								},
   868  							},
   869  							{
   870  								SubnetClassSpec: SubnetClassSpec{
   871  									Role:       SubnetNode,
   872  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
   873  									Name:       "cluster-test-node-subnet",
   874  								},
   875  								SecurityGroup: SecurityGroup{
   876  									Name: "my-custom-node-sg",
   877  									SecurityGroupClass: SecurityGroupClass{
   878  										SecurityRules: []SecurityRule{
   879  											{
   880  												Name:             "allow_port_50000",
   881  												Description:      "allow port 50000",
   882  												Protocol:         "*",
   883  												Priority:         2202,
   884  												SourcePorts:      ptr.To("*"),
   885  												DestinationPorts: ptr.To("*"),
   886  												Source:           ptr.To("*"),
   887  												Destination:      ptr.To("*"),
   888  												Direction:        SecurityRuleDirectionInbound,
   889  												Action:           SecurityRuleActionAllow,
   890  											},
   891  										},
   892  									},
   893  								},
   894  								RouteTable: RouteTable{Name: "cluster-test-node-routetable"},
   895  								NatGateway: NatGateway{
   896  									NatGatewayIP: PublicIPSpec{
   897  										Name: "pip-cluster-test-node-natgw-1",
   898  									},
   899  									NatGatewayClassSpec: NatGatewayClassSpec{
   900  										Name: "cluster-test-node-natgw-1",
   901  									},
   902  								},
   903  							},
   904  						},
   905  					},
   906  				},
   907  			},
   908  		},
   909  		{
   910  			name: "subnets with custom security group to deny port 49999",
   911  			cluster: &AzureCluster{
   912  				ObjectMeta: metav1.ObjectMeta{
   913  					Name: "cluster-test",
   914  				},
   915  				Spec: AzureClusterSpec{
   916  					NetworkSpec: NetworkSpec{
   917  						Subnets: Subnets{
   918  							{
   919  								SubnetClassSpec: SubnetClassSpec{
   920  									Role: "control-plane",
   921  									Name: "cluster-test-controlplane-subnet",
   922  								},
   923  								SecurityGroup: SecurityGroup{
   924  									SecurityGroupClass: SecurityGroupClass{
   925  										SecurityRules: []SecurityRule{
   926  											{
   927  												Name:             "deny_port_49999",
   928  												Description:      "deny port 49999",
   929  												Protocol:         "*",
   930  												Priority:         2201,
   931  												SourcePorts:      ptr.To("*"),
   932  												DestinationPorts: ptr.To("*"),
   933  												Source:           ptr.To("*"),
   934  												Destination:      ptr.To("*"),
   935  												Action:           SecurityRuleActionDeny,
   936  											},
   937  										},
   938  									},
   939  									Name: "my-custom-sg",
   940  								},
   941  							},
   942  						},
   943  					},
   944  				},
   945  			},
   946  			output: &AzureCluster{
   947  				ObjectMeta: metav1.ObjectMeta{
   948  					Name: "cluster-test",
   949  				},
   950  				Spec: AzureClusterSpec{
   951  					NetworkSpec: NetworkSpec{
   952  						Subnets: Subnets{
   953  							{
   954  								SubnetClassSpec: SubnetClassSpec{
   955  									Role:       "control-plane",
   956  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
   957  									Name:       "cluster-test-controlplane-subnet",
   958  								},
   959  								SecurityGroup: SecurityGroup{
   960  									SecurityGroupClass: SecurityGroupClass{
   961  										SecurityRules: []SecurityRule{
   962  											{
   963  												Name:             "deny_port_49999",
   964  												Description:      "deny port 49999",
   965  												Protocol:         "*",
   966  												Priority:         2201,
   967  												SourcePorts:      ptr.To("*"),
   968  												DestinationPorts: ptr.To("*"),
   969  												Source:           ptr.To("*"),
   970  												Destination:      ptr.To("*"),
   971  												Direction:        SecurityRuleDirectionInbound,
   972  												Action:           SecurityRuleActionDeny,
   973  											},
   974  										},
   975  									},
   976  									Name: "my-custom-sg",
   977  								},
   978  							},
   979  							{
   980  								SubnetClassSpec: SubnetClassSpec{
   981  									Role:       SubnetNode,
   982  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
   983  									Name:       "cluster-test-node-subnet",
   984  								},
   985  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
   986  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
   987  								NatGateway: NatGateway{
   988  									NatGatewayIP: PublicIPSpec{
   989  										Name: "",
   990  									},
   991  									NatGatewayClassSpec: NatGatewayClassSpec{
   992  										Name: "cluster-test-node-natgw",
   993  									},
   994  								},
   995  							},
   996  						},
   997  					},
   998  				},
   999  			},
  1000  		},
  1001  		{
  1002  			name: "don't default NAT Gateway if subnet already exists",
  1003  			cluster: &AzureCluster{
  1004  				ObjectMeta: metav1.ObjectMeta{
  1005  					Name: "cluster-test",
  1006  				},
  1007  				Spec: AzureClusterSpec{
  1008  					NetworkSpec: NetworkSpec{
  1009  						Subnets: Subnets{
  1010  							{
  1011  								SubnetClassSpec: SubnetClassSpec{
  1012  									Role: SubnetControlPlane,
  1013  									Name: "cluster-test-controlplane-subnet",
  1014  								},
  1015  								ID: "my-subnet-id",
  1016  							},
  1017  							{
  1018  								SubnetClassSpec: SubnetClassSpec{
  1019  									Role: SubnetNode,
  1020  									Name: "cluster-test-node-subnet",
  1021  								},
  1022  								ID: "my-subnet-id-2",
  1023  							},
  1024  						},
  1025  					},
  1026  				},
  1027  			},
  1028  			output: &AzureCluster{
  1029  				ObjectMeta: metav1.ObjectMeta{
  1030  					Name: "cluster-test",
  1031  				},
  1032  				Spec: AzureClusterSpec{
  1033  					NetworkSpec: NetworkSpec{
  1034  						Subnets: Subnets{
  1035  							{
  1036  								SubnetClassSpec: SubnetClassSpec{
  1037  									Role:       SubnetControlPlane,
  1038  									CIDRBlocks: []string{DefaultControlPlaneSubnetCIDR},
  1039  									Name:       "cluster-test-controlplane-subnet",
  1040  								},
  1041  								ID:            "my-subnet-id",
  1042  								SecurityGroup: SecurityGroup{Name: "cluster-test-controlplane-nsg"},
  1043  								RouteTable:    RouteTable{},
  1044  							},
  1045  							{
  1046  								SubnetClassSpec: SubnetClassSpec{
  1047  									Role:       SubnetNode,
  1048  									CIDRBlocks: []string{DefaultNodeSubnetCIDR},
  1049  									Name:       "cluster-test-node-subnet",
  1050  								},
  1051  								ID:            "my-subnet-id-2",
  1052  								SecurityGroup: SecurityGroup{Name: "cluster-test-node-nsg"},
  1053  								RouteTable:    RouteTable{Name: "cluster-test-node-routetable"},
  1054  								NatGateway: NatGateway{
  1055  									NatGatewayClassSpec: NatGatewayClassSpec{
  1056  										Name: "",
  1057  									},
  1058  									NatGatewayIP: PublicIPSpec{
  1059  										Name: "",
  1060  									},
  1061  								},
  1062  							},
  1063  						},
  1064  					},
  1065  				},
  1066  			},
  1067  		},
  1068  	}
  1069  
  1070  	for _, c := range cases {
  1071  		tc := c
  1072  		t.Run(tc.name, func(t *testing.T) {
  1073  			t.Parallel()
  1074  			tc.cluster.setSubnetDefaults()
  1075  			if !reflect.DeepEqual(tc.cluster, tc.output) {
  1076  				expected, _ := json.MarshalIndent(tc.output, "", "\t")
  1077  				actual, _ := json.MarshalIndent(tc.cluster, "", "\t")
  1078  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  1079  			}
  1080  		})
  1081  	}
  1082  }
  1083  
  1084  func TestVnetPeeringDefaults(t *testing.T) {
  1085  	cases := []struct {
  1086  		name    string
  1087  		cluster *AzureCluster
  1088  		output  *AzureCluster
  1089  	}{
  1090  		{
  1091  			name: "no peering",
  1092  			cluster: &AzureCluster{
  1093  				ObjectMeta: metav1.ObjectMeta{
  1094  					Name: "cluster-test",
  1095  				},
  1096  				Spec: AzureClusterSpec{
  1097  					NetworkSpec: NetworkSpec{},
  1098  				},
  1099  			},
  1100  			output: &AzureCluster{
  1101  				ObjectMeta: metav1.ObjectMeta{
  1102  					Name: "cluster-test",
  1103  				},
  1104  				Spec: AzureClusterSpec{
  1105  					NetworkSpec: NetworkSpec{},
  1106  				},
  1107  			},
  1108  		},
  1109  		{
  1110  			name: "peering with resource group",
  1111  			cluster: &AzureCluster{
  1112  				ObjectMeta: metav1.ObjectMeta{
  1113  					Name: "cluster-test",
  1114  				},
  1115  				Spec: AzureClusterSpec{
  1116  					ResourceGroup: "cluster-test",
  1117  					NetworkSpec: NetworkSpec{
  1118  						Vnet: VnetSpec{
  1119  							Peerings: VnetPeerings{
  1120  								{
  1121  									VnetPeeringClassSpec: VnetPeeringClassSpec{
  1122  										RemoteVnetName: "my-vnet",
  1123  										ResourceGroup:  "cluster-test",
  1124  									},
  1125  								},
  1126  							},
  1127  						},
  1128  					},
  1129  				},
  1130  			},
  1131  			output: &AzureCluster{
  1132  				ObjectMeta: metav1.ObjectMeta{
  1133  					Name: "cluster-test",
  1134  				},
  1135  				Spec: AzureClusterSpec{
  1136  					ResourceGroup: "cluster-test",
  1137  					NetworkSpec: NetworkSpec{
  1138  						Vnet: VnetSpec{
  1139  							Peerings: VnetPeerings{
  1140  								{
  1141  									VnetPeeringClassSpec: VnetPeeringClassSpec{
  1142  										RemoteVnetName: "my-vnet",
  1143  										ResourceGroup:  "cluster-test",
  1144  									},
  1145  								},
  1146  							},
  1147  						},
  1148  					},
  1149  				},
  1150  			},
  1151  		},
  1152  		{
  1153  			name: "peering without resource group",
  1154  			cluster: &AzureCluster{
  1155  				ObjectMeta: metav1.ObjectMeta{
  1156  					Name: "cluster-test",
  1157  				},
  1158  				Spec: AzureClusterSpec{
  1159  					ResourceGroup: "cluster-test",
  1160  					NetworkSpec: NetworkSpec{
  1161  						Vnet: VnetSpec{
  1162  							Peerings: VnetPeerings{
  1163  								{
  1164  									VnetPeeringClassSpec: VnetPeeringClassSpec{RemoteVnetName: "my-vnet"},
  1165  								},
  1166  							},
  1167  						},
  1168  					},
  1169  				},
  1170  			},
  1171  			output: &AzureCluster{
  1172  				ObjectMeta: metav1.ObjectMeta{
  1173  					Name: "cluster-test",
  1174  				},
  1175  				Spec: AzureClusterSpec{
  1176  					ResourceGroup: "cluster-test",
  1177  					NetworkSpec: NetworkSpec{
  1178  						Vnet: VnetSpec{
  1179  							Peerings: VnetPeerings{
  1180  								{
  1181  									VnetPeeringClassSpec: VnetPeeringClassSpec{
  1182  										RemoteVnetName: "my-vnet",
  1183  										ResourceGroup:  "cluster-test",
  1184  									},
  1185  								},
  1186  							},
  1187  						},
  1188  					},
  1189  				},
  1190  			},
  1191  		},
  1192  	}
  1193  
  1194  	for _, c := range cases {
  1195  		tc := c
  1196  		t.Run(tc.name, func(t *testing.T) {
  1197  			t.Parallel()
  1198  			tc.cluster.setVnetPeeringDefaults()
  1199  			if !reflect.DeepEqual(tc.cluster, tc.output) {
  1200  				expected, _ := json.MarshalIndent(tc.output, "", "\t")
  1201  				actual, _ := json.MarshalIndent(tc.cluster, "", "\t")
  1202  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  1203  			}
  1204  		})
  1205  	}
  1206  }
  1207  
  1208  func TestAPIServerLBDefaults(t *testing.T) {
  1209  	cases := []struct {
  1210  		name    string
  1211  		cluster *AzureCluster
  1212  		output  *AzureCluster
  1213  	}{
  1214  		{
  1215  			name: "no lb",
  1216  			cluster: &AzureCluster{
  1217  				ObjectMeta: metav1.ObjectMeta{
  1218  					Name: "cluster-test",
  1219  				},
  1220  				Spec: AzureClusterSpec{
  1221  					NetworkSpec: NetworkSpec{},
  1222  				},
  1223  			},
  1224  			output: &AzureCluster{
  1225  				ObjectMeta: metav1.ObjectMeta{
  1226  					Name: "cluster-test",
  1227  				},
  1228  				Spec: AzureClusterSpec{
  1229  					NetworkSpec: NetworkSpec{
  1230  						APIServerLB: LoadBalancerSpec{
  1231  							Name: "cluster-test-public-lb",
  1232  							FrontendIPs: []FrontendIP{
  1233  								{
  1234  									Name: "cluster-test-public-lb-frontEnd",
  1235  									PublicIP: &PublicIPSpec{
  1236  										Name:    "pip-cluster-test-apiserver",
  1237  										DNSName: "",
  1238  									},
  1239  								},
  1240  							},
  1241  							BackendPool: BackendPool{
  1242  								Name: "cluster-test-public-lb-backendPool",
  1243  							},
  1244  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1245  								SKU:                  SKUStandard,
  1246  								Type:                 Public,
  1247  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  1248  							},
  1249  						},
  1250  					},
  1251  				},
  1252  			},
  1253  		},
  1254  		{
  1255  			name: "internal lb",
  1256  			cluster: &AzureCluster{
  1257  				ObjectMeta: metav1.ObjectMeta{
  1258  					Name: "cluster-test",
  1259  				},
  1260  				Spec: AzureClusterSpec{
  1261  					NetworkSpec: NetworkSpec{
  1262  						APIServerLB: LoadBalancerSpec{
  1263  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1264  								Type: Internal,
  1265  							},
  1266  						},
  1267  					},
  1268  				},
  1269  			},
  1270  			output: &AzureCluster{
  1271  				ObjectMeta: metav1.ObjectMeta{
  1272  					Name: "cluster-test",
  1273  				},
  1274  				Spec: AzureClusterSpec{
  1275  					NetworkSpec: NetworkSpec{
  1276  						APIServerLB: LoadBalancerSpec{
  1277  							FrontendIPs: []FrontendIP{
  1278  								{
  1279  									Name: "cluster-test-internal-lb-frontEnd",
  1280  									FrontendIPClass: FrontendIPClass{
  1281  										PrivateIPAddress: DefaultInternalLBIPAddress,
  1282  									},
  1283  								},
  1284  							},
  1285  							BackendPool: BackendPool{
  1286  								Name: "cluster-test-internal-lb-backendPool",
  1287  							},
  1288  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1289  								SKU:                  SKUStandard,
  1290  								Type:                 Internal,
  1291  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  1292  							},
  1293  							Name: "cluster-test-internal-lb",
  1294  						},
  1295  					},
  1296  				},
  1297  			},
  1298  		},
  1299  		{
  1300  			name: "with custom backend pool name",
  1301  			cluster: &AzureCluster{
  1302  				ObjectMeta: metav1.ObjectMeta{
  1303  					Name: "cluster-test",
  1304  				},
  1305  				Spec: AzureClusterSpec{
  1306  					NetworkSpec: NetworkSpec{
  1307  						APIServerLB: LoadBalancerSpec{
  1308  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1309  								Type: Internal,
  1310  							},
  1311  							BackendPool: BackendPool{
  1312  								Name: "custom-backend-pool",
  1313  							},
  1314  						},
  1315  					},
  1316  				},
  1317  			},
  1318  			output: &AzureCluster{
  1319  				ObjectMeta: metav1.ObjectMeta{
  1320  					Name: "cluster-test",
  1321  				},
  1322  				Spec: AzureClusterSpec{
  1323  					NetworkSpec: NetworkSpec{
  1324  						APIServerLB: LoadBalancerSpec{
  1325  							FrontendIPs: []FrontendIP{
  1326  								{
  1327  									Name: "cluster-test-internal-lb-frontEnd",
  1328  									FrontendIPClass: FrontendIPClass{
  1329  										PrivateIPAddress: DefaultInternalLBIPAddress,
  1330  									},
  1331  								},
  1332  							},
  1333  							BackendPool: BackendPool{
  1334  								Name: "custom-backend-pool",
  1335  							},
  1336  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1337  								SKU:                  SKUStandard,
  1338  								Type:                 Internal,
  1339  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  1340  							},
  1341  							Name: "cluster-test-internal-lb",
  1342  						},
  1343  					},
  1344  				},
  1345  			},
  1346  		},
  1347  	}
  1348  
  1349  	for _, c := range cases {
  1350  		tc := c
  1351  		t.Run(tc.name, func(t *testing.T) {
  1352  			t.Parallel()
  1353  			tc.cluster.setAPIServerLBDefaults()
  1354  			if !reflect.DeepEqual(tc.cluster, tc.output) {
  1355  				expected, _ := json.MarshalIndent(tc.output, "", "\t")
  1356  				actual, _ := json.MarshalIndent(tc.cluster, "", "\t")
  1357  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  1358  			}
  1359  		})
  1360  	}
  1361  }
  1362  
  1363  func TestAzureEnviromentDefault(t *testing.T) {
  1364  	cases := map[string]struct {
  1365  		cluster *AzureCluster
  1366  		output  *AzureCluster
  1367  	}{
  1368  		"default empty azure env": {
  1369  			cluster: &AzureCluster{
  1370  				ObjectMeta: metav1.ObjectMeta{
  1371  					Name: "foo",
  1372  				},
  1373  				Spec: AzureClusterSpec{},
  1374  			},
  1375  			output: &AzureCluster{
  1376  				ObjectMeta: metav1.ObjectMeta{
  1377  					Name: "foo",
  1378  				},
  1379  				Spec: AzureClusterSpec{
  1380  					AzureClusterClassSpec: AzureClusterClassSpec{
  1381  						AzureEnvironment: DefaultAzureCloud,
  1382  					},
  1383  				},
  1384  			},
  1385  		},
  1386  		"azure env set to AzurePublicCloud": {
  1387  			cluster: &AzureCluster{
  1388  				ObjectMeta: metav1.ObjectMeta{
  1389  					Name: "foo",
  1390  				},
  1391  				Spec: AzureClusterSpec{
  1392  					AzureClusterClassSpec: AzureClusterClassSpec{
  1393  						AzureEnvironment: DefaultAzureCloud,
  1394  					},
  1395  				},
  1396  			},
  1397  			output: &AzureCluster{
  1398  				ObjectMeta: metav1.ObjectMeta{
  1399  					Name: "foo",
  1400  				},
  1401  				Spec: AzureClusterSpec{
  1402  					AzureClusterClassSpec: AzureClusterClassSpec{
  1403  						AzureEnvironment: DefaultAzureCloud,
  1404  					},
  1405  				},
  1406  			},
  1407  		},
  1408  		"azure env set to AzureGermanCloud": {
  1409  			cluster: &AzureCluster{
  1410  				ObjectMeta: metav1.ObjectMeta{
  1411  					Name: "foo",
  1412  				},
  1413  				Spec: AzureClusterSpec{
  1414  					AzureClusterClassSpec: AzureClusterClassSpec{
  1415  						AzureEnvironment: "AzureGermanCloud",
  1416  					},
  1417  				},
  1418  			},
  1419  			output: &AzureCluster{
  1420  				ObjectMeta: metav1.ObjectMeta{
  1421  					Name: "foo",
  1422  				},
  1423  				Spec: AzureClusterSpec{
  1424  					AzureClusterClassSpec: AzureClusterClassSpec{
  1425  						AzureEnvironment: "AzureGermanCloud",
  1426  					},
  1427  				},
  1428  			},
  1429  		},
  1430  	}
  1431  
  1432  	for name := range cases {
  1433  		c := cases[name]
  1434  		t.Run(name, func(t *testing.T) {
  1435  			t.Parallel()
  1436  			c.cluster.setAzureEnvironmentDefault()
  1437  			if !reflect.DeepEqual(c.cluster, c.output) {
  1438  				expected, _ := json.MarshalIndent(c.output, "", "\t")
  1439  				actual, _ := json.MarshalIndent(c.cluster, "", "\t")
  1440  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  1441  			}
  1442  		})
  1443  	}
  1444  }
  1445  
  1446  func TestNodeOutboundLBDefaults(t *testing.T) {
  1447  	cases := []struct {
  1448  		name    string
  1449  		cluster *AzureCluster
  1450  		output  *AzureCluster
  1451  	}{
  1452  		{
  1453  			name: "default no lb for public clusters",
  1454  			cluster: &AzureCluster{
  1455  				ObjectMeta: metav1.ObjectMeta{
  1456  					Name: "cluster-test",
  1457  				},
  1458  				Spec: AzureClusterSpec{
  1459  					NetworkSpec: NetworkSpec{
  1460  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  1461  						Subnets: Subnets{
  1462  							{
  1463  								SubnetClassSpec: SubnetClassSpec{
  1464  									Role: SubnetControlPlane,
  1465  									Name: "control-plane-subnet",
  1466  								},
  1467  								SecurityGroup: SecurityGroup{},
  1468  								RouteTable:    RouteTable{},
  1469  							},
  1470  							{
  1471  								SubnetClassSpec: SubnetClassSpec{
  1472  									Role: SubnetNode,
  1473  									Name: "node-subnet",
  1474  								},
  1475  								SecurityGroup: SecurityGroup{},
  1476  								RouteTable:    RouteTable{},
  1477  							},
  1478  						},
  1479  					},
  1480  				},
  1481  			},
  1482  			output: &AzureCluster{
  1483  				ObjectMeta: metav1.ObjectMeta{
  1484  					Name: "cluster-test",
  1485  				},
  1486  				Spec: AzureClusterSpec{
  1487  					NetworkSpec: NetworkSpec{
  1488  						Subnets: Subnets{
  1489  							{
  1490  								SubnetClassSpec: SubnetClassSpec{
  1491  									Role: SubnetControlPlane,
  1492  									Name: "control-plane-subnet",
  1493  								},
  1494  								SecurityGroup: SecurityGroup{},
  1495  								RouteTable:    RouteTable{},
  1496  							},
  1497  							{
  1498  								SubnetClassSpec: SubnetClassSpec{
  1499  									Role: SubnetNode,
  1500  									Name: "node-subnet",
  1501  								},
  1502  								SecurityGroup: SecurityGroup{},
  1503  								RouteTable:    RouteTable{},
  1504  							},
  1505  						},
  1506  						APIServerLB: LoadBalancerSpec{
  1507  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1508  								Type: Public,
  1509  							},
  1510  						},
  1511  					},
  1512  				},
  1513  			},
  1514  		},
  1515  		{
  1516  			name: "IPv6 enabled",
  1517  			cluster: &AzureCluster{
  1518  				ObjectMeta: metav1.ObjectMeta{
  1519  					Name: "cluster-test",
  1520  				},
  1521  				Spec: AzureClusterSpec{
  1522  					NetworkSpec: NetworkSpec{
  1523  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  1524  						Subnets: Subnets{
  1525  							{
  1526  								SubnetClassSpec: SubnetClassSpec{
  1527  									Role: SubnetControlPlane,
  1528  									Name: "control-plane-subnet",
  1529  								},
  1530  								SecurityGroup: SecurityGroup{},
  1531  								RouteTable:    RouteTable{},
  1532  							},
  1533  							{
  1534  								SubnetClassSpec: SubnetClassSpec{
  1535  									Role:       "node",
  1536  									CIDRBlocks: []string{"2001:beea::1/64"},
  1537  									Name:       "cluster-test-node-subnet",
  1538  								},
  1539  								SecurityGroup: SecurityGroup{},
  1540  								RouteTable:    RouteTable{},
  1541  							},
  1542  						},
  1543  					},
  1544  				},
  1545  			},
  1546  			output: &AzureCluster{
  1547  				ObjectMeta: metav1.ObjectMeta{
  1548  					Name: "cluster-test",
  1549  				},
  1550  				Spec: AzureClusterSpec{
  1551  					NetworkSpec: NetworkSpec{
  1552  						Subnets: Subnets{
  1553  							{
  1554  								SubnetClassSpec: SubnetClassSpec{
  1555  									Role: SubnetControlPlane,
  1556  									Name: "control-plane-subnet",
  1557  								},
  1558  								SecurityGroup: SecurityGroup{},
  1559  								RouteTable:    RouteTable{},
  1560  							},
  1561  							{
  1562  								SubnetClassSpec: SubnetClassSpec{
  1563  									Role:       "node",
  1564  									CIDRBlocks: []string{"2001:beea::1/64"},
  1565  									Name:       "cluster-test-node-subnet",
  1566  								},
  1567  								SecurityGroup: SecurityGroup{},
  1568  								RouteTable:    RouteTable{},
  1569  							},
  1570  						},
  1571  						APIServerLB: LoadBalancerSpec{
  1572  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1573  								Type: Public,
  1574  							},
  1575  						},
  1576  						NodeOutboundLB: &LoadBalancerSpec{
  1577  							Name: "cluster-test",
  1578  							FrontendIPs: []FrontendIP{{
  1579  								Name: "cluster-test-frontEnd",
  1580  								PublicIP: &PublicIPSpec{
  1581  									Name: "pip-cluster-test-node-outbound",
  1582  								},
  1583  							}},
  1584  							BackendPool: BackendPool{
  1585  								Name: "cluster-test-outboundBackendPool",
  1586  							},
  1587  							FrontendIPsCount: ptr.To[int32](1),
  1588  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1589  								SKU:                  SKUStandard,
  1590  								Type:                 Public,
  1591  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  1592  							},
  1593  						},
  1594  					},
  1595  				},
  1596  			},
  1597  		},
  1598  		{
  1599  			name: "IPv6 enabled on 1 of 2 node subnets",
  1600  			cluster: &AzureCluster{
  1601  				ObjectMeta: metav1.ObjectMeta{
  1602  					Name: "cluster-test",
  1603  				},
  1604  				Spec: AzureClusterSpec{
  1605  					NetworkSpec: NetworkSpec{
  1606  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  1607  						Subnets: Subnets{
  1608  							{
  1609  								SubnetClassSpec: SubnetClassSpec{
  1610  									Role: SubnetControlPlane,
  1611  									Name: "control-plane-subnet",
  1612  								},
  1613  								SecurityGroup: SecurityGroup{},
  1614  								RouteTable:    RouteTable{},
  1615  							},
  1616  							{
  1617  								SubnetClassSpec: SubnetClassSpec{
  1618  									Role:       SubnetNode,
  1619  									CIDRBlocks: []string{"2001:beea::1/64"},
  1620  									Name:       "node-subnet-1",
  1621  								},
  1622  								SecurityGroup: SecurityGroup{},
  1623  								RouteTable:    RouteTable{},
  1624  							},
  1625  							{
  1626  								SubnetClassSpec: SubnetClassSpec{
  1627  									Role: SubnetNode,
  1628  									Name: "node-subnet-2",
  1629  								},
  1630  								SecurityGroup: SecurityGroup{},
  1631  								RouteTable:    RouteTable{},
  1632  							},
  1633  						},
  1634  					},
  1635  				},
  1636  			},
  1637  			output: &AzureCluster{
  1638  				ObjectMeta: metav1.ObjectMeta{
  1639  					Name: "cluster-test",
  1640  				},
  1641  				Spec: AzureClusterSpec{
  1642  					NetworkSpec: NetworkSpec{
  1643  						Subnets: Subnets{
  1644  							{
  1645  								SubnetClassSpec: SubnetClassSpec{
  1646  									Role: SubnetControlPlane,
  1647  									Name: "control-plane-subnet",
  1648  								},
  1649  								SecurityGroup: SecurityGroup{},
  1650  								RouteTable:    RouteTable{},
  1651  							},
  1652  							{
  1653  								SubnetClassSpec: SubnetClassSpec{
  1654  									Role:       SubnetNode,
  1655  									CIDRBlocks: []string{"2001:beea::1/64"},
  1656  									Name:       "node-subnet-1",
  1657  								},
  1658  								SecurityGroup: SecurityGroup{},
  1659  								RouteTable:    RouteTable{},
  1660  							},
  1661  							{
  1662  								SubnetClassSpec: SubnetClassSpec{
  1663  									Role: SubnetNode,
  1664  									Name: "node-subnet-2",
  1665  								},
  1666  								SecurityGroup: SecurityGroup{},
  1667  								RouteTable:    RouteTable{},
  1668  							},
  1669  						},
  1670  						APIServerLB: LoadBalancerSpec{
  1671  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1672  								Type: Public,
  1673  							},
  1674  						},
  1675  						NodeOutboundLB: &LoadBalancerSpec{
  1676  							Name: "cluster-test",
  1677  							FrontendIPs: []FrontendIP{{
  1678  								Name: "cluster-test-frontEnd",
  1679  								PublicIP: &PublicIPSpec{
  1680  									Name: "pip-cluster-test-node-outbound",
  1681  								},
  1682  							}},
  1683  							BackendPool: BackendPool{
  1684  								Name: "cluster-test-outboundBackendPool",
  1685  							},
  1686  							FrontendIPsCount: ptr.To[int32](1),
  1687  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1688  								SKU:                  SKUStandard,
  1689  								Type:                 Public,
  1690  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  1691  							},
  1692  						},
  1693  					},
  1694  				},
  1695  			},
  1696  		},
  1697  		{
  1698  			name: "multiple node subnets, IPv6 not enabled in any of them",
  1699  			cluster: &AzureCluster{
  1700  				ObjectMeta: metav1.ObjectMeta{
  1701  					Name: "cluster-test",
  1702  				},
  1703  				Spec: AzureClusterSpec{
  1704  					NetworkSpec: NetworkSpec{
  1705  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  1706  						Subnets: Subnets{
  1707  							{
  1708  								SubnetClassSpec: SubnetClassSpec{
  1709  									Role: SubnetControlPlane,
  1710  									Name: "control-plane-subnet",
  1711  								},
  1712  								SecurityGroup: SecurityGroup{},
  1713  								RouteTable:    RouteTable{},
  1714  							},
  1715  							{
  1716  								SubnetClassSpec: SubnetClassSpec{
  1717  									Role: SubnetNode,
  1718  									Name: "node-subnet",
  1719  								},
  1720  								SecurityGroup: SecurityGroup{},
  1721  								RouteTable:    RouteTable{},
  1722  							},
  1723  							{
  1724  								SubnetClassSpec: SubnetClassSpec{
  1725  									Role: SubnetNode,
  1726  									Name: "node-subnet-2",
  1727  								},
  1728  								SecurityGroup: SecurityGroup{},
  1729  								RouteTable:    RouteTable{},
  1730  							},
  1731  							{
  1732  								SubnetClassSpec: SubnetClassSpec{
  1733  									Role: SubnetNode,
  1734  									Name: "node-subnet-3",
  1735  								},
  1736  								SecurityGroup: SecurityGroup{},
  1737  								RouteTable:    RouteTable{},
  1738  							},
  1739  						},
  1740  					},
  1741  				},
  1742  			},
  1743  			output: &AzureCluster{
  1744  				ObjectMeta: metav1.ObjectMeta{
  1745  					Name: "cluster-test",
  1746  				},
  1747  				Spec: AzureClusterSpec{
  1748  					NetworkSpec: NetworkSpec{
  1749  						Subnets: Subnets{
  1750  							{
  1751  								SubnetClassSpec: SubnetClassSpec{
  1752  									Role: SubnetControlPlane,
  1753  									Name: "control-plane-subnet",
  1754  								},
  1755  								SecurityGroup: SecurityGroup{},
  1756  								RouteTable:    RouteTable{},
  1757  							},
  1758  							{
  1759  								SubnetClassSpec: SubnetClassSpec{
  1760  									Role: SubnetNode,
  1761  									Name: "node-subnet",
  1762  								},
  1763  								SecurityGroup: SecurityGroup{},
  1764  								RouteTable:    RouteTable{},
  1765  							},
  1766  							{
  1767  								SubnetClassSpec: SubnetClassSpec{
  1768  									Role: SubnetNode,
  1769  									Name: "node-subnet-2",
  1770  								},
  1771  								SecurityGroup: SecurityGroup{},
  1772  								RouteTable:    RouteTable{},
  1773  							},
  1774  							{
  1775  								SubnetClassSpec: SubnetClassSpec{
  1776  									Role: SubnetNode,
  1777  									Name: "node-subnet-3",
  1778  								},
  1779  								SecurityGroup: SecurityGroup{},
  1780  								RouteTable:    RouteTable{},
  1781  							},
  1782  						},
  1783  						APIServerLB: LoadBalancerSpec{
  1784  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1785  								Type: Public,
  1786  							},
  1787  						},
  1788  					},
  1789  				},
  1790  			},
  1791  		},
  1792  		{
  1793  			name: "multiple node subnets, IPv6 enabled on all of them",
  1794  			cluster: &AzureCluster{
  1795  				ObjectMeta: metav1.ObjectMeta{
  1796  					Name: "cluster-test",
  1797  				},
  1798  				Spec: AzureClusterSpec{
  1799  					NetworkSpec: NetworkSpec{
  1800  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  1801  						Subnets: Subnets{
  1802  							{
  1803  								SubnetClassSpec: SubnetClassSpec{
  1804  									Role: SubnetControlPlane,
  1805  									Name: "control-plane-subnet",
  1806  								},
  1807  								SecurityGroup: SecurityGroup{},
  1808  								RouteTable:    RouteTable{},
  1809  							},
  1810  							{
  1811  								SubnetClassSpec: SubnetClassSpec{
  1812  									Role:       SubnetNode,
  1813  									CIDRBlocks: []string{"2001:beea::1/64"},
  1814  									Name:       "node-subnet-1",
  1815  								},
  1816  								SecurityGroup: SecurityGroup{},
  1817  								RouteTable:    RouteTable{},
  1818  							},
  1819  							{
  1820  								SubnetClassSpec: SubnetClassSpec{
  1821  									Role:       SubnetNode,
  1822  									CIDRBlocks: []string{"2002:beee::1/64"},
  1823  									Name:       "node-subnet-2",
  1824  								},
  1825  								SecurityGroup: SecurityGroup{},
  1826  								RouteTable:    RouteTable{},
  1827  							},
  1828  							{
  1829  								SubnetClassSpec: SubnetClassSpec{
  1830  									Role:       SubnetNode,
  1831  									CIDRBlocks: []string{"2003:befa::1/64"},
  1832  									Name:       "node-subnet-3",
  1833  								},
  1834  								SecurityGroup: SecurityGroup{},
  1835  								RouteTable:    RouteTable{},
  1836  							},
  1837  						},
  1838  					},
  1839  				},
  1840  			},
  1841  			output: &AzureCluster{
  1842  				ObjectMeta: metav1.ObjectMeta{
  1843  					Name: "cluster-test",
  1844  				},
  1845  				Spec: AzureClusterSpec{
  1846  					NetworkSpec: NetworkSpec{
  1847  						Subnets: Subnets{
  1848  							{
  1849  								SubnetClassSpec: SubnetClassSpec{
  1850  									Role: SubnetControlPlane,
  1851  									Name: "control-plane-subnet",
  1852  								},
  1853  								SecurityGroup: SecurityGroup{},
  1854  								RouteTable:    RouteTable{},
  1855  							},
  1856  							{
  1857  								SubnetClassSpec: SubnetClassSpec{
  1858  									Role:       SubnetNode,
  1859  									CIDRBlocks: []string{"2001:beea::1/64"},
  1860  									Name:       "node-subnet-1",
  1861  								},
  1862  								SecurityGroup: SecurityGroup{},
  1863  								RouteTable:    RouteTable{},
  1864  							},
  1865  							{
  1866  								SubnetClassSpec: SubnetClassSpec{
  1867  									Role:       SubnetNode,
  1868  									CIDRBlocks: []string{"2002:beee::1/64"},
  1869  									Name:       "node-subnet-2",
  1870  								},
  1871  								SecurityGroup: SecurityGroup{},
  1872  								RouteTable:    RouteTable{},
  1873  							},
  1874  							{
  1875  								SubnetClassSpec: SubnetClassSpec{
  1876  									Role:       SubnetNode,
  1877  									CIDRBlocks: []string{"2003:befa::1/64"},
  1878  									Name:       "node-subnet-3",
  1879  								},
  1880  								SecurityGroup: SecurityGroup{},
  1881  								RouteTable:    RouteTable{},
  1882  							},
  1883  						},
  1884  						APIServerLB: LoadBalancerSpec{
  1885  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1886  								Type: Public,
  1887  							},
  1888  						},
  1889  						NodeOutboundLB: &LoadBalancerSpec{
  1890  							Name: "cluster-test",
  1891  							FrontendIPs: []FrontendIP{{
  1892  								Name: "cluster-test-frontEnd",
  1893  								PublicIP: &PublicIPSpec{
  1894  									Name: "pip-cluster-test-node-outbound",
  1895  								},
  1896  							}},
  1897  							BackendPool: BackendPool{
  1898  								Name: "cluster-test-outboundBackendPool",
  1899  							},
  1900  							FrontendIPsCount: ptr.To[int32](1),
  1901  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1902  								SKU:                  SKUStandard,
  1903  								Type:                 Public,
  1904  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  1905  							},
  1906  						},
  1907  					},
  1908  				},
  1909  			},
  1910  		},
  1911  		{
  1912  			name: "no lb for private clusters",
  1913  			cluster: &AzureCluster{
  1914  				ObjectMeta: metav1.ObjectMeta{
  1915  					Name: "cluster-test",
  1916  				},
  1917  				Spec: AzureClusterSpec{
  1918  					NetworkSpec: NetworkSpec{
  1919  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Internal}},
  1920  					},
  1921  				},
  1922  			},
  1923  			output: &AzureCluster{
  1924  				ObjectMeta: metav1.ObjectMeta{
  1925  					Name: "cluster-test",
  1926  				},
  1927  				Spec: AzureClusterSpec{
  1928  					NetworkSpec: NetworkSpec{
  1929  						APIServerLB: LoadBalancerSpec{
  1930  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1931  								Type: Internal,
  1932  							},
  1933  						},
  1934  					},
  1935  				},
  1936  			},
  1937  		},
  1938  		{
  1939  			name: "NodeOutboundLB declared as input with non-default IdleTimeoutInMinutes, FrontendIPsCount, BackendPool values",
  1940  			cluster: &AzureCluster{
  1941  				ObjectMeta: metav1.ObjectMeta{
  1942  					Name: "cluster-test",
  1943  				},
  1944  				Spec: AzureClusterSpec{
  1945  					NetworkSpec: NetworkSpec{
  1946  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  1947  						NodeOutboundLB: &LoadBalancerSpec{
  1948  							FrontendIPsCount: ptr.To[int32](2),
  1949  							BackendPool: BackendPool{
  1950  								Name: "custom-backend-pool",
  1951  							},
  1952  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1953  								IdleTimeoutInMinutes: ptr.To[int32](15),
  1954  							},
  1955  						},
  1956  					},
  1957  				},
  1958  			},
  1959  			output: &AzureCluster{
  1960  				ObjectMeta: metav1.ObjectMeta{
  1961  					Name: "cluster-test",
  1962  				},
  1963  				Spec: AzureClusterSpec{
  1964  					NetworkSpec: NetworkSpec{
  1965  						APIServerLB: LoadBalancerSpec{
  1966  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1967  								Type: Public,
  1968  							},
  1969  						},
  1970  						NodeOutboundLB: &LoadBalancerSpec{
  1971  							FrontendIPs: []FrontendIP{
  1972  								{
  1973  									Name: "cluster-test-frontEnd-1",
  1974  									PublicIP: &PublicIPSpec{
  1975  										Name: "pip-cluster-test-node-outbound-1",
  1976  									},
  1977  								},
  1978  								{
  1979  									Name: "cluster-test-frontEnd-2",
  1980  									PublicIP: &PublicIPSpec{
  1981  										Name: "pip-cluster-test-node-outbound-2",
  1982  									},
  1983  								},
  1984  							},
  1985  							BackendPool: BackendPool{
  1986  								Name: "custom-backend-pool",
  1987  							},
  1988  							FrontendIPsCount: ptr.To[int32](2), // we expect the original value to be respected here
  1989  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  1990  								SKU:                  SKUStandard,
  1991  								Type:                 Public,
  1992  								IdleTimeoutInMinutes: ptr.To[int32](15), // we expect the original value to be respected here
  1993  							},
  1994  							Name: "cluster-test",
  1995  						},
  1996  					},
  1997  				},
  1998  			},
  1999  		},
  2000  		{
  2001  			name: "ensure that existing lb names are not overwritten",
  2002  			cluster: &AzureCluster{
  2003  				ObjectMeta: metav1.ObjectMeta{
  2004  					Name: "cluster-test",
  2005  				},
  2006  				Spec: AzureClusterSpec{
  2007  					NetworkSpec: NetworkSpec{
  2008  						APIServerLB: LoadBalancerSpec{
  2009  							Name: "user-defined-name",
  2010  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2011  								Type: Public,
  2012  							},
  2013  						},
  2014  						Subnets: Subnets{
  2015  							{
  2016  								SubnetClassSpec: SubnetClassSpec{
  2017  									Role: SubnetControlPlane,
  2018  									Name: "control-plane-subnet",
  2019  								},
  2020  								SecurityGroup: SecurityGroup{},
  2021  								RouteTable:    RouteTable{},
  2022  							},
  2023  							{
  2024  								SubnetClassSpec: SubnetClassSpec{
  2025  									Role: SubnetNode,
  2026  									Name: "node-subnet",
  2027  								},
  2028  								SecurityGroup: SecurityGroup{},
  2029  								RouteTable:    RouteTable{},
  2030  							},
  2031  						},
  2032  						ControlPlaneOutboundLB: &LoadBalancerSpec{
  2033  							Name: "user-defined-name",
  2034  						},
  2035  						NodeOutboundLB: &LoadBalancerSpec{
  2036  							Name: "user-defined-name",
  2037  						},
  2038  					},
  2039  				},
  2040  			},
  2041  			output: &AzureCluster{
  2042  				ObjectMeta: metav1.ObjectMeta{
  2043  					Name: "cluster-test",
  2044  				},
  2045  				Spec: AzureClusterSpec{
  2046  					NetworkSpec: NetworkSpec{
  2047  						Subnets: Subnets{
  2048  							{
  2049  								SubnetClassSpec: SubnetClassSpec{
  2050  									Role: SubnetControlPlane,
  2051  									Name: "control-plane-subnet",
  2052  								},
  2053  								SecurityGroup: SecurityGroup{},
  2054  								RouteTable:    RouteTable{},
  2055  							},
  2056  							{
  2057  								SubnetClassSpec: SubnetClassSpec{
  2058  									Role: SubnetNode,
  2059  									Name: "node-subnet",
  2060  								},
  2061  								SecurityGroup: SecurityGroup{},
  2062  								RouteTable:    RouteTable{},
  2063  							},
  2064  						},
  2065  						APIServerLB: LoadBalancerSpec{
  2066  							Name: "user-defined-name",
  2067  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2068  								Type: Public,
  2069  							},
  2070  						},
  2071  						NodeOutboundLB: &LoadBalancerSpec{
  2072  							Name: "user-defined-name",
  2073  							FrontendIPs: []FrontendIP{{
  2074  								Name: "user-defined-name-frontEnd",
  2075  								PublicIP: &PublicIPSpec{
  2076  									Name: "pip-cluster-test-node-outbound",
  2077  								},
  2078  							}},
  2079  							BackendPool: BackendPool{
  2080  								Name: "user-defined-name-outboundBackendPool",
  2081  							},
  2082  							FrontendIPsCount: ptr.To[int32](1),
  2083  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2084  								SKU:                  SKUStandard,
  2085  								Type:                 Public,
  2086  								IdleTimeoutInMinutes: ptr.To[int32](DefaultOutboundRuleIdleTimeoutInMinutes),
  2087  							},
  2088  						},
  2089  						ControlPlaneOutboundLB: &LoadBalancerSpec{
  2090  							Name: "user-defined-name",
  2091  						},
  2092  					},
  2093  				},
  2094  			},
  2095  		},
  2096  	}
  2097  
  2098  	for _, c := range cases {
  2099  		tc := c
  2100  		t.Run(tc.name, func(t *testing.T) {
  2101  			t.Parallel()
  2102  			tc.cluster.SetNodeOutboundLBDefaults()
  2103  			if !reflect.DeepEqual(tc.cluster, tc.output) {
  2104  				expected, _ := json.MarshalIndent(tc.output, "", "\t")
  2105  				actual, _ := json.MarshalIndent(tc.cluster, "", "\t")
  2106  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  2107  			}
  2108  		})
  2109  	}
  2110  }
  2111  
  2112  func TestControlPlaneOutboundLBDefaults(t *testing.T) {
  2113  	cases := []struct {
  2114  		name    string
  2115  		cluster *AzureCluster
  2116  		output  *AzureCluster
  2117  	}{
  2118  		{
  2119  			name: "no cp lb for public clusters",
  2120  			cluster: &AzureCluster{
  2121  				ObjectMeta: metav1.ObjectMeta{
  2122  					Name: "cluster-test",
  2123  				},
  2124  				Spec: AzureClusterSpec{
  2125  					NetworkSpec: NetworkSpec{
  2126  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Public}},
  2127  					},
  2128  				},
  2129  			},
  2130  			output: &AzureCluster{
  2131  				ObjectMeta: metav1.ObjectMeta{
  2132  					Name: "cluster-test",
  2133  				},
  2134  				Spec: AzureClusterSpec{
  2135  					NetworkSpec: NetworkSpec{
  2136  						APIServerLB: LoadBalancerSpec{
  2137  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2138  								Type: Public,
  2139  							},
  2140  						},
  2141  					},
  2142  				},
  2143  			},
  2144  		},
  2145  		{
  2146  			name: "no cp lb for private clusters",
  2147  			cluster: &AzureCluster{
  2148  				ObjectMeta: metav1.ObjectMeta{
  2149  					Name: "cluster-test",
  2150  				},
  2151  				Spec: AzureClusterSpec{
  2152  					NetworkSpec: NetworkSpec{
  2153  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Internal}},
  2154  					},
  2155  				},
  2156  			},
  2157  			output: &AzureCluster{
  2158  				ObjectMeta: metav1.ObjectMeta{
  2159  					Name: "cluster-test",
  2160  				},
  2161  				Spec: AzureClusterSpec{
  2162  					NetworkSpec: NetworkSpec{
  2163  						APIServerLB: LoadBalancerSpec{
  2164  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2165  								Type: Internal,
  2166  							},
  2167  						},
  2168  					},
  2169  				},
  2170  			},
  2171  		},
  2172  		{
  2173  			name: "frontendIPsCount > 1",
  2174  			cluster: &AzureCluster{
  2175  				ObjectMeta: metav1.ObjectMeta{
  2176  					Name: "cluster-test",
  2177  				},
  2178  				Spec: AzureClusterSpec{
  2179  					NetworkSpec: NetworkSpec{
  2180  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Internal}},
  2181  						ControlPlaneOutboundLB: &LoadBalancerSpec{
  2182  							FrontendIPsCount: ptr.To[int32](2),
  2183  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2184  								IdleTimeoutInMinutes: ptr.To[int32](15),
  2185  							},
  2186  						},
  2187  					},
  2188  				},
  2189  			},
  2190  			output: &AzureCluster{
  2191  				ObjectMeta: metav1.ObjectMeta{
  2192  					Name: "cluster-test",
  2193  				},
  2194  				Spec: AzureClusterSpec{
  2195  					NetworkSpec: NetworkSpec{
  2196  						APIServerLB: LoadBalancerSpec{
  2197  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2198  								Type: Internal,
  2199  							},
  2200  						},
  2201  						ControlPlaneOutboundLB: &LoadBalancerSpec{
  2202  							Name: "cluster-test-outbound-lb",
  2203  							BackendPool: BackendPool{
  2204  								Name: "cluster-test-outbound-lb-outboundBackendPool",
  2205  							},
  2206  							FrontendIPs: []FrontendIP{
  2207  								{
  2208  									Name: "cluster-test-outbound-lb-frontEnd-1",
  2209  									PublicIP: &PublicIPSpec{
  2210  										Name: "pip-cluster-test-controlplane-outbound-1",
  2211  									},
  2212  								},
  2213  								{
  2214  									Name: "cluster-test-outbound-lb-frontEnd-2",
  2215  									PublicIP: &PublicIPSpec{
  2216  										Name: "pip-cluster-test-controlplane-outbound-2",
  2217  									},
  2218  								},
  2219  							},
  2220  							FrontendIPsCount: ptr.To[int32](2),
  2221  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2222  								SKU:                  SKUStandard,
  2223  								Type:                 Public,
  2224  								IdleTimeoutInMinutes: ptr.To[int32](15),
  2225  							},
  2226  						},
  2227  					},
  2228  				},
  2229  			},
  2230  		},
  2231  		{
  2232  			name: "custom outbound lb backend pool",
  2233  			cluster: &AzureCluster{
  2234  				ObjectMeta: metav1.ObjectMeta{
  2235  					Name: "cluster-test",
  2236  				},
  2237  				Spec: AzureClusterSpec{
  2238  					NetworkSpec: NetworkSpec{
  2239  						APIServerLB: LoadBalancerSpec{LoadBalancerClassSpec: LoadBalancerClassSpec{Type: Internal}},
  2240  						ControlPlaneOutboundLB: &LoadBalancerSpec{
  2241  							BackendPool: BackendPool{
  2242  								Name: "custom-outbound-lb",
  2243  							},
  2244  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2245  								IdleTimeoutInMinutes: ptr.To[int32](15),
  2246  							},
  2247  						},
  2248  					},
  2249  				},
  2250  			},
  2251  			output: &AzureCluster{
  2252  				ObjectMeta: metav1.ObjectMeta{
  2253  					Name: "cluster-test",
  2254  				},
  2255  				Spec: AzureClusterSpec{
  2256  					NetworkSpec: NetworkSpec{
  2257  						APIServerLB: LoadBalancerSpec{
  2258  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2259  								Type: Internal,
  2260  							},
  2261  						},
  2262  						ControlPlaneOutboundLB: &LoadBalancerSpec{
  2263  							Name: "cluster-test-outbound-lb",
  2264  							BackendPool: BackendPool{
  2265  								Name: "custom-outbound-lb",
  2266  							},
  2267  							FrontendIPs: []FrontendIP{
  2268  								{
  2269  									Name: "cluster-test-outbound-lb-frontEnd",
  2270  									PublicIP: &PublicIPSpec{
  2271  										Name: "pip-cluster-test-controlplane-outbound",
  2272  									},
  2273  								},
  2274  							},
  2275  							FrontendIPsCount: ptr.To[int32](1),
  2276  							LoadBalancerClassSpec: LoadBalancerClassSpec{
  2277  								SKU:                  SKUStandard,
  2278  								Type:                 Public,
  2279  								IdleTimeoutInMinutes: ptr.To[int32](15),
  2280  							},
  2281  						},
  2282  					},
  2283  				},
  2284  			},
  2285  		},
  2286  	}
  2287  
  2288  	for _, c := range cases {
  2289  		tc := c
  2290  		t.Run(tc.name, func(t *testing.T) {
  2291  			t.Parallel()
  2292  			tc.cluster.SetControlPlaneOutboundLBDefaults()
  2293  			if !reflect.DeepEqual(tc.cluster, tc.output) {
  2294  				expected, _ := json.MarshalIndent(tc.output, "", "\t")
  2295  				actual, _ := json.MarshalIndent(tc.cluster, "", "\t")
  2296  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  2297  			}
  2298  		})
  2299  	}
  2300  }
  2301  
  2302  func TestBastionDefault(t *testing.T) {
  2303  	cases := map[string]struct {
  2304  		cluster *AzureCluster
  2305  		output  *AzureCluster
  2306  	}{
  2307  		"no bastion set": {
  2308  			cluster: &AzureCluster{
  2309  				ObjectMeta: metav1.ObjectMeta{
  2310  					Name: "foo",
  2311  				},
  2312  				Spec: AzureClusterSpec{},
  2313  			},
  2314  			output: &AzureCluster{
  2315  				ObjectMeta: metav1.ObjectMeta{
  2316  					Name: "foo",
  2317  				},
  2318  				Spec: AzureClusterSpec{},
  2319  			},
  2320  		},
  2321  		"azure bastion enabled with no settings": {
  2322  			cluster: &AzureCluster{
  2323  				ObjectMeta: metav1.ObjectMeta{
  2324  					Name: "foo",
  2325  				},
  2326  				Spec: AzureClusterSpec{
  2327  					BastionSpec: BastionSpec{
  2328  						AzureBastion: &AzureBastion{},
  2329  					},
  2330  				},
  2331  			},
  2332  			output: &AzureCluster{
  2333  				ObjectMeta: metav1.ObjectMeta{
  2334  					Name: "foo",
  2335  				},
  2336  				Spec: AzureClusterSpec{
  2337  					BastionSpec: BastionSpec{
  2338  						AzureBastion: &AzureBastion{
  2339  							Name: "foo-azure-bastion",
  2340  							Subnet: SubnetSpec{
  2341  
  2342  								SubnetClassSpec: SubnetClassSpec{
  2343  									CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
  2344  									Role:       DefaultAzureBastionSubnetRole,
  2345  									Name:       "AzureBastionSubnet",
  2346  								},
  2347  							},
  2348  							PublicIP: PublicIPSpec{
  2349  								Name: "foo-azure-bastion-pip",
  2350  							},
  2351  						},
  2352  					},
  2353  				},
  2354  			},
  2355  		},
  2356  		"azure bastion enabled with name set": {
  2357  			cluster: &AzureCluster{
  2358  				ObjectMeta: metav1.ObjectMeta{
  2359  					Name: "foo",
  2360  				},
  2361  				Spec: AzureClusterSpec{
  2362  					BastionSpec: BastionSpec{
  2363  						AzureBastion: &AzureBastion{
  2364  							Name: "my-fancy-name",
  2365  						},
  2366  					},
  2367  				},
  2368  			},
  2369  			output: &AzureCluster{
  2370  				ObjectMeta: metav1.ObjectMeta{
  2371  					Name: "foo",
  2372  				},
  2373  				Spec: AzureClusterSpec{
  2374  					BastionSpec: BastionSpec{
  2375  						AzureBastion: &AzureBastion{
  2376  							Name: "my-fancy-name",
  2377  							Subnet: SubnetSpec{
  2378  
  2379  								SubnetClassSpec: SubnetClassSpec{
  2380  									CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
  2381  									Role:       DefaultAzureBastionSubnetRole,
  2382  									Name:       "AzureBastionSubnet",
  2383  								},
  2384  							},
  2385  							PublicIP: PublicIPSpec{
  2386  								Name: "foo-azure-bastion-pip",
  2387  							},
  2388  						},
  2389  					},
  2390  				},
  2391  			},
  2392  		},
  2393  		"azure bastion enabled with subnet partially set": {
  2394  			cluster: &AzureCluster{
  2395  				ObjectMeta: metav1.ObjectMeta{
  2396  					Name: "foo",
  2397  				},
  2398  				Spec: AzureClusterSpec{
  2399  					BastionSpec: BastionSpec{
  2400  						AzureBastion: &AzureBastion{
  2401  							Subnet: SubnetSpec{},
  2402  						},
  2403  					},
  2404  				},
  2405  			},
  2406  			output: &AzureCluster{
  2407  				ObjectMeta: metav1.ObjectMeta{
  2408  					Name: "foo",
  2409  				},
  2410  				Spec: AzureClusterSpec{
  2411  					BastionSpec: BastionSpec{
  2412  						AzureBastion: &AzureBastion{
  2413  							Name: "foo-azure-bastion",
  2414  							Subnet: SubnetSpec{
  2415  								SubnetClassSpec: SubnetClassSpec{
  2416  									CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
  2417  									Role:       DefaultAzureBastionSubnetRole,
  2418  									Name:       "AzureBastionSubnet",
  2419  								},
  2420  							},
  2421  							PublicIP: PublicIPSpec{
  2422  								Name: "foo-azure-bastion-pip",
  2423  							},
  2424  						},
  2425  					},
  2426  				},
  2427  			},
  2428  		},
  2429  		"azure bastion enabled with subnet fully set": {
  2430  			cluster: &AzureCluster{
  2431  				ObjectMeta: metav1.ObjectMeta{
  2432  					Name: "foo",
  2433  				},
  2434  				Spec: AzureClusterSpec{
  2435  					BastionSpec: BastionSpec{
  2436  						AzureBastion: &AzureBastion{
  2437  							Subnet: SubnetSpec{
  2438  								SubnetClassSpec: SubnetClassSpec{
  2439  									CIDRBlocks: []string{"10.10.0.0/16"},
  2440  									Name:       "my-superfancy-name",
  2441  								},
  2442  							},
  2443  						},
  2444  					},
  2445  				},
  2446  			},
  2447  			output: &AzureCluster{
  2448  				ObjectMeta: metav1.ObjectMeta{
  2449  					Name: "foo",
  2450  				},
  2451  				Spec: AzureClusterSpec{
  2452  					BastionSpec: BastionSpec{
  2453  						AzureBastion: &AzureBastion{
  2454  							Name: "foo-azure-bastion",
  2455  							Subnet: SubnetSpec{
  2456  								SubnetClassSpec: SubnetClassSpec{
  2457  									CIDRBlocks: []string{"10.10.0.0/16"},
  2458  									Role:       DefaultAzureBastionSubnetRole,
  2459  									Name:       "my-superfancy-name",
  2460  								},
  2461  							},
  2462  							PublicIP: PublicIPSpec{
  2463  								Name: "foo-azure-bastion-pip",
  2464  							},
  2465  						},
  2466  					},
  2467  				},
  2468  			},
  2469  		},
  2470  		"azure bastion enabled with public IP name set": {
  2471  			cluster: &AzureCluster{
  2472  				ObjectMeta: metav1.ObjectMeta{
  2473  					Name: "foo",
  2474  				},
  2475  				Spec: AzureClusterSpec{
  2476  					BastionSpec: BastionSpec{
  2477  						AzureBastion: &AzureBastion{
  2478  							PublicIP: PublicIPSpec{
  2479  								Name: "my-ultrafancy-pip-name",
  2480  							},
  2481  						},
  2482  					},
  2483  				},
  2484  			},
  2485  			output: &AzureCluster{
  2486  				ObjectMeta: metav1.ObjectMeta{
  2487  					Name: "foo",
  2488  				},
  2489  				Spec: AzureClusterSpec{
  2490  					BastionSpec: BastionSpec{
  2491  						AzureBastion: &AzureBastion{
  2492  							Name: "foo-azure-bastion",
  2493  							Subnet: SubnetSpec{
  2494  								SubnetClassSpec: SubnetClassSpec{
  2495  									CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
  2496  									Role:       DefaultAzureBastionSubnetRole,
  2497  									Name:       "AzureBastionSubnet",
  2498  								},
  2499  							},
  2500  							PublicIP: PublicIPSpec{
  2501  								Name: "my-ultrafancy-pip-name",
  2502  							},
  2503  						},
  2504  					},
  2505  				},
  2506  			},
  2507  		},
  2508  	}
  2509  
  2510  	for name := range cases {
  2511  		c := cases[name]
  2512  		t.Run(name, func(t *testing.T) {
  2513  			t.Parallel()
  2514  			c.cluster.setBastionDefaults()
  2515  			if !reflect.DeepEqual(c.cluster, c.output) {
  2516  				expected, _ := json.MarshalIndent(c.output, "", "\t")
  2517  				actual, _ := json.MarshalIndent(c.cluster, "", "\t")
  2518  				t.Errorf("Expected %s, got %s", string(expected), string(actual))
  2519  			}
  2520  		})
  2521  	}
  2522  }