sigs.k8s.io/cluster-api@v1.7.1/exp/internal/controllers/machinepool_controller_phases_test.go (about)

     1  /*
     2  Copyright 2019 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 controllers
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/go-logr/logr"
    25  	. "github.com/onsi/gomega"
    26  	corev1 "k8s.io/api/core/v1"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/client-go/tools/record"
    32  	"k8s.io/utils/ptr"
    33  	ctrl "sigs.k8s.io/controller-runtime"
    34  	"sigs.k8s.io/controller-runtime/pkg/client"
    35  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    36  	"sigs.k8s.io/controller-runtime/pkg/log"
    37  
    38  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    39  	"sigs.k8s.io/cluster-api/controllers/external"
    40  	"sigs.k8s.io/cluster-api/controllers/remote"
    41  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    42  	"sigs.k8s.io/cluster-api/internal/test/builder"
    43  	"sigs.k8s.io/cluster-api/internal/util/ssa"
    44  	"sigs.k8s.io/cluster-api/util/kubeconfig"
    45  	"sigs.k8s.io/cluster-api/util/labels/format"
    46  )
    47  
    48  const (
    49  	clusterName    = "test-cluster"
    50  	wrongNamespace = "wrong-namespace"
    51  )
    52  
    53  func TestReconcileMachinePoolPhases(t *testing.T) {
    54  	deletionTimestamp := metav1.Now()
    55  
    56  	var defaultKubeconfigSecret *corev1.Secret
    57  	defaultCluster := &clusterv1.Cluster{
    58  		ObjectMeta: metav1.ObjectMeta{
    59  			Name:      clusterName,
    60  			Namespace: metav1.NamespaceDefault,
    61  		},
    62  	}
    63  
    64  	defaultMachinePool := expv1.MachinePool{
    65  		ObjectMeta: metav1.ObjectMeta{
    66  			Name:      "machinepool-test",
    67  			Namespace: metav1.NamespaceDefault,
    68  		},
    69  		Spec: expv1.MachinePoolSpec{
    70  			ClusterName: defaultCluster.Name,
    71  			Replicas:    ptr.To[int32](1),
    72  			Template: clusterv1.MachineTemplateSpec{
    73  				Spec: clusterv1.MachineSpec{
    74  					Bootstrap: clusterv1.Bootstrap{
    75  						ConfigRef: &corev1.ObjectReference{
    76  							APIVersion: builder.BootstrapGroupVersion.String(),
    77  							Kind:       builder.TestBootstrapConfigKind,
    78  							Name:       "bootstrap-config1",
    79  						},
    80  					},
    81  					InfrastructureRef: corev1.ObjectReference{
    82  						APIVersion: builder.InfrastructureGroupVersion.String(),
    83  						Kind:       builder.TestInfrastructureMachineTemplateKind,
    84  						Name:       "infra-config1",
    85  					},
    86  				},
    87  			},
    88  		},
    89  	}
    90  
    91  	defaultBootstrap := &unstructured.Unstructured{
    92  		Object: map[string]interface{}{
    93  			"kind":       builder.TestBootstrapConfigKind,
    94  			"apiVersion": builder.BootstrapGroupVersion.String(),
    95  			"metadata": map[string]interface{}{
    96  				"name":      "bootstrap-config1",
    97  				"namespace": metav1.NamespaceDefault,
    98  			},
    99  			"spec":   map[string]interface{}{},
   100  			"status": map[string]interface{}{},
   101  		},
   102  	}
   103  
   104  	defaultInfra := &unstructured.Unstructured{
   105  		Object: map[string]interface{}{
   106  			"kind":       builder.TestInfrastructureMachineTemplateKind,
   107  			"apiVersion": builder.InfrastructureGroupVersion.String(),
   108  			"metadata": map[string]interface{}{
   109  				"name":      "infra-config1",
   110  				"namespace": metav1.NamespaceDefault,
   111  			},
   112  			"spec": map[string]interface{}{
   113  				"providerIDList": []interface{}{
   114  					"test://id-1",
   115  				},
   116  			},
   117  			"status": map[string]interface{}{},
   118  		},
   119  	}
   120  
   121  	t.Run("Should set OwnerReference and cluster name label on external objects", func(t *testing.T) {
   122  		g := NewWithT(t)
   123  
   124  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   125  		machinepool := defaultMachinePool.DeepCopy()
   126  		bootstrapConfig := defaultBootstrap.DeepCopy()
   127  		infraConfig := defaultInfra.DeepCopy()
   128  
   129  		r := &MachinePoolReconciler{
   130  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   131  		}
   132  
   133  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   134  		g.Expect(err).ToNot(HaveOccurred())
   135  		g.Expect(res.Requeue).To(BeFalse())
   136  
   137  		r.reconcilePhase(machinepool)
   138  
   139  		g.Expect(r.Client.Get(ctx, types.NamespacedName{Name: bootstrapConfig.GetName(), Namespace: bootstrapConfig.GetNamespace()}, bootstrapConfig)).To(Succeed())
   140  
   141  		g.Expect(bootstrapConfig.GetOwnerReferences()).To(HaveLen(1))
   142  		g.Expect(bootstrapConfig.GetLabels()[clusterv1.ClusterNameLabel]).To(BeEquivalentTo(clusterName))
   143  
   144  		g.Expect(r.Client.Get(ctx, types.NamespacedName{Name: infraConfig.GetName(), Namespace: infraConfig.GetNamespace()}, infraConfig)).To(Succeed())
   145  
   146  		g.Expect(infraConfig.GetOwnerReferences()).To(HaveLen(1))
   147  		g.Expect(infraConfig.GetLabels()[clusterv1.ClusterNameLabel]).To(BeEquivalentTo(clusterName))
   148  	})
   149  
   150  	t.Run("Should set `Pending` with a new MachinePool", func(t *testing.T) {
   151  		g := NewWithT(t)
   152  
   153  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   154  		machinepool := defaultMachinePool.DeepCopy()
   155  		bootstrapConfig := defaultBootstrap.DeepCopy()
   156  		infraConfig := defaultInfra.DeepCopy()
   157  
   158  		r := &MachinePoolReconciler{
   159  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   160  		}
   161  
   162  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   163  		g.Expect(err).ToNot(HaveOccurred())
   164  		g.Expect(res.Requeue).To(BeFalse())
   165  
   166  		r.reconcilePhase(machinepool)
   167  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhasePending))
   168  	})
   169  
   170  	t.Run("Should set `Provisioning` when bootstrap is ready", func(t *testing.T) {
   171  		g := NewWithT(t)
   172  
   173  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   174  		machinepool := defaultMachinePool.DeepCopy()
   175  		bootstrapConfig := defaultBootstrap.DeepCopy()
   176  		infraConfig := defaultInfra.DeepCopy()
   177  
   178  		// Set bootstrap ready.
   179  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   180  		g.Expect(err).ToNot(HaveOccurred())
   181  
   182  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   183  		g.Expect(err).ToNot(HaveOccurred())
   184  
   185  		r := &MachinePoolReconciler{
   186  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   187  		}
   188  
   189  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   190  		g.Expect(err).ToNot(HaveOccurred())
   191  		g.Expect(res.Requeue).To(BeFalse())
   192  
   193  		r.reconcilePhase(machinepool)
   194  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseProvisioning))
   195  	})
   196  
   197  	t.Run("Should set `Running` when bootstrap and infra is ready", func(t *testing.T) {
   198  		g := NewWithT(t)
   199  
   200  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   201  		machinepool := defaultMachinePool.DeepCopy()
   202  		bootstrapConfig := defaultBootstrap.DeepCopy()
   203  		infraConfig := defaultInfra.DeepCopy()
   204  
   205  		// Set bootstrap ready.
   206  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   207  		g.Expect(err).ToNot(HaveOccurred())
   208  
   209  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   210  		g.Expect(err).ToNot(HaveOccurred())
   211  
   212  		// Set infra ready.
   213  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   214  		g.Expect(err).ToNot(HaveOccurred())
   215  
   216  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
   217  		g.Expect(err).ToNot(HaveOccurred())
   218  
   219  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://machinepool-test-node"}, "spec", "providerIDList")
   220  		g.Expect(err).ToNot(HaveOccurred())
   221  
   222  		err = unstructured.SetNestedField(infraConfig.Object, "us-east-2a", "spec", "failureDomain")
   223  		g.Expect(err).ToNot(HaveOccurred())
   224  
   225  		// Set NodeRef.
   226  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   227  
   228  		fakeClient := fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
   229  		r := &MachinePoolReconciler{
   230  			Client:  fakeClient,
   231  			Tracker: remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), fakeClient, fakeClient, fakeClient.Scheme(), client.ObjectKey{Name: defaultCluster.Name, Namespace: defaultCluster.Namespace}),
   232  		}
   233  
   234  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   235  		g.Expect(err).ToNot(HaveOccurred())
   236  		g.Expect(res.Requeue).To(BeFalse())
   237  
   238  		// Set ReadyReplicas
   239  		machinepool.Status.ReadyReplicas = 1
   240  
   241  		r.reconcilePhase(machinepool)
   242  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
   243  	})
   244  
   245  	t.Run("Should set `Running` when bootstrap, infra, and ready replicas equals spec replicas", func(t *testing.T) {
   246  		g := NewWithT(t)
   247  
   248  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   249  		machinepool := defaultMachinePool.DeepCopy()
   250  		bootstrapConfig := defaultBootstrap.DeepCopy()
   251  		infraConfig := defaultInfra.DeepCopy()
   252  
   253  		// Set bootstrap ready.
   254  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   255  		g.Expect(err).ToNot(HaveOccurred())
   256  
   257  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   258  		g.Expect(err).ToNot(HaveOccurred())
   259  
   260  		// Set infra ready.
   261  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://id-1"}, "spec", "providerIDList")
   262  		g.Expect(err).ToNot(HaveOccurred())
   263  
   264  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   265  		g.Expect(err).ToNot(HaveOccurred())
   266  
   267  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
   268  		g.Expect(err).ToNot(HaveOccurred())
   269  
   270  		err = unstructured.SetNestedField(infraConfig.Object, []interface{}{
   271  			map[string]interface{}{
   272  				"type":    "InternalIP",
   273  				"address": "10.0.0.1",
   274  			},
   275  			map[string]interface{}{
   276  				"type":    "InternalIP",
   277  				"address": "10.0.0.2",
   278  			},
   279  		}, "addresses")
   280  		g.Expect(err).ToNot(HaveOccurred())
   281  
   282  		// Set NodeRef.
   283  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   284  
   285  		fakeClient := fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
   286  		r := &MachinePoolReconciler{
   287  			Client:  fakeClient,
   288  			Tracker: remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), fakeClient, fakeClient, fakeClient.Scheme(), client.ObjectKey{Name: defaultCluster.Name, Namespace: defaultCluster.Namespace}),
   289  		}
   290  
   291  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   292  		g.Expect(err).ToNot(HaveOccurred())
   293  		g.Expect(res.Requeue).To(BeFalse())
   294  
   295  		// Set ReadyReplicas
   296  		machinepool.Status.ReadyReplicas = 1
   297  
   298  		r.reconcilePhase(machinepool)
   299  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
   300  	})
   301  
   302  	t.Run("Should set `Provisioned` when there is a NodeRef but infra is not ready ", func(t *testing.T) {
   303  		g := NewWithT(t)
   304  
   305  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   306  		machinepool := defaultMachinePool.DeepCopy()
   307  		bootstrapConfig := defaultBootstrap.DeepCopy()
   308  		infraConfig := defaultInfra.DeepCopy()
   309  
   310  		// Set bootstrap ready.
   311  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   312  		g.Expect(err).ToNot(HaveOccurred())
   313  
   314  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   315  		g.Expect(err).ToNot(HaveOccurred())
   316  
   317  		// Set NodeRef.
   318  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   319  
   320  		r := &MachinePoolReconciler{
   321  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   322  		}
   323  
   324  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   325  		g.Expect(err).ToNot(HaveOccurred())
   326  		g.Expect(res.Requeue).To(BeFalse())
   327  
   328  		r.reconcilePhase(machinepool)
   329  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseProvisioned))
   330  	})
   331  
   332  	t.Run("Should set `ScalingUp` when infra is scaling up", func(t *testing.T) {
   333  		g := NewWithT(t)
   334  
   335  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   336  		machinepool := defaultMachinePool.DeepCopy()
   337  		bootstrapConfig := defaultBootstrap.DeepCopy()
   338  		infraConfig := defaultInfra.DeepCopy()
   339  
   340  		// Set bootstrap ready.
   341  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   342  		g.Expect(err).ToNot(HaveOccurred())
   343  
   344  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   345  		g.Expect(err).ToNot(HaveOccurred())
   346  
   347  		// Set infra ready.
   348  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://id-1"}, "spec", "providerIDList")
   349  		g.Expect(err).ToNot(HaveOccurred())
   350  
   351  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   352  		g.Expect(err).ToNot(HaveOccurred())
   353  
   354  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
   355  		g.Expect(err).ToNot(HaveOccurred())
   356  
   357  		// Set NodeRef.
   358  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   359  
   360  		fakeClient := fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
   361  		r := &MachinePoolReconciler{
   362  			Client:  fakeClient,
   363  			Tracker: remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), fakeClient, fakeClient, fakeClient.Scheme(), client.ObjectKey{Name: defaultCluster.Name, Namespace: defaultCluster.Namespace}),
   364  		}
   365  
   366  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   367  		g.Expect(err).ToNot(HaveOccurred())
   368  		g.Expect(res.Requeue).To(BeFalse())
   369  
   370  		// Set ReadyReplicas
   371  		machinepool.Status.ReadyReplicas = 1
   372  
   373  		// Scale up
   374  		machinepool.Spec.Replicas = ptr.To[int32](5)
   375  
   376  		r.reconcilePhase(machinepool)
   377  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseScalingUp))
   378  	})
   379  
   380  	t.Run("Should set `ScalingDown` when infra is scaling down", func(t *testing.T) {
   381  		g := NewWithT(t)
   382  
   383  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   384  		machinepool := defaultMachinePool.DeepCopy()
   385  		bootstrapConfig := defaultBootstrap.DeepCopy()
   386  		infraConfig := defaultInfra.DeepCopy()
   387  
   388  		// Set bootstrap ready.
   389  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   390  		g.Expect(err).ToNot(HaveOccurred())
   391  
   392  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   393  		g.Expect(err).ToNot(HaveOccurred())
   394  
   395  		// Set infra ready.
   396  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://id-1"}, "spec", "providerIDList")
   397  		g.Expect(err).ToNot(HaveOccurred())
   398  
   399  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   400  		g.Expect(err).ToNot(HaveOccurred())
   401  
   402  		err = unstructured.SetNestedField(infraConfig.Object, int64(4), "status", "replicas")
   403  		g.Expect(err).ToNot(HaveOccurred())
   404  
   405  		machinepool.Spec.Replicas = ptr.To[int32](4)
   406  
   407  		// Set NodeRef.
   408  		machinepool.Status.NodeRefs = []corev1.ObjectReference{
   409  			{Kind: "Node", Name: "machinepool-test-node-0"},
   410  			{Kind: "Node", Name: "machinepool-test-node-1"},
   411  			{Kind: "Node", Name: "machinepool-test-node-2"},
   412  			{Kind: "Node", Name: "machinepool-test-node-3"},
   413  		}
   414  
   415  		fakeClient := fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
   416  		r := &MachinePoolReconciler{
   417  			Client:  fakeClient,
   418  			Tracker: remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), fakeClient, fakeClient, fakeClient.Scheme(), client.ObjectKey{Name: defaultCluster.Name, Namespace: defaultCluster.Namespace}),
   419  		}
   420  
   421  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   422  		g.Expect(err).ToNot(HaveOccurred())
   423  		g.Expect(res.Requeue).To(BeFalse())
   424  
   425  		// Set ReadyReplicas
   426  		machinepool.Status.ReadyReplicas = 4
   427  
   428  		// Scale down
   429  		machinepool.Spec.Replicas = ptr.To[int32](1)
   430  
   431  		r.reconcilePhase(machinepool)
   432  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseScalingDown))
   433  	})
   434  
   435  	t.Run("Should set `Deleting` when MachinePool is being deleted", func(t *testing.T) {
   436  		g := NewWithT(t)
   437  
   438  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   439  		machinepool := defaultMachinePool.DeepCopy()
   440  		bootstrapConfig := defaultBootstrap.DeepCopy()
   441  		infraConfig := defaultInfra.DeepCopy()
   442  
   443  		// Set bootstrap ready.
   444  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   445  		g.Expect(err).ToNot(HaveOccurred())
   446  
   447  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   448  		g.Expect(err).ToNot(HaveOccurred())
   449  
   450  		// Set infra ready.
   451  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://id-1"}, "spec", "providerIDList")
   452  		g.Expect(err).ToNot(HaveOccurred())
   453  
   454  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   455  		g.Expect(err).ToNot(HaveOccurred())
   456  
   457  		err = unstructured.SetNestedField(infraConfig.Object, []interface{}{
   458  			map[string]interface{}{
   459  				"type":    "InternalIP",
   460  				"address": "10.0.0.1",
   461  			},
   462  			map[string]interface{}{
   463  				"type":    "InternalIP",
   464  				"address": "10.0.0.2",
   465  			},
   466  		}, "addresses")
   467  		g.Expect(err).ToNot(HaveOccurred())
   468  
   469  		// Set NodeRef.
   470  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   471  
   472  		// Set Deletion Timestamp.
   473  		machinepool.SetDeletionTimestamp(&deletionTimestamp)
   474  		machinepool.Finalizers = []string{expv1.MachinePoolFinalizer}
   475  
   476  		r := &MachinePoolReconciler{
   477  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   478  		}
   479  
   480  		res, err := r.reconcile(ctx, defaultCluster, machinepool)
   481  		g.Expect(err).ToNot(HaveOccurred())
   482  		g.Expect(res.Requeue).To(BeFalse())
   483  
   484  		r.reconcilePhase(machinepool)
   485  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseDeleting))
   486  	})
   487  
   488  	t.Run("Should keep `Running` when MachinePool bootstrap config is changed to another ready one", func(t *testing.T) {
   489  		g := NewWithT(t)
   490  
   491  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   492  		machinePool := defaultMachinePool.DeepCopy()
   493  		bootstrapConfig := defaultBootstrap.DeepCopy()
   494  		infraConfig := defaultInfra.DeepCopy()
   495  
   496  		// Set bootstrap ready.
   497  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   498  		g.Expect(err).ToNot(HaveOccurred())
   499  
   500  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   501  		g.Expect(err).ToNot(HaveOccurred())
   502  
   503  		// Set infra ready.
   504  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://id-1"}, "spec", "providerIDList")
   505  		g.Expect(err).ToNot(HaveOccurred())
   506  
   507  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   508  		g.Expect(err).ToNot(HaveOccurred())
   509  
   510  		err = unstructured.SetNestedField(infraConfig.Object, []interface{}{
   511  			map[string]interface{}{
   512  				"type":    "InternalIP",
   513  				"address": "10.0.0.1",
   514  			},
   515  			map[string]interface{}{
   516  				"type":    "InternalIP",
   517  				"address": "10.0.0.2",
   518  			},
   519  		}, "addresses")
   520  		g.Expect(err).ToNot(HaveOccurred())
   521  
   522  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
   523  		g.Expect(err).ToNot(HaveOccurred())
   524  
   525  		// Set NodeRef.
   526  		machinePool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   527  
   528  		// Set replicas to fully reconciled
   529  		machinePool.Spec.ProviderIDList = []string{"test://id-1"}
   530  		machinePool.Status.ReadyReplicas = 1
   531  		machinePool.Status.Replicas = 1
   532  
   533  		r := &MachinePoolReconciler{
   534  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinePool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   535  		}
   536  
   537  		res, err := r.reconcile(ctx, defaultCluster, machinePool)
   538  		g.Expect(err).ToNot(HaveOccurred())
   539  		g.Expect(res.Requeue).To(BeFalse())
   540  
   541  		r.reconcilePhase(machinePool)
   542  		g.Expect(machinePool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
   543  		g.Expect(*machinePool.Spec.Template.Spec.Bootstrap.DataSecretName).To(Equal("secret-data"))
   544  
   545  		// Change bootstrap reference.
   546  		newBootstrapConfig := defaultBootstrap.DeepCopy()
   547  		newBootstrapConfig.SetName("bootstrap-config2")
   548  		err = unstructured.SetNestedField(newBootstrapConfig.Object, true, "status", "ready")
   549  		g.Expect(err).ToNot(HaveOccurred())
   550  		err = unstructured.SetNestedField(newBootstrapConfig.Object, "secret-data-new", "status", "dataSecretName")
   551  		g.Expect(err).ToNot(HaveOccurred())
   552  		err = r.Client.Create(ctx, newBootstrapConfig)
   553  		g.Expect(err).ToNot(HaveOccurred())
   554  		machinePool.Spec.Template.Spec.Bootstrap.ConfigRef.Name = newBootstrapConfig.GetName()
   555  
   556  		// Reconcile again. The new bootstrap config should be used.
   557  		res, err = r.reconcile(ctx, defaultCluster, machinePool)
   558  		g.Expect(err).ToNot(HaveOccurred())
   559  		g.Expect(res.Requeue).To(BeFalse())
   560  
   561  		r.reconcilePhase(machinePool)
   562  		g.Expect(*machinePool.Spec.Template.Spec.Bootstrap.DataSecretName).To(Equal("secret-data-new"))
   563  		g.Expect(machinePool.Status.BootstrapReady).To(BeTrue())
   564  		g.Expect(machinePool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
   565  	})
   566  
   567  	t.Run("Should keep `Running` when MachinePool bootstrap config is changed to a non-ready one", func(t *testing.T) {
   568  		g := NewWithT(t)
   569  
   570  		defaultKubeconfigSecret = kubeconfig.GenerateSecret(defaultCluster, kubeconfig.FromEnvTestConfig(env.Config, defaultCluster))
   571  		machinePool := defaultMachinePool.DeepCopy()
   572  		bootstrapConfig := defaultBootstrap.DeepCopy()
   573  		infraConfig := defaultInfra.DeepCopy()
   574  
   575  		// Set bootstrap ready
   576  		err := unstructured.SetNestedField(bootstrapConfig.Object, true, "status", "ready")
   577  		g.Expect(err).ToNot(HaveOccurred())
   578  
   579  		err = unstructured.SetNestedField(bootstrapConfig.Object, "secret-data", "status", "dataSecretName")
   580  		g.Expect(err).ToNot(HaveOccurred())
   581  
   582  		// Set infra ready
   583  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://id-1"}, "spec", "providerIDList")
   584  		g.Expect(err).ToNot(HaveOccurred())
   585  
   586  		err = unstructured.SetNestedField(infraConfig.Object, true, "status", "ready")
   587  		g.Expect(err).ToNot(HaveOccurred())
   588  
   589  		err = unstructured.SetNestedField(infraConfig.Object, []interface{}{
   590  			map[string]interface{}{
   591  				"type":    "InternalIP",
   592  				"address": "10.0.0.1",
   593  			},
   594  			map[string]interface{}{
   595  				"type":    "InternalIP",
   596  				"address": "10.0.0.2",
   597  			},
   598  		}, "addresses")
   599  		g.Expect(err).ToNot(HaveOccurred())
   600  
   601  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
   602  		g.Expect(err).ToNot(HaveOccurred())
   603  
   604  		// Set NodeRef
   605  		machinePool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
   606  
   607  		// Set replicas to fully reconciled
   608  		machinePool.Spec.ProviderIDList = []string{"test://id-1"}
   609  		machinePool.Status.ReadyReplicas = 1
   610  		machinePool.Status.Replicas = 1
   611  
   612  		r := &MachinePoolReconciler{
   613  			Client: fake.NewClientBuilder().WithObjects(defaultCluster, defaultKubeconfigSecret, machinePool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   614  		}
   615  
   616  		res, err := r.reconcile(ctx, defaultCluster, machinePool)
   617  		g.Expect(err).ToNot(HaveOccurred())
   618  		g.Expect(res.Requeue).To(BeFalse())
   619  
   620  		r.reconcilePhase(machinePool)
   621  		g.Expect(machinePool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
   622  		g.Expect(*machinePool.Spec.Template.Spec.Bootstrap.DataSecretName).To(Equal("secret-data"))
   623  
   624  		// Change bootstrap reference
   625  		newBootstrapConfig := defaultBootstrap.DeepCopy()
   626  		newBootstrapConfig.SetName("bootstrap-config2")
   627  		err = unstructured.SetNestedField(newBootstrapConfig.Object, false, "status", "ready")
   628  		g.Expect(err).ToNot(HaveOccurred())
   629  		// Fill the `dataSecretName` so we can check if the machine pool uses the non-ready secret immediately or,
   630  		// as it should, not yet
   631  		err = unstructured.SetNestedField(newBootstrapConfig.Object, "secret-data-new", "status", "dataSecretName")
   632  		g.Expect(err).ToNot(HaveOccurred())
   633  		err = r.Client.Create(ctx, newBootstrapConfig)
   634  		g.Expect(err).ToNot(HaveOccurred())
   635  		machinePool.Spec.Template.Spec.Bootstrap.ConfigRef.Name = newBootstrapConfig.GetName()
   636  
   637  		// Reconcile again. The new bootstrap config should be used
   638  		res, err = r.reconcile(ctx, defaultCluster, machinePool)
   639  		g.Expect(err).ToNot(HaveOccurred())
   640  
   641  		// Controller should wait until bootstrap provider reports ready bootstrap config
   642  		g.Expect(res.Requeue).To(BeFalse())
   643  
   644  		r.reconcilePhase(machinePool)
   645  
   646  		// The old secret should still be used, as the new bootstrap config is not marked ready
   647  		g.Expect(*machinePool.Spec.Template.Spec.Bootstrap.DataSecretName).To(Equal("secret-data"))
   648  		g.Expect(machinePool.Status.BootstrapReady).To(BeFalse())
   649  
   650  		// There is no phase defined for "changing to new bootstrap config", so it should still be `Running` the
   651  		// old configuration
   652  		g.Expect(machinePool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
   653  	})
   654  }
   655  
   656  func TestReconcileMachinePoolBootstrap(t *testing.T) {
   657  	defaultMachinePool := expv1.MachinePool{
   658  		ObjectMeta: metav1.ObjectMeta{
   659  			Name:      "machinepool-test",
   660  			Namespace: metav1.NamespaceDefault,
   661  			Labels: map[string]string{
   662  				clusterv1.ClusterNameLabel: clusterName,
   663  			},
   664  		},
   665  		Spec: expv1.MachinePoolSpec{
   666  			Template: clusterv1.MachineTemplateSpec{
   667  				Spec: clusterv1.MachineSpec{
   668  					Bootstrap: clusterv1.Bootstrap{
   669  						ConfigRef: &corev1.ObjectReference{
   670  							APIVersion: builder.BootstrapGroupVersion.String(),
   671  							Kind:       builder.TestBootstrapConfigKind,
   672  							Name:       "bootstrap-config1",
   673  						},
   674  					},
   675  				},
   676  			},
   677  		},
   678  	}
   679  
   680  	defaultCluster := &clusterv1.Cluster{
   681  		ObjectMeta: metav1.ObjectMeta{
   682  			Name:      clusterName,
   683  			Namespace: metav1.NamespaceDefault,
   684  		},
   685  	}
   686  
   687  	testCases := []struct {
   688  		name            string
   689  		bootstrapConfig map[string]interface{}
   690  		machinepool     *expv1.MachinePool
   691  		expectError     bool
   692  		expectResult    ctrl.Result
   693  		expected        func(g *WithT, m *expv1.MachinePool)
   694  	}{
   695  		{
   696  			name: "new machinepool, bootstrap config ready with data",
   697  			bootstrapConfig: map[string]interface{}{
   698  				"kind":       builder.TestBootstrapConfigKind,
   699  				"apiVersion": builder.BootstrapGroupVersion.String(),
   700  				"metadata": map[string]interface{}{
   701  					"name":      "bootstrap-config1",
   702  					"namespace": metav1.NamespaceDefault,
   703  				},
   704  				"spec": map[string]interface{}{},
   705  				"status": map[string]interface{}{
   706  					"ready":          true,
   707  					"dataSecretName": "secret-data",
   708  				},
   709  			},
   710  			expectError: false,
   711  			expected: func(g *WithT, m *expv1.MachinePool) {
   712  				g.Expect(m.Status.BootstrapReady).To(BeTrue())
   713  				g.Expect(m.Spec.Template.Spec.Bootstrap.DataSecretName).ToNot(BeNil())
   714  				g.Expect(*m.Spec.Template.Spec.Bootstrap.DataSecretName).To(ContainSubstring("secret-data"))
   715  			},
   716  		},
   717  		{
   718  			name: "new machinepool, bootstrap config ready with no data",
   719  			bootstrapConfig: map[string]interface{}{
   720  				"kind":       builder.TestBootstrapConfigKind,
   721  				"apiVersion": builder.BootstrapGroupVersion.String(),
   722  				"metadata": map[string]interface{}{
   723  					"name":      "bootstrap-config1",
   724  					"namespace": metav1.NamespaceDefault,
   725  				},
   726  				"spec": map[string]interface{}{},
   727  				"status": map[string]interface{}{
   728  					"ready": true,
   729  				},
   730  			},
   731  			expectError: true,
   732  			expected: func(g *WithT, m *expv1.MachinePool) {
   733  				g.Expect(m.Status.BootstrapReady).To(BeFalse())
   734  				g.Expect(m.Spec.Template.Spec.Bootstrap.DataSecretName).To(BeNil())
   735  			},
   736  		},
   737  		{
   738  			name: "new machinepool, bootstrap config not ready",
   739  			bootstrapConfig: map[string]interface{}{
   740  				"kind":       builder.TestBootstrapConfigKind,
   741  				"apiVersion": builder.BootstrapGroupVersion.String(),
   742  				"metadata": map[string]interface{}{
   743  					"name":      "bootstrap-config1",
   744  					"namespace": metav1.NamespaceDefault,
   745  				},
   746  				"spec":   map[string]interface{}{},
   747  				"status": map[string]interface{}{},
   748  			},
   749  			expectError:  false,
   750  			expectResult: ctrl.Result{},
   751  			expected: func(g *WithT, m *expv1.MachinePool) {
   752  				g.Expect(m.Status.BootstrapReady).To(BeFalse())
   753  			},
   754  		},
   755  		{
   756  			name: "new machinepool, bootstrap config is not found",
   757  			bootstrapConfig: map[string]interface{}{
   758  				"kind":       builder.TestBootstrapConfigKind,
   759  				"apiVersion": builder.BootstrapGroupVersion.String(),
   760  				"metadata": map[string]interface{}{
   761  					"name":      "bootstrap-config1",
   762  					"namespace": wrongNamespace,
   763  				},
   764  				"spec":   map[string]interface{}{},
   765  				"status": map[string]interface{}{},
   766  			},
   767  			expectError: true,
   768  			expected: func(g *WithT, m *expv1.MachinePool) {
   769  				g.Expect(m.Status.BootstrapReady).To(BeFalse())
   770  			},
   771  		},
   772  		{
   773  			name: "new machinepool, no bootstrap config or data",
   774  			bootstrapConfig: map[string]interface{}{
   775  				"kind":       builder.TestBootstrapConfigKind,
   776  				"apiVersion": builder.BootstrapGroupVersion.String(),
   777  				"metadata": map[string]interface{}{
   778  					"name":      "bootstrap-config1",
   779  					"namespace": wrongNamespace,
   780  				},
   781  				"spec":   map[string]interface{}{},
   782  				"status": map[string]interface{}{},
   783  			},
   784  			expectError: true,
   785  		},
   786  		{
   787  			name: "existing machinepool with config ref, update data secret name",
   788  			bootstrapConfig: map[string]interface{}{
   789  				"kind":       builder.TestBootstrapConfigKind,
   790  				"apiVersion": builder.BootstrapGroupVersion.String(),
   791  				"metadata": map[string]interface{}{
   792  					"name":      "bootstrap-config1",
   793  					"namespace": metav1.NamespaceDefault,
   794  				},
   795  				"spec": map[string]interface{}{},
   796  				"status": map[string]interface{}{
   797  					"ready":          true,
   798  					"dataSecretName": "secret-data",
   799  				},
   800  			},
   801  			machinepool: &expv1.MachinePool{
   802  				ObjectMeta: metav1.ObjectMeta{
   803  					Name:      "bootstrap-test-existing",
   804  					Namespace: metav1.NamespaceDefault,
   805  				},
   806  				Spec: expv1.MachinePoolSpec{
   807  					Template: clusterv1.MachineTemplateSpec{
   808  						Spec: clusterv1.MachineSpec{
   809  							Bootstrap: clusterv1.Bootstrap{
   810  								ConfigRef: &corev1.ObjectReference{
   811  									APIVersion: builder.BootstrapGroupVersion.String(),
   812  									Kind:       builder.TestBootstrapConfigKind,
   813  									Name:       "bootstrap-config1",
   814  								},
   815  								DataSecretName: ptr.To("data"),
   816  							},
   817  						},
   818  					},
   819  				},
   820  				Status: expv1.MachinePoolStatus{
   821  					BootstrapReady: true,
   822  				},
   823  			},
   824  			expectError: false,
   825  			expected: func(g *WithT, m *expv1.MachinePool) {
   826  				g.Expect(m.Status.BootstrapReady).To(BeTrue())
   827  				g.Expect(*m.Spec.Template.Spec.Bootstrap.DataSecretName).To(Equal("secret-data"))
   828  			},
   829  		},
   830  		{
   831  			name: "existing machinepool without config ref, do not update data secret name",
   832  			bootstrapConfig: map[string]interface{}{
   833  				"kind":       builder.TestBootstrapConfigKind,
   834  				"apiVersion": builder.BootstrapGroupVersion.String(),
   835  				"metadata": map[string]interface{}{
   836  					"name":      "bootstrap-config1",
   837  					"namespace": metav1.NamespaceDefault,
   838  				},
   839  				"spec": map[string]interface{}{},
   840  				"status": map[string]interface{}{
   841  					"ready":          true,
   842  					"dataSecretName": "secret-data",
   843  				},
   844  			},
   845  			machinepool: &expv1.MachinePool{
   846  				ObjectMeta: metav1.ObjectMeta{
   847  					Name:      "bootstrap-test-existing",
   848  					Namespace: metav1.NamespaceDefault,
   849  				},
   850  				Spec: expv1.MachinePoolSpec{
   851  					Template: clusterv1.MachineTemplateSpec{
   852  						Spec: clusterv1.MachineSpec{
   853  							Bootstrap: clusterv1.Bootstrap{
   854  								DataSecretName: ptr.To("data"),
   855  							},
   856  						},
   857  					},
   858  				},
   859  				Status: expv1.MachinePoolStatus{
   860  					BootstrapReady: true,
   861  				},
   862  			},
   863  			expectError: false,
   864  			expected: func(g *WithT, m *expv1.MachinePool) {
   865  				g.Expect(m.Status.BootstrapReady).To(BeTrue())
   866  				g.Expect(*m.Spec.Template.Spec.Bootstrap.DataSecretName).To(Equal("data"))
   867  			},
   868  		},
   869  		{
   870  			name: "existing machinepool, bootstrap provider is not ready",
   871  			bootstrapConfig: map[string]interface{}{
   872  				"kind":       builder.TestBootstrapConfigKind,
   873  				"apiVersion": builder.BootstrapGroupVersion.String(),
   874  				"metadata": map[string]interface{}{
   875  					"name":      "bootstrap-config1",
   876  					"namespace": metav1.NamespaceDefault,
   877  				},
   878  				"spec": map[string]interface{}{},
   879  				"status": map[string]interface{}{
   880  					"ready": false,
   881  					"data":  "#!/bin/bash ... data",
   882  				},
   883  			},
   884  			machinepool: &expv1.MachinePool{
   885  				ObjectMeta: metav1.ObjectMeta{
   886  					Name:      "bootstrap-test-existing",
   887  					Namespace: metav1.NamespaceDefault,
   888  				},
   889  				Spec: expv1.MachinePoolSpec{
   890  					Template: clusterv1.MachineTemplateSpec{
   891  						Spec: clusterv1.MachineSpec{
   892  							Bootstrap: clusterv1.Bootstrap{
   893  								ConfigRef: &corev1.ObjectReference{
   894  									APIVersion: builder.BootstrapGroupVersion.String(),
   895  									Kind:       builder.TestBootstrapConfigKind,
   896  									Name:       "bootstrap-config1",
   897  								},
   898  								DataSecretName: ptr.To("data"),
   899  							},
   900  						},
   901  					},
   902  				},
   903  				Status: expv1.MachinePoolStatus{
   904  					BootstrapReady: false,
   905  				},
   906  			},
   907  			expectError:  false,
   908  			expectResult: ctrl.Result{},
   909  			expected: func(g *WithT, m *expv1.MachinePool) {
   910  				g.Expect(m.Status.BootstrapReady).To(BeFalse())
   911  			},
   912  		},
   913  	}
   914  
   915  	for _, tc := range testCases {
   916  		t.Run(tc.name, func(t *testing.T) {
   917  			g := NewWithT(t)
   918  			if tc.machinepool == nil {
   919  				tc.machinepool = defaultMachinePool.DeepCopy()
   920  			}
   921  
   922  			bootstrapConfig := &unstructured.Unstructured{Object: tc.bootstrapConfig}
   923  			r := &MachinePoolReconciler{
   924  				Client: fake.NewClientBuilder().WithObjects(tc.machinepool, bootstrapConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
   925  			}
   926  
   927  			res, err := r.reconcileBootstrap(ctx, defaultCluster, tc.machinepool)
   928  			g.Expect(res).To(BeComparableTo(tc.expectResult))
   929  			if tc.expectError {
   930  				g.Expect(err).To(HaveOccurred())
   931  			} else {
   932  				g.Expect(err).ToNot(HaveOccurred())
   933  			}
   934  
   935  			if tc.expected != nil {
   936  				tc.expected(g, tc.machinepool)
   937  			}
   938  		})
   939  	}
   940  }
   941  
   942  func TestReconcileMachinePoolInfrastructure(t *testing.T) {
   943  	defaultMachinePool := expv1.MachinePool{
   944  		ObjectMeta: metav1.ObjectMeta{
   945  			Name:      "machinepool-test",
   946  			Namespace: metav1.NamespaceDefault,
   947  			Labels: map[string]string{
   948  				clusterv1.ClusterNameLabel: clusterName,
   949  			},
   950  		},
   951  		Spec: expv1.MachinePoolSpec{
   952  			Replicas: ptr.To[int32](1),
   953  			Template: clusterv1.MachineTemplateSpec{
   954  				Spec: clusterv1.MachineSpec{
   955  					Bootstrap: clusterv1.Bootstrap{
   956  						ConfigRef: &corev1.ObjectReference{
   957  							APIVersion: builder.BootstrapGroupVersion.String(),
   958  							Kind:       builder.TestBootstrapConfigKind,
   959  							Name:       "bootstrap-config1",
   960  						},
   961  					},
   962  					InfrastructureRef: corev1.ObjectReference{
   963  						APIVersion: builder.InfrastructureGroupVersion.String(),
   964  						Kind:       builder.TestInfrastructureMachineTemplateKind,
   965  						Name:       "infra-config1",
   966  					},
   967  				},
   968  			},
   969  		},
   970  	}
   971  
   972  	defaultCluster := &clusterv1.Cluster{
   973  		ObjectMeta: metav1.ObjectMeta{
   974  			Name:      clusterName,
   975  			Namespace: metav1.NamespaceDefault,
   976  		},
   977  	}
   978  
   979  	testCases := []struct {
   980  		name               string
   981  		bootstrapConfig    map[string]interface{}
   982  		infraConfig        map[string]interface{}
   983  		machinepool        *expv1.MachinePool
   984  		expectError        bool
   985  		expectChanged      bool
   986  		expectRequeueAfter bool
   987  		expected           func(g *WithT, m *expv1.MachinePool)
   988  	}{
   989  		{
   990  			name: "new machinepool, infrastructure config ready",
   991  			infraConfig: map[string]interface{}{
   992  				"kind":       builder.TestInfrastructureMachineTemplateKind,
   993  				"apiVersion": builder.InfrastructureGroupVersion.String(),
   994  				"metadata": map[string]interface{}{
   995  					"name":      "infra-config1",
   996  					"namespace": metav1.NamespaceDefault,
   997  				},
   998  				"spec": map[string]interface{}{
   999  					"providerIDList": []interface{}{
  1000  						"test://id-1",
  1001  					},
  1002  				},
  1003  				"status": map[string]interface{}{
  1004  					"ready": true,
  1005  					"addresses": []interface{}{
  1006  						map[string]interface{}{
  1007  							"type":    "InternalIP",
  1008  							"address": "10.0.0.1",
  1009  						},
  1010  						map[string]interface{}{
  1011  							"type":    "InternalIP",
  1012  							"address": "10.0.0.2",
  1013  						},
  1014  					},
  1015  				},
  1016  			},
  1017  			expectError:   false,
  1018  			expectChanged: true,
  1019  			expected: func(g *WithT, m *expv1.MachinePool) {
  1020  				g.Expect(m.Status.InfrastructureReady).To(BeTrue())
  1021  			},
  1022  		},
  1023  		{
  1024  			name: "ready bootstrap, infra, and nodeRef, machinepool is running, infra object is deleted, expect failed",
  1025  			machinepool: &expv1.MachinePool{
  1026  				ObjectMeta: metav1.ObjectMeta{
  1027  					Name:      "machinepool-test",
  1028  					Namespace: metav1.NamespaceDefault,
  1029  				},
  1030  				Spec: expv1.MachinePoolSpec{
  1031  					Replicas: ptr.To[int32](1),
  1032  					Template: clusterv1.MachineTemplateSpec{
  1033  						Spec: clusterv1.MachineSpec{
  1034  							Bootstrap: clusterv1.Bootstrap{
  1035  								ConfigRef: &corev1.ObjectReference{
  1036  									APIVersion: builder.BootstrapGroupVersion.String(),
  1037  									Kind:       builder.TestBootstrapConfigKind,
  1038  									Name:       "bootstrap-config1",
  1039  								},
  1040  							},
  1041  							InfrastructureRef: corev1.ObjectReference{
  1042  								APIVersion: builder.InfrastructureGroupVersion.String(),
  1043  								Kind:       builder.TestInfrastructureMachineTemplateKind,
  1044  								Name:       "infra-config1",
  1045  							},
  1046  						},
  1047  					},
  1048  				},
  1049  				Status: expv1.MachinePoolStatus{
  1050  					BootstrapReady:      true,
  1051  					InfrastructureReady: true,
  1052  					NodeRefs:            []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}},
  1053  				},
  1054  			},
  1055  			bootstrapConfig: map[string]interface{}{
  1056  				"kind":       builder.TestBootstrapConfigKind,
  1057  				"apiVersion": builder.BootstrapGroupVersion.String(),
  1058  				"metadata": map[string]interface{}{
  1059  					"name":      "bootstrap-config1",
  1060  					"namespace": metav1.NamespaceDefault,
  1061  				},
  1062  				"spec": map[string]interface{}{},
  1063  				"status": map[string]interface{}{
  1064  					"ready":          true,
  1065  					"dataSecretName": "secret-data",
  1066  				},
  1067  			},
  1068  			infraConfig: map[string]interface{}{
  1069  				"kind":       builder.TestInfrastructureMachineTemplateKind,
  1070  				"apiVersion": builder.InfrastructureGroupVersion.String(),
  1071  				"metadata":   map[string]interface{}{},
  1072  			},
  1073  			expectError:        true,
  1074  			expectRequeueAfter: false,
  1075  			expected: func(g *WithT, m *expv1.MachinePool) {
  1076  				g.Expect(m.Status.InfrastructureReady).To(BeTrue())
  1077  				g.Expect(m.Status.FailureMessage).ToNot(BeNil())
  1078  				g.Expect(m.Status.FailureReason).ToNot(BeNil())
  1079  				g.Expect(m.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseFailed))
  1080  			},
  1081  		},
  1082  		{
  1083  			name: "infrastructure ref is paused",
  1084  			infraConfig: map[string]interface{}{
  1085  				"kind":       builder.TestInfrastructureMachineTemplateKind,
  1086  				"apiVersion": builder.InfrastructureGroupVersion.String(),
  1087  				"metadata": map[string]interface{}{
  1088  					"name":      "infra-config1",
  1089  					"namespace": metav1.NamespaceDefault,
  1090  					"annotations": map[string]interface{}{
  1091  						"cluster.x-k8s.io/paused": "true",
  1092  					},
  1093  				},
  1094  				"spec": map[string]interface{}{
  1095  					"providerIDList": []interface{}{
  1096  						"test://id-1",
  1097  					},
  1098  				},
  1099  				"status": map[string]interface{}{
  1100  					"ready": true,
  1101  					"addresses": []interface{}{
  1102  						map[string]interface{}{
  1103  							"type":    "InternalIP",
  1104  							"address": "10.0.0.1",
  1105  						},
  1106  						map[string]interface{}{
  1107  							"type":    "InternalIP",
  1108  							"address": "10.0.0.2",
  1109  						},
  1110  					},
  1111  				},
  1112  			},
  1113  			expectError:   false,
  1114  			expectChanged: false,
  1115  			expected: func(g *WithT, m *expv1.MachinePool) {
  1116  				g.Expect(m.Status.InfrastructureReady).To(BeFalse())
  1117  			},
  1118  		},
  1119  		{
  1120  			name: "ready bootstrap, infra, and nodeRef, machinepool is running, replicas 0, providerIDList not set",
  1121  			machinepool: &expv1.MachinePool{
  1122  				ObjectMeta: metav1.ObjectMeta{
  1123  					Name:      "machinepool-test",
  1124  					Namespace: metav1.NamespaceDefault,
  1125  				},
  1126  				Spec: expv1.MachinePoolSpec{
  1127  					Replicas: ptr.To[int32](0),
  1128  					Template: clusterv1.MachineTemplateSpec{
  1129  						Spec: clusterv1.MachineSpec{
  1130  							Bootstrap: clusterv1.Bootstrap{
  1131  								ConfigRef: &corev1.ObjectReference{
  1132  									APIVersion: builder.BootstrapGroupVersion.String(),
  1133  									Kind:       builder.TestBootstrapConfigKind,
  1134  									Name:       "bootstrap-config1",
  1135  								},
  1136  							},
  1137  							InfrastructureRef: corev1.ObjectReference{
  1138  								APIVersion: builder.InfrastructureGroupVersion.String(),
  1139  								Kind:       builder.TestInfrastructureMachineTemplateKind,
  1140  								Name:       "infra-config1",
  1141  							},
  1142  						},
  1143  					},
  1144  				},
  1145  				Status: expv1.MachinePoolStatus{
  1146  					BootstrapReady:      true,
  1147  					InfrastructureReady: true,
  1148  					NodeRefs:            []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}},
  1149  				},
  1150  			},
  1151  			bootstrapConfig: map[string]interface{}{
  1152  				"kind":       builder.TestBootstrapConfigKind,
  1153  				"apiVersion": builder.BootstrapGroupVersion.String(),
  1154  				"metadata": map[string]interface{}{
  1155  					"name":      "bootstrap-config1",
  1156  					"namespace": metav1.NamespaceDefault,
  1157  				},
  1158  				"spec": map[string]interface{}{},
  1159  				"status": map[string]interface{}{
  1160  					"ready":          true,
  1161  					"dataSecretName": "secret-data",
  1162  				},
  1163  			},
  1164  			infraConfig: map[string]interface{}{
  1165  				"kind":       builder.TestInfrastructureMachineTemplateKind,
  1166  				"apiVersion": builder.InfrastructureGroupVersion.String(),
  1167  				"metadata": map[string]interface{}{
  1168  					"name":      "infra-config1",
  1169  					"namespace": metav1.NamespaceDefault,
  1170  				},
  1171  				"spec": map[string]interface{}{
  1172  					"providerIDList": []interface{}{},
  1173  				},
  1174  				"status": map[string]interface{}{
  1175  					"ready": true,
  1176  					"addresses": []interface{}{
  1177  						map[string]interface{}{
  1178  							"type":    "InternalIP",
  1179  							"address": "10.0.0.1",
  1180  						},
  1181  						map[string]interface{}{
  1182  							"type":    "InternalIP",
  1183  							"address": "10.0.0.2",
  1184  						},
  1185  					},
  1186  				},
  1187  			},
  1188  			expectError:        false,
  1189  			expectRequeueAfter: false,
  1190  			expected: func(g *WithT, m *expv1.MachinePool) {
  1191  				g.Expect(m.Status.InfrastructureReady).To(BeTrue())
  1192  				g.Expect(m.Status.ReadyReplicas).To(Equal(int32(0)))
  1193  				g.Expect(m.Status.AvailableReplicas).To(Equal(int32(0)))
  1194  				g.Expect(m.Status.UnavailableReplicas).To(Equal(int32(0)))
  1195  				g.Expect(m.Status.FailureMessage).To(BeNil())
  1196  				g.Expect(m.Status.FailureReason).To(BeNil())
  1197  				g.Expect(m.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
  1198  			},
  1199  		},
  1200  	}
  1201  
  1202  	for _, tc := range testCases {
  1203  		t.Run(tc.name, func(t *testing.T) {
  1204  			g := NewWithT(t)
  1205  
  1206  			if tc.machinepool == nil {
  1207  				tc.machinepool = defaultMachinePool.DeepCopy()
  1208  			}
  1209  
  1210  			infraConfig := &unstructured.Unstructured{Object: tc.infraConfig}
  1211  			r := &MachinePoolReconciler{
  1212  				Client: fake.NewClientBuilder().WithObjects(tc.machinepool, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
  1213  			}
  1214  
  1215  			res, err := r.reconcileInfrastructure(ctx, defaultCluster, tc.machinepool)
  1216  			if tc.expectRequeueAfter {
  1217  				g.Expect(res.RequeueAfter).To(BeNumerically(">=", 0))
  1218  			} else {
  1219  				g.Expect(res.RequeueAfter).To(Equal(time.Duration(0)))
  1220  			}
  1221  			r.reconcilePhase(tc.machinepool)
  1222  			if tc.expectError {
  1223  				g.Expect(err).To(HaveOccurred())
  1224  			} else {
  1225  				g.Expect(err).ToNot(HaveOccurred())
  1226  			}
  1227  
  1228  			if tc.expected != nil {
  1229  				tc.expected(g, tc.machinepool)
  1230  			}
  1231  		})
  1232  	}
  1233  }
  1234  
  1235  func TestReconcileMachinePoolMachines(t *testing.T) {
  1236  	t.Run("Reconcile MachinePool Machines", func(t *testing.T) {
  1237  		g := NewWithT(t)
  1238  
  1239  		ns, err := env.CreateNamespace(ctx, "test-machinepool-machines")
  1240  		g.Expect(err).ToNot(HaveOccurred())
  1241  
  1242  		cluster := builder.Cluster(ns.Name, clusterName).Build()
  1243  		g.Expect(env.Create(ctx, cluster)).To(Succeed())
  1244  
  1245  		t.Run("Should do nothing if machines already exist", func(*testing.T) {
  1246  			machinePool := getMachinePool(2, "machinepool-test-1", clusterName, ns.Name)
  1247  			g.Expect(env.Create(ctx, &machinePool)).To(Succeed())
  1248  
  1249  			infraMachines := getInfraMachines(2, machinePool.Name, clusterName, ns.Name)
  1250  			for i := range infraMachines {
  1251  				g.Expect(env.Create(ctx, &infraMachines[i])).To(Succeed())
  1252  			}
  1253  
  1254  			machines := getMachines(2, machinePool.Name, clusterName, ns.Name)
  1255  			for i := range machines {
  1256  				g.Expect(env.Create(ctx, &machines[i])).To(Succeed())
  1257  			}
  1258  
  1259  			infraConfig := map[string]interface{}{
  1260  				"kind":       builder.GenericInfrastructureMachinePoolKind,
  1261  				"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1262  				"metadata": map[string]interface{}{
  1263  					"name":      "infra-config1",
  1264  					"namespace": ns.Name,
  1265  				},
  1266  				"spec": map[string]interface{}{
  1267  					"providerIDList": []interface{}{
  1268  						"test://id-1",
  1269  					},
  1270  				},
  1271  				"status": map[string]interface{}{
  1272  					"ready": true,
  1273  					"addresses": []interface{}{
  1274  						map[string]interface{}{
  1275  							"type":    "InternalIP",
  1276  							"address": "10.0.0.1",
  1277  						},
  1278  						map[string]interface{}{
  1279  							"type":    "InternalIP",
  1280  							"address": "10.0.0.2",
  1281  						},
  1282  					},
  1283  					"infrastructureMachineKind": builder.GenericInfrastructureMachineKind,
  1284  				},
  1285  			}
  1286  			g.Expect(env.Create(ctx, &unstructured.Unstructured{Object: infraConfig})).To(Succeed())
  1287  
  1288  			r := &MachinePoolReconciler{
  1289  				Client:   env,
  1290  				ssaCache: ssa.NewCache(),
  1291  			}
  1292  
  1293  			err = r.reconcileMachines(ctx, &machinePool, &unstructured.Unstructured{Object: infraConfig})
  1294  			r.reconcilePhase(&machinePool)
  1295  			g.Expect(err).ToNot(HaveOccurred())
  1296  
  1297  			machineList := &clusterv1.MachineList{}
  1298  			labels := map[string]string{
  1299  				clusterv1.ClusterNameLabel:     clusterName,
  1300  				clusterv1.MachinePoolNameLabel: machinePool.Name,
  1301  			}
  1302  			g.Expect(env.GetAPIReader().List(ctx, machineList, client.InNamespace(cluster.Namespace), client.MatchingLabels(labels))).To(Succeed())
  1303  			g.Expect(machineList.Items).To(HaveLen(2))
  1304  			for i := range machineList.Items {
  1305  				machine := &machineList.Items[i]
  1306  				_, err := external.Get(ctx, r.Client, &machine.Spec.InfrastructureRef, machine.Namespace)
  1307  				g.Expect(err).ToNot(HaveOccurred())
  1308  			}
  1309  		})
  1310  
  1311  		t.Run("Should create two machines if two infra machines exist", func(*testing.T) {
  1312  			machinePool := getMachinePool(2, "machinepool-test-2", clusterName, ns.Name)
  1313  			g.Expect(env.Create(ctx, &machinePool)).To(Succeed())
  1314  
  1315  			infraMachines := getInfraMachines(2, machinePool.Name, clusterName, ns.Name)
  1316  			for i := range infraMachines {
  1317  				g.Expect(env.Create(ctx, &infraMachines[i])).To(Succeed())
  1318  			}
  1319  
  1320  			infraConfig := map[string]interface{}{
  1321  				"kind":       builder.GenericInfrastructureMachinePoolKind,
  1322  				"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1323  				"metadata": map[string]interface{}{
  1324  					"name":      "infra-config2",
  1325  					"namespace": ns.Name,
  1326  				},
  1327  				"spec": map[string]interface{}{
  1328  					"providerIDList": []interface{}{
  1329  						"test://id-1",
  1330  					},
  1331  				},
  1332  				"status": map[string]interface{}{
  1333  					"ready": true,
  1334  					"addresses": []interface{}{
  1335  						map[string]interface{}{
  1336  							"type":    "InternalIP",
  1337  							"address": "10.0.0.1",
  1338  						},
  1339  						map[string]interface{}{
  1340  							"type":    "InternalIP",
  1341  							"address": "10.0.0.2",
  1342  						},
  1343  					},
  1344  					"infrastructureMachineKind": builder.GenericInfrastructureMachineKind,
  1345  				},
  1346  			}
  1347  			g.Expect(env.Create(ctx, &unstructured.Unstructured{Object: infraConfig})).To(Succeed())
  1348  
  1349  			r := &MachinePoolReconciler{
  1350  				Client:   env,
  1351  				ssaCache: ssa.NewCache(),
  1352  			}
  1353  
  1354  			err = r.reconcileMachines(ctx, &machinePool, &unstructured.Unstructured{Object: infraConfig})
  1355  			r.reconcilePhase(&machinePool)
  1356  			g.Expect(err).ToNot(HaveOccurred())
  1357  
  1358  			machineList := &clusterv1.MachineList{}
  1359  			labels := map[string]string{
  1360  				clusterv1.ClusterNameLabel:     clusterName,
  1361  				clusterv1.MachinePoolNameLabel: machinePool.Name,
  1362  			}
  1363  			g.Expect(env.GetAPIReader().List(ctx, machineList, client.InNamespace(cluster.Namespace), client.MatchingLabels(labels))).To(Succeed())
  1364  			g.Expect(machineList.Items).To(HaveLen(2))
  1365  			for i := range machineList.Items {
  1366  				machine := &machineList.Items[i]
  1367  				_, err := external.Get(ctx, r.Client, &machine.Spec.InfrastructureRef, machine.Namespace)
  1368  				g.Expect(err).ToNot(HaveOccurred())
  1369  			}
  1370  		})
  1371  
  1372  		t.Run("Should do nothing if machinepool does not support machinepool machines", func(*testing.T) {
  1373  			machinePool := getMachinePool(2, "machinepool-test-3", clusterName, ns.Name)
  1374  			g.Expect(env.Create(ctx, &machinePool)).To(Succeed())
  1375  
  1376  			infraConfig := map[string]interface{}{
  1377  				"kind":       builder.GenericInfrastructureMachinePoolKind,
  1378  				"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1379  				"metadata": map[string]interface{}{
  1380  					"name":      "infra-config3",
  1381  					"namespace": ns.Name,
  1382  				},
  1383  				"spec": map[string]interface{}{
  1384  					"providerIDList": []interface{}{
  1385  						"test://id-1",
  1386  					},
  1387  				},
  1388  				"status": map[string]interface{}{
  1389  					"ready": true,
  1390  					"addresses": []interface{}{
  1391  						map[string]interface{}{
  1392  							"type":    "InternalIP",
  1393  							"address": "10.0.0.1",
  1394  						},
  1395  						map[string]interface{}{
  1396  							"type":    "InternalIP",
  1397  							"address": "10.0.0.2",
  1398  						},
  1399  					},
  1400  				},
  1401  			}
  1402  			g.Expect(env.Create(ctx, &unstructured.Unstructured{Object: infraConfig})).To(Succeed())
  1403  
  1404  			r := &MachinePoolReconciler{
  1405  				Client:   env,
  1406  				ssaCache: ssa.NewCache(),
  1407  			}
  1408  
  1409  			err = r.reconcileMachines(ctx, &machinePool, &unstructured.Unstructured{Object: infraConfig})
  1410  			r.reconcilePhase(&machinePool)
  1411  			g.Expect(err).ToNot(HaveOccurred())
  1412  
  1413  			machineList := &clusterv1.MachineList{}
  1414  			labels := map[string]string{
  1415  				clusterv1.ClusterNameLabel:     clusterName,
  1416  				clusterv1.MachinePoolNameLabel: machinePool.Name,
  1417  			}
  1418  			g.Expect(env.GetAPIReader().List(ctx, machineList, client.InNamespace(cluster.Namespace), client.MatchingLabels(labels))).To(Succeed())
  1419  			g.Expect(machineList.Items).To(BeEmpty())
  1420  		})
  1421  	})
  1422  }
  1423  
  1424  func TestInfraMachineToMachinePoolMapper(t *testing.T) {
  1425  	machinePool1 := expv1.MachinePool{
  1426  		ObjectMeta: metav1.ObjectMeta{
  1427  			Name:      "machinepool-1",
  1428  			Namespace: metav1.NamespaceDefault,
  1429  			Labels: map[string]string{
  1430  				clusterv1.ClusterNameLabel: clusterName,
  1431  			},
  1432  		},
  1433  	}
  1434  
  1435  	machinePool2 := expv1.MachinePool{
  1436  		ObjectMeta: metav1.ObjectMeta{
  1437  			Name:      "machinepool-2",
  1438  			Namespace: "other-namespace",
  1439  			Labels: map[string]string{
  1440  				clusterv1.ClusterNameLabel: clusterName,
  1441  			},
  1442  		},
  1443  	}
  1444  
  1445  	machinePool3 := expv1.MachinePool{
  1446  		ObjectMeta: metav1.ObjectMeta{
  1447  			Name:      "machinepool-3",
  1448  			Namespace: metav1.NamespaceDefault,
  1449  			Labels: map[string]string{
  1450  				clusterv1.ClusterNameLabel: "other-cluster",
  1451  			},
  1452  		},
  1453  	}
  1454  
  1455  	machinePoolLongName := expv1.MachinePool{
  1456  		ObjectMeta: metav1.ObjectMeta{
  1457  			Name:      "machinepool-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long", // Use a name longer than 64 characters to trigger a hash
  1458  			Namespace: metav1.NamespaceDefault,
  1459  			Labels: map[string]string{
  1460  				clusterv1.ClusterNameLabel: "other-cluster",
  1461  			},
  1462  		},
  1463  	}
  1464  
  1465  	infraMachine1 := unstructured.Unstructured{
  1466  		Object: map[string]interface{}{
  1467  			"kind":       "InfrastructureMachine",
  1468  			"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1469  			"metadata": map[string]interface{}{
  1470  				"name":      "infra-machine1",
  1471  				"namespace": metav1.NamespaceDefault,
  1472  				"labels": map[string]interface{}{
  1473  					clusterv1.ClusterNameLabel:     clusterName,
  1474  					clusterv1.MachinePoolNameLabel: format.MustFormatValue(machinePool1.Name),
  1475  				},
  1476  			},
  1477  		},
  1478  	}
  1479  
  1480  	infraMachine2 := unstructured.Unstructured{
  1481  		Object: map[string]interface{}{
  1482  			"kind":       "InfrastructureMachine",
  1483  			"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1484  			"metadata": map[string]interface{}{
  1485  				"name":      "infra-machine2",
  1486  				"namespace": metav1.NamespaceDefault,
  1487  				"labels": map[string]interface{}{
  1488  					clusterv1.ClusterNameLabel:     "other-cluster",
  1489  					clusterv1.MachinePoolNameLabel: format.MustFormatValue(machinePoolLongName.Name),
  1490  				},
  1491  			},
  1492  		},
  1493  	}
  1494  
  1495  	infraMachine3 := unstructured.Unstructured{
  1496  		Object: map[string]interface{}{
  1497  			"kind":       "InfrastructureMachine",
  1498  			"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1499  			"metadata": map[string]interface{}{
  1500  				"name":      "infra-machine3",
  1501  				"namespace": metav1.NamespaceDefault,
  1502  				"labels": map[string]interface{}{
  1503  					clusterv1.ClusterNameLabel:     "other-cluster",
  1504  					clusterv1.MachinePoolNameLabel: format.MustFormatValue("missing-machinepool"),
  1505  				},
  1506  			},
  1507  		},
  1508  	}
  1509  
  1510  	testCases := []struct {
  1511  		name                string
  1512  		infraMachine        *unstructured.Unstructured
  1513  		machinepools        []expv1.MachinePool
  1514  		expectedMachinePool *expv1.MachinePool
  1515  	}{
  1516  		{
  1517  			name:         "match machinePool name with label value",
  1518  			infraMachine: &infraMachine1,
  1519  			machinepools: []expv1.MachinePool{
  1520  				machinePool1,
  1521  				machinePool2,
  1522  				machinePool3,
  1523  				machinePoolLongName,
  1524  			},
  1525  			expectedMachinePool: &machinePool1,
  1526  		},
  1527  		{
  1528  			name:         "match hash of machinePool name with label hash",
  1529  			infraMachine: &infraMachine2,
  1530  			machinepools: []expv1.MachinePool{
  1531  				machinePool1,
  1532  				machinePool2,
  1533  				machinePool3,
  1534  				machinePoolLongName,
  1535  			},
  1536  			expectedMachinePool: &machinePoolLongName,
  1537  		},
  1538  		{
  1539  			name:         "return nil if no machinePool matches",
  1540  			infraMachine: &infraMachine3,
  1541  			machinepools: []expv1.MachinePool{
  1542  				machinePool1,
  1543  				machinePool2,
  1544  				machinePool3,
  1545  				machinePoolLongName,
  1546  			},
  1547  			expectedMachinePool: nil,
  1548  		},
  1549  	}
  1550  
  1551  	for _, tc := range testCases {
  1552  		t.Run(tc.name, func(t *testing.T) {
  1553  			g := NewWithT(t)
  1554  
  1555  			objs := []client.Object{tc.infraMachine.DeepCopy()}
  1556  
  1557  			for _, mp := range tc.machinepools {
  1558  				objs = append(objs, mp.DeepCopy())
  1559  			}
  1560  
  1561  			r := &MachinePoolReconciler{
  1562  				Client: fake.NewClientBuilder().WithObjects(objs...).Build(),
  1563  			}
  1564  
  1565  			result := r.infraMachineToMachinePoolMapper(ctx, tc.infraMachine)
  1566  			if tc.expectedMachinePool == nil {
  1567  				g.Expect(result).To(BeNil())
  1568  			} else {
  1569  				g.Expect(result).To(HaveLen(1))
  1570  				g.Expect(result[0].Name).To(Equal(tc.expectedMachinePool.Name))
  1571  				g.Expect(result[0].Namespace).To(Equal(tc.expectedMachinePool.Namespace))
  1572  			}
  1573  		})
  1574  	}
  1575  }
  1576  
  1577  func TestReconcileMachinePoolScaleToFromZero(t *testing.T) {
  1578  	g := NewWithT(t)
  1579  
  1580  	ns, err := env.CreateNamespace(ctx, "machinepool-scale-zero")
  1581  	g.Expect(err).ToNot(HaveOccurred())
  1582  
  1583  	// Set up cluster to test against.
  1584  	testCluster := &clusterv1.Cluster{
  1585  		ObjectMeta: metav1.ObjectMeta{
  1586  			GenerateName: "machinepool-scale-zero-",
  1587  			Namespace:    ns.Name,
  1588  		},
  1589  	}
  1590  	g.Expect(env.CreateAndWait(ctx, testCluster)).To(Succeed())
  1591  	g.Expect(env.CreateKubeconfigSecret(ctx, testCluster)).To(Succeed())
  1592  	defer func(do ...client.Object) {
  1593  		g.Expect(env.CleanupAndWait(ctx, do...)).To(Succeed())
  1594  	}(testCluster)
  1595  
  1596  	defaultMachinePool := expv1.MachinePool{
  1597  		ObjectMeta: metav1.ObjectMeta{
  1598  			Name:      "machinepool-test",
  1599  			Namespace: ns.Name,
  1600  		},
  1601  		Spec: expv1.MachinePoolSpec{
  1602  			ClusterName: testCluster.Name,
  1603  			Template: clusterv1.MachineTemplateSpec{
  1604  				Spec: clusterv1.MachineSpec{
  1605  					Bootstrap: clusterv1.Bootstrap{
  1606  						ConfigRef: &corev1.ObjectReference{
  1607  							APIVersion: builder.BootstrapGroupVersion.String(),
  1608  							Kind:       builder.TestBootstrapConfigKind,
  1609  							Name:       "bootstrap-config1",
  1610  						},
  1611  					},
  1612  					InfrastructureRef: corev1.ObjectReference{
  1613  						APIVersion: builder.InfrastructureGroupVersion.String(),
  1614  						Kind:       builder.TestInfrastructureMachineTemplateKind,
  1615  						Name:       "infra-config1",
  1616  					},
  1617  				},
  1618  			},
  1619  		},
  1620  		Status: expv1.MachinePoolStatus{},
  1621  	}
  1622  
  1623  	defaultBootstrap := &unstructured.Unstructured{
  1624  		Object: map[string]interface{}{
  1625  			"kind":       builder.TestBootstrapConfigKind,
  1626  			"apiVersion": builder.BootstrapGroupVersion.String(),
  1627  			"metadata": map[string]interface{}{
  1628  				"name":      "bootstrap-config1",
  1629  				"namespace": ns.Name,
  1630  			},
  1631  			"spec": map[string]interface{}{},
  1632  			"status": map[string]interface{}{
  1633  				"ready":          true,
  1634  				"dataSecretName": "secret-data",
  1635  			},
  1636  		},
  1637  	}
  1638  
  1639  	defaultInfra := &unstructured.Unstructured{
  1640  		Object: map[string]interface{}{
  1641  			"kind":       builder.TestInfrastructureMachineTemplateKind,
  1642  			"apiVersion": builder.InfrastructureGroupVersion.String(),
  1643  			"metadata": map[string]interface{}{
  1644  				"name":      "infra-config1",
  1645  				"namespace": ns.Name,
  1646  			},
  1647  			"spec": map[string]interface{}{},
  1648  			"status": map[string]interface{}{
  1649  				"ready": true,
  1650  			},
  1651  		},
  1652  	}
  1653  
  1654  	t.Run("Should set `ScalingDown` when scaling to zero", func(t *testing.T) {
  1655  		g := NewWithT(t)
  1656  
  1657  		node := &corev1.Node{
  1658  			ObjectMeta: metav1.ObjectMeta{
  1659  				Name: "machinepool-test-node",
  1660  			},
  1661  			Spec: corev1.NodeSpec{
  1662  				ProviderID: "test://machinepool-test-node",
  1663  			},
  1664  			Status: corev1.NodeStatus{
  1665  				Conditions: []corev1.NodeCondition{
  1666  					{Type: corev1.NodeReady, Status: corev1.ConditionTrue},
  1667  				},
  1668  			},
  1669  		}
  1670  		g.Expect(env.CreateAndWait(ctx, node)).To(Succeed())
  1671  		defer func(do ...client.Object) {
  1672  			g.Expect(env.CleanupAndWait(ctx, do...)).To(Succeed())
  1673  		}(node)
  1674  
  1675  		kubeconfigSecret := kubeconfig.GenerateSecret(testCluster, kubeconfig.FromEnvTestConfig(env.Config, testCluster))
  1676  		machinepool := defaultMachinePool.DeepCopy()
  1677  		bootstrapConfig := defaultBootstrap.DeepCopy()
  1678  		infraConfig := defaultInfra.DeepCopy()
  1679  
  1680  		// Setup prerequisites - a running MachinePool with one instance and user sets Replicas to 0
  1681  
  1682  		// set replicas to 0
  1683  		machinepool.Spec.Replicas = ptr.To[int32](0)
  1684  
  1685  		// set nodeRefs to one instance
  1686  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
  1687  
  1688  		// set infra providerIDList
  1689  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://machinepool-test-node"}, "spec", "providerIDList")
  1690  		g.Expect(err).ToNot(HaveOccurred())
  1691  
  1692  		// set infra replicas
  1693  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
  1694  		g.Expect(err).ToNot(HaveOccurred())
  1695  
  1696  		fakeClient := fake.NewClientBuilder().WithObjects(testCluster, kubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
  1697  		r := &MachinePoolReconciler{
  1698  			Client:   fakeClient,
  1699  			Tracker:  remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), env.GetClient(), env.GetClient(), env.GetClient().Scheme(), client.ObjectKey{Name: testCluster.Name, Namespace: testCluster.Namespace}),
  1700  			recorder: record.NewFakeRecorder(32),
  1701  		}
  1702  
  1703  		res, err := r.reconcile(ctx, testCluster, machinepool)
  1704  		g.Expect(err).ToNot(HaveOccurred())
  1705  		g.Expect(res.Requeue).To(BeFalse())
  1706  
  1707  		r.reconcilePhase(machinepool)
  1708  
  1709  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseScalingDown))
  1710  
  1711  		delNode := &corev1.Node{}
  1712  		g.Expect(env.Get(ctx, client.ObjectKeyFromObject(node), delNode)).To(Succeed())
  1713  	})
  1714  
  1715  	t.Run("Should delete retired nodes when scaled to zero", func(t *testing.T) {
  1716  		g := NewWithT(t)
  1717  
  1718  		node := &corev1.Node{
  1719  			ObjectMeta: metav1.ObjectMeta{
  1720  				Name: "machinepool-test-node",
  1721  			},
  1722  			Spec: corev1.NodeSpec{
  1723  				ProviderID: "test://machinepool-test-node",
  1724  			},
  1725  			Status: corev1.NodeStatus{
  1726  				Conditions: []corev1.NodeCondition{
  1727  					{Type: corev1.NodeReady, Status: corev1.ConditionTrue},
  1728  				},
  1729  			},
  1730  		}
  1731  		g.Expect(env.CreateAndWait(ctx, node)).To(Succeed())
  1732  		defer func(do ...client.Object) {
  1733  			g.Expect(env.CleanupAndWait(ctx, do...)).To(Succeed())
  1734  		}(node)
  1735  
  1736  		kubeconfigSecret := kubeconfig.GenerateSecret(testCluster, kubeconfig.FromEnvTestConfig(env.Config, testCluster))
  1737  		machinepool := defaultMachinePool.DeepCopy()
  1738  		bootstrapConfig := defaultBootstrap.DeepCopy()
  1739  		infraConfig := defaultInfra.DeepCopy()
  1740  
  1741  		// Setup prerequisites - a running MachinePool with one instance and user sets Replicas to 0
  1742  
  1743  		// set replicas to 0
  1744  		machinepool.Spec.Replicas = ptr.To[int32](0)
  1745  
  1746  		// set nodeRefs to one instance
  1747  		machinepool.Status.NodeRefs = []corev1.ObjectReference{{Kind: "Node", Name: "machinepool-test-node"}}
  1748  
  1749  		// set infra replicas
  1750  		err = unstructured.SetNestedField(infraConfig.Object, int64(0), "status", "replicas")
  1751  		g.Expect(err).ToNot(HaveOccurred())
  1752  
  1753  		fakeClient := fake.NewClientBuilder().WithObjects(testCluster, kubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
  1754  		r := &MachinePoolReconciler{
  1755  			Client:   fakeClient,
  1756  			Tracker:  remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), env.GetClient(), env.GetClient(), env.GetClient().Scheme(), client.ObjectKey{Name: testCluster.Name, Namespace: testCluster.Namespace}),
  1757  			recorder: record.NewFakeRecorder(32),
  1758  		}
  1759  
  1760  		res, err := r.reconcile(ctx, testCluster, machinepool)
  1761  		g.Expect(err).ToNot(HaveOccurred())
  1762  		g.Expect(res.Requeue).To(BeFalse())
  1763  
  1764  		r.reconcilePhase(machinepool)
  1765  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
  1766  
  1767  		delNode := &corev1.Node{}
  1768  		err = env.GetAPIReader().Get(ctx, client.ObjectKeyFromObject(node), delNode)
  1769  		g.Expect(err).To(HaveOccurred())
  1770  		g.Expect(apierrors.IsNotFound(err)).To(BeTrue())
  1771  	})
  1772  
  1773  	t.Run("Should set `Running` when scaled to zero", func(t *testing.T) {
  1774  		g := NewWithT(t)
  1775  
  1776  		kubeconfigSecret := kubeconfig.GenerateSecret(testCluster, kubeconfig.FromEnvTestConfig(env.Config, testCluster))
  1777  		machinepool := defaultMachinePool.DeepCopy()
  1778  		bootstrapConfig := defaultBootstrap.DeepCopy()
  1779  		infraConfig := defaultInfra.DeepCopy()
  1780  
  1781  		// Setup prerequisites - a running MachinePool with no instances and replicas set to 0
  1782  
  1783  		// set replicas to 0
  1784  		machinepool.Spec.Replicas = ptr.To[int32](0)
  1785  
  1786  		// set nodeRefs to no instance
  1787  		machinepool.Status.NodeRefs = []corev1.ObjectReference{}
  1788  
  1789  		// set infra replicas
  1790  		err := unstructured.SetNestedField(infraConfig.Object, int64(0), "status", "replicas")
  1791  		g.Expect(err).ToNot(HaveOccurred())
  1792  
  1793  		r := &MachinePoolReconciler{
  1794  			Client:   fake.NewClientBuilder().WithObjects(testCluster, kubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
  1795  			recorder: record.NewFakeRecorder(32),
  1796  		}
  1797  
  1798  		res, err := r.reconcile(ctx, testCluster, machinepool)
  1799  		g.Expect(err).ToNot(HaveOccurred())
  1800  		g.Expect(res.Requeue).To(BeFalse())
  1801  
  1802  		r.reconcilePhase(machinepool)
  1803  
  1804  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
  1805  	})
  1806  
  1807  	t.Run("Should set `ScalingUp` when scaling from zero to one", func(t *testing.T) {
  1808  		g := NewWithT(t)
  1809  
  1810  		kubeconfigSecret := kubeconfig.GenerateSecret(testCluster, kubeconfig.FromEnvTestConfig(env.Config, testCluster))
  1811  		machinepool := defaultMachinePool.DeepCopy()
  1812  		bootstrapConfig := defaultBootstrap.DeepCopy()
  1813  		infraConfig := defaultInfra.DeepCopy()
  1814  
  1815  		// Setup prerequisites - a running MachinePool with no instances and replicas set to 1
  1816  
  1817  		// set replicas to 1
  1818  		machinepool.Spec.Replicas = ptr.To[int32](1)
  1819  
  1820  		// set nodeRefs to no instance
  1821  		machinepool.Status.NodeRefs = []corev1.ObjectReference{}
  1822  
  1823  		// set infra replicas
  1824  		err := unstructured.SetNestedField(infraConfig.Object, int64(0), "status", "replicas")
  1825  		g.Expect(err).ToNot(HaveOccurred())
  1826  
  1827  		r := &MachinePoolReconciler{
  1828  			Client:   fake.NewClientBuilder().WithObjects(testCluster, kubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build(),
  1829  			recorder: record.NewFakeRecorder(32),
  1830  		}
  1831  
  1832  		res, err := r.reconcile(ctx, testCluster, machinepool)
  1833  		g.Expect(err).ToNot(HaveOccurred())
  1834  		g.Expect(res.Requeue).To(BeFalse())
  1835  
  1836  		r.reconcilePhase(machinepool)
  1837  
  1838  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseScalingUp))
  1839  	})
  1840  
  1841  	t.Run("Should set `Running` when scaled from zero to one", func(t *testing.T) {
  1842  		g := NewWithT(t)
  1843  
  1844  		node := &corev1.Node{
  1845  			ObjectMeta: metav1.ObjectMeta{
  1846  				Name: "machinepool-test-node",
  1847  			},
  1848  			Spec: corev1.NodeSpec{
  1849  				ProviderID: "test://machinepool-test-node",
  1850  			},
  1851  			Status: corev1.NodeStatus{
  1852  				Conditions: []corev1.NodeCondition{
  1853  					{Type: corev1.NodeReady, Status: corev1.ConditionTrue},
  1854  				},
  1855  			},
  1856  		}
  1857  		g.Expect(env.CreateAndWait(ctx, node)).To(Succeed())
  1858  		defer func(do ...client.Object) {
  1859  			g.Expect(env.CleanupAndWait(ctx, do...)).To(Succeed())
  1860  		}(node)
  1861  
  1862  		kubeconfigSecret := kubeconfig.GenerateSecret(testCluster, kubeconfig.FromEnvTestConfig(env.Config, testCluster))
  1863  		machinepool := defaultMachinePool.DeepCopy()
  1864  		bootstrapConfig := defaultBootstrap.DeepCopy()
  1865  		infraConfig := defaultInfra.DeepCopy()
  1866  
  1867  		// Setup prerequisites - a running MachinePool with no refs but providerIDList and replicas set to 1
  1868  
  1869  		// set replicas to 1
  1870  		machinepool.Spec.Replicas = ptr.To[int32](1)
  1871  
  1872  		// set nodeRefs to no instance
  1873  		machinepool.Status.NodeRefs = []corev1.ObjectReference{}
  1874  
  1875  		// set infra providerIDList
  1876  		err = unstructured.SetNestedStringSlice(infraConfig.Object, []string{"test://machinepool-test-node"}, "spec", "providerIDList")
  1877  		g.Expect(err).ToNot(HaveOccurred())
  1878  
  1879  		// set infra replicas
  1880  		err = unstructured.SetNestedField(infraConfig.Object, int64(1), "status", "replicas")
  1881  		g.Expect(err).ToNot(HaveOccurred())
  1882  
  1883  		fakeClient := fake.NewClientBuilder().WithObjects(testCluster, kubeconfigSecret, machinepool, bootstrapConfig, infraConfig, builder.TestBootstrapConfigCRD, builder.TestInfrastructureMachineTemplateCRD).Build()
  1884  		r := &MachinePoolReconciler{
  1885  			Client:   fakeClient,
  1886  			Tracker:  remote.NewTestClusterCacheTracker(logr.New(log.NullLogSink{}), env.GetClient(), env.GetClient(), env.GetClient().Scheme(), client.ObjectKey{Name: testCluster.Name, Namespace: testCluster.Namespace}),
  1887  			recorder: record.NewFakeRecorder(32),
  1888  		}
  1889  
  1890  		res, err := r.reconcile(ctx, testCluster, machinepool)
  1891  		g.Expect(err).ToNot(HaveOccurred())
  1892  		g.Expect(res.Requeue).To(BeFalse())
  1893  
  1894  		r.reconcilePhase(machinepool)
  1895  
  1896  		g.Expect(machinepool.Status.GetTypedPhase()).To(Equal(expv1.MachinePoolPhaseRunning))
  1897  
  1898  		delNode := &corev1.Node{}
  1899  		g.Expect(env.Get(ctx, client.ObjectKeyFromObject(node), delNode)).To(Succeed())
  1900  	})
  1901  }
  1902  
  1903  func getInfraMachines(replicas int, mpName, clusterName, nsName string) []unstructured.Unstructured {
  1904  	infraMachines := make([]unstructured.Unstructured, replicas)
  1905  	for i := 0; i < replicas; i++ {
  1906  		infraMachines[i] = unstructured.Unstructured{
  1907  			Object: map[string]interface{}{
  1908  				"kind":       builder.GenericInfrastructureMachineKind,
  1909  				"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1910  				"metadata": map[string]interface{}{
  1911  					"name":      fmt.Sprintf("%s-infra-%d", mpName, i),
  1912  					"namespace": nsName,
  1913  					"labels": map[string]interface{}{
  1914  						clusterv1.ClusterNameLabel:     clusterName,
  1915  						clusterv1.MachinePoolNameLabel: mpName,
  1916  					},
  1917  				},
  1918  			},
  1919  		}
  1920  	}
  1921  	return infraMachines
  1922  }
  1923  
  1924  func getMachines(replicas int, mpName, clusterName, nsName string) []clusterv1.Machine {
  1925  	machines := make([]clusterv1.Machine, replicas)
  1926  	for i := 0; i < replicas; i++ {
  1927  		machines[i] = clusterv1.Machine{
  1928  			ObjectMeta: metav1.ObjectMeta{
  1929  				Name:      fmt.Sprintf("%s-machine-%d", mpName, i),
  1930  				Namespace: nsName,
  1931  				Labels: map[string]string{
  1932  					clusterv1.ClusterNameLabel:     clusterName,
  1933  					clusterv1.MachinePoolNameLabel: mpName,
  1934  				},
  1935  			},
  1936  			Spec: clusterv1.MachineSpec{
  1937  				ClusterName: clusterName,
  1938  				Bootstrap: clusterv1.Bootstrap{
  1939  					ConfigRef: &corev1.ObjectReference{
  1940  						APIVersion: builder.BootstrapGroupVersion.String(),
  1941  						Kind:       builder.GenericBootstrapConfigKind,
  1942  						Name:       fmt.Sprintf("bootstrap-config-%d", i),
  1943  					},
  1944  				},
  1945  				InfrastructureRef: corev1.ObjectReference{
  1946  					APIVersion: builder.InfrastructureGroupVersion.String(),
  1947  					Kind:       builder.GenericInfrastructureMachineKind,
  1948  					Name:       fmt.Sprintf("%s-infra-%d", mpName, i),
  1949  				},
  1950  			},
  1951  		}
  1952  	}
  1953  	return machines
  1954  }
  1955  
  1956  func getMachinePool(replicas int, mpName, clusterName, nsName string) expv1.MachinePool {
  1957  	return expv1.MachinePool{
  1958  		ObjectMeta: metav1.ObjectMeta{
  1959  			Name:      mpName,
  1960  			Namespace: nsName,
  1961  			Labels: map[string]string{
  1962  				clusterv1.ClusterNameLabel: clusterName,
  1963  			},
  1964  		},
  1965  		Spec: expv1.MachinePoolSpec{
  1966  			ClusterName: clusterName,
  1967  			Replicas:    ptr.To[int32](int32(replicas)),
  1968  			Template: clusterv1.MachineTemplateSpec{
  1969  				Spec: clusterv1.MachineSpec{
  1970  					ClusterName: clusterName,
  1971  					Bootstrap: clusterv1.Bootstrap{
  1972  						ConfigRef: &corev1.ObjectReference{
  1973  							APIVersion: builder.BootstrapGroupVersion.String(),
  1974  							Kind:       builder.GenericBootstrapConfigKind,
  1975  							Name:       "bootstrap-config1",
  1976  						},
  1977  					},
  1978  					InfrastructureRef: corev1.ObjectReference{
  1979  						APIVersion: builder.InfrastructureGroupVersion.String(),
  1980  						Kind:       builder.GenericInfrastructureMachineKind,
  1981  						Name:       "infra-config1",
  1982  					},
  1983  				},
  1984  			},
  1985  		},
  1986  	}
  1987  }