Skip to content

Commit e130760

Browse files
committed
enable conditional smart requeuing
1 parent 40cc54a commit e130760

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

docs/libs/status.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ You can then `Build()` the status updater and run `UpdateStatus()` to do the act
164164
- By using `WithSmartRequeue`, the [smart requeuing logic](./smartrequeue.md) can be used.
165165
- A `smartrequeue.Store` is required to be configured outside of the status updater, because it has to be persisted across multiple reconciliations.
166166
- It is also possible to use the smart requeue logic explicitly and modify the `ReconcileResult`'s `Result` field with the returned value, but the integration should be easier to use, since both, the smart requeue logic as well as the status updater, return a `reconcile.Result` and an `error`, which are intended to be directly used as return values for the `Reconcile` method.
167+
- The `WithSmartRequeue` function takes `SmartRequeueConditional`s as optional arguments, which are basically functions that take the `ReconcileResult` and return a smart requeue value (see below). This is especially useful to set the requeue depending on the object's new conditions, which would otherwise be difficult, because the conditions have not yet been updated before `UpdateStatus` is called and the requeue time has already been determined when `UpdateStatus` returns.
167168

168169
### The ReconcileResult
169170

pkg/controller/status_updater.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func (b *StatusUpdaterBuilder[Obj]) WithCustomUpdateFunc(f func(obj Obj, rr Reco
122122
}
123123

124124
type SmartRequeueAction string
125+
type SmartRequeueConditional[Obj client.Object] func(rr ReconcileResult[Obj]) SmartRequeueAction
125126

126127
const (
127128
SR_BACKOFF SmartRequeueAction = "Backoff"
@@ -135,11 +136,17 @@ const (
135136
// - "Backoff": the object is requeued with an increasing backoff, as specified in the store.
136137
// - "Reset": the object is requeued, but the backoff is reset to its minimal value, as specified in the store.
137138
// - "NoRequeue": the object is not requeued.
139+
//
140+
// If any SmartRequeueConditionals are passed in, they will be evaluated in order and can override the action set in the ReconcileResult.
141+
// As determining the requeue time is the last thing that happens in the status updater, these functions can be used react to the final status of the reconciled object, which might not be known earlier in the reconciliation
142+
// (e.g. because the conditions were only updated in the status updater).
143+
//
138144
// 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.
139145
// 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.
140146
// 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.
141-
func (b *StatusUpdaterBuilder[Obj]) WithSmartRequeue(store *smartrequeue.Store) *StatusUpdaterBuilder[Obj] {
147+
func (b *StatusUpdaterBuilder[Obj]) WithSmartRequeue(store *smartrequeue.Store, smartRequeueConditionals ...SmartRequeueConditional[Obj]) *StatusUpdaterBuilder[Obj] {
142148
b.internal.smartRequeueStore = store
149+
b.internal.smartRequeueConditionals = smartRequeueConditionals
143150
return b
144151
}
145152

@@ -182,6 +189,7 @@ type statusUpdater[Obj client.Object] struct {
182189
eventRecorder record.EventRecorder
183190
eventVerbosity conditions.EventVerbosity
184191
smartRequeueStore *smartrequeue.Store
192+
smartRequeueConditionals []SmartRequeueConditional[Obj]
185193
}
186194

187195
func newStatusUpdater[Obj client.Object]() *statusUpdater[Obj] {
@@ -295,6 +303,11 @@ func (s *statusUpdater[Obj]) UpdateStatus(ctx context.Context, c client.Client,
295303
if rr.ReconcileError != nil {
296304
srRes, _ = s.smartRequeueStore.For(rr.Object).Error(rr.ReconcileError)
297305
} else {
306+
for _, srcFunc := range s.smartRequeueConditionals {
307+
if srcFunc != nil {
308+
rr.SmartRequeue = srcFunc(rr)
309+
}
310+
}
298311
switch rr.SmartRequeue {
299312
case SR_BACKOFF:
300313
srRes, _ = s.smartRequeueStore.For(rr.Object).Backoff()

pkg/controller/status_updater_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,28 @@ var _ = Describe("Status Updater", func() {
239239
Expect(res.RequeueAfter).To(Equal(30 * time.Second))
240240
})
241241

242+
It("should use the SmartRequeueConditional functions if specified", func() {
243+
env := testutils.NewEnvironmentBuilder().WithFakeClient(coScheme).WithInitObjectPath("testdata", "test-02").WithDynamicObjectsWithStatus(&CustomObject{}).Build()
244+
obj := &CustomObject{}
245+
Expect(env.Client().Get(env.Ctx, controller.ObjectKey("status", "default"), obj)).To(Succeed())
246+
rr := controller.ReconcileResult[*CustomObject]{
247+
Object: obj,
248+
Conditions: dummyConditions(),
249+
SmartRequeue: controller.SR_NO_REQUEUE,
250+
}
251+
store := smartrequeue.NewStore(1*time.Second, 10*time.Second, 2.0)
252+
su := preconfiguredStatusUpdaterBuilder().WithPhaseUpdateFunc(func(obj *CustomObject, rr controller.ReconcileResult[*CustomObject]) (string, error) {
253+
return PhaseSucceeded, nil
254+
}).WithSmartRequeue(store, func(rr controller.ReconcileResult[*CustomObject]) controller.SmartRequeueAction {
255+
return controller.SR_NO_REQUEUE
256+
}, func(rr controller.ReconcileResult[*CustomObject]) controller.SmartRequeueAction {
257+
return controller.SR_RESET
258+
}).Build()
259+
res, err := su.UpdateStatus(env.Ctx, env.Client(), rr)
260+
Expect(err).ToNot(HaveOccurred())
261+
Expect(res.RequeueAfter).To(Equal(1 * time.Second))
262+
})
263+
242264
})
243265

244266
Context("GenerateCreateConditionFunc", func() {

0 commit comments

Comments
 (0)