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

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1beta1
    18  
    19  import (
    20  	"testing"
    21  
    22  	. "github.com/onsi/gomega"
    23  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    24  )
    25  
    26  func TestAzureCluster_ValidateCreate(t *testing.T) {
    27  	tests := []struct {
    28  		name    string
    29  		cluster *AzureCluster
    30  		wantErr bool
    31  	}{
    32  		{
    33  			name:    "azurecluster with pre-existing vnet - valid spec",
    34  			cluster: createValidCluster(),
    35  			wantErr: false,
    36  		},
    37  		{
    38  			name: "azurecluster with pre-existing control plane endpoint - valid spec",
    39  			cluster: func() *AzureCluster {
    40  				cluster := createValidCluster()
    41  				cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
    42  					Host: "apiserver.example.com",
    43  					Port: 8443,
    44  				}
    45  				return cluster
    46  			}(),
    47  			wantErr: false,
    48  		},
    49  		{
    50  			name: "azurecluster without pre-existing vnet - valid spec",
    51  			cluster: func() *AzureCluster {
    52  				cluster := createValidCluster()
    53  				cluster.Spec.NetworkSpec.Vnet.ResourceGroup = ""
    54  				return cluster
    55  			}(),
    56  			wantErr: false,
    57  		},
    58  		{
    59  			name: "azurecluster with pre-existing vnet - lack control plane subnet",
    60  			cluster: func() *AzureCluster {
    61  				cluster := createValidCluster()
    62  				cluster.Spec.NetworkSpec.Subnets = cluster.Spec.NetworkSpec.Subnets[1:]
    63  				return cluster
    64  			}(),
    65  			wantErr: true,
    66  		},
    67  		{
    68  			name: "azurecluster with pre-existing vnet - lack node subnet",
    69  			cluster: func() *AzureCluster {
    70  				cluster := createValidCluster()
    71  				cluster.Spec.NetworkSpec.Subnets = cluster.Spec.NetworkSpec.Subnets[:1]
    72  				return cluster
    73  			}(),
    74  			wantErr: true,
    75  		},
    76  		{
    77  			name: "azurecluster with pre-existing vnet - invalid resourcegroup name",
    78  			cluster: func() *AzureCluster {
    79  				cluster := createValidCluster()
    80  				cluster.Spec.NetworkSpec.Vnet.ResourceGroup = "invalid-rg-name###"
    81  				return cluster
    82  			}(),
    83  			wantErr: true,
    84  		},
    85  		{
    86  			name: "azurecluster with pre-existing vnet - invalid subnet name",
    87  			cluster: func() *AzureCluster {
    88  				cluster := createValidCluster()
    89  				cluster.Spec.NetworkSpec.Subnets = append(cluster.Spec.NetworkSpec.Subnets,
    90  					SubnetSpec{SubnetClassSpec: SubnetClassSpec{Name: "invalid-subnet-name###", Role: "random-role"}})
    91  				return cluster
    92  			}(),
    93  			wantErr: true,
    94  		},
    95  		{
    96  			name: "azurecluster with ExtendedLocation and false EdgeZone feature flag",
    97  			cluster: func() *AzureCluster {
    98  				cluster := createValidCluster()
    99  				cluster.Spec.ExtendedLocation = &ExtendedLocationSpec{
   100  					Name: "rr4",
   101  					Type: "EdgeZone",
   102  				}
   103  				return cluster
   104  			}(),
   105  			wantErr: true,
   106  		},
   107  	}
   108  	for _, tc := range tests {
   109  		t.Run(tc.name, func(t *testing.T) {
   110  			g := NewWithT(t)
   111  			_, err := tc.cluster.ValidateCreate()
   112  			if tc.wantErr {
   113  				g.Expect(err).To(HaveOccurred())
   114  			} else {
   115  				g.Expect(err).NotTo(HaveOccurred())
   116  			}
   117  		})
   118  	}
   119  }
   120  
   121  func TestAzureCluster_ValidateUpdate(t *testing.T) {
   122  	tests := []struct {
   123  		name       string
   124  		oldCluster *AzureCluster
   125  		cluster    *AzureCluster
   126  		wantErr    bool
   127  	}{
   128  		{
   129  			name: "azurecluster with pre-existing control plane endpoint - valid spec",
   130  			oldCluster: func() *AzureCluster {
   131  				cluster := createValidCluster()
   132  				cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
   133  					Host: "apiserver.example.com",
   134  					Port: 8443,
   135  				}
   136  				return cluster
   137  			}(),
   138  			cluster: func() *AzureCluster {
   139  				cluster := createValidCluster()
   140  				cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
   141  					Host: "apiserver.example.io",
   142  					Port: 6443,
   143  				}
   144  				return cluster
   145  			}(),
   146  			wantErr: true,
   147  		},
   148  		{
   149  			name:       "azurecluster with no control plane endpoint - valid spec",
   150  			oldCluster: createValidCluster(),
   151  			cluster: func() *AzureCluster {
   152  				cluster := createValidCluster()
   153  				cluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
   154  					Host: "apiserver.example.com",
   155  					Port: 8443,
   156  				}
   157  				return cluster
   158  			}(),
   159  			wantErr: false,
   160  		},
   161  		{
   162  			name:       "azurecluster with pre-existing vnet - valid spec",
   163  			oldCluster: createValidCluster(),
   164  			cluster:    createValidCluster(),
   165  			wantErr:    false,
   166  		},
   167  		{
   168  			name: "azurecluster without pre-existing vnet - valid spec",
   169  			oldCluster: func() *AzureCluster {
   170  				cluster := createValidCluster()
   171  				cluster.Spec.NetworkSpec.Vnet.ResourceGroup = ""
   172  				return cluster
   173  			}(),
   174  			cluster: func() *AzureCluster {
   175  				cluster := createValidCluster()
   176  				cluster.Spec.NetworkSpec.Vnet.ResourceGroup = ""
   177  				return cluster
   178  			}(),
   179  			wantErr: false,
   180  		},
   181  		{
   182  			name:       "azurecluster with pre-existing vnet - lack control plane subnet",
   183  			oldCluster: createValidCluster(),
   184  			cluster: func() *AzureCluster {
   185  				cluster := createValidCluster()
   186  				cluster.Spec.NetworkSpec.Subnets = cluster.Spec.NetworkSpec.Subnets[1:]
   187  				return cluster
   188  			}(),
   189  			wantErr: true,
   190  		},
   191  		{
   192  			name:       "azurecluster with pre-existing vnet - lack node subnet",
   193  			oldCluster: createValidCluster(),
   194  			cluster: func() *AzureCluster {
   195  				cluster := createValidCluster()
   196  				cluster.Spec.NetworkSpec.Subnets = cluster.Spec.NetworkSpec.Subnets[:1]
   197  				return cluster
   198  			}(),
   199  			wantErr: true,
   200  		},
   201  		{
   202  			name:       "azurecluster with pre-existing vnet - invalid resourcegroup name",
   203  			oldCluster: createValidCluster(),
   204  			cluster: func() *AzureCluster {
   205  				cluster := createValidCluster()
   206  				cluster.Spec.NetworkSpec.Vnet.ResourceGroup = "invalid-name###"
   207  				return cluster
   208  			}(),
   209  			wantErr: true,
   210  		},
   211  		{
   212  			name:       "azurecluster with pre-existing vnet - invalid subnet name",
   213  			oldCluster: createValidCluster(),
   214  			cluster: func() *AzureCluster {
   215  				cluster := createValidCluster()
   216  				cluster.Spec.NetworkSpec.Subnets = append(cluster.Spec.NetworkSpec.Subnets,
   217  					SubnetSpec{SubnetClassSpec: SubnetClassSpec{Name: "invalid-name###", Role: "random-role"}})
   218  				return cluster
   219  			}(),
   220  			wantErr: true,
   221  		},
   222  		{
   223  			name: "azurecluster resource group is immutable",
   224  			oldCluster: &AzureCluster{
   225  				Spec: AzureClusterSpec{
   226  					ResourceGroup: "demoResourceGroup",
   227  				},
   228  			},
   229  			cluster: &AzureCluster{
   230  				Spec: AzureClusterSpec{
   231  					ResourceGroup: "demoResourceGroup-2",
   232  				},
   233  			},
   234  			wantErr: true,
   235  		},
   236  		{
   237  			name: "azurecluster subscription ID is immutable",
   238  			oldCluster: &AzureCluster{
   239  				Spec: AzureClusterSpec{
   240  					AzureClusterClassSpec: AzureClusterClassSpec{
   241  						SubscriptionID: "212ec1q8",
   242  					},
   243  				},
   244  			},
   245  			cluster: &AzureCluster{
   246  				Spec: AzureClusterSpec{
   247  					AzureClusterClassSpec: AzureClusterClassSpec{
   248  						SubscriptionID: "212ec1q9",
   249  					},
   250  				},
   251  			},
   252  			wantErr: true,
   253  		},
   254  		{
   255  			name: "azurecluster location is immutable",
   256  			oldCluster: &AzureCluster{
   257  				Spec: AzureClusterSpec{
   258  					AzureClusterClassSpec: AzureClusterClassSpec{
   259  						Location: "North Europe",
   260  					},
   261  				},
   262  			},
   263  			cluster: &AzureCluster{
   264  				Spec: AzureClusterSpec{
   265  					AzureClusterClassSpec: AzureClusterClassSpec{
   266  						Location: "West Europe",
   267  					},
   268  				},
   269  			},
   270  			wantErr: true,
   271  		},
   272  		{
   273  			name: "azurecluster azureEnvironment is immutable",
   274  			oldCluster: &AzureCluster{
   275  				Spec: AzureClusterSpec{
   276  					AzureClusterClassSpec: AzureClusterClassSpec{
   277  						AzureEnvironment: "AzureGermanCloud",
   278  					},
   279  				},
   280  			},
   281  			cluster: &AzureCluster{
   282  				Spec: AzureClusterSpec{
   283  					AzureClusterClassSpec: AzureClusterClassSpec{
   284  						AzureEnvironment: "AzureChinaCloud",
   285  					},
   286  				},
   287  			},
   288  			wantErr: true,
   289  		},
   290  		{
   291  			name:       "azurecluster azureEnvironment default mismatch",
   292  			oldCluster: createValidCluster(),
   293  			cluster: func() *AzureCluster {
   294  				cluster := createValidCluster()
   295  				cluster.Spec.AzureEnvironment = "AzurePublicCloud"
   296  				return cluster
   297  			}(),
   298  			wantErr: false,
   299  		},
   300  		{
   301  			name: "control plane outbound lb is immutable",
   302  			oldCluster: &AzureCluster{
   303  				Spec: AzureClusterSpec{
   304  					NetworkSpec: NetworkSpec{
   305  						ControlPlaneOutboundLB: &LoadBalancerSpec{Name: "cp-lb"},
   306  					},
   307  				},
   308  			},
   309  			cluster: &AzureCluster{
   310  				Spec: AzureClusterSpec{
   311  					NetworkSpec: NetworkSpec{
   312  						ControlPlaneOutboundLB: &LoadBalancerSpec{Name: "cp-lb-new"},
   313  					},
   314  				},
   315  			},
   316  			wantErr: true,
   317  		},
   318  		{
   319  			name: "natGateway name is immutable",
   320  			oldCluster: func() *AzureCluster {
   321  				cluster := createValidCluster()
   322  				cluster.Spec.NetworkSpec.Subnets[0].NatGateway.Name = "cluster-test-node-natgw-0"
   323  				return cluster
   324  			}(),
   325  			cluster: func() *AzureCluster {
   326  				cluster := createValidCluster()
   327  				cluster.Spec.NetworkSpec.Subnets[0].NatGateway.Name = "cluster-test-node-natgw-1"
   328  				return cluster
   329  			}(),
   330  			wantErr: true,
   331  		},
   332  		{
   333  			name:       "natGateway name can be empty before AzureCluster is updated",
   334  			oldCluster: createValidCluster(),
   335  			cluster: func() *AzureCluster {
   336  				cluster := createValidCluster()
   337  				cluster.Spec.NetworkSpec.Subnets[0].NatGateway.Name = "cluster-test-node-natgw"
   338  				return cluster
   339  			}(),
   340  			wantErr: false,
   341  		},
   342  	}
   343  	for _, tc := range tests {
   344  		tc := tc
   345  		t.Run(tc.name, func(t *testing.T) {
   346  			t.Parallel()
   347  			g := NewWithT(t)
   348  			_, err := tc.cluster.ValidateUpdate(tc.oldCluster)
   349  			if tc.wantErr {
   350  				g.Expect(err).To(HaveOccurred())
   351  			} else {
   352  				g.Expect(err).NotTo(HaveOccurred())
   353  			}
   354  		})
   355  	}
   356  }