Skip to content

Commit 1c75cb0

Browse files
authored
Merge pull request #3333 from sbueringer/pr-result-priority
✨ Add optional Priority field to reconcile.Result
2 parents bcb29e7 + c71b8c8 commit 1c75cb0

File tree

3 files changed

+99
-4
lines changed

3 files changed

+99
-4
lines changed

pkg/internal/controller/controller.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ func (c *Controller[request]) reconcileHandler(ctx context.Context, req request,
459459
// resource to be synced.
460460
log.V(5).Info("Reconciling")
461461
result, err := c.Reconcile(ctx, req)
462+
if result.Priority != nil {
463+
priority = *result.Priority
464+
}
462465
switch {
463466
case err != nil:
464467
if errors.Is(err, reconcile.TerminalError(nil)) {
@@ -468,8 +471,8 @@ func (c *Controller[request]) reconcileHandler(ctx context.Context, req request,
468471
}
469472
ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Inc()
470473
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelError).Inc()
471-
if !result.IsZero() {
472-
log.Info("Warning: Reconciler returned both a non-zero result and a non-nil error. The result will always be ignored if the error is non-nil and the non-nil error causes requeuing with exponential backoff. For more details, see: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler")
474+
if result.RequeueAfter > 0 || result.Requeue { //nolint: staticcheck // We have to handle Requeue until it is removed
475+
log.Info("Warning: Reconciler returned both a result with either RequeueAfter or Requeue set and a non-nil error. RequeueAfter and Requeue will always be ignored if the error is non-nil. For more details, see: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler")
473476
}
474477
log.Error(err, "Reconciler error")
475478
case result.RequeueAfter > 0:

pkg/internal/controller/controller_test.go

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,36 @@ var _ = Describe("controller", func() {
745745
}}))
746746
})
747747

748-
It("should requeue a Request after a duration (but not rate-limitted) if the Result sets RequeueAfter (regardless of Requeue)", func(ctx SpecContext) {
748+
It("should use the priority from Result when the reconciler requests a requeue", func(ctx SpecContext) {
749+
q := &fakePriorityQueue{PriorityQueue: priorityqueue.New[reconcile.Request]("controller1")}
750+
ctrl.NewQueue = func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
751+
return q
752+
}
753+
754+
go func() {
755+
defer GinkgoRecover()
756+
Expect(ctrl.Start(ctx)).NotTo(HaveOccurred())
757+
}()
758+
759+
q.PriorityQueue.AddWithOpts(priorityqueue.AddOpts{Priority: ptr.To(10)}, request)
760+
761+
By("Invoking Reconciler which will request a requeue")
762+
fakeReconcile.AddResult(reconcile.Result{Requeue: true, Priority: ptr.To(99)}, nil)
763+
Expect(<-reconciled).To(Equal(request))
764+
Eventually(func() []priorityQueueAddition {
765+
q.lock.Lock()
766+
defer q.lock.Unlock()
767+
return q.added
768+
}).Should(Equal([]priorityQueueAddition{{
769+
AddOpts: priorityqueue.AddOpts{
770+
RateLimited: true,
771+
Priority: ptr.To(99),
772+
},
773+
items: []reconcile.Request{request},
774+
}}))
775+
})
776+
777+
It("should requeue a Request after a duration (but not rate-limited) if the Result sets RequeueAfter (regardless of Requeue)", func(ctx SpecContext) {
749778
dq := &DelegatingQueue{TypedRateLimitingInterface: ctrl.NewQueue("controller1", nil)}
750779
ctrl.NewQueue = func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
751780
return dq
@@ -775,7 +804,7 @@ var _ = Describe("controller", func() {
775804
Eventually(func() int { return dq.NumRequeues(request) }).Should(Equal(0))
776805
})
777806

778-
It("should retain the priority with RequeAfter", func(ctx SpecContext) {
807+
It("should retain the priority with RequeueAfter", func(ctx SpecContext) {
779808
q := &fakePriorityQueue{PriorityQueue: priorityqueue.New[reconcile.Request]("controller1")}
780809
ctrl.NewQueue = func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
781810
return q
@@ -804,6 +833,35 @@ var _ = Describe("controller", func() {
804833
}}))
805834
})
806835

836+
It("should use the priority from Result with RequeueAfter", func(ctx SpecContext) {
837+
q := &fakePriorityQueue{PriorityQueue: priorityqueue.New[reconcile.Request]("controller1")}
838+
ctrl.NewQueue = func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
839+
return q
840+
}
841+
842+
go func() {
843+
defer GinkgoRecover()
844+
Expect(ctrl.Start(ctx)).NotTo(HaveOccurred())
845+
}()
846+
847+
q.PriorityQueue.AddWithOpts(priorityqueue.AddOpts{Priority: ptr.To(10)}, request)
848+
849+
By("Invoking Reconciler which will ask for RequeueAfter")
850+
fakeReconcile.AddResult(reconcile.Result{RequeueAfter: time.Millisecond * 100, Priority: ptr.To(99)}, nil)
851+
Expect(<-reconciled).To(Equal(request))
852+
Eventually(func() []priorityQueueAddition {
853+
q.lock.Lock()
854+
defer q.lock.Unlock()
855+
return q.added
856+
}).Should(Equal([]priorityQueueAddition{{
857+
AddOpts: priorityqueue.AddOpts{
858+
After: time.Millisecond * 100,
859+
Priority: ptr.To(99),
860+
},
861+
items: []reconcile.Request{request},
862+
}}))
863+
})
864+
807865
It("should perform error behavior if error is not nil, regardless of RequeueAfter", func(ctx SpecContext) {
808866
dq := &DelegatingQueue{TypedRateLimitingInterface: ctrl.NewQueue("controller1", nil)}
809867
ctrl.NewQueue = func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
@@ -862,6 +920,35 @@ var _ = Describe("controller", func() {
862920
}}))
863921
})
864922

