sigs.k8s.io/controller-runtime@v0.18.2/pkg/handler/enqueue_owner.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 handler 18 19 import ( 20 "context" 21 "fmt" 22 23 "k8s.io/apimachinery/pkg/api/meta" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 "k8s.io/apimachinery/pkg/types" 28 "k8s.io/client-go/util/workqueue" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 "sigs.k8s.io/controller-runtime/pkg/event" 31 logf "sigs.k8s.io/controller-runtime/pkg/internal/log" 32 "sigs.k8s.io/controller-runtime/pkg/reconcile" 33 ) 34 35 var _ EventHandler = &enqueueRequestForOwner[client.Object]{} 36 37 var log = logf.RuntimeLog.WithName("eventhandler").WithName("enqueueRequestForOwner") 38 39 // OwnerOption modifies an EnqueueRequestForOwner EventHandler. 40 type OwnerOption func(e enqueueRequestForOwnerInterface) 41 42 // EnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created 43 // the object that was the source of the Event. 44 // 45 // If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using: 46 // 47 // - a source.Kind Source with Type of Pod. 48 // 49 // - a handler.enqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true. 50 func EnqueueRequestForOwner(scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) EventHandler { 51 return TypedEnqueueRequestForOwner[client.Object](scheme, mapper, ownerType, opts...) 52 } 53 54 // TypedEnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created 55 // the object that was the source of the Event. 56 // 57 // If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using: 58 // 59 // - a source.Kind Source with Type of Pod. 60 // 61 // - a handler.typedEnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true. 62 // 63 // TypedEnqueueRequestForOwner is experimental and subject to future change. 64 func TypedEnqueueRequestForOwner[T client.Object](scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) TypedEventHandler[T] { 65 e := &enqueueRequestForOwner[T]{ 66 ownerType: ownerType, 67 mapper: mapper, 68 } 69 if err := e.parseOwnerTypeGroupKind(scheme); err != nil { 70 panic(err) 71 } 72 for _, opt := range opts { 73 opt(e) 74 } 75 return e 76 } 77 78 // OnlyControllerOwner if provided will only look at the first OwnerReference with Controller: true. 79 func OnlyControllerOwner() OwnerOption { 80 return func(e enqueueRequestForOwnerInterface) { 81 e.setIsController(true) 82 } 83 } 84 85 type enqueueRequestForOwnerInterface interface { 86 setIsController(bool) 87 } 88 89 type enqueueRequestForOwner[T client.Object] struct { 90 // ownerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared. 91 ownerType runtime.Object 92 93 // isController if set will only look at the first OwnerReference with Controller: true. 94 isController bool 95 96 // groupKind is the cached Group and Kind from OwnerType 97 groupKind schema.GroupKind 98 99 // mapper maps GroupVersionKinds to Resources 100 mapper meta.RESTMapper 101 } 102 103 func (e *enqueueRequestForOwner[T]) setIsController(isController bool) { 104 e.isController = isController 105 } 106 107 // Create implements EventHandler. 108 func (e *enqueueRequestForOwner[T]) Create(ctx context.Context, evt event.TypedCreateEvent[T], q workqueue.RateLimitingInterface) { 109 reqs := map[reconcile.Request]empty{} 110 e.getOwnerReconcileRequest(evt.Object, reqs) 111 for req := range reqs { 112 q.Add(req) 113 } 114 } 115 116 // Update implements EventHandler. 117 func (e *enqueueRequestForOwner[T]) Update(ctx context.Context, evt event.TypedUpdateEvent[T], q workqueue.RateLimitingInterface) { 118 reqs := map[reconcile.Request]empty{} 119 e.getOwnerReconcileRequest(evt.ObjectOld, reqs) 120 e.getOwnerReconcileRequest(evt.ObjectNew, reqs) 121 for req := range reqs { 122 q.Add(req) 123 } 124 } 125 126 // Delete implements EventHandler. 127 func (e *enqueueRequestForOwner[T]) Delete(ctx context.Context, evt event.TypedDeleteEvent[T], q workqueue.RateLimitingInterface) { 128 reqs := map[reconcile.Request]empty{} 129 e.getOwnerReconcileRequest(evt.Object, reqs) 130 for req := range reqs { 131 q.Add(req) 132 } 133 } 134 135 // Generic implements EventHandler. 136 func (e *enqueueRequestForOwner[T]) Generic(ctx context.Context, evt event.TypedGenericEvent[T], q workqueue.RateLimitingInterface) { 137 reqs := map[reconcile.Request]empty{} 138 e.getOwnerReconcileRequest(evt.Object, reqs) 139 for req := range reqs { 140 q.Add(req) 141 } 142 } 143 144 // parseOwnerTypeGroupKind parses the OwnerType into a Group and Kind and caches the result. Returns false 145 // if the OwnerType could not be parsed using the scheme. 146 func (e *enqueueRequestForOwner[T]) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error { 147 // Get the kinds of the type 148 kinds, _, err := scheme.ObjectKinds(e.ownerType) 149 if err != nil { 150 log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType)) 151 return err 152 } 153 // Expect only 1 kind. If there is more than one kind this is probably an edge case such as ListOptions. 154 if len(kinds) != 1 { 155 err := fmt.Errorf("expected exactly 1 kind for OwnerType %T, but found %s kinds", e.ownerType, kinds) 156 log.Error(nil, "expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType), "kinds", kinds) 157 return err 158 } 159 // Cache the Group and Kind for the OwnerType 160 e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind} 161 return nil 162 } 163 164 // getOwnerReconcileRequest looks at object and builds a map of reconcile.Request to reconcile 165 // owners of object that match e.OwnerType. 166 func (e *enqueueRequestForOwner[T]) getOwnerReconcileRequest(object metav1.Object, result map[reconcile.Request]empty) { 167 // Iterate through the OwnerReferences looking for a match on Group and Kind against what was requested 168 // by the user 169 for _, ref := range e.getOwnersReferences(object) { 170 // Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType 171 refGV, err := schema.ParseGroupVersion(ref.APIVersion) 172 if err != nil { 173 log.Error(err, "Could not parse OwnerReference APIVersion", 174 "api version", ref.APIVersion) 175 return 176 } 177 178 // Compare the OwnerReference Group and Kind against the OwnerType Group and Kind specified by the user. 179 // If the two match, create a Request for the objected referred to by 180 // the OwnerReference. Use the Name from the OwnerReference and the Namespace from the 181 // object in the event. 182 if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group { 183 // Match found - add a Request for the object referred to in the OwnerReference 184 request := reconcile.Request{NamespacedName: types.NamespacedName{ 185 Name: ref.Name, 186 }} 187 188 // if owner is not namespaced then we should not set the namespace 189 mapping, err := e.mapper.RESTMapping(e.groupKind, refGV.Version) 190 if err != nil { 191 log.Error(err, "Could not retrieve rest mapping", "kind", e.groupKind) 192 return 193 } 194 if mapping.Scope.Name() != meta.RESTScopeNameRoot { 195 request.Namespace = object.GetNamespace() 196 } 197 198 result[request] = empty{} 199 } 200 } 201 } 202 203 // getOwnersReferences returns the OwnerReferences for an object as specified by the enqueueRequestForOwner 204 // - if IsController is true: only take the Controller OwnerReference (if found) 205 // - if IsController is false: take all OwnerReferences. 206 func (e *enqueueRequestForOwner[T]) getOwnersReferences(object metav1.Object) []metav1.OwnerReference { 207 if object == nil { 208 return nil 209 } 210 211 // If not filtered as Controller only, then use all the OwnerReferences 212 if !e.isController { 213 return object.GetOwnerReferences() 214 } 215 // If filtered to a Controller, only take the Controller OwnerReference 216 if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { 217 return []metav1.OwnerReference{*ownerRef} 218 } 219 // No Controller OwnerReference found 220 return nil 221 }