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 }