1
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
40 type OwnerOption func(e enqueueRequestForOwnerInterface)
41
42
43
44
45
46
47
48
49
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
55
56
57
58
59
60
61
62
63
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
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
91 ownerType runtime.Object
92
93
94 isController bool
95
96
97 groupKind schema.GroupKind
98
99
100 mapper meta.RESTMapper
101 }
102
103 func (e *enqueueRequestForOwner[T]) setIsController(isController bool) {
104 e.isController = isController
105 }
106
107
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
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
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
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
145
146 func (e *enqueueRequestForOwner[T]) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
147
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
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
160 e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
161 return nil
162 }
163
164
165
166 func (e *enqueueRequestForOwner[T]) getOwnerReconcileRequest(object metav1.Object, result map[reconcile.Request]empty) {
167
168
169 for _, ref := range e.getOwnersReferences(object) {
170
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
179
180
181
182 if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group {
183
184 request := reconcile.Request{NamespacedName: types.NamespacedName{
185 Name: ref.Name,
186 }}
187
188
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
204
205
206 func (e *enqueueRequestForOwner[T]) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
207 if object == nil {
208 return nil
209 }
210
211
212 if !e.isController {
213 return object.GetOwnerReferences()
214 }
215
216 if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
217 return []metav1.OwnerReference{*ownerRef}
218 }
219
220 return nil
221 }
222
View as plain text