sigs.k8s.io/controller-runtime@v0.18.2/pkg/internal/source/internal_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 }