Skip to content

Commit 7d816f3

Browse files
committed
Exit without updating status if suspended
The convention is now to simply exit, when a GOTK object is suspended. Previously this would update the status to indicate that it was unready; now it just leaves it in whichever state it was before. This also applies to the reconcile request annotation; it will _not_ be marked as seen if the object is suspended. The effect of this is any change to the object will be passed by the predicate and therefore reach Reconcile, until the object is unsuspended. Since it will _also_ exit early until unsuspended, this is harmless except for some extra log lines. (But changing that ordering might be worth considering in the future.) This change required a few changes to tests: - to check that suspend makes the reconciliation exit without doing anything, explicitly run `r.Reconcile(...)`. - to avoid waiting for the reconciler's caching client to see changes, use an uncached client. - (fix a problem caused by comparing a time pointer with its alias) Signed-off-by: Michael Bridgen <[email protected]>
1 parent a582871 commit 7d816f3

File tree

3 files changed

+41
-30
lines changed

3 files changed

+41
-30
lines changed

controllers/imageupdateautomation_controller.go

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(req ctrl.Request) (ctrl.Resu
8787
return ctrl.Result{}, client.IgnoreNotFound(err)
8888
}
8989

90+
if auto.Spec.Suspend {
91+
log.Info("ImageUpdateAutomation is suspended, skipping automation run")
92+
return ctrl.Result{}, nil
93+
}
94+
9095
// whatever else happens, we've now "seen" the reconcile
9196
// annotation if it's there
9297
if token, ok := meta.ReconcileAnnotationValue(auto.GetAnnotations()); ok {
@@ -96,21 +101,6 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(req ctrl.Request) (ctrl.Resu
96101
}
97102
}
98103

