sigs.k8s.io/cluster-api@v1.6.3/internal/controllers/machinedeployment/machinedeployment_controller_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 machinedeployment
    18  
    19  import (
    20  	"testing"
    21  	"time"
    22  
    23  	. "github.com/onsi/gomega"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"k8s.io/client-go/tools/record"
    28  	"k8s.io/utils/pointer"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    31  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    32  
    33  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    34  	"sigs.k8s.io/cluster-api/controllers/external"
    35  	"sigs.k8s.io/cluster-api/internal/util/ssa"
    36  	"sigs.k8s.io/cluster-api/util"
    37  	"sigs.k8s.io/cluster-api/util/conditions"
    38  )
    39  
    40  const (
    41  	machineDeploymentNamespace = "md-test"
    42  )
    43  
    44  var _ reconcile.Reconciler = &Reconciler{}
    45  
    46  func TestMachineDeploymentReconciler(t *testing.T) {
    47  	setup := func(t *testing.T, g *WithT) (*corev1.Namespace, *clusterv1.Cluster) {
    48  		t.Helper()
    49  
    50  		t.Log("Creating the namespace")
    51  		ns, err := env.CreateNamespace(ctx, machineDeploymentNamespace)
    52  		g.Expect(err).ToNot(HaveOccurred())
    53  
    54  		t.Log("Creating the Cluster")
    55  		cluster := &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Namespace: ns.Name, Name: "test-cluster"}}
    56  		g.Expect(env.Create(ctx, cluster)).To(Succeed())
    57  
    58  		t.Log("Creating the Cluster Kubeconfig Secret")
    59  		g.Expect(env.CreateKubeconfigSecret(ctx, cluster)).To(Succeed())
    60  
    61  		return ns, cluster
    62  	}
    63  
    64  	teardown := func(t *testing.T, g *WithT, ns *corev1.Namespace, cluster *clusterv1.Cluster) {
    65  		t.Helper()
    66  
    67  		t.Log("Deleting the Cluster")
    68  		g.Expect(env.Delete(ctx, cluster)).To(Succeed())
    69  		t.Log("Deleting the namespace")
    70  		g.Expect(env.Delete(ctx, ns)).To(Succeed())
    71  	}
    72  
    73  	t.Run("Should reconcile a MachineDeployment", func(t *testing.T) {
    74  		g := NewWithT(t)
    75  		namespace, testCluster := setup(t, g)
    76  		defer teardown(t, g, namespace, testCluster)
    77  
    78  		labels := map[string]string{
    79  			"foo":                      "bar",
    80  			clusterv1.ClusterNameLabel: testCluster.Name,
    81  		}
    82  		version := "v1.10.3"
    83  		deployment := &clusterv1.MachineDeployment{
    84  			ObjectMeta: metav1.ObjectMeta{
    85  				GenerateName: "md-",
    86  				Namespace:    namespace.Name,
    87  				Labels: map[string]string{
    88  					clusterv1.ClusterNameLabel: testCluster.Name,
    89  				},
    90  			},
    91  			Spec: clusterv1.MachineDeploymentSpec{
    92  				ClusterName:          testCluster.Name,
    93  				MinReadySeconds:      pointer.Int32(0),
    94  				Replicas:             pointer.Int32(2),
    95  				RevisionHistoryLimit: pointer.Int32(0),
    96  				Selector: metav1.LabelSelector{
    97  					// We're using the same labels for spec.selector and spec.template.labels.
    98  					// The labels are later changed and we will use the initial labels later to
    99  					// verify that all original MachineSets have been deleted.
   100  					MatchLabels: labels,
   101  				},
   102  				Strategy: &clusterv1.MachineDeploymentStrategy{
   103  					Type: clusterv1.RollingUpdateMachineDeploymentStrategyType,
   104  					RollingUpdate: &clusterv1.MachineRollingUpdateDeployment{
   105  						MaxUnavailable: intOrStrPtr(0),
   106  						MaxSurge:       intOrStrPtr(1),
   107  						DeletePolicy:   pointer.String("Oldest"),
   108  					},
   109  				},
   110  				Template: clusterv1.MachineTemplateSpec{
   111  					ObjectMeta: clusterv1.ObjectMeta{
   112  						Labels: labels,
   113  					},
   114  					Spec: clusterv1.MachineSpec{
   115  						ClusterName: testCluster.Name,
   116  						Version:     &version,
   117  						InfrastructureRef: corev1.ObjectReference{
   118  							APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
   119  							Kind:       "GenericInfrastructureMachineTemplate",
   120  							Name:       "md-template",
   121  						},
   122  						Bootstrap: clusterv1.Bootstrap{
   123  							DataSecretName: pointer.String("data-secret-name"),
   124  						},
   125  					},
   126  				},
   127  			},
   128  		}
   129  		msListOpts := []client.ListOption{
   130  			client.InNamespace(namespace.Name),
   131  			client.MatchingLabels(labels),
   132  		}
   133  
   134  		// Create infrastructure template resource.
   135  		infraResource := map[string]interface{}{
   136  			"kind":       "GenericInfrastructureMachine",
   137  			"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
   138  			"metadata":   map[string]interface{}{},
   139  			"spec": map[string]interface{}{
   140  				"size": "3xlarge",
   141  			},
   142  		}
   143  		infraTmpl := &unstructured.Unstructured{
   144  			Object: map[string]interface{}{
   145  				"kind":       "GenericInfrastructureMachineTemplate",
   146  				"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
   147  				"metadata": map[string]interface{}{
   148  					"name":      "md-template",
   149  					"namespace": namespace.Name,
   150  				},
   151  				"spec": map[string]interface{}{
   152  					"template": infraResource,
   153  				},
   154  			},
   155  		}
   156  		t.Log("Creating the infrastructure template")
   157  		g.Expect(env.Create(ctx, infraTmpl)).To(Succeed())
   158  
   159  		// Create the MachineDeployment object and expect Reconcile to be called.
   160  		t.Log("Creating the MachineDeployment")
   161  		g.Expect(env.Create(ctx, deployment)).To(Succeed())
   162  		defer func() {
   163  			t.Log("Deleting the MachineDeployment")
   164  			g.Expect(env.Delete(ctx, deployment)).To(Succeed())
   165  		}()
   166  
   167  		t.Log("Verifying the MachineDeployment has a cluster label and ownerRef")
   168  		g.Eventually(func() bool {
   169  			key := client.ObjectKey{Name: deployment.Name, Namespace: deployment.Namespace}
   170  			if err := env.Get(ctx, key, deployment); err != nil {
   171  				return false
   172  			}
   173  			if len(deployment.Labels) == 0 || deployment.Labels[clusterv1.ClusterNameLabel] != testCluster.Name {
   174  				return false
   175  			}
   176  			if len(deployment.OwnerReferences) == 0 || deployment.OwnerReferences[0].Name != testCluster.Name {
   177  				return false
   178  			}
   179  			return true
   180  		}, timeout).Should(BeTrue())
   181  
   182  		// Verify that the MachineSet was created.
   183  		t.Log("Verifying the MachineSet was created")
   184  		machineSets := &clusterv1.MachineSetList{}
   185  		g.Eventually(func() int {
   186  			if err := env.List(ctx, machineSets, msListOpts...); err != nil {
   187  				return -1
   188  			}
   189  			return len(machineSets.Items)
   190  		}, timeout).Should(BeEquivalentTo(1))
   191  
   192  		t.Log("Verifying that the deployment's deletePolicy was propagated to the machineset")
   193  		g.Expect(machineSets.Items[0].Spec.DeletePolicy).To(Equal("Oldest"))
   194  
   195  		t.Log("Verifying the linked infrastructure template has a cluster owner reference")
   196  		g.Eventually(func() bool {
   197  			obj, err := external.Get(ctx, env, &deployment.Spec.Template.Spec.InfrastructureRef, deployment.Namespace)
   198  			if err != nil {
   199  				return false
   200  			}
   201  
   202  			return util.HasOwnerRef(obj.GetOwnerReferences(), metav1.OwnerReference{
   203  				APIVersion: clusterv1.GroupVersion.String(),
   204  				Kind:       "Cluster",
   205  				Name:       testCluster.Name,
   206  				UID:        testCluster.UID,
   207  			})
   208  		}, timeout).Should(BeTrue())
   209  
   210  		t.Log("Verify MachineSet has expected replicas and version")
   211  		firstMachineSet := machineSets.Items[0]
   212  		g.Expect(*firstMachineSet.Spec.Replicas).To(BeEquivalentTo(2))
   213  		g.Expect(*firstMachineSet.Spec.Template.Spec.Version).To(BeEquivalentTo("v1.10.3"))
   214  
   215  		t.Log("Verify MachineSet has expected ClusterNameLabel and MachineDeploymentNameLabel")
   216  		g.Expect(firstMachineSet.Labels[clusterv1.ClusterNameLabel]).To(Equal(testCluster.Name))
   217  		g.Expect(firstMachineSet.Labels[clusterv1.MachineDeploymentNameLabel]).To(Equal(deployment.Name))
   218  
   219  		t.Log("Verify expected number of Machines are created")
   220  		machines := &clusterv1.MachineList{}
   221  		g.Eventually(func() int {
   222  			if err := env.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
   223  				return -1
   224  			}
   225  			return len(machines.Items)
   226  		}, timeout).Should(BeEquivalentTo(*deployment.Spec.Replicas))
   227  
   228  		t.Log("Verify Machines have expected ClusterNameLabel, MachineDeploymentNameLabel and MachineSetNameLabel")
   229  		for _, m := range machines.Items {
   230  			g.Expect(m.Labels[clusterv1.ClusterNameLabel]).To(Equal(testCluster.Name))
   231  			g.Expect(m.Labels[clusterv1.MachineDeploymentNameLabel]).To(Equal(deployment.Name))
   232  			g.Expect(m.Labels[clusterv1.MachineSetNameLabel]).To(Equal(firstMachineSet.Name))
   233  		}
   234  
   235  		//
   236  		// Delete firstMachineSet and expect Reconcile to be called to replace it.
   237  		//
   238  		t.Log("Deleting the initial MachineSet")
   239  		g.Expect(env.Delete(ctx, &firstMachineSet)).To(Succeed())
   240  		g.Eventually(func() bool {
   241  			if err := env.List(ctx, machineSets, msListOpts...); err != nil {
   242  				return false
   243  			}
   244  			for _, ms := range machineSets.Items {
   245  				if ms.UID == firstMachineSet.UID {
   246  					return false
   247  				}
   248  			}
   249  			return len(machineSets.Items) > 0
   250  		}, timeout).Should(BeTrue())
   251  
   252  		//
   253  		// Scale the MachineDeployment and expect Reconcile to be called.
   254  		//
   255  		secondMachineSet := machineSets.Items[0]
   256  		t.Log("Scaling the MachineDeployment to 3 replicas")
   257  		desiredMachineDeploymentReplicas := int32(3)
   258  		modifyFunc := func(d *clusterv1.MachineDeployment) {
   259  			d.Spec.Replicas = pointer.Int32(desiredMachineDeploymentReplicas)
   260  		}
   261  		g.Expect(updateMachineDeployment(ctx, env, deployment, modifyFunc)).To(Succeed())
   262  		g.Eventually(func() int {
   263  			key := client.ObjectKey{Name: secondMachineSet.Name, Namespace: secondMachineSet.Namespace}
   264  			if err := env.Get(ctx, key, &secondMachineSet); err != nil {
   265  				return -1
   266  			}
   267  			return int(*secondMachineSet.Spec.Replicas)
   268  		}, timeout).Should(BeEquivalentTo(desiredMachineDeploymentReplicas))
   269  
   270  		//
   271  		// Update the InfraStructureRef of the MachineDeployment, expect Reconcile to be called and a new MachineSet to appear.
   272  		//
   273  
   274  		t.Log("Updating the InfrastructureRef on the MachineDeployment")
   275  		// Create the InfrastructureTemplate
   276  		// Create infrastructure template resource.
   277  		infraTmpl2 := &unstructured.Unstructured{
   278  			Object: map[string]interface{}{
   279  				"kind":       "GenericInfrastructureMachineTemplate",
   280  				"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
   281  				"metadata": map[string]interface{}{
   282  					"name":      "md-template-2",
   283  					"namespace": namespace.Name,
   284  				},
   285  				"spec": map[string]interface{}{
   286  					"template": map[string]interface{}{
   287  						"kind":       "GenericInfrastructureMachine",
   288  						"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
   289  						"metadata":   map[string]interface{}{},
   290  						"spec": map[string]interface{}{
   291  							"size": "5xlarge",
   292  						},
   293  					},
   294  				},
   295  			},
   296  		}
   297  		t.Log("Creating the infrastructure template")
   298  		g.Expect(env.Create(ctx, infraTmpl2)).To(Succeed())
   299  
   300  		infraTmpl2Ref := corev1.ObjectReference{
   301  			APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
   302  			Kind:       "GenericInfrastructureMachineTemplate",
   303  			Name:       "md-template-2",
   304  		}
   305  		modifyFunc = func(d *clusterv1.MachineDeployment) { d.Spec.Template.Spec.InfrastructureRef = infraTmpl2Ref }
   306  		g.Expect(updateMachineDeployment(ctx, env, deployment, modifyFunc)).To(Succeed())
   307  		g.Eventually(func() int {
   308  			if err := env.List(ctx, machineSets, msListOpts...); err != nil {
   309  				return -1
   310  			}
   311  			return len(machineSets.Items)
   312  		}, timeout).Should(BeEquivalentTo(2))
   313  
   314  		// Update the Labels of the MachineDeployment, expect Reconcile to be called and the MachineSet to be updated in-place.
   315  		t.Log("Setting a label on the MachineDeployment")
   316  		modifyFunc = func(d *clusterv1.MachineDeployment) { d.Spec.Template.Labels["updated"] = "true" }
   317  		g.Expect(updateMachineDeployment(ctx, env, deployment, modifyFunc)).To(Succeed())
   318  		g.Eventually(func(g Gomega) {
   319  			g.Expect(env.List(ctx, machineSets, msListOpts...)).To(Succeed())
   320  			// Verify we still only have 2 MachineSets.
   321  			g.Expect(machineSets.Items).To(HaveLen(2))
   322  			// Verify that the new MachineSet gets the updated labels.
   323  			g.Expect(machineSets.Items[0].Spec.Template.Labels).To(HaveKeyWithValue("updated", "true"))
   324  			// Verify that the old MachineSet does not get the updated labels.
   325  			g.Expect(machineSets.Items[1].Spec.Template.Labels).ShouldNot(HaveKeyWithValue("updated", "true"))
   326  		}, timeout).Should(Succeed())
   327  
   328  		// Update the NodeDrainTimout, NodeDeletionTimeout, NodeVolumeDetachTimeout of the MachineDeployment,
   329  		// expect the Reconcile to be called and the MachineSet to be updated in-place.
   330  		t.Log("Setting NodeDrainTimout, NodeDeletionTimeout, NodeVolumeDetachTimeout on the MachineDeployment")
   331  		duration10s := metav1.Duration{Duration: 10 * time.Second}
   332  		modifyFunc = func(d *clusterv1.MachineDeployment) {
   333  			d.Spec.Template.Spec.NodeDrainTimeout = &duration10s
   334  			d.Spec.Template.Spec.NodeDeletionTimeout = &duration10s
   335  			d.Spec.Template.Spec.NodeVolumeDetachTimeout = &duration10s
   336  		}
   337  		g.Expect(updateMachineDeployment(ctx, env, deployment, modifyFunc)).To(Succeed())
   338  		g.Eventually(func(g Gomega) {
   339  			g.Expect(env.List(ctx, machineSets, msListOpts...)).Should(Succeed())
   340  			// Verify we still only have 2 MachineSets.
   341  			g.Expect(machineSets.Items).To(HaveLen(2))
   342  			// Verify the NodeDrainTimeout value is updated
   343  			g.Expect(machineSets.Items[0].Spec.Template.Spec.NodeDrainTimeout).Should(And(
   344  				Not(BeNil()),
   345  				HaveValue(Equal(duration10s)),
   346  			), "NodeDrainTimout value does not match expected")
   347  			// Verify the NodeDeletionTimeout value is updated
   348  			g.Expect(machineSets.Items[0].Spec.Template.Spec.NodeDeletionTimeout).Should(And(
   349  				Not(BeNil()),
   350  				HaveValue(Equal(duration10s)),
   351  			), "NodeDeletionTimeout value does not match expected")
   352  			// Verify the NodeVolumeDetachTimeout value is updated
   353  			g.Expect(machineSets.Items[0].Spec.Template.Spec.NodeVolumeDetachTimeout).Should(And(
   354  				Not(BeNil()),
   355  				HaveValue(Equal(duration10s)),
   356  			), "NodeVolumeDetachTimeout value does not match expected")
   357  
   358  			// Verify that the old machine set keeps the old values.
   359  			g.Expect(machineSets.Items[1].Spec.Template.Spec.NodeDrainTimeout).Should(BeNil())
   360  			g.Expect(machineSets.Items[1].Spec.Template.Spec.NodeDeletionTimeout).Should(BeNil())
   361  			g.Expect(machineSets.Items[1].Spec.Template.Spec.NodeVolumeDetachTimeout).Should(BeNil())
   362  		}).Should(Succeed())
   363  
   364  		// Update the DeletePolicy of the MachineDeployment,
   365  		// expect the Reconcile to be called and the MachineSet to be updated in-place.
   366  		t.Log("Updating deletePolicy on the MachineDeployment")
   367  		modifyFunc = func(d *clusterv1.MachineDeployment) {
   368  			d.Spec.Strategy.RollingUpdate.DeletePolicy = pointer.String("Newest")
   369  		}
   370  		g.Expect(updateMachineDeployment(ctx, env, deployment, modifyFunc)).To(Succeed())
   371  		g.Eventually(func(g Gomega) {
   372  			g.Expect(env.List(ctx, machineSets, msListOpts...)).Should(Succeed())
   373  			// Verify we still only have 2 MachineSets.
   374  			g.Expect(machineSets.Items).To(HaveLen(2))
   375  			// Verify the DeletePolicy value is updated
   376  			g.Expect(machineSets.Items[0].Spec.DeletePolicy).Should(Equal("Newest"))
   377  
   378  			// Verify that the old machine set retains its delete policy
   379  			g.Expect(machineSets.Items[1].Spec.DeletePolicy).To(Equal("Oldest"))
   380  		}).Should(Succeed())
   381  
   382  		// Verify that all the MachineSets have the expected OwnerRef.
   383  		t.Log("Verifying MachineSet owner references")
   384  		g.Eventually(func() bool {
   385  			if err := env.List(ctx, machineSets, msListOpts...); err != nil {
   386  				return false
   387  			}
   388  			for i := 0; i < len(machineSets.Items); i++ {
   389  				ms := machineSets.Items[0]
   390  				if !metav1.IsControlledBy(&ms, deployment) || metav1.GetControllerOf(&ms).Kind != "MachineDeployment" {
   391  					return false
   392  				}
   393  			}
   394  			return true
   395  		}, timeout).Should(BeTrue())
   396  
   397  		t.Log("Locating the newest MachineSet")
   398  		var newestMachineSet *clusterv1.MachineSet
   399  		for i := range machineSets.Items {
   400  			ms := &machineSets.Items[i]
   401  			if ms.UID != secondMachineSet.UID {
   402  				newestMachineSet = ms
   403  				break
   404  			}
   405  		}
   406  		g.Expect(newestMachineSet).NotTo(BeNil())
   407  
   408  		t.Log("Verifying the initial MachineSet is deleted")
   409  		g.Eventually(func() int {
   410  			// Set the all non-deleted machines as ready with a NodeRef, so the MachineSet controller can proceed
   411  			// to properly set AvailableReplicas.
   412  			foundMachines := &clusterv1.MachineList{}
   413  			g.Expect(env.List(ctx, foundMachines, client.InNamespace(namespace.Name))).To(Succeed())
   414  			for i := 0; i < len(foundMachines.Items); i++ {
   415  				m := foundMachines.Items[i]
   416  				// Skip over deleted Machines
   417  				if !m.DeletionTimestamp.IsZero() {
   418  					continue
   419  				}
   420  				// Skip over Machines controlled by other (previous) MachineSets
   421  				if !metav1.IsControlledBy(&m, newestMachineSet) {
   422  					continue
   423  				}
   424  				providerID := fakeInfrastructureRefReady(m.Spec.InfrastructureRef, infraResource, g)
   425  				fakeMachineNodeRef(&m, providerID, g)
   426  			}
   427  
   428  			if err := env.List(ctx, machineSets, msListOpts...); err != nil {
   429  				return -1
   430  			}
   431  			return len(machineSets.Items)
   432  		}, timeout*3).Should(BeEquivalentTo(1))
   433  
   434  		t.Log("Verifying new MachineSet has desired number of replicas")
   435  		g.Eventually(func() bool {
   436  			g.Expect(env.List(ctx, machineSets, msListOpts...)).Should(Succeed())
   437  			newms := machineSets.Items[0]
   438  			// Set the all non-deleted machines as ready with a NodeRef, so the MachineSet controller can proceed
   439  			// to properly set AvailableReplicas.
   440  			foundMachines := &clusterv1.MachineList{}
   441  			g.Expect(env.List(ctx, foundMachines, client.InNamespace(namespace.Name))).To(Succeed())
   442  			for i := 0; i < len(foundMachines.Items); i++ {
   443  				m := foundMachines.Items[i]
   444  				if !m.DeletionTimestamp.IsZero() {
   445  					continue
   446  				}
   447  				// Skip over Machines controlled by other (previous) MachineSets
   448  				if !metav1.IsControlledBy(&m, &newms) {
   449  					continue
   450  				}
   451  				providerID := fakeInfrastructureRefReady(m.Spec.InfrastructureRef, infraResource, g)
   452  				fakeMachineNodeRef(&m, providerID, g)
   453  			}
   454  
   455  			return newms.Status.Replicas == desiredMachineDeploymentReplicas
   456  		}, timeout*5).Should(BeTrue())
   457  
   458  		t.Log("Verifying MachineDeployment has correct Conditions")
   459  		g.Eventually(func() bool {
   460  			key := client.ObjectKey{Name: deployment.Name, Namespace: deployment.Namespace}
   461  			g.Expect(env.Get(ctx, key, deployment)).To(Succeed())
   462  			return conditions.IsTrue(deployment, clusterv1.MachineDeploymentAvailableCondition)
   463  		}, timeout).Should(BeTrue())
   464  
   465  		// Validate that the controller set the cluster name label in selector.
   466  		g.Expect(deployment.Status.Selector).To(ContainSubstring(testCluster.Name))
   467  	})
   468  }
   469  
   470  func TestMachineDeploymentReconciler_CleanUpManagedFieldsForSSAAdoption(t *testing.T) {
   471  	setup := func(t *testing.T, g *WithT) (*corev1.Namespace, *clusterv1.Cluster) {
   472  		t.Helper()
   473  
   474  		t.Log("Creating the namespace")
   475  		ns, err := env.CreateNamespace(ctx, machineDeploymentNamespace)
   476  		g.Expect(err).ToNot(HaveOccurred())
   477  
   478  		t.Log("Creating the Cluster")
   479  		cluster := &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Namespace: ns.Name, Name: "test-cluster"}}
   480  		g.Expect(env.Create(ctx, cluster)).To(Succeed())
   481  
   482  		t.Log("Creating the Cluster Kubeconfig Secret")
   483  		g.Expect(env.CreateKubeconfigSecret(ctx, cluster)).To(Succeed())
   484  
   485  		return ns, cluster
   486  	}
   487  
   488  	teardown := func(t *testing.T, g *WithT, ns *corev1.Namespace, cluster *clusterv1.Cluster) {
   489  		t.Helper()
   490  
   491  		t.Log("Deleting the Cluster")
   492  		g.Expect(env.Delete(ctx, cluster)).To(Succeed())
   493  		t.Log("Deleting the namespace")
   494  		g.Expect(env.Delete(ctx, ns)).To(Succeed())
   495  	}
   496  
   497  	g := NewWithT(t)
   498  	namespace, testCluster := setup(t, g)
   499  	defer teardown(t, g, namespace, testCluster)
   500  
   501  	labels := map[string]string{
   502  		"foo":                      "bar",
   503  		clusterv1.ClusterNameLabel: testCluster.Name,
   504  	}
   505  	version := "v1.10.3"
   506  	deployment := &clusterv1.MachineDeployment{
   507  		ObjectMeta: metav1.ObjectMeta{
   508  			GenerateName: "md-",
   509  			Namespace:    namespace.Name,
   510  			Labels: map[string]string{
   511  				clusterv1.ClusterNameLabel: testCluster.Name,
   512  			},
   513  		},
   514  		Spec: clusterv1.MachineDeploymentSpec{
   515  			Paused:               true, // Set this to true as we do not want to test the other parts of the reconciler in this test.
   516  			ClusterName:          testCluster.Name,
   517  			MinReadySeconds:      pointer.Int32(0),
   518  			Replicas:             pointer.Int32(2),
   519  			RevisionHistoryLimit: pointer.Int32(0),
   520  			Selector: metav1.LabelSelector{
   521  				// We're using the same labels for spec.selector and spec.template.labels.
   522  				MatchLabels: labels,
   523  			},
   524  			Strategy: &clusterv1.MachineDeploymentStrategy{
   525  				Type: clusterv1.RollingUpdateMachineDeploymentStrategyType,
   526  				RollingUpdate: &clusterv1.MachineRollingUpdateDeployment{
   527  					MaxUnavailable: intOrStrPtr(0),
   528  					MaxSurge:       intOrStrPtr(1),
   529  					DeletePolicy:   pointer.String("Oldest"),
   530  				},
   531  			},
   532  			Template: clusterv1.MachineTemplateSpec{
   533  				ObjectMeta: clusterv1.ObjectMeta{
   534  					Labels: labels,
   535  				},
   536  				Spec: clusterv1.MachineSpec{
   537  					ClusterName: testCluster.Name,
   538  					Version:     &version,
   539  					InfrastructureRef: corev1.ObjectReference{
   540  						APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
   541  						Kind:       "GenericInfrastructureMachineTemplate",
   542  						Name:       "md-template",
   543  					},
   544  					Bootstrap: clusterv1.Bootstrap{
   545  						DataSecretName: pointer.String("data-secret-name"),
   546  					},
   547  				},
   548  			},
   549  		},
   550  	}
   551  	msListOpts := []client.ListOption{
   552  		client.InNamespace(namespace.Name),
   553  		client.MatchingLabels(labels),
   554  	}
   555  
   556  	// Create infrastructure template resource.
   557  	infraResource := map[string]interface{}{
   558  		"kind":       "GenericInfrastructureMachine",
   559  		"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
   560  		"metadata":   map[string]interface{}{},
   561  		"spec": map[string]interface{}{
   562  			"size": "3xlarge",
   563  		},
   564  	}
   565  	infraTmpl := &unstructured.Unstructured{
   566  		Object: map[string]interface{}{
   567  			"kind":       "GenericInfrastructureMachineTemplate",
   568  			"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
   569  			"metadata": map[string]interface{}{
   570  				"name":      "md-template",
   571  				"namespace": namespace.Name,
   572  			},
   573  			"spec": map[string]interface{}{
   574  				"template": infraResource,
   575  			},
   576  		},
   577  	}
   578  	t.Log("Creating the infrastructure template")
   579  	g.Expect(env.Create(ctx, infraTmpl)).To(Succeed())
   580  
   581  	// Create the MachineDeployment object and expect Reconcile to be called.
   582  	t.Log("Creating the MachineDeployment")
   583  	g.Expect(env.Create(ctx, deployment)).To(Succeed())
   584  
   585  	// Create a MachineSet for the MachineDeployment.
   586  	classicManagerMS := &clusterv1.MachineSet{
   587  		TypeMeta: metav1.TypeMeta{
   588  			Kind:       "MachineSet",
   589  			APIVersion: clusterv1.GroupVersion.String(),
   590  		},
   591  		ObjectMeta: metav1.ObjectMeta{
   592  			Name:      deployment.Name + "-" + "classic-ms",
   593  			Namespace: testCluster.Namespace,
   594  			Labels:    labels,
   595  		},
   596  		Spec: clusterv1.MachineSetSpec{
   597  			ClusterName:     testCluster.Name,
   598  			Replicas:        pointer.Int32(0),
   599  			MinReadySeconds: 0,
   600  			Selector: metav1.LabelSelector{
   601  				MatchLabels: labels,
   602  			},
   603  			Template: clusterv1.MachineTemplateSpec{
   604  				ObjectMeta: clusterv1.ObjectMeta{
   605  					Labels: labels,
   606  				},
   607  				Spec: clusterv1.MachineSpec{
   608  					ClusterName: testCluster.Name,
   609  					InfrastructureRef: corev1.ObjectReference{
   610  						APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
   611  						Kind:       "GenericInfrastructureMachineTemplate",
   612  						Name:       "md-template",
   613  					},
   614  					Bootstrap: clusterv1.Bootstrap{
   615  						DataSecretName: pointer.String("data-secret-name"),
   616  					},
   617  					Version: &version,
   618  				},
   619  			},
   620  		},
   621  	}
   622  	ssaManagerMS := classicManagerMS.DeepCopy()
   623  	ssaManagerMS.Name = deployment.Name + "-" + "ssa-ms"
   624  
   625  	// Create one using the "old manager".
   626  	g.Expect(env.Create(ctx, classicManagerMS, client.FieldOwner("manager"))).To(Succeed())
   627  
   628  	// Create one using SSA.
   629  	g.Expect(env.Patch(ctx, ssaManagerMS, client.Apply, client.FieldOwner(machineDeploymentManagerName), client.ForceOwnership)).To(Succeed())
   630  
   631  	// Verify that for both the MachineSets the ManagedFields are updated.
   632  	g.Eventually(func(g Gomega) {
   633  		machineSets := &clusterv1.MachineSetList{}
   634  		g.Expect(env.List(ctx, machineSets, msListOpts...)).To(Succeed())
   635  
   636  		g.Expect(machineSets.Items).To(HaveLen(2))
   637  		for _, ms := range machineSets.Items {
   638  			// Verify the ManagedFields are updated.
   639  			g.Expect(ms.GetManagedFields()).Should(
   640  				ContainElement(ssa.MatchManagedFieldsEntry(machineDeploymentManagerName, metav1.ManagedFieldsOperationApply)))
   641  			g.Expect(ms.GetManagedFields()).ShouldNot(
   642  				ContainElement(ssa.MatchManagedFieldsEntry("manager", metav1.ManagedFieldsOperationUpdate)))
   643  		}
   644  	}).Should(Succeed())
   645  }
   646  
   647  func TestMachineSetToDeployments(t *testing.T) {
   648  	g := NewWithT(t)
   649  
   650  	machineDeployment := &clusterv1.MachineDeployment{
   651  		ObjectMeta: metav1.ObjectMeta{
   652  			Name:      "withMatchingLabels",
   653  			Namespace: metav1.NamespaceDefault,
   654  		},
   655  		Spec: clusterv1.MachineDeploymentSpec{
   656  			Selector: metav1.LabelSelector{
   657  				MatchLabels: map[string]string{
   658  					"foo":                      "bar",
   659  					clusterv1.ClusterNameLabel: "test-cluster",
   660  				},
   661  			},
   662  		},
   663  	}
   664  
   665  	machineDeplopymentList := []client.Object{machineDeployment}
   666  
   667  	ms1 := clusterv1.MachineSet{
   668  		TypeMeta: metav1.TypeMeta{
   669  			Kind: "MachineSet",
   670  		},
   671  		ObjectMeta: metav1.ObjectMeta{
   672  			Name:      "withOwnerRef",
   673  			Namespace: metav1.NamespaceDefault,
   674  			OwnerReferences: []metav1.OwnerReference{
   675  				*metav1.NewControllerRef(machineDeployment, machineDeploymentKind),
   676  			},
   677  			Labels: map[string]string{
   678  				clusterv1.ClusterNameLabel: "test-cluster",
   679  			},
   680  		},
   681  	}
   682  	ms2 := clusterv1.MachineSet{
   683  		TypeMeta: metav1.TypeMeta{
   684  			Kind: "MachineSet",
   685  		},
   686  		ObjectMeta: metav1.ObjectMeta{
   687  			Name:      "noOwnerRefNoLabels",
   688  			Namespace: metav1.NamespaceDefault,
   689  			Labels: map[string]string{
   690  				clusterv1.ClusterNameLabel: "test-cluster",
   691  			},
   692  		},
   693  	}
   694  	ms3 := clusterv1.MachineSet{
   695  		TypeMeta: metav1.TypeMeta{
   696  			Kind: "MachineSet",
   697  		},
   698  		ObjectMeta: metav1.ObjectMeta{
   699  			Name:      "withMatchingLabels",
   700  			Namespace: metav1.NamespaceDefault,
   701  			Labels: map[string]string{
   702  				"foo":                      "bar",
   703  				clusterv1.ClusterNameLabel: "test-cluster",
   704  			},
   705  		},
   706  	}
   707  
   708  	testsCases := []struct {
   709  		machineSet clusterv1.MachineSet
   710  		mapObject  client.Object
   711  		expected   []reconcile.Request
   712  	}{
   713  		{
   714  			machineSet: ms1,
   715  			mapObject:  &ms1,
   716  			expected:   []reconcile.Request{},
   717  		},
   718  		{
   719  			machineSet: ms2,
   720  			mapObject:  &ms2,
   721  			expected:   nil,
   722  		},
   723  		{
   724  			machineSet: ms3,
   725  			mapObject:  &ms3,
   726  			expected: []reconcile.Request{
   727  				{NamespacedName: client.ObjectKey{Namespace: metav1.NamespaceDefault, Name: "withMatchingLabels"}},
   728  			},
   729  		},
   730  	}
   731  
   732  	r := &Reconciler{
   733  		Client:   fake.NewClientBuilder().WithObjects(machineDeplopymentList...).Build(),
   734  		recorder: record.NewFakeRecorder(32),
   735  	}
   736  
   737  	for _, tc := range testsCases {
   738  		got := r.MachineSetToDeployments(ctx, tc.mapObject)
   739  		g.Expect(got).To(BeComparableTo(tc.expected))
   740  	}
   741  }
   742  
   743  func TestGetMachineDeploymentsForMachineSet(t *testing.T) {
   744  	g := NewWithT(t)
   745  
   746  	machineDeployment := &clusterv1.MachineDeployment{
   747  		ObjectMeta: metav1.ObjectMeta{
   748  			Name:      "withLabels",
   749  			Namespace: metav1.NamespaceDefault,
   750  		},
   751  		Spec: clusterv1.MachineDeploymentSpec{
   752  			Selector: metav1.LabelSelector{
   753  				MatchLabels: map[string]string{
   754  					"foo": "bar",
   755  				},
   756  			},
   757  		},
   758  	}
   759  	machineDeploymentList := []client.Object{machineDeployment}
   760  
   761  	ms1 := clusterv1.MachineSet{
   762  		TypeMeta: metav1.TypeMeta{
   763  			Kind: "MachineSet",
   764  		},
   765  		ObjectMeta: metav1.ObjectMeta{
   766  			Name:      "NoMatchingLabels",
   767  			Namespace: metav1.NamespaceDefault,
   768  		},
   769  	}
   770  	ms2 := clusterv1.MachineSet{
   771  		TypeMeta: metav1.TypeMeta{
   772  			Kind: "MachineSet",
   773  		},
   774  		ObjectMeta: metav1.ObjectMeta{
   775  			Name:      "withMatchingLabels",
   776  			Namespace: metav1.NamespaceDefault,
   777  			Labels: map[string]string{
   778  				"foo": "bar",
   779  			},
   780  		},
   781  	}
   782  
   783  	testCases := []struct {
   784  		machineSet clusterv1.MachineSet
   785  		expected   []client.Object
   786  	}{
   787  		{
   788  			machineSet: ms1,
   789  			expected:   nil,
   790  		},
   791  		{
   792  			machineSet: ms2,
   793  			expected:   []client.Object{machineDeployment},
   794  		},
   795  	}
   796  
   797  	r := &Reconciler{
   798  		Client:   fake.NewClientBuilder().WithObjects(append(machineDeploymentList, &ms1, &ms2)...).Build(),
   799  		recorder: record.NewFakeRecorder(32),
   800  	}
   801  
   802  	for i := range testCases {
   803  		tc := testCases[i]
   804  		var got []client.Object
   805  		for _, x := range r.getMachineDeploymentsForMachineSet(ctx, &tc.machineSet) {
   806  			got = append(got, x)
   807  		}
   808  		g.Expect(got).To(BeComparableTo(tc.expected))
   809  	}
   810  }
   811  
   812  func TestGetMachineSetsForDeployment(t *testing.T) {
   813  	machineDeployment1 := clusterv1.MachineDeployment{
   814  		ObjectMeta: metav1.ObjectMeta{
   815  			Name:      "withMatchingOwnerRefAndLabels",
   816  			Namespace: metav1.NamespaceDefault,
   817  			UID:       "UID",
   818  		},
   819  		Spec: clusterv1.MachineDeploymentSpec{
   820  			Selector: metav1.LabelSelector{
   821  				MatchLabels: map[string]string{
   822  					"foo": "bar",
   823  				},
   824  			},
   825  		},
   826  	}
   827  	machineDeployment2 := clusterv1.MachineDeployment{
   828  		ObjectMeta: metav1.ObjectMeta{
   829  			Name:      "withNoMatchingOwnerRef",
   830  			Namespace: metav1.NamespaceDefault,
   831  			UID:       "unMatchingUID",
   832  		},
   833  		Spec: clusterv1.MachineDeploymentSpec{
   834  			Selector: metav1.LabelSelector{
   835  				MatchLabels: map[string]string{
   836  					"foo": "bar2",
   837  				},
   838  			},
   839  		},
   840  	}
   841  	machineDeployment3 := clusterv1.MachineDeployment{
   842  		ObjectMeta: metav1.ObjectMeta{
   843  			Name:      "withMatchingOwnerRefAndNoMatchingLabels",
   844  			Namespace: metav1.NamespaceDefault,
   845  			UID:       "UID3",
   846  		},
   847  		Spec: clusterv1.MachineDeploymentSpec{
   848  			Selector: metav1.LabelSelector{
   849  				MatchLabels: map[string]string{
   850  					"foo": "bar",
   851  				},
   852  			},
   853  		},
   854  	}
   855  
   856  	ms1 := clusterv1.MachineSet{
   857  		TypeMeta: metav1.TypeMeta{
   858  			Kind: "MachineSet",
   859  		},
   860  		ObjectMeta: metav1.ObjectMeta{
   861  			Name:      "withNoOwnerRefShouldBeAdopted2",
   862  			Namespace: metav1.NamespaceDefault,
   863  			Labels: map[string]string{
   864  				"foo": "bar2",
   865  			},
   866  		},
   867  	}
   868  	ms2 := clusterv1.MachineSet{
   869  		TypeMeta: metav1.TypeMeta{
   870  			Kind: "MachineSet",
   871  		},
   872  		ObjectMeta: metav1.ObjectMeta{
   873  			Name:      "withOwnerRefAndLabels",
   874  			Namespace: metav1.NamespaceDefault,
   875  			OwnerReferences: []metav1.OwnerReference{
   876  				*metav1.NewControllerRef(&machineDeployment1, machineDeploymentKind),
   877  			},
   878  			Labels: map[string]string{
   879  				"foo": "bar",
   880  			},
   881  		},
   882  	}
   883  	ms3 := clusterv1.MachineSet{
   884  		TypeMeta: metav1.TypeMeta{
   885  			Kind: "MachineSet",
   886  		},
   887  		ObjectMeta: metav1.ObjectMeta{
   888  			Name:      "withNoOwnerRefShouldBeAdopted1",
   889  			Namespace: metav1.NamespaceDefault,
   890  			Labels: map[string]string{
   891  				"foo": "bar",
   892  			},
   893  		},
   894  	}
   895  	ms4 := clusterv1.MachineSet{
   896  		TypeMeta: metav1.TypeMeta{
   897  			Kind: "MachineSet",
   898  		},
   899  		ObjectMeta: metav1.ObjectMeta{
   900  			Name:      "withNoOwnerRefNoMatch",
   901  			Namespace: metav1.NamespaceDefault,
   902  			Labels: map[string]string{
   903  				"foo": "nomatch",
   904  			},
   905  		},
   906  	}
   907  	ms5 := clusterv1.MachineSet{
   908  		TypeMeta: metav1.TypeMeta{
   909  			Kind: "MachineSet",
   910  		},
   911  		ObjectMeta: metav1.ObjectMeta{
   912  			Name:      "withOwnerRefAndNoMatchLabels",
   913  			Namespace: metav1.NamespaceDefault,
   914  			OwnerReferences: []metav1.OwnerReference{
   915  				*metav1.NewControllerRef(&machineDeployment3, machineDeploymentKind),
   916  			},
   917  			Labels: map[string]string{
   918  				"foo": "nomatch",
   919  			},
   920  		},
   921  	}
   922  	machineSetList := []client.Object{
   923  		&ms1,
   924  		&ms2,
   925  		&ms3,
   926  		&ms4,
   927  		&ms5,
   928  	}
   929  
   930  	testCases := []struct {
   931  		name              string
   932  		machineDeployment clusterv1.MachineDeployment
   933  		expected          []*clusterv1.MachineSet
   934  	}{
   935  		{
   936  			name:              "matching ownerRef and labels",
   937  			machineDeployment: machineDeployment1,
   938  			expected:          []*clusterv1.MachineSet{&ms3, &ms2},
   939  		},
   940  		{
   941  			name:              "no matching ownerRef, matching labels",
   942  			machineDeployment: machineDeployment2,
   943  			expected:          []*clusterv1.MachineSet{&ms1},
   944  		},
   945  		{
   946  			name:              "matching ownerRef, mismatch labels",
   947  			machineDeployment: machineDeployment3,
   948  			expected:          []*clusterv1.MachineSet{&ms3, &ms5},
   949  		},
   950  	}
   951  
   952  	for i := range testCases {
   953  		tc := testCases[i]
   954  		t.Run(tc.name, func(t *testing.T) {
   955  			g := NewWithT(t)
   956  
   957  			r := &Reconciler{
   958  				Client:   fake.NewClientBuilder().WithObjects(machineSetList...).Build(),
   959  				recorder: record.NewFakeRecorder(32),
   960  			}
   961  
   962  			got, err := r.getMachineSetsForDeployment(ctx, &tc.machineDeployment)
   963  			g.Expect(err).ToNot(HaveOccurred())
   964  			g.Expect(got).To(HaveLen(len(tc.expected)))
   965  
   966  			for idx, res := range got {
   967  				g.Expect(res.Name).To(Equal(tc.expected[idx].Name))
   968  				g.Expect(res.Namespace).To(Equal(tc.expected[idx].Namespace))
   969  			}
   970  		})
   971  	}
   972  }