sigs.k8s.io/cluster-api@v1.7.1/internal/webhooks/test/clusterclass_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 test 18 19 import ( 20 "testing" 21 22 . "github.com/onsi/gomega" 23 corev1 "k8s.io/api/core/v1" 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 utilfeature "k8s.io/component-base/featuregate/testing" 26 "sigs.k8s.io/controller-runtime/pkg/client" 27 28 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 29 "sigs.k8s.io/cluster-api/feature" 30 "sigs.k8s.io/cluster-api/internal/test/builder" 31 ) 32 33 const ( 34 workerClassName1 = "linux-worker" 35 workerClassName2 = "windows-worker" 36 37 infrastructureClusterTemplateName1 = "infrastructureclustertemplate1" 38 infrastructureClusterTemplateName2 = "infracluster2" 39 40 controlPlaneTemplateName1 = "controlplanetemplate1" 41 42 infrastructureMachineTemplateName1 = "infrastructuremachinetemplate1" 43 infrastructureMachineTemplateName2 = "infrastructuremachinetemplate2" 44 45 clusterClassName1 = "clusterclass1" 46 clusterClassName2 = "clusterclass2" 47 clusterClassName3 = "clusterclass3" 48 clusterClassName4 = "clusterclass4" 49 clusterClassName5 = "clusterclass5" 50 51 clusterName1 = "cluster1" 52 clusterName2 = "cluster2" 53 clusterName3 = "cluster3" 54 clusterName4 = "cluster4" 55 56 bootstrapTemplateName1 = "bootstraptemplate1" 57 ) 58 59 // TestClusterClassWebhook_Succeed_Create tests the correct creation behaviour for a valid ClusterClass. 60 func TestClusterClassWebhook_Succeed_Create(t *testing.T) { 61 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 62 g := NewWithT(t) 63 64 ns, err := env.CreateNamespace(ctx, "test-topology-clusterclass-webhook") 65 g.Expect(err).ToNot(HaveOccurred()) 66 // Create the objects needed for the integration test: 67 // - a ClusterClass with all the related templates 68 clusterClass1 := builder.ClusterClass(ns.Name, clusterClassName1). 69 WithInfrastructureClusterTemplate( 70 builder.InfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 71 WithControlPlaneTemplate( 72 builder.ControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 73 Build()). 74 WithWorkerMachineDeploymentClasses( 75 *builder.MachineDeploymentClass("md1"). 76 WithInfrastructureTemplate( 77 builder.InfrastructureMachineTemplate(ns.Name, "OLD_INFRA").Build()). 78 WithBootstrapTemplate( 79 builder.BootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 80 Build(), 81 *builder.MachineDeploymentClass("md2"). 82 WithInfrastructureTemplate( 83 builder.InfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 84 WithBootstrapTemplate( 85 builder.BootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 86 Build()). 87 Build() 88 89 // Clean up the resources once the test is finished. 90 t.Cleanup(func() { 91 g.Expect(env.Cleanup(ctx, clusterClass1)).To(Succeed()) 92 g.Expect(env.Cleanup(ctx, ns)).To(Succeed()) 93 }) 94 95 // Create the ClusterClass in the API server. Expect no error. 96 g.Expect(env.Create(ctx, clusterClass1)).To(Succeed()) 97 } 98 99 // TestClusterClassWebhook_Fail_Create tests the correct creation behaviour for an invalid ClusterClass with missing references. 100 // In this case creation of the ClusterClass should be blocked by the webhook. 101 func TestClusterClassWebhook_Fail_Create(t *testing.T) { 102 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 103 g := NewWithT(t) 104 105 ns, err := env.CreateNamespace(ctx, "test-topology-clusterclass-webhook") 106 g.Expect(err).ToNot(HaveOccurred()) 107 108 cleanup, err := createTemplates(ns) 109 g.Expect(err).ToNot(HaveOccurred()) 110 111 // Create the objects needed for the integration test: 112 // - a ClusterClass with all the related templates 113 clusterClass1 := builder.ClusterClass(ns.Name, clusterClassName1). 114 WithInfrastructureClusterTemplate( 115 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 116 // ControlPlaneTemplate not defined in this ClusterClass making it invalid. 117 WithWorkerMachineDeploymentClasses( 118 *builder.MachineDeploymentClass("md1"). 119 WithInfrastructureTemplate( 120 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 121 WithBootstrapTemplate( 122 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 123 Build(), 124 *builder.MachineDeploymentClass("md2"). 125 WithInfrastructureTemplate( 126 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 127 WithBootstrapTemplate( 128 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 129 Build()). 130 Build() 131 132 // Clean up the resources once the test is finished. 133 t.Cleanup(func() { 134 g.Expect(cleanup()).To(Succeed()) 135 g.Expect(env.Cleanup(ctx, clusterClass1)).To(Succeed()) 136 g.Expect(env.Cleanup(ctx, ns)).To(Succeed()) 137 }) 138 139 // Create the ClusterClass in the API server. Expect this to fail because of missing templates. 140 g.Expect(env.Create(ctx, clusterClass1)).NotTo(Succeed()) 141 } 142 143 // TestClusterWebhook_Succeed_Update tests the correct update behaviour for a ClusterClass with references in existing Clusters. 144 // In this case deletion of the ClusterClass should succeed by the webhook as the update is compatible with the existing Clusters using the ClusterClass. 145 func TestClusterWebhook_Succeed_Update(t *testing.T) { 146 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 147 g := NewWithT(t) 148 149 ns, err := env.CreateNamespace(ctx, "test-topology-clusterclass-webhook") 150 g.Expect(err).ToNot(HaveOccurred()) 151 152 cleanup, err := createTemplates(ns) 153 g.Expect(err).ToNot(HaveOccurred()) 154 155 // Create the objects needed for the integration test: 156 // - a ClusterClass with all the related templates 157 // - a Cluster using the above ClusterClass 158 clusterClass := builder.ClusterClass(ns.Name, clusterClassName1). 159 WithInfrastructureClusterTemplate( 160 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 161 WithControlPlaneTemplate( 162 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 163 Build()). 164 WithWorkerMachineDeploymentClasses( 165 *builder.MachineDeploymentClass("NOT_USED"). 166 WithInfrastructureTemplate( 167 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 168 WithBootstrapTemplate( 169 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 170 Build(), 171 *builder.MachineDeploymentClass(workerClassName1). 172 WithInfrastructureTemplate( 173 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 174 WithBootstrapTemplate( 175 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 176 Build()). 177 Build() 178 179 cluster := builder.Cluster(ns.Name, clusterName1). 180 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 181 WithTopology( 182 builder.ClusterTopology(). 183 WithVersion("v1.20.1"). 184 WithClass(clusterClassName1). 185 WithMachineDeployment( 186 builder.MachineDeploymentTopology("md1"). 187 WithClass(workerClassName1). 188 Build(), 189 ). 190 Build()). 191 Build() 192 193 // Create the ClusterClass in the API server. 194 g.Expect(env.CreateAndWait(ctx, clusterClass)).To(Succeed()) 195 196 // Create a Cluster using the ClusterClass. 197 g.Expect(env.CreateAndWait(ctx, cluster)).To(Succeed()) 198 // Clean up the resources once the test is finished. 199 t.Cleanup(func() { 200 g.Expect(cleanup()).To(Succeed()) 201 g.Expect(env.CleanupAndWait(ctx, cluster)).To(Succeed()) 202 g.Expect(env.Cleanup(ctx, clusterClass)).To(Succeed()) 203 g.Expect(env.Cleanup(ctx, ns)).To(Succeed()) 204 }) 205 206 // Retrieve the ClusterClass from the API server. 207 actualClusterClass := &clusterv1.ClusterClass{} 208 g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: clusterClassName1}, actualClusterClass)).To(Succeed()) 209 210 // Remove MachineDeploymentClass , which is not used by cluster1 from the clusterClass 211 actualClusterClass.Spec.Workers.MachineDeployments = []clusterv1.MachineDeploymentClass{ 212 actualClusterClass.Spec.Workers.MachineDeployments[1], 213 } 214 // Change the template used in the ClusterClass to a compatible alternative (Only name is changed). 215 actualClusterClass.Spec.Infrastructure.Ref.Name = infrastructureClusterTemplateName2 216 217 // Attempt to update the ClusterClass with the above changes. 218 // Expect no error here as the updates are compatible with the current Clusters using the ClusterClass. 219 g.Expect(env.Update(ctx, actualClusterClass)).To(Succeed()) 220 } 221 222 // TestClusterWebhook_Fail_Update tests the correct update behaviour for a ClusterClass with references in existing Clusters. 223 // In this case deletion of the ClusterClass should be blocked by the webhook as the update is incompatible with the existing Clusters using the ClusterClass. 224 func TestClusterWebhook_Fail_Update(t *testing.T) { 225 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 226 g := NewWithT(t) 227 228 ns, err := env.CreateNamespace(ctx, "test-topology-clusterclass-webhook") 229 g.Expect(err).ToNot(HaveOccurred()) 230 231 cleanup, err := createTemplates(ns) 232 g.Expect(err).ToNot(HaveOccurred()) 233 234 // Create the objects needed for the integration test: 235 // - a ClusterClass with all the related templates 236 // - a Cluster using the above ClusterClass 237 clusterClass := builder.ClusterClass(ns.Name, clusterClassName1). 238 WithInfrastructureClusterTemplate( 239 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 240 WithControlPlaneTemplate( 241 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 242 Build()). 243 WithWorkerMachineDeploymentClasses( 244 *builder.MachineDeploymentClass("NOT_USED"). 245 WithInfrastructureTemplate( 246 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 247 WithBootstrapTemplate( 248 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 249 Build(), 250 *builder.MachineDeploymentClass("IN_USE"). 251 WithInfrastructureTemplate( 252 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 253 WithBootstrapTemplate( 254 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 255 Build()). 256 Build() 257 258 cluster := builder.Cluster(ns.Name, clusterName1). 259 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 260 WithTopology( 261 builder.ClusterTopology(). 262 WithVersion("v1.20.1"). 263 WithClass(clusterClassName1). 264 WithMachineDeployment( 265 builder.MachineDeploymentTopology("md1"). 266 WithClass("IN_USE"). 267 Build(), 268 ). 269 Build()). 270 Build() 271 272 // Create the ClusterClass in the API server. 273 g.Expect(env.CreateAndWait(ctx, clusterClass)).To(Succeed()) 274 275 // Create a cluster using the ClusterClass. 276 g.Expect(env.CreateAndWait(ctx, cluster)).To(Succeed()) 277 278 // Clean up the resources once the test is finished. 279 t.Cleanup(func() { 280 g.Expect(cleanup()).To(Succeed()) 281 g.Expect(env.CleanupAndWait(ctx, cluster)).To(Succeed()) 282 g.Expect(env.Cleanup(ctx, clusterClass)).To(Succeed()) 283 g.Expect(env.Cleanup(ctx, ns)).To(Succeed()) 284 }) 285 286 // Retrieve the clusterClass from the API server. 287 actualClusterClass := &clusterv1.ClusterClass{} 288 g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: clusterClassName1}, actualClusterClass)).To(Succeed()) 289 290 // Remove MachineDeploymentClass "IN_USE", which is used by cluster1 from the clusterClass 291 actualClusterClass.Spec.Workers.MachineDeployments = []clusterv1.MachineDeploymentClass{ 292 actualClusterClass.Spec.Workers.MachineDeployments[0], 293 } 294 295 // Attempt to update the ClusterClass with the above changes. 296 // Expect an error here as the updates are incompatible with the Current Clusters using the ClusterClass. 297 g.Expect(env.Update(ctx, actualClusterClass)).NotTo(Succeed()) 298 } 299 300 // TestClusterClassWebhook_Fail_Delete tests the correct deletion behaviour for a ClusterClass with references in existing Clusters. 301 // In this case deletion of the ClusterClass should be blocked by the webhook. 302 func TestClusterClassWebhook_Delete(t *testing.T) { 303 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)() 304 g := NewWithT(t) 305 306 ns, err := env.CreateNamespace(ctx, "test-topology-clusterclass-webhook") 307 g.Expect(err).ToNot(HaveOccurred()) 308 309 cleanup, err := createTemplates(ns) 310 g.Expect(err).ToNot(HaveOccurred()) 311 312 // Create the objects needed for the integration test: 313 // - a two ClusterClasses with all the related templates 314 // - a Cluster using one of the ClusterClasses 315 clusterClass1 := builder.ClusterClass(ns.Name, clusterClassName1). 316 WithInfrastructureClusterTemplate( 317 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 318 WithControlPlaneTemplate( 319 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 320 Build()). 321 WithWorkerMachineDeploymentClasses( 322 *builder.MachineDeploymentClass(workerClassName1). 323 WithInfrastructureTemplate( 324 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 325 WithBootstrapTemplate( 326 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 327 Build(), 328 *builder.MachineDeploymentClass(workerClassName2). 329 WithInfrastructureTemplate( 330 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 331 WithBootstrapTemplate( 332 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 333 Build()). 334 Build() 335 336 clusterClass2 := builder.ClusterClass(ns.Name, clusterClassName2). 337 WithInfrastructureClusterTemplate( 338 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 339 WithControlPlaneTemplate( 340 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 341 Build()). 342 WithWorkerMachineDeploymentClasses( 343 *builder.MachineDeploymentClass(workerClassName1). 344 WithInfrastructureTemplate( 345 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 346 WithBootstrapTemplate( 347 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 348 Build(), 349 *builder.MachineDeploymentClass(workerClassName2). 350 WithInfrastructureTemplate( 351 builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build()). 352 WithBootstrapTemplate( 353 builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build()). 354 Build()). 355 Build() 356 357 cluster := builder.Cluster(ns.Name, clusterName2). 358 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 359 WithTopology( 360 builder.ClusterTopology(). 361 WithVersion("v1.20.1"). 362 WithClass(clusterClassName1). 363 WithMachineDeployment( 364 builder.MachineDeploymentTopology("md1"). 365 WithClass(workerClassName1). 366 Build(), 367 ). 368 Build()). 369 Build() 370 371 // Create the ClusterClasses in the API server. 372 g.Expect(env.CreateAndWait(ctx, clusterClass1)).To(Succeed()) 373 g.Expect(env.CreateAndWait(ctx, clusterClass2)).To(Succeed()) 374 375 // Create a clusters. 376 g.Expect(env.CreateAndWait(ctx, cluster)).To(Succeed()) 377 378 // Clean up the resources once the test is finished. 379 t.Cleanup(func() { 380 g.Expect(cleanup()).To(Succeed()) 381 g.Expect(env.Cleanup(ctx, cluster)).To(Succeed()) 382 g.Expect(env.Cleanup(ctx, ns)).To(Succeed()) 383 }) 384 385 // Attempt to delete ClusterClass "class1" which is in use. 386 // Expect no error here as the webhook should allow the deletion of an existing ClusterClass. 387 g.Expect(env.Delete(ctx, clusterClass1)).To(Not(Succeed())) 388 389 // Attempt to delete ClusterClass "class2" which is not in use. 390 // Expect no error here as the webhook should allow the deletion of an existing ClusterClass. 391 g.Expect(env.Delete(ctx, clusterClass2)).To(Succeed()) 392 } 393 394 // TestClusterClassWebhook_Succeed_Delete tests the correct deletion behaviour for a ClusterClass with no references in existing Clusters. 395 // In this case deletion of the ClusterClass should succeed. 396 func TestClusterClassWebhook_Delete_MultipleExistingClusters(t *testing.T) { 397 g := NewWithT(t) 398 ns, err := env.CreateNamespace(ctx, "test-clusterclass-webhook") 399 g.Expect(err).ToNot(HaveOccurred()) 400 401 cleanup, err := createTemplates(ns) 402 g.Expect(err).ToNot(HaveOccurred()) 403 // Create the objects needed for the integration test: 404 // - Five ClusterClasses with all the related templates 405 // - Five Clusters using all ClusterClass except "class1" 406 clusterClasses := []client.Object{ 407 builder.ClusterClass(ns.Name, clusterClassName1). 408 WithInfrastructureClusterTemplate( 409 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 410 WithControlPlaneTemplate( 411 builder.ControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 412 Build()). 413 Build(), 414 builder.ClusterClass(ns.Name, clusterClassName2). 415 WithInfrastructureClusterTemplate( 416 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 417 WithControlPlaneTemplate( 418 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 419 Build()). 420 Build(), 421 builder.ClusterClass(ns.Name, clusterClassName3). 422 WithInfrastructureClusterTemplate( 423 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 424 WithControlPlaneTemplate( 425 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 426 Build()). 427 Build(), 428 builder.ClusterClass(ns.Name, clusterClassName4). 429 WithInfrastructureClusterTemplate( 430 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 431 WithControlPlaneTemplate( 432 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 433 Build()). 434 Build(), 435 builder.ClusterClass(ns.Name, clusterClassName5). 436 WithInfrastructureClusterTemplate( 437 builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build()). 438 WithControlPlaneTemplate( 439 builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1). 440 Build()). 441 Build(), 442 } 443 clusters := []client.Object{ 444 // class1 is not referenced in any of the below Clusters 445 builder.Cluster(ns.Name, clusterName1). 446 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 447 WithTopology( 448 builder.ClusterTopology(). 449 WithVersion("v1.20.1"). 450 WithClass(clusterClassName5). 451 Build()). 452 Build(), 453 builder.Cluster(ns.Name, clusterName2). 454 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 455 WithTopology( 456 builder.ClusterTopology(). 457 WithVersion("v1.20.1"). 458 WithClass(clusterClassName2). 459 Build()). 460 Build(), 461 builder.Cluster(ns.Name, clusterName3). 462 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 463 WithTopology( 464 builder.ClusterTopology(). 465 WithVersion("v1.20.1"). 466 WithClass(clusterClassName3). 467 Build()). 468 Build(), 469 builder.Cluster(ns.Name, clusterName4). 470 WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}). 471 WithTopology( 472 builder.ClusterTopology(). 473 WithVersion("v1.20.1"). 474 WithClass(clusterClassName4). 475 Build()). 476 Build(), 477 } 478 var classForDeletion string 479 // Create the ClusterClasses in the API server. 480 for _, class := range clusterClasses { 481 g.Expect(env.CreateAndWait(ctx, class)).To(Succeed()) 482 } 483 484 // Create each of the clusters. 485 for _, c := range clusters { 486 g.Expect(env.CreateAndWait(ctx, c)).To(Succeed()) 487 } 488 489 // defer a function to clean up created resources. 490 defer func() { 491 g.Expect(cleanup()).To(Succeed()) 492 // Delete each of the clusters. 493 for _, c := range clusters { 494 g.Expect(env.CleanupAndWait(ctx, c)).To(Succeed()) 495 } 496 497 // Delete the ClusterClasses in the API server. 498 for _, class := range clusterClasses { 499 // The classForDeletion should not exist at this point. 500 if class.GetName() != classForDeletion { 501 if err := env.Delete(ctx, class); err != nil { 502 if apierrors.IsNotFound(err) { 503 continue 504 } 505 g.Expect(err).ToNot(HaveOccurred()) 506 } 507 } 508 } 509 }() 510 511 // This is the unused clusterClass to be deleted. 512 classForDeletion = clusterClassName1 513 514 class := &clusterv1.ClusterClass{} 515 g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: classForDeletion}, class)).To(Succeed()) 516 517 // Attempt to delete ClusterClass "class1" which is not in use. 518 // Expect no error here as the webhook should allow the deletion of an unused ClusterClass. 519 g.Expect(env.Delete(ctx, class)).To(Succeed()) 520 521 // This is a clusterClass in use to be deleted. 522 classForDeletion = clusterClassName3 523 524 class = &clusterv1.ClusterClass{} 525 g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: classForDeletion}, class)).To(Succeed()) 526 527 // Attempt to delete ClusterClass "class3" which is in use. 528 // Expect an error here as the webhook should not allow the deletion of an existing ClusterClass. 529 g.Expect(env.Delete(ctx, class)).To(Not(Succeed())) 530 } 531 532 // createTemplates builds and then creates all required ClusterClass templates in the envtest API server. 533 func createTemplates(ns *corev1.Namespace) (func() error, error) { 534 // Templates for MachineInfrastructure, ClusterInfrastructure, ControlPlane and Bootstrap. 535 infrastructureMachineTemplate1 := builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName1).Build() 536 infrastructureMachineTemplate2 := builder.TestInfrastructureMachineTemplate(ns.Name, infrastructureMachineTemplateName2).Build() 537 infrastructureClusterTemplate1 := builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName1).Build() 538 infrastructureClusterTemplate2 := builder.TestInfrastructureClusterTemplate(ns.Name, infrastructureClusterTemplateName2).Build() 539 controlPlaneTemplate := builder.TestControlPlaneTemplate(ns.Name, controlPlaneTemplateName1).Build() 540 bootstrapTemplate := builder.TestBootstrapTemplate(ns.Name, bootstrapTemplateName1).Build() 541 542 // Create a set of templates from the objects above to add to the API server when the test environment starts. 543 initObjs := []client.Object{ 544 infrastructureClusterTemplate1, 545 infrastructureClusterTemplate2, 546 infrastructureMachineTemplate1, 547 infrastructureMachineTemplate2, 548 bootstrapTemplate, 549 controlPlaneTemplate, 550 } 551 cleanup := func() error { 552 for _, o := range initObjs { 553 if err := env.CleanupAndWait(ctx, o); err != nil { 554 return err 555 } 556 } 557 return nil 558 } 559 560 for _, obj := range initObjs { 561 if err := env.CreateAndWait(ctx, obj); err != nil { 562 return cleanup, err 563 } 564 } 565 return cleanup, nil 566 }