Skip to content

Commit 9038198

Browse files
authored
Ensure finalizer is present before reconciling (#218)
* Ensure finalizer is present before reconciling * Fix gomod
1 parent a775742 commit 9038198

File tree

5 files changed

+35
-52
lines changed

5 files changed

+35
-52
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/onsi/ginkgo/v2 v2.11.0
1212
github.com/onsi/gomega v1.27.8
1313
github.com/operator-framework/operator-lib v0.11.1-0.20230607132417-ecb9be488378
14+
github.com/pkg/errors v0.9.1
1415
github.com/prometheus/client_golang v1.15.1
1516
github.com/sergi/go-diff v1.2.0
1617
github.com/sirupsen/logrus v1.9.3
@@ -129,7 +130,6 @@ require (
129130
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
130131
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
131132
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
132-
github.com/pkg/errors v0.9.1 // indirect
133133
github.com/pmezard/go-difflib v1.0.0 // indirect
134134
github.com/prometheus/client_model v0.4.0 // indirect
135135
github.com/prometheus/common v0.44.0 // indirect

pkg/reconciler/internal/updater/updater.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,6 @@ func (u *Updater) Apply(ctx context.Context, obj *unstructured.Unstructured) err
122122
return nil
123123
}
124124

125-
func EnsureFinalizer(finalizer string) UpdateFunc {
126-
return func(obj *unstructured.Unstructured) bool {
127-
if controllerutil.ContainsFinalizer(obj, finalizer) {
128-
return false
129-
}
130-
controllerutil.AddFinalizer(obj, finalizer)
131-
return true
132-
}
133-
}
134-
135125
func RemoveFinalizer(finalizer string) UpdateFunc {
136126
return func(obj *unstructured.Unstructured) bool {
137127
if !controllerutil.ContainsFinalizer(obj, finalizer) {

pkg/reconciler/internal/updater/updater_test.go

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ var _ = Describe("Updater", func() {
5959
When("the object does not exist", func() {
6060
It("should fail", func() {
6161
Expect(client.Delete(context.TODO(), obj)).To(Succeed())
62-
u.Update(EnsureFinalizer(testFinalizer))
62+
u.Update(func(u *unstructured.Unstructured) bool {
63+
u.SetAnnotations(map[string]string{"foo": "bar"})
64+
return true
65+
})
6366
err := u.Apply(context.TODO(), obj)
6467
Expect(err).NotTo(BeNil())
6568
Expect(apierrors.IsNotFound(err)).To(BeTrue())
@@ -68,12 +71,15 @@ var _ = Describe("Updater", func() {
6871

6972
When("an update is a change", func() {
7073
It("should apply an update function", func() {
71-
u.Update(EnsureFinalizer(testFinalizer))
74+
u.Update(func(u *unstructured.Unstructured) bool {
75+
u.SetAnnotations(map[string]string{"foo": "bar"})
76+
return true
77+
})
7278
resourceVersion := obj.GetResourceVersion()
7379

7480
Expect(u.Apply(context.TODO(), obj)).To(Succeed())
7581
Expect(client.Get(context.TODO(), types.NamespacedName{Namespace: "testNamespace", Name: "testDeployment"}, obj)).To(Succeed())
76-
Expect(obj.GetFinalizers()).To(Equal([]string{testFinalizer}))
82+
Expect(obj.GetAnnotations()["foo"]).To(Equal("bar"))
7783
Expect(obj.GetResourceVersion()).NotTo(Equal(resourceVersion))
7884
})
7985

@@ -89,25 +95,6 @@ var _ = Describe("Updater", func() {
8995
})
9096
})
9197

92-
var _ = Describe("EnsureFinalizer", func() {
93-
var obj *unstructured.Unstructured
94-
95-
BeforeEach(func() {
96-
obj = &unstructured.Unstructured{}
97-
})
98-
99-
It("should add finalizer if not present", func() {
100-
Expect(EnsureFinalizer(testFinalizer)(obj)).To(BeTrue())
101-
Expect(obj.GetFinalizers()).To(Equal([]string{testFinalizer}))
102-
})
103-
104-
It("should not add duplicate finalizer", func() {
105-
obj.SetFinalizers([]string{testFinalizer})
106-
Expect(EnsureFinalizer(testFinalizer)(obj)).To(BeFalse())
107-
Expect(obj.GetFinalizers()).To(Equal([]string{testFinalizer}))
108-
})
109-
})
110-
11198
var _ = Describe("RemoveFinalizer", func() {
11299
var obj *unstructured.Unstructured
113100

pkg/reconciler/reconciler.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
errs "github.com/pkg/errors"
2324
"strings"
2425
"sync"
2526
"time"
@@ -553,6 +554,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.
553554
return ctrl.Result{}, err
554555
}
555556

557+
// The finalizer must be present on the CR before we can do anything. Otherwise, if the reconciliation fails,
558+
// there might be resources created by the chart that will not be garbage-collected
559+
// (cluster-scoped resources or resources in other namespaces, which are not bound by an owner reference).
560+
// This is a safety measure to ensure that the chart is fully uninstalled before the CR is deleted.
561+
if obj.GetDeletionTimestamp() == nil && !controllerutil.ContainsFinalizer(obj, uninstallFinalizer) {
562+
log.V(1).Info("Adding uninstall finalizer.")
563+
obj.SetFinalizers(append(obj.GetFinalizers(), uninstallFinalizer))
564+
if err := r.client.Update(ctx, obj); err != nil {
565+
return ctrl.Result{}, errs.Wrapf(err, "failed to add uninstall finalizer to %s/%s", req.NamespacedName.Namespace, req.NamespacedName.Name)
566+
}
567+
}
568+
556569
u := updater.New(r.client)
557570
defer func() {
558571
applyErr := u.Apply(ctx, obj)
@@ -570,14 +583,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.
570583
updater.EnsureConditionUnknown(conditions.TypeReleaseFailed),
571584
updater.EnsureDeployedRelease(nil),
572585
)
573-
// NOTE: If obj has the uninstall finalizer, that means a release WAS deployed at some point
574-
// in the past, but we don't know if it still is because we don't have an actionClient to check.
575-
// So the question is, what do we do with the finalizer? We could:
576-
// - Leave it in place. This would make the CR impossible to delete without either resolving this error, or
577-
// manually uninstalling the release, deleting the finalizer, and deleting the CR.
578-
// - Remove the finalizer. This would make it possible to delete the CR, but it would leave around any
579-
// release resources that are not owned by the CR (those in the cluster scope or in other namespaces).
580-
//
586+
// When it is impossible to obtain an actionClient, we cannot proceed with the reconciliation. Question is
587+
// what to do with the finalizer?
581588
// The decision made for now is to leave the finalizer in place, so that the user can intervene and try to
582589
// resolve the issue, instead of the operator silently leaving some dangling resources hanging around after the
583590
// CR is deleted.
@@ -984,7 +991,6 @@ func ensureDeployedRelease(u *updater.Updater, rel *release.Release) {
984991
if rel.Info != nil && len(rel.Info.Notes) > 0 {
985992
message = rel.Info.Notes
986993
}
987-
u.Update(updater.EnsureFinalizer(uninstallFinalizer))
988994
u.UpdateStatus(
989995
updater.EnsureCondition(conditions.Deployed(corev1.ConditionTrue, reason, message)),
990996
updater.EnsureDeployedRelease(rel),

pkg/reconciler/reconciler_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -607,8 +607,8 @@ var _ = Describe("Reconciler", func() {
607607
Expect(c.Message).To(Equal(acgErr.Error()))
608608
})
609609

610-
By("verifying the uninstall finalizer is not present on the CR", func() {
611-
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeFalse())
610+
By("verifying the uninstall finalizer is present on the CR", func() {
611+
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeTrue())
612612
})
613613
})
614614
It("returns an error getting the release", func() {
@@ -644,8 +644,8 @@ var _ = Describe("Reconciler", func() {
644644
Expect(c.Message).To(Equal("get not implemented"))
645645
})
646646

647-
By("verifying the uninstall finalizer is not present on the CR", func() {
648-
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeFalse())
647+
By("verifying the uninstall finalizer is present on the CR", func() {
648+
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeTrue())
649649
})
650650
})
651651
})
@@ -680,8 +680,8 @@ var _ = Describe("Reconciler", func() {
680680
Expect(c.Message).To(ContainSubstring("error parsing index"))
681681
})
682682

683-
By("verifying the uninstall finalizer is not present on the CR", func() {
684-
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeFalse())
683+
By("verifying the uninstall finalizer is present on the CR", func() {
684+
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeTrue())
685685
})
686686
})
687687
})
@@ -747,8 +747,8 @@ var _ = Describe("Reconciler", func() {
747747
Expect(c.Message).To(ContainSubstring("install failed: foobar"))
748748
})
749749

750-
By("ensuring the uninstall finalizer is not present on the CR", func() {
751-
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeFalse())
750+
By("verifying the uninstall finalizer is present on the CR", func() {
751+
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeTrue())
752752
})
753753
})
754754
})
@@ -928,7 +928,7 @@ var _ = Describe("Reconciler", func() {
928928
Expect(objStat.Status.DeployedRelease.Manifest).To(Equal(currentRelease.Manifest))
929929
})
930930

931-
By("verifying the uninstall finalizer is not present on the CR", func() {
931+
By("verifying the uninstall finalizer is present on the CR", func() {
932932
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeTrue())
933933
})
934934
})
@@ -967,7 +967,7 @@ var _ = Describe("Reconciler", func() {
967967
Expect(objStat.Status.DeployedRelease.Manifest).To(Equal(currentRelease.Manifest))
968968
})
969969

970-
By("verifying the uninstall finalizer is not present on the CR", func() {
970+
By("verifying the uninstall finalizer is present on the CR", func() {
971971
Expect(controllerutil.ContainsFinalizer(obj, uninstallFinalizer)).To(BeTrue())
972972
})
973973
})

0 commit comments

Comments
 (0)