From 17285081d34ae886247ecd9459f5b99f4c3d1d20 Mon Sep 17 00:00:00 2001 From: Johannes Aubart Date: Tue, 5 Aug 2025 09:31:18 +0200 Subject: [PATCH] move smart requeue action into reconcile result --- docs/libs/smartrequeue.md | 2 ++ docs/libs/status.md | 7 +++++++ pkg/controller/status_updater.go | 9 +++++---- pkg/controller/status_updater_test.go | 12 +++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/docs/libs/smartrequeue.md b/docs/libs/smartrequeue.md index ad03fa8..d045883 100644 --- a/docs/libs/smartrequeue.md +++ b/docs/libs/smartrequeue.md @@ -7,3 +7,5 @@ Use `NewStore` in the constructor of the reconciler. During reconciliation, the - `Never` does not requeue the object - `Backoff` requeues the object with an increasing backoff every time it is called on the same object - `Reset` requeues the object, but resets the duration to its minimal value + +There is also an integration into the [status updater](./status.md) for the smart requeuing logic. diff --git a/docs/libs/status.md b/docs/libs/status.md index 113e1f2..76fad64 100644 --- a/docs/libs/status.md +++ b/docs/libs/status.md @@ -181,3 +181,10 @@ The `ReconcileResult` that is passed into the status updater is expected to cont - If this is nil, `Object` will be used instead. - If this is non-nil, it must not point to the same instance as `Object` - use the `DeepCopy()` function to create a different instance. - All changes to `Object`'s status that are not part to `OldObject`'s status will be included in the patch during the status update. This can be used to inject custom changes to the status into the status update (in addition to the `WithCustomUpdateFunc` mentioned above). +- `SmartRequeue` contains the requeuing information for the [smart requeuing logic](./smartrequeue.md). + - This field has no effect unless `WithSmartRequeue` has been called on the status updater builder. + - If `ReconcileError` is not nil, the value has no effect and the smart requeue error logic is used instead. + - Valid values are: + - `Backoff` to requeue the object with an increasing backoff + - `Reset` to requeue the object, but reset the backoff interval to its minimum + - `NoRequeue` to not requeue the object diff --git a/pkg/controller/status_updater.go b/pkg/controller/status_updater.go index e6d609a..58704b7 100644 --- a/pkg/controller/status_updater.go +++ b/pkg/controller/status_updater.go @@ -138,9 +138,8 @@ const ( // If the 'Result' field in the ReconcileResult has a non-zero RequeueAfter value set, that one is used if it is earlier than the one from smart requeue or if "NoRequeue" has been specified. // This function only has an effect if the Object in the ReconcileResult is not nil, the smart requeue store is not nil, and the action is one of the known values. // Also, if a reconciliation error occurred, the requeue interval will be reset, but no requeueAfter duration will be set, because controller-runtime will take care of requeuing the object anyway. -func (b *StatusUpdaterBuilder[Obj]) WithSmartRequeue(store *smartrequeue.Store, action SmartRequeueAction) *StatusUpdaterBuilder[Obj] { +func (b *StatusUpdaterBuilder[Obj]) WithSmartRequeue(store *smartrequeue.Store) *StatusUpdaterBuilder[Obj] { b.internal.smartRequeueStore = store - b.internal.smartRequeueAction = action return b } @@ -183,7 +182,6 @@ type statusUpdater[Obj client.Object] struct { eventRecorder record.EventRecorder eventVerbosity conditions.EventVerbosity smartRequeueStore *smartrequeue.Store - smartRequeueAction SmartRequeueAction } func newStatusUpdater[Obj client.Object]() *statusUpdater[Obj] { @@ -297,7 +295,7 @@ func (s *statusUpdater[Obj]) UpdateStatus(ctx context.Context, c client.Client, if rr.ReconcileError != nil { srRes, _ = s.smartRequeueStore.For(rr.Object).Error(rr.ReconcileError) } else { - switch s.smartRequeueAction { + switch rr.SmartRequeue { case SR_BACKOFF: srRes, _ = s.smartRequeueStore.For(rr.Object).Backoff() case SR_RESET: @@ -424,6 +422,9 @@ type ReconcileResult[Obj client.Object] struct { // ConditionsToRemove is an optional slice of condition types for which the corresponding conditions should be removed from the status. // This is useful if you want to remove conditions that are no longer relevant. ConditionsToRemove []string + // SmartRequeue determines if/when the object should be requeued. + // Has no effect unless WithSmartRequeue() has been called on the status updater. + SmartRequeue SmartRequeueAction } // GenerateCreateConditionFunc returns a function that can be used to add a condition to the given ReconcileResult. diff --git a/pkg/controller/status_updater_test.go b/pkg/controller/status_updater_test.go index b3e113a..8c22c8b 100644 --- a/pkg/controller/status_updater_test.go +++ b/pkg/controller/status_updater_test.go @@ -196,13 +196,14 @@ var _ = Describe("Status Updater", func() { obj := &CustomObject{} Expect(env.Client().Get(env.Ctx, controller.ObjectKey("status", "default"), obj)).To(Succeed()) rr := controller.ReconcileResult[*CustomObject]{ - Object: obj, - Conditions: dummyConditions(), + Object: obj, + Conditions: dummyConditions(), + SmartRequeue: controller.SR_RESET, } store := smartrequeue.NewStore(1*time.Second, 10*time.Second, 2.0) su := preconfiguredStatusUpdaterBuilder().WithPhaseUpdateFunc(func(obj *CustomObject, rr controller.ReconcileResult[*CustomObject]) (string, error) { return PhaseSucceeded, nil - }).WithSmartRequeue(store, controller.SR_RESET).Build() + }).WithSmartRequeue(store).Build() res, err := su.UpdateStatus(env.Ctx, env.Client(), rr) Expect(err).ToNot(HaveOccurred()) Expect(res.RequeueAfter).To(Equal(1 * time.Second)) @@ -218,11 +219,12 @@ var _ = Describe("Status Updater", func() { Result: ctrl.Result{ RequeueAfter: 30 * time.Second, }, + SmartRequeue: controller.SR_RESET, } store := smartrequeue.NewStore(1*time.Second, 10*time.Second, 2.0) su := preconfiguredStatusUpdaterBuilder().WithPhaseUpdateFunc(func(obj *CustomObject, rr controller.ReconcileResult[*CustomObject]) (string, error) { return PhaseSucceeded, nil - }).WithSmartRequeue(store, controller.SR_RESET).Build() + }).WithSmartRequeue(store).Build() res, err := su.UpdateStatus(env.Ctx, env.Client(), rr) Expect(err).ToNot(HaveOccurred()) Expect(res.RequeueAfter).To(Equal(1 * time.Second)) @@ -231,7 +233,7 @@ var _ = Describe("Status Updater", func() { store = smartrequeue.NewStore(1*time.Minute, 10*time.Minute, 2.0) su = preconfiguredStatusUpdaterBuilder().WithPhaseUpdateFunc(func(obj *CustomObject, rr controller.ReconcileResult[*CustomObject]) (string, error) { return PhaseSucceeded, nil - }).WithSmartRequeue(store, controller.SR_RESET).Build() + }).WithSmartRequeue(store).Build() res, err = su.UpdateStatus(env.Ctx, env.Client(), rr) Expect(err).ToNot(HaveOccurred()) Expect(res.RequeueAfter).To(Equal(30 * time.Second))