sigs.k8s.io/cluster-api@v1.6.3/internal/webhooks/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 webhooks
    18  
    19  import (
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	. "github.com/onsi/gomega"
    25  	corev1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	utilfeature "k8s.io/component-base/featuregate/testing"
    29  	"k8s.io/utils/pointer"
    30  	ctrl "sigs.k8s.io/controller-runtime"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    33  
    34  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    35  	"sigs.k8s.io/cluster-api/api/v1beta1/index"
    36  	"sigs.k8s.io/cluster-api/feature"
    37  	"sigs.k8s.io/cluster-api/internal/test/builder"
    38  	"sigs.k8s.io/cluster-api/internal/webhooks/util"
    39  )
    40  
    41  var (
    42  	ctx        = ctrl.SetupSignalHandler()
    43  	fakeScheme = runtime.NewScheme()
    44  )
    45  
    46  func init() {
    47  	_ = clusterv1.AddToScheme(fakeScheme)
    48  }
    49  
    50  func TestClusterClassDefaultNamespaces(t *testing.T) {
    51  	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create or update ClusterClasses.
    52  	// Enabling the feature flag temporarily for this test.
    53  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
    54  
    55  	namespace := "default"
    56  
    57  	in := builder.ClusterClass(namespace, "class1").
    58  		WithInfrastructureClusterTemplate(
    59  			builder.InfrastructureClusterTemplate("", "infra1").Build()).
    60  		WithControlPlaneTemplate(
    61  			builder.ControlPlaneTemplate("", "cp1").
    62  				Build()).
    63  		WithControlPlaneInfrastructureMachineTemplate(
    64  			builder.InfrastructureMachineTemplate("", "cpInfra1").
    65  				Build()).
    66  		WithWorkerMachineDeploymentClasses(
    67  			*builder.MachineDeploymentClass("aa").
    68  				WithInfrastructureTemplate(
    69  					builder.InfrastructureMachineTemplate("", "infra1").Build()).
    70  				WithBootstrapTemplate(
    71  					builder.BootstrapTemplate("", "bootstrap1").Build()).
    72  				Build()).
    73  		Build()
    74  
    75  	fakeClient := fake.NewClientBuilder().
    76  		WithScheme(fakeScheme).
    77  		WithIndex(&clusterv1.Cluster{}, index.ClusterClassNameField, index.ClusterByClusterClassClassName).
    78  		Build()
    79  
    80  	// Create the webhook and add the fakeClient as its client.
    81  	webhook := &ClusterClass{Client: fakeClient}
    82  	t.Run("for ClusterClass", util.CustomDefaultValidateTest(ctx, in, webhook))
    83  
    84  	g := NewWithT(t)
    85  	g.Expect(webhook.Default(ctx, in)).To(Succeed())
    86  
    87  	// Namespace defaulted on references
    88  	g.Expect(in.Spec.Infrastructure.Ref.Namespace).To(Equal(namespace))
    89  	g.Expect(in.Spec.ControlPlane.Ref.Namespace).To(Equal(namespace))
    90  	g.Expect(in.Spec.ControlPlane.MachineInfrastructure.Ref.Namespace).To(Equal(namespace))
    91  	for i := range in.Spec.Workers.MachineDeployments {
    92  		g.Expect(in.Spec.Workers.MachineDeployments[i].Template.Bootstrap.Ref.Namespace).To(Equal(namespace))
    93  		g.Expect(in.Spec.Workers.MachineDeployments[i].Template.Infrastructure.Ref.Namespace).To(Equal(namespace))
    94  	}
    95  }
    96  
    97  func TestClusterClassValidationFeatureGated(t *testing.T) {
    98  	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create or update ClusterClasses.
    99  
   100  	tests := []struct {
   101  		name      string
   102  		in        *clusterv1.ClusterClass
   103  		old       *clusterv1.ClusterClass
   104  		expectErr bool
   105  	}{
   106  		{
   107  			name: "creation should fail if feature flag is disabled, no matter the ClusterClass is valid(or not)",
   108  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   109  				WithInfrastructureClusterTemplate(
   110  					builder.InfrastructureClusterTemplate("", "infra1").Build()).
   111  				WithControlPlaneTemplate(
   112  					builder.ControlPlaneTemplate("", "cp1").
   113  						Build()).
   114  				WithControlPlaneInfrastructureMachineTemplate(
   115  					builder.InfrastructureMachineTemplate("", "cpInfra1").
   116  						Build()).
   117  				WithWorkerMachineDeploymentClasses(
   118  					*builder.MachineDeploymentClass("aa").
   119  						WithInfrastructureTemplate(
   120  							builder.InfrastructureMachineTemplate("", "infra1").Build()).
   121  						WithBootstrapTemplate(
   122  							builder.BootstrapTemplate("", "bootstrap1").Build()).
   123  						Build()).
   124  				Build(),
   125  			expectErr: true,
   126  		},
   127  		{
   128  			name: "update should fail if feature flag is disabled, no matter the ClusterClass is valid(or not)",
   129  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   130  				WithInfrastructureClusterTemplate(
   131  					builder.InfrastructureClusterTemplate("", "infra1").Build()).
   132  				WithControlPlaneTemplate(
   133  					builder.ControlPlaneTemplate("", "cp1").
   134  						Build()).
   135  				WithControlPlaneInfrastructureMachineTemplate(
   136  					builder.InfrastructureMachineTemplate("", "cpInfra1").
   137  						Build()).
   138  				WithWorkerMachineDeploymentClasses(
   139  					*builder.MachineDeploymentClass("aa").
   140  						WithInfrastructureTemplate(
   141  							builder.InfrastructureMachineTemplate("", "infra1").Build()).
   142  						WithBootstrapTemplate(
   143  							builder.BootstrapTemplate("", "bootstrap1").Build()).
   144  						Build()).
   145  				Build(),
   146  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   147  				WithInfrastructureClusterTemplate(
   148  					builder.InfrastructureClusterTemplate("", "infra1").Build()).
   149  				WithControlPlaneTemplate(
   150  					builder.ControlPlaneTemplate("", "cp1").
   151  						Build()).
   152  				WithControlPlaneInfrastructureMachineTemplate(
   153  					builder.InfrastructureMachineTemplate("", "cpInfra1").
   154  						Build()).
   155  				WithWorkerMachineDeploymentClasses(
   156  					*builder.MachineDeploymentClass("aa").
   157  						WithInfrastructureTemplate(
   158  							builder.InfrastructureMachineTemplate("", "infra1").Build()).
   159  						WithBootstrapTemplate(
   160  							builder.BootstrapTemplate("", "bootstrap1").Build()).
   161  						Build()).
   162  				Build(),
   163  			expectErr: true,
   164  		},
   165  	}
   166  
   167  	for _, tt := range tests {
   168  		t.Run(tt.name, func(t *testing.T) {
   169  			g := NewWithT(t)
   170  			webhook := &ClusterClass{}
   171  			err := webhook.validate(ctx, tt.old, tt.in)
   172  			if tt.expectErr {
   173  				g.Expect(err).To(HaveOccurred())
   174  				return
   175  			}
   176  			g.Expect(err).ToNot(HaveOccurred())
   177  		})
   178  	}
   179  }
   180  
   181  func TestClusterClassValidation(t *testing.T) {
   182  	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create or update ClusterClasses.
   183  	// Enabling the feature flag temporarily for this test.
   184  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
   185  
   186  	ref := &corev1.ObjectReference{
   187  		APIVersion: "group.test.io/foo",
   188  		Kind:       "barTemplate",
   189  		Name:       "baz",
   190  		Namespace:  "default",
   191  	}
   192  	refBadTemplate := &corev1.ObjectReference{
   193  		APIVersion: "group.test.io/foo",
   194  		Kind:       "bar",
   195  		Name:       "baz",
   196  		Namespace:  "default",
   197  	}
   198  	refBadAPIVersion := &corev1.ObjectReference{
   199  		APIVersion: "group/test.io/v1/foo",
   200  		Kind:       "barTemplate",
   201  		Name:       "baz",
   202  		Namespace:  "default",
   203  	}
   204  	incompatibleRef := &corev1.ObjectReference{
   205  		APIVersion: "group.test.io/foo",
   206  		Kind:       "another-barTemplate",
   207  		Name:       "baz",
   208  		Namespace:  "default",
   209  	}
   210  	compatibleRef := &corev1.ObjectReference{
   211  		APIVersion: "group.test.io/another-foo",
   212  		Kind:       "barTemplate",
   213  		Name:       "another-baz",
   214  		Namespace:  "default",
   215  	}
   216  
   217  	tests := []struct {
   218  		name      string
   219  		in        *clusterv1.ClusterClass
   220  		old       *clusterv1.ClusterClass
   221  		expectErr bool
   222  	}{
   223  
   224  		/*
   225  			CREATE Tests
   226  		*/
   227  
   228  		{
   229  			name: "create pass",
   230  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   231  				WithInfrastructureClusterTemplate(
   232  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   233  				WithControlPlaneTemplate(
   234  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   235  						Build()).
   236  				WithControlPlaneInfrastructureMachineTemplate(
   237  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   238  						Build()).
   239  				WithWorkerMachineDeploymentClasses(
   240  					*builder.MachineDeploymentClass("aa").
   241  						WithInfrastructureTemplate(
   242  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   243  						WithBootstrapTemplate(
   244  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   245  						Build(),
   246  					*builder.MachineDeploymentClass("bb").
   247  						WithInfrastructureTemplate(
   248  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   249  						WithBootstrapTemplate(
   250  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   251  						Build()).
   252  				Build(),
   253  			expectErr: false,
   254  		},
   255  
   256  		// empty name in ref tests
   257  		{
   258  			name: "create fail infrastructureCluster has empty name",
   259  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   260  				WithInfrastructureClusterTemplate(
   261  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "").Build()).
   262  				WithControlPlaneTemplate(
   263  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   264  						Build()).
   265  				Build(),
   266  			expectErr: true,
   267  		},
   268  		{
   269  			name: "create fail controlPlane class has empty name",
   270  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   271  				WithInfrastructureClusterTemplate(
   272  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   273  				WithControlPlaneTemplate(
   274  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "").
   275  						Build()).
   276  				Build(),
   277  			expectErr: true,
   278  		},
   279  		{
   280  			name: "create fail control plane class machineInfrastructure has empty name",
   281  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   282  				WithInfrastructureClusterTemplate(
   283  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   284  				WithControlPlaneTemplate(
   285  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   286  						Build()).
   287  				WithControlPlaneInfrastructureMachineTemplate(
   288  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "").
   289  						Build()).
   290  				Build(),
   291  			expectErr: true,
   292  		},
   293  		{
   294  			name: "create fail machineDeployment Bootstrap has empty name",
   295  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   296  				WithInfrastructureClusterTemplate(
   297  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   298  				WithControlPlaneTemplate(
   299  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   300  						Build()).
   301  				WithControlPlaneInfrastructureMachineTemplate(
   302  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   303  						Build()).
   304  				WithWorkerMachineDeploymentClasses(
   305  					*builder.MachineDeploymentClass("aa").
   306  						WithInfrastructureTemplate(
   307  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   308  						WithBootstrapTemplate(
   309  							builder.BootstrapTemplate(metav1.NamespaceDefault, "").Build()).
   310  						Build()).
   311  				Build(),
   312  			expectErr: true,
   313  		},
   314  		{
   315  			name: "create fail machineDeployment Infrastructure has empty name",
   316  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   317  				WithInfrastructureClusterTemplate(
   318  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   319  				WithControlPlaneTemplate(
   320  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   321  						Build()).
   322  				WithControlPlaneInfrastructureMachineTemplate(
   323  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   324  						Build()).
   325  				WithWorkerMachineDeploymentClasses(
   326  					*builder.MachineDeploymentClass("aa").
   327  						WithInfrastructureTemplate(
   328  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "").Build()).
   329  						WithBootstrapTemplate(
   330  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap").Build()).
   331  						Build()).
   332  				Build(),
   333  			expectErr: true,
   334  		},
   335  
   336  		// inconsistent namespace in ref tests
   337  		{
   338  			name: "create fail if InfrastructureCluster has inconsistent namespace",
   339  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   340  				WithInfrastructureClusterTemplate(
   341  					builder.InfrastructureClusterTemplate("WrongNamespace", "infra1").Build()).
   342  				WithControlPlaneTemplate(
   343  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   344  						Build()).
   345  				WithControlPlaneInfrastructureMachineTemplate(
   346  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   347  						Build()).
   348  				Build(),
   349  			expectErr: true,
   350  		},
   351  		{
   352  			name: "create fail if controlPlane has inconsistent namespace",
   353  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   354  				WithInfrastructureClusterTemplate(
   355  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   356  				WithControlPlaneTemplate(
   357  					builder.ControlPlaneTemplate("WrongNamespace", "cp1").
   358  						Build()).
   359  				WithControlPlaneInfrastructureMachineTemplate(
   360  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   361  						Build()).
   362  				Build(),
   363  			expectErr: true,
   364  		},
   365  		{
   366  			name: "create fail if controlPlane machineInfrastructure has inconsistent namespace",
   367  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   368  				WithInfrastructureClusterTemplate(
   369  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   370  				WithControlPlaneTemplate(
   371  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   372  						Build()).
   373  				WithControlPlaneInfrastructureMachineTemplate(
   374  					builder.InfrastructureMachineTemplate("WrongNamespace", "cpInfra1").
   375  						Build()).
   376  				Build(),
   377  			expectErr: true,
   378  		},
   379  		{
   380  			name: "create fail if machineDeployment / bootstrap has inconsistent namespace",
   381  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   382  				WithInfrastructureClusterTemplate(
   383  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   384  				WithControlPlaneTemplate(
   385  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   386  						Build()).
   387  				WithControlPlaneInfrastructureMachineTemplate(
   388  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   389  						Build()).
   390  				WithWorkerMachineDeploymentClasses(
   391  					*builder.MachineDeploymentClass("aa").
   392  						WithInfrastructureTemplate(
   393  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   394  						WithBootstrapTemplate(
   395  							builder.BootstrapTemplate("WrongNamespace", "bootstrap1").Build()).
   396  						Build()).
   397  				Build(),
   398  			expectErr: true,
   399  		},
   400  		{
   401  			name: "create fail if machineDeployment / infrastructure has inconsistent namespace",
   402  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   403  				WithInfrastructureClusterTemplate(
   404  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   405  				WithControlPlaneTemplate(
   406  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   407  						Build()).
   408  				WithControlPlaneInfrastructureMachineTemplate(
   409  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   410  						Build()).
   411  				WithWorkerMachineDeploymentClasses(
   412  					*builder.MachineDeploymentClass("aa").
   413  						WithInfrastructureTemplate(
   414  							builder.InfrastructureMachineTemplate("WrongNamespace", "infra1").Build()).
   415  						WithBootstrapTemplate(
   416  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   417  						Build()).
   418  				Build(),
   419  			expectErr: true,
   420  		},
   421  
   422  		// bad template in ref tests
   423  		{
   424  			name: "create fail if bad template in InfrastructureCluster",
   425  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   426  				WithInfrastructureClusterTemplate(
   427  					refToUnstructured(refBadTemplate)).
   428  				WithControlPlaneTemplate(
   429  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   430  						Build()).
   431  				WithControlPlaneInfrastructureMachineTemplate(
   432  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   433  						Build()).
   434  				Build(),
   435  			old:       nil,
   436  			expectErr: true,
   437  		},
   438  		{
   439  			name: "create fail if bad template in controlPlane machineInfrastructure",
   440  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   441  				WithInfrastructureClusterTemplate(
   442  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   443  				WithControlPlaneTemplate(
   444  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   445  						Build()).
   446  				WithControlPlaneInfrastructureMachineTemplate(
   447  					refToUnstructured(refBadTemplate)).
   448  				Build(),
   449  			old:       nil,
   450  			expectErr: true,
   451  		},
   452  		{
   453  			name: "create fail if bad template in ControlPlane",
   454  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   455  				WithInfrastructureClusterTemplate(
   456  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   457  				WithControlPlaneTemplate(
   458  					refToUnstructured(refBadTemplate)).
   459  				WithControlPlaneInfrastructureMachineTemplate(
   460  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   461  						Build()).
   462  				Build(),
   463  			old:       nil,
   464  			expectErr: true,
   465  		},
   466  		{
   467  			name: "create fail if bad template in machineDeployment Bootstrap",
   468  
   469  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   470  				WithInfrastructureClusterTemplate(
   471  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   472  				WithControlPlaneTemplate(
   473  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   474  						Build()).
   475  				WithControlPlaneInfrastructureMachineTemplate(
   476  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   477  						Build()).
   478  				WithWorkerMachineDeploymentClasses(
   479  					*builder.MachineDeploymentClass("aa").
   480  						WithInfrastructureTemplate(
   481  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   482  						WithBootstrapTemplate(
   483  							refToUnstructured(refBadTemplate)).
   484  						Build()).
   485  				Build(),
   486  			old:       nil,
   487  			expectErr: true,
   488  		},
   489  		{
   490  			name: "create fail if bad template in machineDeployment Infrastructure",
   491  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   492  				WithInfrastructureClusterTemplate(
   493  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   494  				WithControlPlaneTemplate(
   495  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   496  						Build()).
   497  				WithControlPlaneInfrastructureMachineTemplate(
   498  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   499  						Build()).
   500  				WithWorkerMachineDeploymentClasses(
   501  					*builder.MachineDeploymentClass("aa").
   502  						WithInfrastructureTemplate(
   503  							refToUnstructured(refBadTemplate)).
   504  						WithBootstrapTemplate(
   505  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   506  						Build()).
   507  				Build(),
   508  			old:       nil,
   509  			expectErr: true,
   510  		},
   511  
   512  		// bad apiVersion in ref tests
   513  		{
   514  			name: "create fail with a bad APIVersion for template in InfrastructureCluster",
   515  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   516  				WithInfrastructureClusterTemplate(
   517  					refToUnstructured(refBadAPIVersion)).
   518  				WithControlPlaneTemplate(
   519  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   520  						Build()).
   521  				WithControlPlaneInfrastructureMachineTemplate(
   522  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   523  						Build()).
   524  				Build(),
   525  			old:       nil,
   526  			expectErr: true,
   527  		},
   528  		{
   529  			name: "create fail with a bad APIVersion for template in controlPlane machineInfrastructure",
   530  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   531  				WithInfrastructureClusterTemplate(
   532  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   533  				WithControlPlaneTemplate(
   534  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   535  						Build()).
   536  				WithControlPlaneInfrastructureMachineTemplate(
   537  					refToUnstructured(refBadAPIVersion)).
   538  				Build(),
   539  			old:       nil,
   540  			expectErr: true,
   541  		},
   542  		{
   543  			name: "create fail with a bad APIVersion for template in ControlPlane",
   544  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   545  				WithInfrastructureClusterTemplate(
   546  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   547  				WithControlPlaneTemplate(
   548  					refToUnstructured(refBadAPIVersion)).
   549  				WithControlPlaneInfrastructureMachineTemplate(
   550  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   551  						Build()).
   552  				Build(),
   553  			old:       nil,
   554  			expectErr: true,
   555  		},
   556  		{
   557  			name: "create fail with a bad APIVersion for template in machineDeployment Bootstrap",
   558  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   559  				WithInfrastructureClusterTemplate(
   560  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   561  				WithControlPlaneTemplate(
   562  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   563  						Build()).
   564  				WithControlPlaneInfrastructureMachineTemplate(
   565  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   566  						Build()).
   567  				WithWorkerMachineDeploymentClasses(
   568  					*builder.MachineDeploymentClass("aa").
   569  						WithInfrastructureTemplate(
   570  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   571  						WithBootstrapTemplate(
   572  							refToUnstructured(refBadAPIVersion)).
   573  						Build()).
   574  				Build(),
   575  			old:       nil,
   576  			expectErr: true,
   577  		},
   578  		{
   579  			name: "create fail with a bad APIVersion for template in machineDeployment Infrastructure",
   580  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   581  				WithInfrastructureClusterTemplate(
   582  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   583  				WithControlPlaneTemplate(
   584  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   585  						Build()).
   586  				WithControlPlaneInfrastructureMachineTemplate(
   587  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   588  						Build()).
   589  				WithWorkerMachineDeploymentClasses(
   590  					*builder.MachineDeploymentClass("aa").
   591  						WithInfrastructureTemplate(
   592  							refToUnstructured(refBadAPIVersion)).
   593  						WithBootstrapTemplate(
   594  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   595  						Build()).
   596  				Build(),
   597  			old:       nil,
   598  			expectErr: true,
   599  		},
   600  
   601  		// create test
   602  		{
   603  			name: "create fail if duplicated machineDeploymentClasses",
   604  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   605  				WithInfrastructureClusterTemplate(
   606  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   607  				WithControlPlaneTemplate(
   608  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   609  						Build()).
   610  				WithControlPlaneInfrastructureMachineTemplate(
   611  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   612  						Build()).
   613  				WithWorkerMachineDeploymentClasses(
   614  					*builder.MachineDeploymentClass("aa").
   615  						WithInfrastructureTemplate(
   616  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   617  						WithBootstrapTemplate(
   618  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).Build(),
   619  					*builder.MachineDeploymentClass("aa").
   620  						WithInfrastructureTemplate(
   621  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   622  						WithBootstrapTemplate(
   623  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   624  						Build()).
   625  				Build(),
   626  			expectErr: true,
   627  		},
   628  		{
   629  			name: "create pass if valid machineHealthCheck defined for ControlPlane with MachineInfrastructure set",
   630  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   631  				WithInfrastructureClusterTemplate(
   632  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   633  				WithControlPlaneTemplate(
   634  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   635  						Build()).
   636  				WithControlPlaneInfrastructureMachineTemplate(
   637  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   638  						Build()).
   639  				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{
   640  					UnhealthyConditions: []clusterv1.UnhealthyCondition{
   641  						{
   642  							Type:    corev1.NodeReady,
   643  							Status:  corev1.ConditionUnknown,
   644  							Timeout: metav1.Duration{Duration: 5 * time.Minute},
   645  						},
   646  					},
   647  					NodeStartupTimeout: &metav1.Duration{
   648  						Duration: time.Duration(6000000000000)}}).
   649  				Build(),
   650  		},
   651  		{
   652  			name: "create fail if MachineHealthCheck defined for ControlPlane with MachineInfrastructure unset",
   653  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   654  				WithInfrastructureClusterTemplate(
   655  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   656  				WithControlPlaneTemplate(
   657  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   658  						Build()).
   659  				// No ControlPlaneMachineInfrastructure makes this an invalid creation request.
   660  				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{
   661  					NodeStartupTimeout: &metav1.Duration{
   662  						Duration: time.Duration(6000000000000)}}).
   663  				Build(),
   664  			expectErr: true,
   665  		},
   666  		{
   667  			name: "create fail if ControlPlane MachineHealthCheck does not define UnhealthyConditions",
   668  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   669  				WithInfrastructureClusterTemplate(
   670  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   671  				WithControlPlaneTemplate(
   672  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   673  						Build()).
   674  				WithControlPlaneInfrastructureMachineTemplate(
   675  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   676  						Build()).
   677  				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{
   678  					NodeStartupTimeout: &metav1.Duration{
   679  						Duration: time.Duration(6000000000000)}}).
   680  				Build(),
   681  			expectErr: true,
   682  		},
   683  		{
   684  			name: "create pass if MachineDeployment MachineHealthCheck is valid",
   685  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   686  				WithInfrastructureClusterTemplate(
   687  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   688  				WithControlPlaneTemplate(
   689  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   690  						Build()).
   691  				WithWorkerMachineDeploymentClasses(
   692  					*builder.MachineDeploymentClass("aa").
   693  						WithInfrastructureTemplate(
   694  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   695  						WithBootstrapTemplate(
   696  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   697  						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{
   698  							UnhealthyConditions: []clusterv1.UnhealthyCondition{
   699  								{
   700  									Type:    corev1.NodeReady,
   701  									Status:  corev1.ConditionUnknown,
   702  									Timeout: metav1.Duration{Duration: 5 * time.Minute},
   703  								},
   704  							},
   705  							NodeStartupTimeout: &metav1.Duration{
   706  								Duration: time.Duration(6000000000000)}}).
   707  						Build()).
   708  				Build(),
   709  		},
   710  		{
   711  			name: "create fail if MachineDeployment MachineHealthCheck NodeStartUpTimeout is too short",
   712  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   713  				WithInfrastructureClusterTemplate(
   714  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   715  				WithControlPlaneTemplate(
   716  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   717  						Build()).
   718  				WithWorkerMachineDeploymentClasses(
   719  					*builder.MachineDeploymentClass("aa").
   720  						WithInfrastructureTemplate(
   721  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   722  						WithBootstrapTemplate(
   723  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   724  						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{
   725  							UnhealthyConditions: []clusterv1.UnhealthyCondition{
   726  								{
   727  									Type:    corev1.NodeReady,
   728  									Status:  corev1.ConditionUnknown,
   729  									Timeout: metav1.Duration{Duration: 5 * time.Minute},
   730  								},
   731  							},
   732  							NodeStartupTimeout: &metav1.Duration{
   733  								// nodeStartupTimeout is too short here - 600ns.
   734  								Duration: time.Duration(600)}}).
   735  						Build()).
   736  				Build(),
   737  			expectErr: true,
   738  		},
   739  		{
   740  			name: "create fail if MachineDeployment MachineHealthCheck does not define UnhealthyConditions",
   741  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   742  				WithInfrastructureClusterTemplate(
   743  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   744  				WithControlPlaneTemplate(
   745  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   746  						Build()).
   747  				WithWorkerMachineDeploymentClasses(
   748  					*builder.MachineDeploymentClass("aa").
   749  						WithInfrastructureTemplate(
   750  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   751  						WithBootstrapTemplate(
   752  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   753  						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{
   754  							NodeStartupTimeout: &metav1.Duration{
   755  								Duration: time.Duration(6000000000000)}}).
   756  						Build()).
   757  				Build(),
   758  			expectErr: true,
   759  		},
   760  
   761  		/*
   762  			UPDATE Tests
   763  		*/
   764  
   765  		{
   766  			name: "update pass in case of no changes",
   767  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   768  				WithInfrastructureClusterTemplate(
   769  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   770  				WithControlPlaneTemplate(
   771  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   772  						Build()).
   773  				WithControlPlaneInfrastructureMachineTemplate(
   774  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   775  						Build()).
   776  				WithWorkerMachineDeploymentClasses(
   777  					*builder.MachineDeploymentClass("aa").
   778  						WithInfrastructureTemplate(
   779  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   780  						WithBootstrapTemplate(
   781  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   782  						Build()).
   783  				Build(),
   784  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   785  				WithInfrastructureClusterTemplate(
   786  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   787  				WithControlPlaneTemplate(
   788  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   789  						Build()).
   790  				WithControlPlaneInfrastructureMachineTemplate(
   791  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   792  						Build()).
   793  				WithWorkerMachineDeploymentClasses(
   794  					*builder.MachineDeploymentClass("aa").
   795  						WithInfrastructureTemplate(
   796  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   797  						WithBootstrapTemplate(
   798  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   799  						Build()).
   800  				Build(),
   801  			expectErr: false,
   802  		},
   803  		{
   804  			name: "update pass if infrastructureCluster changes in a compatible way",
   805  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   806  				WithInfrastructureClusterTemplate(
   807  					refToUnstructured(ref)).
   808  				WithControlPlaneTemplate(
   809  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   810  						Build()).
   811  				WithControlPlaneInfrastructureMachineTemplate(
   812  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   813  						Build()).
   814  				WithWorkerMachineDeploymentClasses(
   815  					*builder.MachineDeploymentClass("aa").
   816  						WithInfrastructureTemplate(
   817  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   818  						WithBootstrapTemplate(
   819  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   820  						Build()).
   821  				Build(),
   822  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   823  				WithInfrastructureClusterTemplate(
   824  					refToUnstructured(compatibleRef)).
   825  				WithControlPlaneTemplate(
   826  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   827  						Build()).
   828  				WithControlPlaneInfrastructureMachineTemplate(
   829  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   830  						Build()).
   831  				WithWorkerMachineDeploymentClasses(
   832  					*builder.MachineDeploymentClass("aa").
   833  						WithInfrastructureTemplate(
   834  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   835  						WithBootstrapTemplate(
   836  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   837  						Build()).
   838  				Build(),
   839  			expectErr: false,
   840  		},
   841  		{
   842  			name: "update fails if infrastructureCluster changes in an incompatible way",
   843  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   844  				WithInfrastructureClusterTemplate(
   845  					refToUnstructured(ref)).
   846  				WithControlPlaneTemplate(
   847  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   848  						Build()).
   849  				WithControlPlaneInfrastructureMachineTemplate(
   850  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   851  						Build()).
   852  				WithWorkerMachineDeploymentClasses(
   853  					*builder.MachineDeploymentClass("aa").
   854  						WithInfrastructureTemplate(
   855  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   856  						WithBootstrapTemplate(
   857  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   858  						Build()).
   859  				Build(),
   860  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   861  				WithInfrastructureClusterTemplate(
   862  					refToUnstructured(incompatibleRef)).
   863  				WithControlPlaneTemplate(
   864  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   865  						Build()).
   866  				WithControlPlaneInfrastructureMachineTemplate(
   867  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   868  						Build()).
   869  				WithWorkerMachineDeploymentClasses(
   870  					*builder.MachineDeploymentClass("aa").
   871  						WithInfrastructureTemplate(
   872  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   873  						WithBootstrapTemplate(
   874  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   875  						Build()).
   876  				Build(),
   877  			expectErr: true,
   878  		},
   879  		{
   880  			name: "update pass if controlPlane changes in a compatible way",
   881  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   882  				WithInfrastructureClusterTemplate(
   883  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   884  				WithControlPlaneTemplate(
   885  					refToUnstructured(ref)).
   886  				WithControlPlaneInfrastructureMachineTemplate(
   887  					refToUnstructured(ref)).
   888  				WithWorkerMachineDeploymentClasses(
   889  					*builder.MachineDeploymentClass("aa").
   890  						WithInfrastructureTemplate(
   891  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   892  						WithBootstrapTemplate(
   893  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   894  						Build()).
   895  				Build(),
   896  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   897  				WithInfrastructureClusterTemplate(
   898  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   899  				WithControlPlaneTemplate(
   900  					refToUnstructured(compatibleRef)).
   901  				WithControlPlaneInfrastructureMachineTemplate(
   902  					refToUnstructured(compatibleRef)).
   903  				WithWorkerMachineDeploymentClasses(
   904  					*builder.MachineDeploymentClass("aa").
   905  						WithInfrastructureTemplate(
   906  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   907  						WithBootstrapTemplate(
   908  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   909  						Build()).
   910  				Build(),
   911  			expectErr: false,
   912  		},
   913  		{
   914  			name: "update fails if controlPlane changes in an incompatible way (controlPlane template)",
   915  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   916  				WithInfrastructureClusterTemplate(
   917  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   918  				WithControlPlaneTemplate(
   919  					refToUnstructured(ref)).
   920  				WithControlPlaneInfrastructureMachineTemplate(
   921  					refToUnstructured(ref)).
   922  				WithWorkerMachineDeploymentClasses(
   923  					*builder.MachineDeploymentClass("aa").
   924  						WithInfrastructureTemplate(
   925  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   926  						WithBootstrapTemplate(
   927  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   928  						Build()).
   929  				Build(),
   930  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   931  				WithInfrastructureClusterTemplate(
   932  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   933  				WithControlPlaneTemplate(
   934  					refToUnstructured(incompatibleRef)).
   935  				WithControlPlaneInfrastructureMachineTemplate(
   936  					refToUnstructured(compatibleRef)).
   937  				WithWorkerMachineDeploymentClasses(
   938  					*builder.MachineDeploymentClass("aa").
   939  						WithInfrastructureTemplate(
   940  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   941  						WithBootstrapTemplate(
   942  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   943  						Build()).
   944  				Build(),
   945  			expectErr: true,
   946  		},
   947  		{
   948  			name: "update fails if controlPlane changes in an incompatible way (controlPlane infrastructureMachineTemplate)",
   949  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   950  				WithInfrastructureClusterTemplate(
   951  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   952  				WithControlPlaneTemplate(
   953  					refToUnstructured(ref)).
   954  				WithControlPlaneInfrastructureMachineTemplate(
   955  					refToUnstructured(ref)).
   956  				WithWorkerMachineDeploymentClasses(
   957  					*builder.MachineDeploymentClass("aa").
   958  						WithInfrastructureTemplate(
   959  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   960  						WithBootstrapTemplate(
   961  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   962  						Build()).
   963  				Build(),
   964  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   965  				WithInfrastructureClusterTemplate(
   966  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   967  				WithControlPlaneTemplate(
   968  					refToUnstructured(compatibleRef)).
   969  				WithControlPlaneInfrastructureMachineTemplate(
   970  					refToUnstructured(incompatibleRef)).
   971  				WithWorkerMachineDeploymentClasses(
   972  					*builder.MachineDeploymentClass("aa").
   973  						WithInfrastructureTemplate(
   974  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
   975  						WithBootstrapTemplate(
   976  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   977  						Build()).
   978  				Build(),
   979  			expectErr: true,
   980  		},
   981  		{
   982  			name: "update pass if a machine deployment changes in a compatible way",
   983  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
   984  				WithInfrastructureClusterTemplate(
   985  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
   986  				WithControlPlaneTemplate(
   987  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
   988  						Build()).
   989  				WithControlPlaneInfrastructureMachineTemplate(
   990  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
   991  						Build()).
   992  				WithWorkerMachineDeploymentClasses(
   993  					*builder.MachineDeploymentClass("aa").
   994  						WithInfrastructureTemplate(
   995  							refToUnstructured(ref)).
   996  						WithBootstrapTemplate(
   997  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
   998  						Build()).
   999  				Build(),
  1000  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1001  				WithInfrastructureClusterTemplate(
  1002  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1003  				WithControlPlaneTemplate(
  1004  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1005  						Build()).
  1006  				WithControlPlaneInfrastructureMachineTemplate(
  1007  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1008  						Build()).
  1009  				WithWorkerMachineDeploymentClasses(
  1010  					*builder.MachineDeploymentClass("aa").
  1011  						WithInfrastructureTemplate(
  1012  							refToUnstructured(compatibleRef)).
  1013  						WithBootstrapTemplate(
  1014  							refToUnstructured(incompatibleRef)).
  1015  						Build()).
  1016  				Build(),
  1017  			expectErr: false,
  1018  		},
  1019  		{
  1020  			name: "update fails a machine deployment changes in an incompatible way",
  1021  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1022  				WithInfrastructureClusterTemplate(
  1023  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1024  				WithControlPlaneTemplate(
  1025  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1026  						Build()).
  1027  				WithControlPlaneInfrastructureMachineTemplate(
  1028  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1029  						Build()).
  1030  				WithWorkerMachineDeploymentClasses(
  1031  					*builder.MachineDeploymentClass("aa").
  1032  						WithInfrastructureTemplate(
  1033  							refToUnstructured(ref)).
  1034  						WithBootstrapTemplate(
  1035  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1036  						Build()).
  1037  				Build(),
  1038  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1039  				WithInfrastructureClusterTemplate(
  1040  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1041  				WithControlPlaneTemplate(
  1042  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1043  						Build()).
  1044  				WithControlPlaneInfrastructureMachineTemplate(
  1045  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1046  						Build()).
  1047  				WithWorkerMachineDeploymentClasses(
  1048  					*builder.MachineDeploymentClass("aa").
  1049  						WithInfrastructureTemplate(
  1050  							refToUnstructured(incompatibleRef)).
  1051  						WithBootstrapTemplate(
  1052  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1053  						Build()).
  1054  				Build(),
  1055  			expectErr: true,
  1056  		},
  1057  		{
  1058  			name: "update pass if a machine deployment class gets added",
  1059  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1060  				WithInfrastructureClusterTemplate(
  1061  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1062  				WithControlPlaneTemplate(
  1063  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1064  						Build()).
  1065  				WithControlPlaneInfrastructureMachineTemplate(
  1066  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1067  						Build()).
  1068  				WithWorkerMachineDeploymentClasses(
  1069  					*builder.MachineDeploymentClass("aa").
  1070  						WithInfrastructureTemplate(
  1071  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1072  						WithBootstrapTemplate(
  1073  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1074  						Build()).
  1075  				Build(),
  1076  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1077  				WithInfrastructureClusterTemplate(
  1078  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1079  				WithControlPlaneTemplate(
  1080  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1081  						Build()).
  1082  				WithControlPlaneInfrastructureMachineTemplate(
  1083  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1084  						Build()).
  1085  				WithWorkerMachineDeploymentClasses(
  1086  					*builder.MachineDeploymentClass("aa").
  1087  						WithInfrastructureTemplate(
  1088  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1089  						WithBootstrapTemplate(
  1090  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1091  						Build(),
  1092  					*builder.MachineDeploymentClass("BB").
  1093  						WithInfrastructureTemplate(
  1094  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1095  						WithBootstrapTemplate(
  1096  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1097  						Build()).
  1098  				Build(),
  1099  			expectErr: false,
  1100  		},
  1101  		{
  1102  			name: "update fails if a duplicated deployment class gets added",
  1103  			old: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1104  				WithInfrastructureClusterTemplate(
  1105  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1106  				WithControlPlaneTemplate(
  1107  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1108  						Build()).
  1109  				WithControlPlaneInfrastructureMachineTemplate(
  1110  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1111  						Build()).
  1112  				WithWorkerMachineDeploymentClasses(
  1113  					*builder.MachineDeploymentClass("aa").
  1114  						WithInfrastructureTemplate(
  1115  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1116  						WithBootstrapTemplate(
  1117  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1118  						Build()).
  1119  				Build(),
  1120  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1121  				WithInfrastructureClusterTemplate(
  1122  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1123  				WithControlPlaneTemplate(
  1124  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1125  						Build()).
  1126  				WithControlPlaneInfrastructureMachineTemplate(
  1127  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1128  						Build()).
  1129  				WithWorkerMachineDeploymentClasses(
  1130  					*builder.MachineDeploymentClass("aa").
  1131  						WithInfrastructureTemplate(
  1132  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1133  						WithBootstrapTemplate(
  1134  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).Build(),
  1135  					*builder.MachineDeploymentClass("aa").
  1136  						WithInfrastructureTemplate(
  1137  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1138  						WithBootstrapTemplate(
  1139  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1140  						Build()).
  1141  				Build(),
  1142  			expectErr: true,
  1143  		},
  1144  		{
  1145  			name: "should return error for invalid labels and annotations",
  1146  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1147  				WithInfrastructureClusterTemplate(
  1148  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1149  				WithControlPlaneTemplate(
  1150  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1151  						Build()).
  1152  				WithControlPlaneInfrastructureMachineTemplate(
  1153  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1154  						Build()).
  1155  				WithWorkerMachineDeploymentClasses(
  1156  					*builder.MachineDeploymentClass("aa").
  1157  						WithInfrastructureTemplate(
  1158  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1159  						WithBootstrapTemplate(
  1160  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1161  						WithLabels(invalidLabels()).
  1162  						WithAnnotations(invalidAnnotations()).
  1163  						Build()).
  1164  				WithControlPlaneMetadata(invalidLabels(), invalidAnnotations()).
  1165  				Build(),
  1166  			expectErr: true,
  1167  		},
  1168  		{
  1169  			name: "should not return error for valid namingStrategy.template",
  1170  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1171  				WithInfrastructureClusterTemplate(
  1172  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1173  				WithControlPlaneTemplate(
  1174  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1175  						Build()).
  1176  				WithControlPlaneNamingStrategy(&clusterv1.ControlPlaneClassNamingStrategy{Template: pointer.String("{{ .cluster.name }}-cp-{{ .random }}")}).
  1177  				WithControlPlaneInfrastructureMachineTemplate(
  1178  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1179  						Build()).
  1180  				WithWorkerMachineDeploymentClasses(
  1181  					*builder.MachineDeploymentClass("aa").
  1182  						WithInfrastructureTemplate(
  1183  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1184  						WithBootstrapTemplate(
  1185  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1186  						WithNamingStrategy(&clusterv1.MachineDeploymentClassNamingStrategy{Template: pointer.String("{{ .cluster.name }}-md-{{ .machineDeployment.topologyName }}-{{ .random }}")}).
  1187  						Build()).
  1188  				WithWorkerMachinePoolClasses(
  1189  					*builder.MachinePoolClass("bb").
  1190  						WithInfrastructureTemplate(
  1191  							builder.InfrastructureMachinePoolTemplate(metav1.NamespaceDefault, "infra2").Build()).
  1192  						WithBootstrapTemplate(
  1193  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap2").Build()).
  1194  						WithNamingStrategy(&clusterv1.MachinePoolClassNamingStrategy{Template: pointer.String("{{ .cluster.name }}-md-{{ .machinePool.topologyName }}-{{ .random }}")}).
  1195  						Build()).
  1196  				Build(),
  1197  			expectErr: false,
  1198  		},
  1199  		{
  1200  			name: "should return error for invalid ControlPlane namingStrategy.template",
  1201  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1202  				WithInfrastructureClusterTemplate(
  1203  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1204  				WithControlPlaneTemplate(
  1205  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1206  						Build()).
  1207  				WithControlPlaneNamingStrategy(&clusterv1.ControlPlaneClassNamingStrategy{Template: pointer.String("template-cp-{{ .invalidkey }}")}).
  1208  				WithControlPlaneInfrastructureMachineTemplate(
  1209  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1210  						Build()).
  1211  				Build(),
  1212  			expectErr: true,
  1213  		},
  1214  		{
  1215  			name: "should return error for ControlPlane namingStrategy.template when the generated name does not conform to RFC 1123",
  1216  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1217  				WithInfrastructureClusterTemplate(
  1218  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1219  				WithControlPlaneTemplate(
  1220  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1221  						Build()).
  1222  				WithControlPlaneNamingStrategy(&clusterv1.ControlPlaneClassNamingStrategy{Template: pointer.String("template-cp-{{ .cluster.name }}-")}).
  1223  				WithControlPlaneInfrastructureMachineTemplate(
  1224  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1225  						Build()).
  1226  				Build(),
  1227  			expectErr: true,
  1228  		},
  1229  		{
  1230  			name: "should return error for invalid MachineDeployment namingStrategy.template",
  1231  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1232  				WithInfrastructureClusterTemplate(
  1233  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1234  				WithControlPlaneTemplate(
  1235  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1236  						Build()).
  1237  				WithControlPlaneInfrastructureMachineTemplate(
  1238  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1239  						Build()).
  1240  				WithWorkerMachineDeploymentClasses(
  1241  					*builder.MachineDeploymentClass("aa").
  1242  						WithInfrastructureTemplate(
  1243  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1244  						WithBootstrapTemplate(
  1245  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1246  						WithNamingStrategy(&clusterv1.MachineDeploymentClassNamingStrategy{Template: pointer.String("template-md-{{ .cluster.name")}).
  1247  						Build()).
  1248  				Build(),
  1249  			expectErr: true,
  1250  		},
  1251  		{
  1252  			name: "should return error for invalid MachineDeployment namingStrategy.template when the generated name does not conform to RFC 1123",
  1253  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1254  				WithInfrastructureClusterTemplate(
  1255  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1256  				WithControlPlaneTemplate(
  1257  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1258  						Build()).
  1259  				WithControlPlaneInfrastructureMachineTemplate(
  1260  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1261  						Build()).
  1262  				WithWorkerMachineDeploymentClasses(
  1263  					*builder.MachineDeploymentClass("aa").
  1264  						WithInfrastructureTemplate(
  1265  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1266  						WithBootstrapTemplate(
  1267  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1268  						WithNamingStrategy(&clusterv1.MachineDeploymentClassNamingStrategy{Template: pointer.String("template-md-{{ .cluster.name }}-")}).
  1269  						Build()).
  1270  				Build(),
  1271  			expectErr: true,
  1272  		},
  1273  		{
  1274  			name: "should return error for invalid MachinePool namingStrategy.template",
  1275  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1276  				WithInfrastructureClusterTemplate(
  1277  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1278  				WithControlPlaneTemplate(
  1279  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1280  						Build()).
  1281  				WithControlPlaneInfrastructureMachineTemplate(
  1282  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1283  						Build()).
  1284  				WithWorkerMachinePoolClasses(
  1285  					*builder.MachinePoolClass("bb").
  1286  						WithInfrastructureTemplate(
  1287  							builder.InfrastructureMachinePoolTemplate(metav1.NamespaceDefault, "infra2").Build()).
  1288  						WithBootstrapTemplate(
  1289  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap2").Build()).
  1290  						WithNamingStrategy(&clusterv1.MachinePoolClassNamingStrategy{Template: pointer.String("template-mp-{{ .cluster.name")}).
  1291  						Build()).
  1292  				Build(),
  1293  			expectErr: true,
  1294  		},
  1295  		{
  1296  			name: "should return error for invalid MachinePool namingStrategy.template when the generated name does not conform to RFC 1123",
  1297  			in: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1298  				WithInfrastructureClusterTemplate(
  1299  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1300  				WithControlPlaneTemplate(
  1301  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1302  						Build()).
  1303  				WithControlPlaneInfrastructureMachineTemplate(
  1304  					builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "cpInfra1").
  1305  						Build()).
  1306  				WithWorkerMachinePoolClasses(
  1307  					*builder.MachinePoolClass("bb").
  1308  						WithInfrastructureTemplate(
  1309  							builder.InfrastructureMachinePoolTemplate(metav1.NamespaceDefault, "infra2").Build()).
  1310  						WithBootstrapTemplate(
  1311  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap2").Build()).
  1312  						WithNamingStrategy(&clusterv1.MachinePoolClassNamingStrategy{Template: pointer.String("template-mp-{{ .cluster.name }}-")}).
  1313  						Build()).
  1314  				Build(),
  1315  			expectErr: true,
  1316  		},
  1317  	}
  1318  
  1319  	for _, tt := range tests {
  1320  		t.Run(tt.name, func(t *testing.T) {
  1321  			g := NewWithT(t)
  1322  
  1323  			// Sets up the fakeClient for the test case.
  1324  			fakeClient := fake.NewClientBuilder().
  1325  				WithScheme(fakeScheme).
  1326  				WithIndex(&clusterv1.Cluster{}, index.ClusterClassNameField, index.ClusterByClusterClassClassName).
  1327  				Build()
  1328  
  1329  			// Create the webhook and add the fakeClient as its client.
  1330  			webhook := &ClusterClass{Client: fakeClient}
  1331  			err := webhook.validate(ctx, tt.old, tt.in)
  1332  			if tt.expectErr {
  1333  				g.Expect(err).To(HaveOccurred())
  1334  				return
  1335  			}
  1336  			g.Expect(err).ToNot(HaveOccurred())
  1337  		})
  1338  	}
  1339  }
  1340  
  1341  func TestClusterClassValidationWithClusterAwareChecks(t *testing.T) {
  1342  	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create or update ClusterClasses.
  1343  	// Enabling the feature flag temporarily for this test.
  1344  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
  1345  
  1346  	tests := []struct {
  1347  		name            string
  1348  		oldClusterClass *clusterv1.ClusterClass
  1349  		newClusterClass *clusterv1.ClusterClass
  1350  		clusters        []client.Object
  1351  		expectErr       bool
  1352  	}{
  1353  		{
  1354  			name: "pass if a MachineDeploymentClass not in use gets removed",
  1355  			clusters: []client.Object{
  1356  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1357  					WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}).
  1358  					WithTopology(
  1359  						builder.ClusterTopology().
  1360  							WithClass("class1").
  1361  							WithMachineDeployment(
  1362  								builder.MachineDeploymentTopology("workers1").
  1363  									WithClass("bb").
  1364  									Build(),
  1365  							).
  1366  							Build()).
  1367  					Build(),
  1368  			},
  1369  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1370  				WithInfrastructureClusterTemplate(
  1371  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1372  				WithControlPlaneTemplate(
  1373  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1374  						Build()).
  1375  				WithWorkerMachineDeploymentClasses(
  1376  					*builder.MachineDeploymentClass("aa").
  1377  						WithInfrastructureTemplate(
  1378  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1379  						WithBootstrapTemplate(
  1380  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1381  						Build(),
  1382  					*builder.MachineDeploymentClass("bb").
  1383  						WithInfrastructureTemplate(
  1384  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1385  						WithBootstrapTemplate(
  1386  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1387  						Build()).
  1388  				Build(),
  1389  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1390  				WithInfrastructureClusterTemplate(
  1391  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1392  				WithControlPlaneTemplate(
  1393  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1394  						Build()).
  1395  				WithWorkerMachineDeploymentClasses(
  1396  					*builder.MachineDeploymentClass("bb").
  1397  						WithInfrastructureTemplate(
  1398  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1399  						WithBootstrapTemplate(
  1400  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1401  						Build()).
  1402  				Build(),
  1403  			expectErr: false,
  1404  		},
  1405  		{
  1406  			name: "error if a MachineDeploymentClass in use gets removed",
  1407  			clusters: []client.Object{
  1408  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1409  					WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}).
  1410  					WithTopology(
  1411  						builder.ClusterTopology().
  1412  							WithClass("class1").
  1413  							WithMachineDeployment(
  1414  								builder.MachineDeploymentTopology("workers1").
  1415  									WithClass("bb").
  1416  									Build(),
  1417  							).
  1418  							Build()).
  1419  					Build(),
  1420  			},
  1421  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1422  				WithInfrastructureClusterTemplate(
  1423  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1424  				WithControlPlaneTemplate(
  1425  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1426  						Build()).
  1427  				WithWorkerMachineDeploymentClasses(
  1428  					*builder.MachineDeploymentClass("aa").
  1429  						WithInfrastructureTemplate(
  1430  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1431  						WithBootstrapTemplate(
  1432  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1433  						Build(),
  1434  					*builder.MachineDeploymentClass("bb").
  1435  						WithInfrastructureTemplate(
  1436  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1437  						WithBootstrapTemplate(
  1438  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1439  						Build()).
  1440  				Build(),
  1441  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1442  				WithInfrastructureClusterTemplate(
  1443  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1444  				WithControlPlaneTemplate(
  1445  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1446  						Build()).
  1447  				WithWorkerMachineDeploymentClasses(
  1448  					*builder.MachineDeploymentClass("aa").
  1449  						WithInfrastructureTemplate(
  1450  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1451  						WithBootstrapTemplate(
  1452  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1453  						Build()).
  1454  				Build(),
  1455  			expectErr: true,
  1456  		},
  1457  		{
  1458  			name: "error if many MachineDeploymentClasses, used in multiple Clusters using the modified ClusterClass, are removed",
  1459  			clusters: []client.Object{
  1460  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1461  					WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}).
  1462  					WithTopology(
  1463  						builder.ClusterTopology().
  1464  							WithClass("class1").
  1465  							WithMachineDeployment(
  1466  								builder.MachineDeploymentTopology("workers1").
  1467  									WithClass("bb").
  1468  									Build(),
  1469  							).
  1470  							WithMachineDeployment(
  1471  								builder.MachineDeploymentTopology("workers2").
  1472  									WithClass("aa").
  1473  									Build(),
  1474  							).
  1475  							Build()).
  1476  					Build(),
  1477  				builder.Cluster(metav1.NamespaceDefault, "cluster2").
  1478  					WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}).
  1479  					WithTopology(
  1480  						builder.ClusterTopology().
  1481  							WithClass("class1").
  1482  							WithMachineDeployment(
  1483  								builder.MachineDeploymentTopology("workers1").
  1484  									WithClass("aa").
  1485  									Build(),
  1486  							).
  1487  							WithMachineDeployment(
  1488  								builder.MachineDeploymentTopology("workers2").
  1489  									WithClass("aa").
  1490  									Build(),
  1491  							).
  1492  							Build()).
  1493  					Build(),
  1494  				builder.Cluster(metav1.NamespaceDefault, "cluster3").
  1495  					WithLabels(map[string]string{clusterv1.ClusterTopologyOwnedLabel: ""}).
  1496  					WithTopology(
  1497  						builder.ClusterTopology().
  1498  							WithClass("class1").
  1499  							WithMachineDeployment(
  1500  								builder.MachineDeploymentTopology("workers1").
  1501  									WithClass("bb").
  1502  									Build(),
  1503  							).
  1504  							Build()).
  1505  					Build(),
  1506  			},
  1507  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1508  				WithInfrastructureClusterTemplate(
  1509  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1510  				WithControlPlaneTemplate(
  1511  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1512  						Build()).
  1513  				WithWorkerMachineDeploymentClasses(
  1514  					*builder.MachineDeploymentClass("aa").
  1515  						WithInfrastructureTemplate(
  1516  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1517  						WithBootstrapTemplate(
  1518  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1519  						Build(),
  1520  					*builder.MachineDeploymentClass("bb").
  1521  						WithInfrastructureTemplate(
  1522  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1523  						WithBootstrapTemplate(
  1524  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1525  						Build()).
  1526  				Build(),
  1527  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "class1").
  1528  				WithInfrastructureClusterTemplate(
  1529  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1530  				WithControlPlaneTemplate(
  1531  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1532  						Build()).
  1533  				WithWorkerMachineDeploymentClasses(
  1534  					*builder.MachineDeploymentClass("bb").
  1535  						WithInfrastructureTemplate(
  1536  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1537  						WithBootstrapTemplate(
  1538  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1539  						Build()).
  1540  				Build(),
  1541  			expectErr: true,
  1542  		},
  1543  		{
  1544  			name: "error if a control plane MachineHealthCheck that is in use by a cluster is removed",
  1545  			clusters: []client.Object{
  1546  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1547  					WithTopology(builder.ClusterTopology().
  1548  						WithClass("clusterclass1").
  1549  						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
  1550  							Enable: pointer.Bool(true),
  1551  						}).
  1552  						Build()).
  1553  					Build(),
  1554  			},
  1555  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1556  				WithInfrastructureClusterTemplate(
  1557  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1558  				WithControlPlaneTemplate(
  1559  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1560  						Build()).
  1561  				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{
  1562  					UnhealthyConditions: []clusterv1.UnhealthyCondition{
  1563  						{
  1564  							Type:    corev1.NodeReady,
  1565  							Status:  corev1.ConditionUnknown,
  1566  							Timeout: metav1.Duration{Duration: 5 * time.Minute},
  1567  						},
  1568  					},
  1569  				}).
  1570  				Build(),
  1571  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1572  				WithInfrastructureClusterTemplate(
  1573  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1574  				WithControlPlaneTemplate(
  1575  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1576  						Build()).
  1577  				Build(),
  1578  			expectErr: true,
  1579  		},
  1580  		{
  1581  			name: "pass if a control plane MachineHealthCheck is removed but no cluster enforces it",
  1582  			clusters: []client.Object{
  1583  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1584  					WithTopology(builder.ClusterTopology().
  1585  						WithClass("clusterclass1").
  1586  						Build()).
  1587  					Build(),
  1588  			},
  1589  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1590  				WithInfrastructureClusterTemplate(
  1591  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1592  				WithControlPlaneTemplate(
  1593  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1594  						Build()).
  1595  				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
  1596  				Build(),
  1597  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1598  				WithInfrastructureClusterTemplate(
  1599  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1600  				WithControlPlaneTemplate(
  1601  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1602  						Build()).
  1603  				Build(),
  1604  			expectErr: false,
  1605  		},
  1606  		{
  1607  			name: "pass if a control plane MachineHealthCheck is removed when clusters are not using it (clusters have overrides in topology)",
  1608  			clusters: []client.Object{
  1609  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1610  					WithTopology(builder.ClusterTopology().
  1611  						WithClass("clusterclass1").
  1612  						WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
  1613  							Enable: pointer.Bool(true),
  1614  							MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
  1615  								UnhealthyConditions: []clusterv1.UnhealthyCondition{
  1616  									{
  1617  										Type:    corev1.NodeReady,
  1618  										Status:  corev1.ConditionUnknown,
  1619  										Timeout: metav1.Duration{Duration: 5 * time.Minute},
  1620  									},
  1621  								},
  1622  							},
  1623  						}).
  1624  						Build()).
  1625  					Build(),
  1626  			},
  1627  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1628  				WithInfrastructureClusterTemplate(
  1629  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1630  				WithControlPlaneTemplate(
  1631  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1632  						Build()).
  1633  				WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}).
  1634  				Build(),
  1635  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1636  				WithInfrastructureClusterTemplate(
  1637  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1638  				WithControlPlaneTemplate(
  1639  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1640  						Build()).
  1641  				Build(),
  1642  			expectErr: false,
  1643  		},
  1644  		{
  1645  			name: "error if a MachineDeployment MachineHealthCheck that is in use by a cluster is removed",
  1646  			clusters: []client.Object{
  1647  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1648  					WithTopology(builder.ClusterTopology().
  1649  						WithClass("clusterclass1").
  1650  						WithMachineDeployment(builder.MachineDeploymentTopology("md1").
  1651  							WithClass("mdclass1").
  1652  							WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
  1653  								Enable: pointer.Bool(true),
  1654  							}).
  1655  							Build()).
  1656  						Build()).
  1657  					Build(),
  1658  			},
  1659  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1660  				WithInfrastructureClusterTemplate(
  1661  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1662  				WithControlPlaneTemplate(
  1663  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1664  						Build()).
  1665  				WithWorkerMachineDeploymentClasses(
  1666  					*builder.MachineDeploymentClass("mdclass1").
  1667  						WithInfrastructureTemplate(
  1668  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1669  						WithBootstrapTemplate(
  1670  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1671  						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
  1672  						Build(),
  1673  				).
  1674  				Build(),
  1675  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1676  				WithInfrastructureClusterTemplate(
  1677  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1678  				WithControlPlaneTemplate(
  1679  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1680  						Build()).
  1681  				WithWorkerMachineDeploymentClasses(
  1682  					*builder.MachineDeploymentClass("mdclass1").
  1683  						WithInfrastructureTemplate(
  1684  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1685  						WithBootstrapTemplate(
  1686  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1687  						Build(),
  1688  				).
  1689  				Build(),
  1690  			expectErr: true,
  1691  		},
  1692  		{
  1693  			name: "pass if a MachineDeployment MachineHealthCheck is removed but no cluster enforces it",
  1694  			clusters: []client.Object{
  1695  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1696  					WithTopology(builder.ClusterTopology().
  1697  						WithClass("clusterclass1").
  1698  						WithMachineDeployment(builder.MachineDeploymentTopology("md1").
  1699  							WithClass("mdclass1").
  1700  							Build()).
  1701  						Build()).
  1702  					Build(),
  1703  			},
  1704  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1705  				WithInfrastructureClusterTemplate(
  1706  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1707  				WithControlPlaneTemplate(
  1708  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1709  						Build()).
  1710  				WithWorkerMachineDeploymentClasses(
  1711  					*builder.MachineDeploymentClass("mdclass1").
  1712  						WithInfrastructureTemplate(
  1713  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1714  						WithBootstrapTemplate(
  1715  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1716  						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
  1717  						Build(),
  1718  				).
  1719  				Build(),
  1720  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1721  				WithInfrastructureClusterTemplate(
  1722  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1723  				WithControlPlaneTemplate(
  1724  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1725  						Build()).
  1726  				WithWorkerMachineDeploymentClasses(
  1727  					*builder.MachineDeploymentClass("mdclass1").
  1728  						WithInfrastructureTemplate(
  1729  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1730  						WithBootstrapTemplate(
  1731  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1732  						Build(),
  1733  				).
  1734  				Build(),
  1735  			expectErr: false,
  1736  		},
  1737  		{
  1738  			name: "pass if a MachineDeployment MachineHealthCheck is removed when clusters are not using it (clusters have overrides in topology)",
  1739  			clusters: []client.Object{
  1740  				builder.Cluster(metav1.NamespaceDefault, "cluster1").
  1741  					WithTopology(builder.ClusterTopology().
  1742  						WithClass("clusterclass1").
  1743  						WithMachineDeployment(builder.MachineDeploymentTopology("md1").
  1744  							WithClass("mdclass1").
  1745  							WithMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{
  1746  								Enable: pointer.Bool(true),
  1747  								MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{
  1748  									UnhealthyConditions: []clusterv1.UnhealthyCondition{
  1749  										{
  1750  											Type:    corev1.NodeReady,
  1751  											Status:  corev1.ConditionUnknown,
  1752  											Timeout: metav1.Duration{Duration: 5 * time.Minute},
  1753  										},
  1754  									},
  1755  								},
  1756  							}).
  1757  							Build()).
  1758  						Build()).
  1759  					Build(),
  1760  			},
  1761  			oldClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1762  				WithInfrastructureClusterTemplate(
  1763  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1764  				WithControlPlaneTemplate(
  1765  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1766  						Build()).
  1767  				WithWorkerMachineDeploymentClasses(
  1768  					*builder.MachineDeploymentClass("mdclass1").
  1769  						WithInfrastructureTemplate(
  1770  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1771  						WithBootstrapTemplate(
  1772  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1773  						WithMachineHealthCheckClass(&clusterv1.MachineHealthCheckClass{}).
  1774  						Build(),
  1775  				).
  1776  				Build(),
  1777  			newClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "clusterclass1").
  1778  				WithInfrastructureClusterTemplate(
  1779  					builder.InfrastructureClusterTemplate(metav1.NamespaceDefault, "inf").Build()).
  1780  				WithControlPlaneTemplate(
  1781  					builder.ControlPlaneTemplate(metav1.NamespaceDefault, "cp1").
  1782  						Build()).
  1783  				WithWorkerMachineDeploymentClasses(
  1784  					*builder.MachineDeploymentClass("mdclass1").
  1785  						WithInfrastructureTemplate(
  1786  							builder.InfrastructureMachineTemplate(metav1.NamespaceDefault, "infra1").Build()).
  1787  						WithBootstrapTemplate(
  1788  							builder.BootstrapTemplate(metav1.NamespaceDefault, "bootstrap1").Build()).
  1789  						Build(),
  1790  				).
  1791  				Build(),
  1792  			expectErr: false,
  1793  		},
  1794  	}
  1795  
  1796  	for _, tt := range tests {
  1797  		t.Run(tt.name, func(t *testing.T) {
  1798  			g := NewWithT(t)
  1799  
  1800  			// Sets up the fakeClient for the test case.
  1801  			fakeClient := fake.NewClientBuilder().
  1802  				WithScheme(fakeScheme).
  1803  				WithObjects(tt.clusters...).
  1804  				WithIndex(&clusterv1.Cluster{}, index.ClusterClassNameField, index.ClusterByClusterClassClassName).
  1805  				Build()
  1806  
  1807  			// Create the webhook and add the fakeClient as its client.
  1808  			webhook := &ClusterClass{Client: fakeClient}
  1809  			err := webhook.validate(ctx, tt.oldClusterClass, tt.newClusterClass)
  1810  			if tt.expectErr {
  1811  				g.Expect(err).To(HaveOccurred())
  1812  				return
  1813  			}
  1814  			g.Expect(err).ToNot(HaveOccurred())
  1815  		})
  1816  	}
  1817  }
  1818  
  1819  func invalidLabels() map[string]string {
  1820  	return map[string]string{
  1821  		"foo":          "$invalid-key",
  1822  		"bar":          strings.Repeat("a", 64) + "too-long-value",
  1823  		"/invalid-key": "foo",
  1824  	}
  1825  }
  1826  
  1827  func invalidAnnotations() map[string]string {
  1828  	return map[string]string{
  1829  		"/invalid-key": "foo",
  1830  	}
  1831  }