1
16
17 package internal_test
18
19 import (
20 "context"
21
22 . "github.com/onsi/ginkgo/v2"
23 . "github.com/onsi/gomega"
24 "k8s.io/client-go/tools/cache"
25 "k8s.io/client-go/util/workqueue"
26 "sigs.k8s.io/controller-runtime/pkg/client"
27 "sigs.k8s.io/controller-runtime/pkg/event"
28 "sigs.k8s.io/controller-runtime/pkg/handler"
29 internal "sigs.k8s.io/controller-runtime/pkg/internal/source"
30
31 corev1 "k8s.io/api/core/v1"
32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/runtime/schema"
35 "sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
36 "sigs.k8s.io/controller-runtime/pkg/predicate"
37 )
38
39 var _ = Describe("Internal", func() {
40 var ctx = context.Background()
41 var instance *internal.EventHandler[client.Object]
42 var funcs, setfuncs *handler.Funcs
43 var set bool
44 BeforeEach(func() {
45 funcs = &handler.Funcs{
46 CreateFunc: func(context.Context, event.CreateEvent, workqueue.RateLimitingInterface) {
47 defer GinkgoRecover()
48 Fail("Did not expect CreateEvent to be called.")
49 },
50 DeleteFunc: func(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface) {
51 defer GinkgoRecover()
52 Fail("Did not expect DeleteEvent to be called.")
53 },
54 UpdateFunc: func(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface) {
55 defer GinkgoRecover()
56 Fail("Did not expect UpdateEvent to be called.")
57 },
58 GenericFunc: func(context.Context, event.GenericEvent, workqueue.RateLimitingInterface) {
59 defer GinkgoRecover()
60 Fail("Did not expect GenericEvent to be called.")
61 },
62 }
63
64 setfuncs = &handler.Funcs{
65 CreateFunc: func(context.Context, event.CreateEvent, workqueue.RateLimitingInterface) {
66 set = true
67 },
68 DeleteFunc: func(context.Context, event.DeleteEvent, workqueue.RateLimitingInterface) {
69 set = true
70 },
71 UpdateFunc: func(context.Context, event.UpdateEvent, workqueue.RateLimitingInterface) {
72 set = true
73 },
74 GenericFunc: func(context.Context, event.GenericEvent, workqueue.RateLimitingInterface) {
75 set = true
76 },
77 }
78 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, funcs, nil)
79 })
80
81 Describe("EventHandler", func() {
82 var pod, newPod *corev1.Pod
83
84 BeforeEach(func() {
85 pod = &corev1.Pod{
86 Spec: corev1.PodSpec{
87 Containers: []corev1.Container{{Name: "test", Image: "test"}},
88 },
89 }
90 newPod = pod.DeepCopy()
91 newPod.Labels = map[string]string{"foo": "bar"}
92 })
93
94 It("should create a CreateEvent", func() {
95 funcs.CreateFunc = func(ctx context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
96 defer GinkgoRecover()
97 Expect(evt.Object).To(Equal(pod))
98 }
99 instance.OnAdd(pod)
100 })
101
102 It("should used Predicates to filter CreateEvents", func() {
103 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
104 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
105 })
106 set = false
107 instance.OnAdd(pod)
108 Expect(set).To(BeFalse())
109
110 set = false
111 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
112 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
113 })
114 instance.OnAdd(pod)
115 Expect(set).To(BeTrue())
116
117 set = false
118 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
119 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
120 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
121 })
122 instance.OnAdd(pod)
123 Expect(set).To(BeFalse())
124
125 set = false
126 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
127 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return false }},
128 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
129 })
130 instance.OnAdd(pod)
131 Expect(set).To(BeFalse())
132
133 set = false
134 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
135 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
136 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
137 })
138 instance.OnAdd(pod)
139 Expect(set).To(BeTrue())
140 })
141
142 It("should not call Create EventHandler if the object is not a runtime.Object", func() {
143 instance.OnAdd(&metav1.ObjectMeta{})
144 })
145
146 It("should not call Create EventHandler if the object does not have metadata", func() {
147 instance.OnAdd(FooRuntimeObject{})
148 })
149
150 It("should create an UpdateEvent", func() {
151 funcs.UpdateFunc = func(ctx context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
152 defer GinkgoRecover()
153 Expect(evt.ObjectOld).To(Equal(pod))
154 Expect(evt.ObjectNew).To(Equal(newPod))
155 }
156 instance.OnUpdate(pod, newPod)
157 })
158
159 It("should used Predicates to filter UpdateEvents", func() {
160 set = false
161 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
162 predicate.Funcs{UpdateFunc: func(updateEvent event.UpdateEvent) bool { return false }},
163 })
164 instance.OnUpdate(pod, newPod)
165 Expect(set).To(BeFalse())
166
167 set = false
168 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
169 predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return true }},
170 })
171 instance.OnUpdate(pod, newPod)
172 Expect(set).To(BeTrue())
173
174 set = false
175 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
176 predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return true }},
177 predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return false }},
178 })
179 instance.OnUpdate(pod, newPod)
180 Expect(set).To(BeFalse())
181
182 set = false
183 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
184 predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return false }},
185 predicate.Funcs{UpdateFunc: func(event.UpdateEvent) bool { return true }},
186 })
187 instance.OnUpdate(pod, newPod)
188 Expect(set).To(BeFalse())
189
190 set = false
191 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
192 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
193 predicate.Funcs{CreateFunc: func(event.CreateEvent) bool { return true }},
194 })
195 instance.OnUpdate(pod, newPod)
196 Expect(set).To(BeTrue())
197 })
198
199 It("should not call Update EventHandler if the object is not a runtime.Object", func() {
200 instance.OnUpdate(&metav1.ObjectMeta{}, &corev1.Pod{})
201 instance.OnUpdate(&corev1.Pod{}, &metav1.ObjectMeta{})
202 })
203
204 It("should not call Update EventHandler if the object does not have metadata", func() {
205 instance.OnUpdate(FooRuntimeObject{}, &corev1.Pod{})
206 instance.OnUpdate(&corev1.Pod{}, FooRuntimeObject{})
207 })
208
209 It("should create a DeleteEvent", func() {
210 funcs.DeleteFunc = func(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
211 defer GinkgoRecover()
212 Expect(evt.Object).To(Equal(pod))
213 }
214 instance.OnDelete(pod)
215 })
216
217 It("should used Predicates to filter DeleteEvents", func() {
218 set = false
219 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
220 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return false }},
221 })
222 instance.OnDelete(pod)
223 Expect(set).To(BeFalse())
224
225 set = false
226 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
227 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
228 })
229 instance.OnDelete(pod)
230 Expect(set).To(BeTrue())
231
232 set = false
233 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
234 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
235 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return false }},
236 })
237 instance.OnDelete(pod)
238 Expect(set).To(BeFalse())
239
240 set = false
241 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
242 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return false }},
243 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
244 })
245 instance.OnDelete(pod)
246 Expect(set).To(BeFalse())
247
248 set = false
249 instance = internal.NewEventHandler(ctx, &controllertest.Queue{}, setfuncs, []predicate.Predicate{
250 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
251 predicate.Funcs{DeleteFunc: func(event.DeleteEvent) bool { return true }},
252 })
253 instance.OnDelete(pod)
254 Expect(set).To(BeTrue())
255 })
256
257 It("should not call Delete EventHandler if the object is not a runtime.Object", func() {
258 instance.OnDelete(&metav1.ObjectMeta{})
259 })
260
261 It("should not call Delete EventHandler if the object does not have metadata", func() {
262 instance.OnDelete(FooRuntimeObject{})
263 })
264
265 It("should create a DeleteEvent from a tombstone", func() {
266
267 tombstone := cache.DeletedFinalStateUnknown{
268 Obj: pod,
269 }
270 funcs.DeleteFunc = func(ctx context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
271 defer GinkgoRecover()
272 Expect(evt.Object).To(Equal(pod))
273 Expect(evt.DeleteStateUnknown).Should(BeTrue())
274 }
275
276 instance.OnDelete(tombstone)
277 })
278
279 It("should ignore tombstone objects without meta", func() {
280 tombstone := cache.DeletedFinalStateUnknown{Obj: Foo{}}
281 instance.OnDelete(tombstone)
282 })
283 It("should ignore objects without meta", func() {
284 instance.OnAdd(Foo{})
285 instance.OnUpdate(Foo{}, Foo{})
286 instance.OnDelete(Foo{})
287 })
288 })
289
290 Describe("Kind", func() {
291 It("should return kind source type", func() {
292 kind := internal.Kind[*corev1.Pod]{
293 Type: &corev1.Pod{},
294 }
295 Expect(kind.String()).Should(Equal("kind source: *v1.Pod"))
296 })
297 })
298 })
299
300 type Foo struct{}
301
302 var _ runtime.Object = FooRuntimeObject{}
303
304 type FooRuntimeObject struct{}
305
306 func (FooRuntimeObject) GetObjectKind() schema.ObjectKind { return nil }
307 func (FooRuntimeObject) DeepCopyObject() runtime.Object { return nil }
308
View as plain text