sigs.k8s.io/controller-runtime@v0.18.2/pkg/internal/source/event_handler.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 18 19 import ( 20 "context" 21 "fmt" 22 23 "k8s.io/client-go/tools/cache" 24 "k8s.io/client-go/util/workqueue" 25 "sigs.k8s.io/controller-runtime/pkg/client" 26 "sigs.k8s.io/controller-runtime/pkg/event" 27 "sigs.k8s.io/controller-runtime/pkg/handler" 28 logf "sigs.k8s.io/controller-runtime/pkg/internal/log" 29 30 "sigs.k8s.io/controller-runtime/pkg/predicate" 31 ) 32 33 var log = logf.RuntimeLog.WithName("source").WithName("EventHandler") 34 35 // NewEventHandler creates a new EventHandler. 36 func NewEventHandler[T client.Object](ctx context.Context, queue workqueue.RateLimitingInterface, handler handler.TypedEventHandler[T], predicates []predicate.TypedPredicate[T]) *EventHandler[T] { 37 return &EventHandler[T]{ 38 ctx: ctx, 39 handler: handler, 40 queue: queue, 41 predicates: predicates, 42 } 43 } 44 45 // EventHandler adapts a handler.EventHandler interface to a cache.ResourceEventHandler interface. 46 type EventHandler[T client.Object] struct { 47 // ctx stores the context that created the event handler 48 // that is used to propagate cancellation signals to each handler function. 49 ctx context.Context 50 51 handler handler.TypedEventHandler[T] 52 queue workqueue.RateLimitingInterface 53 predicates []predicate.TypedPredicate[T] 54 } 55 56 // HandlerFuncs converts EventHandler to a ResourceEventHandlerFuncs 57 // TODO: switch to ResourceEventHandlerDetailedFuncs with client-go 1.27 58 func (e *EventHandler[T]) HandlerFuncs() cache.ResourceEventHandlerFuncs { 59 return cache.ResourceEventHandlerFuncs{ 60 AddFunc: e.OnAdd, 61 UpdateFunc: e.OnUpdate, 62 DeleteFunc: e.OnDelete, 63 } 64 } 65 66 // OnAdd creates CreateEvent and calls Create on EventHandler. 67 func (e *EventHandler[T]) OnAdd(obj interface{}) { 68 c := event.TypedCreateEvent[T]{} 69 70 // Pull Object out of the object 71 if o, ok := obj.(T); ok { 72 c.Object = o 73 } else { 74 log.Error(nil, "OnAdd missing Object", 75 "object", obj, "type", fmt.Sprintf("%T", obj)) 76 return 77 } 78 79 for _, p := range e.predicates { 80 if !p.Create(c) { 81 return 82 } 83 } 84 85 // Invoke create handler 86 ctx, cancel := context.WithCancel(e.ctx) 87 defer cancel() 88 e.handler.Create(ctx, c, e.queue) 89 } 90 91 // OnUpdate creates UpdateEvent and calls Update on EventHandler. 92 func (e *EventHandler[T]) OnUpdate(oldObj, newObj interface{}) { 93 u := event.TypedUpdateEvent[T]{} 94 95 if o, ok := oldObj.(T); ok { 96 u.ObjectOld = o 97 } else { 98 log.Error(nil, "OnUpdate missing ObjectOld", 99 "object", oldObj, "type", fmt.Sprintf("%T", oldObj)) 100 return 101 } 102 103 // Pull Object out of the object 104 if o, ok := newObj.(T); ok { 105 u.ObjectNew = o 106 } else { 107 log.Error(nil, "OnUpdate missing ObjectNew", 108 "object", newObj, "type", fmt.Sprintf("%T", newObj)) 109 return 110 } 111 112 for _, p := range e.predicates { 113 if !p.Update(u) { 114 return 115 } 116 } 117 118 // Invoke update handler 119 ctx, cancel := context.WithCancel(e.ctx) 120 defer cancel() 121 e.handler.Update(ctx, u, e.queue) 122 } 123 124 // OnDelete creates DeleteEvent and calls Delete on EventHandler. 125 func (e *EventHandler[T]) OnDelete(obj interface{}) { 126 d := event.TypedDeleteEvent[T]{} 127 128 // Deal with tombstone events by pulling the object out. Tombstone events wrap the object in a 129 // DeleteFinalStateUnknown struct, so the object needs to be pulled out. 130 // Copied from sample-controller 131 // This should never happen if we aren't missing events, which we have concluded that we are not 132 // and made decisions off of this belief. Maybe this shouldn't be here? 133 var ok bool 134 if _, ok = obj.(client.Object); !ok { 135 // If the object doesn't have Metadata, assume it is a tombstone object of type DeletedFinalStateUnknown 136 tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 137 if !ok { 138 log.Error(nil, "Error decoding objects. Expected cache.DeletedFinalStateUnknown", 139 "type", fmt.Sprintf("%T", obj), 140 "object", obj) 141 return 142 } 143 144 // Set DeleteStateUnknown to true 145 d.DeleteStateUnknown = true 146 147 // Set obj to the tombstone obj 148 obj = tombstone.Obj 149 } 150 151 // Pull Object out of the object 152 if o, ok := obj.(T); ok { 153 d.Object = o 154 } else { 155 log.Error(nil, "OnDelete missing Object", 156 "object", obj, "type", fmt.Sprintf("%T", obj)) 157 return 158 } 159 160 for _, p := range e.predicates { 161 if !p.Delete(d) { 162 return 163 } 164 } 165 166 // Invoke delete handler 167 ctx, cancel := context.WithCancel(e.ctx) 168 defer cancel() 169 e.handler.Delete(ctx, d, e.queue) 170 }