923+
It("should use the priority from Result when there was an error", func(ctx SpecContext) {
924+
q := &fakePriorityQueue{PriorityQueue: priorityqueue.New[reconcile.Request]("controller1")}
925+
ctrl.NewQueue = func(string, workqueue.TypedRateLimiter[reconcile.Request]) workqueue.TypedRateLimitingInterface[reconcile.Request] {
926+
return q
927+
}
928+
929+
go func() {
930+
defer GinkgoRecover()
931+
Expect(ctrl.Start(ctx)).NotTo(HaveOccurred())
932+
}()
933+
934+
q.PriorityQueue.AddWithOpts(priorityqueue.AddOpts{Priority: ptr.To(10)}, request)
935+
936+
By("Invoking Reconciler which will return an error")
937+
fakeReconcile.AddResult(reconcile.Result{Priority: ptr.To(99)}, errors.New("oups, I did it again"))
938+
Expect(<-reconciled).To(Equal(request))
939+
Eventually(func() []priorityQueueAddition {
940+
q.lock.Lock()
941+
defer q.lock.Unlock()
942+
return q.added
943+
}).Should(Equal([]priorityQueueAddition{{
944+
AddOpts: priorityqueue.AddOpts{
945+
RateLimited: true,
946+
Priority: ptr.To(99),
947+
},
948+
items: []reconcile.Request{request},
949+
}}))
950+
})
951+
865952
PIt("should return if the queue is shutdown", func() {
866953
// TODO(community): write this test
867954
})

pkg/reconcile/reconcile.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ type Result struct {
4444
// RequeueAfter if greater than 0, tells the Controller to requeue the reconcile key after the Duration.
4545
// Implies that Requeue is true, there is no need to set Requeue to true at the same time as RequeueAfter.
4646
RequeueAfter time.Duration
47+
48+
// Priority is the priority that will be used if the item gets re-enqueued (also if an error is returned).
49+
// If Priority is not set the original Priority of the request is preserved.
50+
// Note: Priority is only respected if the controller is using a priorityqueue.PriorityQueue.
51+
Priority *int
4752
}
4853

4954
// IsZero returns true if this result is empty.

0 commit comments

Comments
 (0)