Skip to content

Commit 4d0e5f9

Browse files
committed
Fix finalizer calculation in MD/MS controller
Signed-off-by: Stefan Büringer [email protected]
1 parent 2568aa2 commit 4d0e5f9

File tree

6 files changed

+76
-16
lines changed

6 files changed

+76
-16
lines changed

internal/controllers/machinedeployment/machinedeployment_sync.go

+2-15
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"k8s.io/apimachinery/pkg/types"
3030
kerrors "k8s.io/apimachinery/pkg/util/errors"
3131
apirand "k8s.io/apimachinery/pkg/util/rand"
32-
"k8s.io/apimachinery/pkg/util/sets"
3332
"k8s.io/apimachinery/pkg/util/wait"
3433
"k8s.io/klog/v2"
3534
"k8s.io/utils/ptr"
@@ -230,11 +229,6 @@ func (r *Reconciler) computeDesiredMachineSet(ctx context.Context, deployment *c
230229
name, randomSuffix = computeNewMachineSetName(deployment.Name + "-")
231230
uniqueIdentifierLabelValue = fmt.Sprintf("%d-%s", templateHash, randomSuffix)
232231

233-
// Add foregroundDeletion finalizer to MachineSet if the MachineDeployment has it.
234-
if sets.New[string](deployment.Finalizers...).Has(metav1.FinalizerDeleteDependents) {
235-
finalizers = []string{metav1.FinalizerDeleteDependents}
236-
}
237-
238232
replicas, err = mdutil.NewMSNewReplicas(deployment, oldMSs, 0)
239233
if err != nil {
240234
return nil, errors.Wrap(err, "failed to compute desired MachineSet")
@@ -257,15 +251,8 @@ func (r *Reconciler) computeDesiredMachineSet(ctx context.Context, deployment *c
257251
name = existingMS.Name
258252
uid = existingMS.UID
259253

260-
// Keep foregroundDeletion finalizer if the existingMS has it.
261-
// Note: This case is a little different from the create case. In the update case we preserve
262-
// the finalizer on the MachineSet if it already exists. Because of SSA we should not build
263-
// the finalizer information from the MachineDeployment when updating a MachineSet because that could lead
264-
// to dropping the finalizer from the MachineSet if it is dropped from the MachineDeployment.
265-
// We should not drop the finalizer on the MachineSet if the finalizer is dropped from the MachineDeployment.
266-
if sets.New[string](existingMS.Finalizers...).Has(metav1.FinalizerDeleteDependents) {
267-
finalizers = []string{metav1.FinalizerDeleteDependents}
268-
}
254+
// Preserve all existing finalizers (including foregroundDeletion finalizer).
255+
finalizers = existingMS.Finalizers
269256

270257
replicas = *existingMS.Spec.Replicas
271258

internal/controllers/machinedeployment/machinedeployment_sync_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,8 @@ func TestComputeDesiredMachineSet(t *testing.T) {
642642
"ms-label-1": "ms-value-1",
643643
}
644644
existingMS.Annotations = nil
645+
// Pre-existing finalizer should be preserved.
646+
existingMS.Finalizers = []string{"pre-existing-finalizer"}
645647
existingMS.Spec.Template.Labels = map[string]string{
646648
clusterv1.MachineDeploymentUniqueLabel: uniqueID,
647649
"ms-label-2": "ms-value-2",
@@ -657,6 +659,9 @@ func TestComputeDesiredMachineSet(t *testing.T) {
657659
expectedMS.UID = existingMSUID
658660
expectedMS.Name = deployment.Name + "-" + uniqueID
659661
expectedMS.Labels[clusterv1.MachineDeploymentUniqueLabel] = uniqueID
662+
// Pre-existing finalizer should be preserved.
663+
expectedMS.Finalizers = []string{"pre-existing-finalizer"}
664+
660665
expectedMS.Spec.Template.Labels[clusterv1.MachineDeploymentUniqueLabel] = uniqueID
661666

662667
g := NewWithT(t)
@@ -676,6 +681,8 @@ func TestComputeDesiredMachineSet(t *testing.T) {
676681
"ms-label-1": "ms-value-1",
677682
}
678683
existingMS.Annotations = nil
684+
// Pre-existing finalizer should be preserved.
685+
existingMS.Finalizers = []string{"pre-existing-finalizer"}
679686
existingMS.Spec.Template.Labels = map[string]string{
680687
clusterv1.MachineDeploymentUniqueLabel: uniqueID,
681688
"ms-label-2": "ms-value-2",
@@ -698,6 +705,8 @@ func TestComputeDesiredMachineSet(t *testing.T) {
698705
expectedMS.UID = existingMSUID
699706
expectedMS.Name = deployment.Name + "-" + uniqueID
700707
expectedMS.Labels[clusterv1.MachineDeploymentUniqueLabel] = uniqueID
708+
// Pre-existing finalizer should be preserved.
709+
expectedMS.Finalizers = []string{"pre-existing-finalizer"}
701710
expectedMS.Spec.Template.Labels[clusterv1.MachineDeploymentUniqueLabel] = uniqueID
702711

703712
g := NewWithT(t)
@@ -765,6 +774,9 @@ func assertMachineSet(g *WithT, actualMS *clusterv1.MachineSet, expectedMS *clus
765774
// Check Namespace
766775
g.Expect(actualMS.Namespace).Should(Equal(expectedMS.Namespace))
767776

777+
// Check finalizers
778+
g.Expect(actualMS.Finalizers).Should(Equal(expectedMS.Finalizers))
779+
768780
// Check Replicas
769781
g.Expect(actualMS.Spec.Replicas).ShouldNot(BeNil())
770782
g.Expect(actualMS.Spec.Replicas).Should(HaveValue(Equal(*expectedMS.Spec.Replicas)))

internal/controllers/machineset/machineset_controller.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3030
"k8s.io/apimachinery/pkg/labels"
3131
kerrors "k8s.io/apimachinery/pkg/util/errors"
32+
"k8s.io/apimachinery/pkg/util/sets"
3233
"k8s.io/apimachinery/pkg/util/wait"
3334
"k8s.io/apiserver/pkg/storage/names"
3435
"k8s.io/client-go/tools/record"
@@ -637,10 +638,18 @@ func (r *Reconciler) computeDesiredMachine(machineSet *clusterv1.MachineSet, exi
637638
if existingMachine != nil {
638639
desiredMachine.SetName(existingMachine.Name)
639640
desiredMachine.SetUID(existingMachine.UID)
641+
642+
// Preserve all existing finalizers (including foregroundDeletion finalizer).
643+
finalizers := existingMachine.Finalizers
644+
// Ensure MachineFinalizer is set.
645+
if !sets.New[string](finalizers...).Has(clusterv1.MachineFinalizer) {
646+
finalizers = append(finalizers, clusterv1.MachineFinalizer)
647+
}
648+
desiredMachine.Finalizers = finalizers
649+
640650
desiredMachine.Spec.Bootstrap.ConfigRef = existingMachine.Spec.Bootstrap.ConfigRef
641651
desiredMachine.Spec.InfrastructureRef = existingMachine.Spec.InfrastructureRef
642652
}
643-
644653
// Set the in-place mutable fields.
645654
// When we create a new Machine we will just create the Machine with those fields.
646655
// When we update an existing Machine will we update the fields on the existing Machine (in-place mutate).

internal/controllers/machineset/machineset_controller_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,8 @@ func TestComputeDesiredMachine(t *testing.T) {
16201620
existingMachine.UID = "abc-123-existing-machine-1"
16211621
existingMachine.Labels = nil
16221622
existingMachine.Annotations = nil
1623+
// Pre-existing finalizer should be preserved.
1624+
existingMachine.Finalizers = []string{"pre-existing-finalizer"}
16231625
existingMachine.Spec.InfrastructureRef = corev1.ObjectReference{
16241626
Kind: "GenericInfrastructureMachine",
16251627
Name: "infra-machine-1",
@@ -1637,6 +1639,8 @@ func TestComputeDesiredMachine(t *testing.T) {
16371639
expectedUpdatedMachine := skeletonMachine.DeepCopy()
16381640
expectedUpdatedMachine.Name = existingMachine.Name
16391641
expectedUpdatedMachine.UID = existingMachine.UID
1642+
// Pre-existing finalizer should be preserved.
1643+
expectedUpdatedMachine.Finalizers = []string{"pre-existing-finalizer", clusterv1.MachineFinalizer}
16401644
expectedUpdatedMachine.Spec.InfrastructureRef = *existingMachine.Spec.InfrastructureRef.DeepCopy()
16411645
expectedUpdatedMachine.Spec.Bootstrap.ConfigRef = existingMachine.Spec.Bootstrap.ConfigRef.DeepCopy()
16421646

test/e2e/md_scale.go

+12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
. "github.com/onsi/ginkgo/v2"
2626
. "github.com/onsi/gomega"
2727
corev1 "k8s.io/api/core/v1"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829
"k8s.io/utils/ptr"
2930

3031
"sigs.k8s.io/cluster-api/test/framework"
@@ -123,6 +124,17 @@ func MachineDeploymentScaleSpec(ctx context.Context, inputGetter func() MachineD
123124
Replicas: 1,
124125
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
125126
})
127+
128+
By("Deleting the MachineDeployment with foreground deletion")
129+
foreground := metav1.DeletePropagationForeground
130+
framework.DeleteAndWaitMachineDeployment(ctx, framework.DeleteAndWaitMachineDeploymentInput{
131+
ClusterProxy: input.BootstrapClusterProxy,
132+
Cluster: clusterResources.Cluster,
133+
MachineDeployment: clusterResources.MachineDeployments[0],
134+
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
135+
DeletePropagationPolicy: &foreground,
136+
})
137+
126138
By("PASSED!")
127139
})
128140

test/framework/machinedeployment_helpers.go

+36
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
. "github.com/onsi/ginkgo/v2"
2626
. "github.com/onsi/gomega"
2727
"github.com/pkg/errors"
28+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3031
"k8s.io/apimachinery/pkg/util/sets"
@@ -448,6 +449,41 @@ func machineDeploymentOptions(deployment clusterv1.MachineDeployment) []client.L
448449
}
449450
}
450451

452+
// DeleteAndWaitMachineDeploymentInput is the input for DeleteAndWaitMachineDeployment.
453+
type DeleteAndWaitMachineDeploymentInput struct {
454+
ClusterProxy ClusterProxy
455+
Cluster *clusterv1.Cluster
456+
MachineDeployment *clusterv1.MachineDeployment
457+
WaitForMachineDeployments []interface{}
458+
DeletePropagationPolicy *metav1.DeletionPropagation
459+
}
460+
461+
// DeleteAndWaitMachineDeployment deletes MachineDeployment and waits until it is gone.
462+
func DeleteAndWaitMachineDeployment(ctx context.Context, input DeleteAndWaitMachineDeploymentInput) {
463+
Expect(ctx).NotTo(BeNil(), "ctx is required for DeleteAndWaitMachineDeployment")
464+
Expect(input.ClusterProxy).ToNot(BeNil(), "Invalid argument. input.ClusterProxy can't be nil when calling DeleteAndWaitMachineDeployment")
465+
Expect(input.Cluster).ToNot(BeNil(), "Invalid argument. input.Cluster can't be nil when calling DeleteAndWaitMachineDeployment")
466+
Expect(input.MachineDeployment).ToNot(BeNil(), "Invalid argument. input.MachineDeployment can't be nil when calling DeleteAndWaitMachineDeployment")
467+
468+
log.Logf("Deleting MachineDeployment %s", klog.KObj(input.MachineDeployment))
469+
Expect(input.ClusterProxy.GetClient().Delete(ctx, input.MachineDeployment, &client.DeleteOptions{PropagationPolicy: input.DeletePropagationPolicy})).To(Succeed())
470+
471+
log.Logf("Waiting for MD to be deleted")
472+
Eventually(func(g Gomega) {
473+
selectorMap, err := metav1.LabelSelectorAsMap(&input.MachineDeployment.Spec.Selector)
474+
g.Expect(err).ToNot(HaveOccurred())
475+
476+
ms := &clusterv1.MachineSetList{}
477+
err = input.ClusterProxy.GetClient().List(ctx, ms, client.InNamespace(input.Cluster.Namespace), client.MatchingLabels(selectorMap))
478+
g.Expect(err).ToNot(HaveOccurred())
479+
g.Expect(ms.Items).To(BeEmpty())
480+
481+
md := &clusterv1.MachineDeployment{}
482+
err = input.ClusterProxy.GetClient().Get(ctx, client.ObjectKeyFromObject(input.MachineDeployment), md)
483+
g.Expect(apierrors.IsNotFound(err)).To(BeTrue())
484+
}, input.WaitForMachineDeployments...).Should(Succeed(), "Timed out waiting for Machine Deployment deletion")
485+
}
486+
451487
// ScaleAndWaitMachineDeploymentInput is the input for ScaleAndWaitMachineDeployment.
452488
type ScaleAndWaitMachineDeploymentInput struct {
453489
ClusterProxy ClusterProxy

0 commit comments

Comments
 (0)