99-
if auto.Spec.Suspend {
100-
msg := "ImageUpdateAutomation is suspended, skipping automation run"
101-
imagev1.SetImageUpdateAutomationReadiness(
102-
&auto,
103-
metav1.ConditionFalse,
104-
meta.SuspendedReason,
105-
msg,
106-
)
107-
if err := r.Status().Update(ctx, &auto); err != nil {
108-
return ctrl.Result{Requeue: true}, err
109-
}
110-
log.Info(msg)
111-
return ctrl.Result{}, nil
112-
}
113-
114104
// failWithError is a helper for bailing on the reconciliation.
115105
failWithError := func(err error) (ctrl.Result, error) {
116106
r.event(auto, events.EventSeverityError, err.Error())

controllers/suite_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
var cfg *rest.Config
4545
var k8sClient client.Client
4646
var k8sManager ctrl.Manager
47+
var imageAutoReconciler *ImageUpdateAutomationReconciler
4748
var testEnv *envtest.Environment
4849

4950
func TestAPIs(t *testing.T) {
@@ -81,19 +82,22 @@ var _ = BeforeSuite(func(done Done) {
8182
})
8283
Expect(err).ToNot(HaveOccurred())
8384

84-
err = (&ImageUpdateAutomationReconciler{
85+
imageAutoReconciler = &ImageUpdateAutomationReconciler{
8586
Client: k8sManager.GetClient(),
8687
Log: ctrl.Log.WithName("controllers").WithName("ImageUpdateAutomation"),
8788
Scheme: scheme.Scheme,
88-
}).SetupWithManager(k8sManager)
89-
Expect(err).ToNot(HaveOccurred())
89+
}
90+
Expect(imageAutoReconciler.SetupWithManager(k8sManager)).To(Succeed())
9091

9192
go func() {
9293
err = k8sManager.Start(ctrl.SetupSignalHandler())
9394
Expect(err).ToNot(HaveOccurred())
9495
}()
9596

96-
k8sClient = k8sManager.GetClient()
97+
// Specifically an uncached client. Use <reconciler>.Get if you
98+
// want to see what the reconcilers see.
99+
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
100+
Expect(err).ToNot(HaveOccurred())
97101
Expect(k8sClient).ToNot(BeNil())
98102

99103
close(done)

controllers/update_test.go

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ import (
3636
. "github.com/onsi/gomega"
3737
"github.com/otiai10/copy"
3838
corev1 "k8s.io/api/core/v1"
39-
apimeta "k8s.io/apimachinery/pkg/api/meta"
39+
// apimeta "k8s.io/apimachinery/pkg/api/meta"
4040
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4141
"k8s.io/apimachinery/pkg/types"
42+
ctrl "sigs.k8s.io/controller-runtime"
4243
"sigs.k8s.io/controller-runtime/pkg/client"
4344

4445
imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
@@ -252,23 +253,36 @@ var _ = Describe("ImageUpdateAutomation", func() {
252253
updatePatch.Namespace = updateKey.Namespace
253254
updatePatch.Spec.Suspend = true
254255
Expect(k8sClient.Patch(context.Background(), &updatePatch, client.Merge)).To(Succeed())
256+
// wait for the suspension to reach the cache
257+
var newUpdate imagev1.ImageUpdateAutomation
255258
Eventually(func() bool {
256-
if err := k8sClient.Get(context.Background(), updateKey, updateBySetters); err != nil {
259+
if err := imageAutoReconciler.Get(context.Background(), updateKey, &newUpdate); err != nil {
257260
return false
258261
}
259-
ready := apimeta.FindStatusCondition(updateBySetters.Status.Conditions, meta.ReadyCondition)
260-
return ready != nil && ready.Status == metav1.ConditionFalse && ready.Reason == meta.SuspendedReason
262+
return newUpdate.Spec.Suspend
261263
}, timeout, time.Second).Should(BeTrue())
264+
// run the reconciliation explicitly, and make sure it
265+
// doesn't do anything
266+
result, err := imageAutoReconciler.Reconcile(ctrl.Request{
267+
NamespacedName: updateKey,
268+
})
269+
Expect(err).To(BeNil())
270+
Expect(result).To(Equal(ctrl.Result{})) // this ought to fail, since it should be rescheduled; but if not, additional checks lie below
271+
272+
var checkUpdate imagev1.ImageUpdateAutomation
273+
Expect(k8sClient.Get(context.Background(), updateKey, &checkUpdate)).To(Succeed())
274+
Expect(checkUpdate.Status.ObservedGeneration).NotTo(Equal(checkUpdate.ObjectMeta.Generation))
262275
})
263276

264277
It("runs when the reconcile request annotation is added", func() {
278+
println("[DEBUG]", updateKey.String())
265279
// the automation has run, and is not expected to run
266280
// again for 2 hours. Make a commit to the git repo
267281
// which needs to be undone by automation, then add
268282
// the annotation and make sure it runs again.
269283
Expect(k8sClient.Get(context.Background(), updateKey, updateBySetters)).To(Succeed())
270-
lastRun := updateBySetters.Status.LastAutomationRunTime
271-
Expect(lastRun).ToNot(BeNil())
284+
Expect(updateBySetters.Status.LastAutomationRunTime).ToNot(BeNil())
285+
lastRunTime := updateBySetters.Status.LastAutomationRunTime.Time
272286

273287
commitInRepo(repoURL, "Revert image update", func(tmp string) {
274288
// revert the change made by copying the old version
@@ -277,7 +291,7 @@ var _ = Describe("ImageUpdateAutomation", func() {
277291
copy.Copy("testdata/appconfig/deploy.yaml", filepath.Join(tmp, "deploy.yaml"))
278292
replaceMarker(tmp, policyKey)
279293
})
280-
// check that it was reverted
294+
// check that it was reverted correctly
281295
compareRepoWithExpected(repoURL, "testdata/appconfig", func(tmp string) {
282296
replaceMarker(tmp, policyKey)
283297
})
@@ -291,15 +305,18 @@ var _ = Describe("ImageUpdateAutomation", func() {
291305
}
292306
Expect(k8sClient.Patch(context.Background(), &updatePatch, client.Merge)).To(Succeed())
293307

308+
// ... this is where the reconciler is supposed to do its work ...
309+
310+
var newUpdate imagev1.ImageUpdateAutomation
294311
Eventually(func() bool {
295-
if err := k8sClient.Get(context.Background(), updateKey, updateBySetters); err != nil {
312+
if err := k8sClient.Get(context.Background(), updateKey, &newUpdate); err != nil {
296313
return false
297314
}
298-
newLastRun := updateBySetters.Status.LastAutomationRunTime
299-
return newLastRun != nil && newLastRun.Time.After(lastRun.Time)
315+
newLastRun := newUpdate.Status.LastAutomationRunTime
316+
return newLastRun != nil && newLastRun.Time.After(lastRunTime)
300317
}, timeout, time.Second).Should(BeTrue())
301318
// check that the annotation was recorded as seen
302-
Expect(updateBySetters.Status.LastHandledReconcileAt).To(Equal(ts))
319+
Expect(newUpdate.Status.LastHandledReconcileAt).To(Equal(ts))
303320

304321
// check that a new commit was made
305322
compareRepoWithExpected(repoURL, "testdata/appconfig-setters-expected", func(tmp string) {

0 commit comments

Comments
 (0)