Skip to content

Commit f4ad346

Browse files
committed
Leverage Informer OnAdd IsInInitialList
Signed-off-by: Stefan Büringer buringerst@vmware.com
1 parent d3b0880 commit f4ad346

File tree

10 files changed

+38
-102
lines changed

10 files changed

+38
-102
lines changed

pkg/cache/multi_namespace_cache.go

+3-10
Original file line numberDiff line numberDiff line change
@@ -337,18 +337,11 @@ type handlerRegistration struct {
337337
handles map[string]toolscache.ResourceEventHandlerRegistration
338338
}
339339

340-
type syncer interface {
341-
HasSynced() bool
342-
}
343-
344340
// HasSynced asserts that the handler has been called for the full initial state of the informer.
345-
// This uses syncer to be compatible between client-go 1.27+ and older versions when the interface changed.
346341
func (h handlerRegistration) HasSynced() bool {
347-
for _, reg := range h.handles {
348-
if s, ok := reg.(syncer); ok {
349-
if !s.HasSynced() {
350-
return false
351-
}
342+
for _, h := range h.handles {
343+
if !h.HasSynced() {
344+
return false
352345
}
353346
}
354347
return true

pkg/controller/controllertest/util.go

+5-47
Original file line numberDiff line numberDiff line change
@@ -34,49 +34,7 @@ type FakeInformer struct {
3434
// RunCount is incremented each time RunInformersAndControllers is called
3535
RunCount int
3636

37-
handlers []eventHandlerWrapper
38-
}
39-
40-
type modernResourceEventHandler interface {
41-
OnAdd(obj interface{}, isInInitialList bool)
42-
OnUpdate(oldObj, newObj interface{})
43-
OnDelete(obj interface{})
44-
}
45-
46-
type legacyResourceEventHandler interface {
47-
OnAdd(obj interface{})
48-
OnUpdate(oldObj, newObj interface{})
49-
OnDelete(obj interface{})
50-
}
51-
52-
// eventHandlerWrapper wraps a ResourceEventHandler in a manner that is compatible with client-go 1.27+ and older.
53-
// The interface was changed in these versions.
54-
type eventHandlerWrapper struct {
55-
handler any
56-
}
57-
58-
func (e eventHandlerWrapper) OnAdd(obj interface{}) {
59-
if m, ok := e.handler.(modernResourceEventHandler); ok {
60-
m.OnAdd(obj, false)
61-
return
62-
}
63-
e.handler.(legacyResourceEventHandler).OnAdd(obj)
64-
}
65-
66-
func (e eventHandlerWrapper) OnUpdate(oldObj, newObj interface{}) {
67-
if m, ok := e.handler.(modernResourceEventHandler); ok {
68-
m.OnUpdate(oldObj, newObj)
69-
return
70-
}
71-
e.handler.(legacyResourceEventHandler).OnUpdate(oldObj, newObj)
72-
}
73-
74-
func (e eventHandlerWrapper) OnDelete(obj interface{}) {
75-
if m, ok := e.handler.(modernResourceEventHandler); ok {
76-
m.OnDelete(obj)
77-
return
78-
}
79-
e.handler.(legacyResourceEventHandler).OnDelete(obj)
37+
handlers []cache.ResourceEventHandler
8038
}
8139

8240
// AddIndexers does nothing. TODO(community): Implement this.
@@ -101,19 +59,19 @@ func (f *FakeInformer) HasSynced() bool {
10159

10260
// AddEventHandler implements the Informer interface. Adds an EventHandler to the fake Informers. TODO(community): Implement Registration.
10361
func (f *FakeInformer) AddEventHandler(handler cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error) {
104-
f.handlers = append(f.handlers, eventHandlerWrapper{handler})
62+
f.handlers = append(f.handlers, handler)
10563
return nil, nil
10664
}
10765

10866
// AddEventHandlerWithResyncPeriod implements the Informer interface. Adds an EventHandler to the fake Informers (ignores resyncPeriod). TODO(community): Implement Registration.
10967
func (f *FakeInformer) AddEventHandlerWithResyncPeriod(handler cache.ResourceEventHandler, _ time.Duration) (cache.ResourceEventHandlerRegistration, error) {
110-
f.handlers = append(f.handlers, eventHandlerWrapper{handler})
68+
f.handlers = append(f.handlers, handler)
11169
return nil, nil
11270
}
11371

11472
// AddEventHandlerWithOptions implements the Informer interface. Adds an EventHandler to the fake Informers (ignores options). TODO(community): Implement Registration.
11573
func (f *FakeInformer) AddEventHandlerWithOptions(handler cache.ResourceEventHandler, _ cache.HandlerOptions) (cache.ResourceEventHandlerRegistration, error) {
116-
f.handlers = append(f.handlers, eventHandlerWrapper{handler})
74+
f.handlers = append(f.handlers, handler)
11775
return nil, nil
11876
}
11977

@@ -129,7 +87,7 @@ func (f *FakeInformer) RunWithContext(_ context.Context) {
12987
// Add fakes an Add event for obj.
13088
func (f *FakeInformer) Add(obj metav1.Object) {
13189
for _, h := range f.handlers {
132-
h.OnAdd(obj)
90+
h.OnAdd(obj, false)
13391
}
13492
}
13593

pkg/event/event.go

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ type GenericEvent = TypedGenericEvent[client.Object]
4040
type TypedCreateEvent[object any] struct {
4141
// Object is the object from the event
4242
Object object
43+
44+
// IsInInitialList is true if the Create event was triggered by the initial list.
45+
IsInInitialList bool
4346
}
4447

4548
// TypedUpdateEvent is an event where a Kubernetes object was updated. TypedUpdateEvent should be generated

pkg/handler/enqueue_mapped.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,8 @@ func (e *enqueueRequestsFromMapFunc[object, request]) Create(
8686
reqs := map[request]empty{}
8787

8888
var lowPriority bool
89-
if e.objectImplementsClientObject && isPriorityQueue(q) && !isNil(evt.Object) {
90-
clientObjectEvent := event.CreateEvent{Object: any(evt.Object).(client.Object)}
91-
if isObjectUnchanged(clientObjectEvent) {
89+
if isPriorityQueue(q) && !isNil(evt.Object) {
90+
if evt.IsInInitialList {
9291
lowPriority = true
9392
}
9493
}

pkg/handler/eventhandler.go

+2-16
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package handler
1919
import (
2020
"context"
2121
"reflect"
22-
"time"
2322

2423
"k8s.io/client-go/util/workqueue"
2524
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -132,14 +131,8 @@ func (h TypedFuncs[object, request]) Create(ctx context.Context, e event.TypedCr
132131
// We already know that we have a priority queue, that event.Object implements
133132
// client.Object and that its not nil
134133
addFunc: func(item request, q workqueue.TypedRateLimitingInterface[request]) {
135-
// We construct a new event typed to client.Object because isObjectUnchanged
136-
// is a generic and hence has to know at compile time the type of the event
137-
// it gets. We only figure that out at runtime though, but we know for sure
138-
// that it implements client.Object at this point so we can hardcode the event
139-
// type to that.
140-
evt := event.CreateEvent{Object: any(e.Object).(client.Object)}
141134
var priority int
142-
if isObjectUnchanged(evt) {
135+
if e.IsInInitialList {
143136
priority = LowPriority
144137
}
145138
q.(priorityqueue.PriorityQueue[request]).AddWithOpts(
@@ -217,13 +210,6 @@ func (w workqueueWithCustomAddFunc[request]) Add(item request) {
217210
w.addFunc(item, w.TypedRateLimitingInterface)
218211
}
219212

220-
// isObjectUnchanged checks if the object in a create event is unchanged, for example because
221-
// we got it in our initial listwatch. The heuristic it uses is to check if the object is older
222-
// than one minute.
223-
func isObjectUnchanged[object client.Object](e event.TypedCreateEvent[object]) bool {
224-
return e.Object.GetCreationTimestamp().Time.Before(time.Now().Add(-time.Minute))
225-
}
226-
227213
// addToQueueCreate adds the reconcile.Request to the priorityqueue in the handler
228214
// for Create requests if and only if the workqueue being used is of type priorityqueue.PriorityQueue[reconcile.Request]
229215
func addToQueueCreate[T client.Object, request comparable](q workqueue.TypedRateLimitingInterface[request], evt event.TypedCreateEvent[T], item request) {
@@ -234,7 +220,7 @@ func addToQueueCreate[T client.Object, request comparable](q workqueue.TypedRate
234220
}
235221

236222
var priority int
237-
if isObjectUnchanged(evt) {
223+
if evt.IsInInitialList {
238224
priority = LowPriority
239225
}
240226
priorityQueue.AddWithOpts(priorityqueue.AddOpts{Priority: priority}, item)

pkg/handler/eventhandler_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ var _ = Describe("Eventhandler", func() {
831831
}
832832
for _, test := range handlerPriorityTests {
833833
When("handler is "+test.name, func() {
834-
It("should lower the priority of a create request for an object that was created more than one minute in the past", func() {
834+
It("should lower the priority of a create request for an object that was part of the initial list", func() {
835835
actualOpts := priorityqueue.AddOpts{}
836836
var actualRequests []reconcile.Request
837837
wq := &fakePriorityQueue{
@@ -843,19 +843,21 @@ var _ = Describe("Eventhandler", func() {
843843

844844
test.handler().Create(ctx, event.CreateEvent{
845845
Object: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{
846-
Name: "my-pod",
846+
Name: "my-pod",
847+
CreationTimestamp: metav1.Now(),
847848
OwnerReferences: []metav1.OwnerReference{{
848849
Kind: "Pod",
849850
Name: "my-pod",
850851
}},
851852
}},
853+
IsInInitialList: true,
852854
}, wq)
853855

854856
Expect(actualOpts).To(Equal(priorityqueue.AddOpts{Priority: handler.LowPriority}))
855857
Expect(actualRequests).To(Equal([]reconcile.Request{{NamespacedName: types.NamespacedName{Name: "my-pod"}}}))
856858
})
857859

858-
It("should not lower the priority of a create request for an object that was created less than one minute in the past", func() {
860+
It("should not lower the priority of a create request for an object that was not part of the initial list", func() {
859861
actualOpts := priorityqueue.AddOpts{}
860862
var actualRequests []reconcile.Request
861863
wq := &fakePriorityQueue{
@@ -874,6 +876,7 @@ var _ = Describe("Eventhandler", func() {
874876
Name: "my-pod",
875877
}},
876878
}},
879+
IsInInitialList: false,
877880
}, wq)
878881

879882
Expect(actualOpts).To(Equal(priorityqueue.AddOpts{}))

pkg/internal/source/event_handler.go

+6-12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232

3333
var log = logf.RuntimeLog.WithName("source").WithName("EventHandler")
3434

35+
var _ cache.ResourceEventHandler = &EventHandler[client.Object, any]{}
36+
3537
// NewEventHandler creates a new EventHandler.
3638
func NewEventHandler[object client.Object, request comparable](
3739
ctx context.Context,
@@ -57,19 +59,11 @@ type EventHandler[object client.Object, request comparable] struct {
5759
predicates []predicate.TypedPredicate[object]
5860
}
5961

60-
// HandlerFuncs converts EventHandler to a ResourceEventHandlerFuncs
61-
// TODO: switch to ResourceEventHandlerDetailedFuncs with client-go 1.27
62-
func (e *EventHandler[object, request]) HandlerFuncs() cache.ResourceEventHandlerFuncs {
63-
return cache.ResourceEventHandlerFuncs{
64-
AddFunc: e.OnAdd,
65-
UpdateFunc: e.OnUpdate,
66-
DeleteFunc: e.OnDelete,
67-
}
68-
}
69-
7062
// OnAdd creates CreateEvent and calls Create on EventHandler.
71-
func (e *EventHandler[object, request]) OnAdd(obj interface{}) {
72-
c := event.TypedCreateEvent[object]{}
63+
func (e *EventHandler[object, request]) OnAdd(obj interface{}, isInInitialList bool) {
64+
c := event.TypedCreateEvent[object]{
65+
IsInInitialList: isInInitialList,
66+
}
7367

7468
// Pull Object out of the object
7569
if o, ok := obj.(object); ok {

pkg/internal/source/internal_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -97,55 +97,55 @@ var _ = Describe("Internal", func() {
9797
defer GinkgoRecover()
9898
Expect(evt.Object).To(Equal(pod))
9999
}
100-
instance.OnAdd(pod)
100+
instance.OnAdd(pod, false)
101101
})
102102

103103
It("should used Predicates to filter CreateEvents", func() {
104104
instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
105105
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
106106
})
107107
set = false
108-
instance.OnAdd(pod)
108+
instance.OnAdd(pod, false)
109109
Expect(set).To(BeFalse())
110110

111111
set = false
112112
instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
113113
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
114114
})
115-
instance.OnAdd(pod)
115+
instance.OnAdd(pod, false)
116116
Expect(set).To(BeTrue())
117117

118118
set = false
119119
instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
120120
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
121121
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
122122
})
123-
instance.OnAdd(pod)
123+
instance.OnAdd(pod, false)
124124
Expect(set).To(BeFalse())
125125

126126
set = false
127127
instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
128128
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
129129
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
130130
})
131-
instance.OnAdd(pod)
131+
instance.OnAdd(pod, false)
132132
Expect(set).To(BeFalse())
133133

134134
set = false
135135
instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
136136
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
137137
predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
138138
})
139-
instance.OnAdd(pod)
139+
instance.OnAdd(pod, false)
140140
Expect(set).To(BeTrue())
141141
})
142142

143143
It("should not call Create EventHandler if the object is not a runtime.Object", func() {
144-
instance.OnAdd(&metav1.ObjectMeta{})
144+
instance.OnAdd(&metav1.ObjectMeta{}, false)
145145
})
146146

147147
It("should not call Create EventHandler if the object does not have metadata", func() {
148-
instance.OnAdd(FooRuntimeObject{})
148+
instance.OnAdd(FooRuntimeObject{}, false)
149149
})
150150

151151
It("should create an UpdateEvent", func() {
@@ -281,7 +281,7 @@ var _ = Describe("Internal", func() {
281281
instance.OnDelete(tombstone)
282282
})
283283
It("should ignore objects without meta", func() {
284-
instance.OnAdd(Foo{})
284+
instance.OnAdd(Foo{}, false)
285285
instance.OnUpdate(Foo{}, Foo{})
286286
instance.OnDelete(Foo{})
287287
})

pkg/internal/source/kind.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (ks *Kind[object, request]) Start(ctx context.Context, queue workqueue.Type
9191
return
9292
}
9393

94-
_, err := i.AddEventHandlerWithOptions(NewEventHandler(ctx, queue, ks.Handler, ks.Predicates).HandlerFuncs(), toolscache.HandlerOptions{
94+
_, err := i.AddEventHandlerWithOptions(NewEventHandler(ctx, queue, ks.Handler, ks.Predicates), toolscache.HandlerOptions{
9595
Logger: &logKind,
9696
})
9797
if err != nil {

pkg/source/source.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func (is *Informer) Start(ctx context.Context, queue workqueue.TypedRateLimiting
286286
return errors.New("must specify Informer.Handler")
287287
}
288288

289-
_, err := is.Informer.AddEventHandlerWithOptions(internal.NewEventHandler(ctx, queue, is.Handler, is.Predicates).HandlerFuncs(), toolscache.HandlerOptions{
289+
_, err := is.Informer.AddEventHandlerWithOptions(internal.NewEventHandler(ctx, queue, is.Handler, is.Predicates), toolscache.HandlerOptions{
290290
Logger: &logInformer,
291291
})
292292
if err != nil {

0 commit comments

Comments
 (0)