@@ -19,14 +19,18 @@ package cache
19
19
import (
20
20
"context"
21
21
"fmt"
22
+ "reflect"
22
23
"time"
23
24
24
25
"k8s.io/apimachinery/pkg/api/meta"
26
+ "k8s.io/apimachinery/pkg/fields"
27
+ "k8s.io/apimachinery/pkg/labels"
25
28
"k8s.io/apimachinery/pkg/runtime"
26
29
"k8s.io/apimachinery/pkg/runtime/schema"
27
30
"k8s.io/client-go/kubernetes/scheme"
28
31
"k8s.io/client-go/rest"
29
32
toolscache "k8s.io/client-go/tools/cache"
33
+
30
34
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
31
35
"sigs.k8s.io/controller-runtime/pkg/client"
32
36
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
@@ -150,49 +154,244 @@ func New(config *rest.Config, opts Options) (Cache, error) {
150
154
if err != nil {
151
155
return nil , err
152
156
}
153
- selectorsByGVK , err := convertToSelectorsByGVK (opts .SelectorsByObject , opts .DefaultSelector , opts .Scheme )
157
+ selectorsByGVK , err := convertToByGVK (opts .SelectorsByObject , opts .DefaultSelector , opts .Scheme )
154
158
if err != nil {
155
159
return nil , err
156
160
}
157
161
disableDeepCopyByGVK , err := convertToDisableDeepCopyByGVK (opts .UnsafeDisableDeepCopyByObject , opts .Scheme )
158
162
if err != nil {
159
163
return nil , err
160
164
}
161
- transformByGVK , err := convertToTransformByKindAndGVK (opts .TransformByObject , opts .DefaultTransform , opts .Scheme )
165
+ transformByGVK , err := convertToByGVK (opts .TransformByObject , opts .DefaultTransform , opts .Scheme )
162
166
if err != nil {
163
167
return nil , err
164
168
}
169
+ transformByObj := internal .TransformFuncByObjectFromMap (transformByGVK )
170
+
171
+ internalSelectorsByGVK := internal.SelectorsByGVK {}
172
+ for gvk , selector := range selectorsByGVK {
173
+ internalSelectorsByGVK [gvk ] = internal .Selector (selector )
174
+ }
165
175
166
- im := internal .NewInformersMap (config , opts .Scheme , opts .Mapper , * opts .Resync , opts .Namespace , selectorsByGVK , disableDeepCopyByGVK , transformByGVK )
176
+ im := internal .NewInformersMap (config , opts .Scheme , opts .Mapper , * opts .Resync , opts .Namespace , internalSelectorsByGVK , disableDeepCopyByGVK , transformByObj )
167
177
return & informerCache {InformersMap : im }, nil
168
178
}
169
179
170
- // BuilderWithOptions returns a Cache constructor that will build the a cache
180
+ // BuilderWithOptions returns a Cache constructor that will build a cache
171
181
// honoring the options argument, this is useful to specify options like
172
182
// SelectorsByObject
173
183
// WARNING: if SelectorsByObject is specified. filtered out resources are not
174
184
// returned.
175
185
// WARNING: if UnsafeDisableDeepCopy is enabled, you must DeepCopy any object
176
186
// returned from cache get/list before mutating it.
177
187
func BuilderWithOptions (options Options ) NewCacheFunc {
178
- return func (config * rest.Config , opts Options ) (Cache , error ) {
179
- if options .Scheme == nil {
180
- options .Scheme = opts .Scheme
188
+ return func (config * rest.Config , inherited Options ) (Cache , error ) {
189
+ var err error
190
+ inherited , err = defaultOpts (config , inherited )
191
+ if err != nil {
192
+ return nil , err
193
+ }
194
+ options , err = defaultOpts (config , options )
195
+ if err != nil {
196
+ return nil , err
197
+ }
198
+ combined , err := options .inheritFrom (inherited )
199
+ if err != nil {
200
+ return nil , err
201
+ }
202
+ return New (config , * combined )
203
+ }
204
+ }
205
+
206
+ func (options Options ) inheritFrom (inherited Options ) (* Options , error ) {
207
+ var (
208
+ combined Options
209
+ err error
210
+ )
211
+ combined .Scheme = combineScheme (inherited .Scheme , options .Scheme )
212
+ combined .Mapper = selectMapper (inherited .Mapper , options .Mapper )
213
+ combined .Resync = selectResync (inherited .Resync , options .Resync )
214
+ combined .Namespace = selectNamespace (inherited .Namespace , options .Namespace )
215
+ combined .SelectorsByObject , combined .DefaultSelector , err = combineSelectors (inherited , options , combined .Scheme )
216
+ if err != nil {
217
+ return nil , fmt .Errorf ("combine selectors: %v" , err )
218
+ }
219
+ combined .UnsafeDisableDeepCopyByObject , err = combineUnsafeDeepCopy (inherited , options , combined .Scheme )
220
+ if err != nil {
221
+ return nil , fmt .Errorf ("combine unsafe disable deep copy by object: %v" , err )
222
+ }
223
+
224
+ combined .TransformByObject , combined .DefaultTransform , err = combineTransforms (inherited , options , combined .Scheme )
225
+ if err != nil {
226
+ return nil , fmt .Errorf ("combine transforms: %v" , err )
227
+ }
228
+
229
+ return & combined , nil
230
+ }
231
+
232
+ func combineScheme (schemes ... * runtime.Scheme ) * runtime.Scheme {
233
+ var out * runtime.Scheme
234
+ for _ , sch := range schemes {
235
+ if sch == nil {
236
+ continue
237
+ }
238
+ for gvk , t := range sch .AllKnownTypes () {
239
+ if out == nil {
240
+ out = runtime .NewScheme ()
241
+ }
242
+ out .AddKnownTypeWithName (gvk , reflect .New (t ).Interface ().(runtime.Object ))
243
+ }
244
+ }
245
+ return out
246
+ }
247
+
248
+ func selectMapper (def , override meta.RESTMapper ) meta.RESTMapper {
249
+ if override != nil {
250
+ return override
251
+ }
252
+ return def
253
+ }
254
+
255
+ func selectResync (def , override * time.Duration ) * time.Duration {
256
+ if override != nil {
257
+ return override
258
+ }
259
+ return def
260
+ }
261
+
262
+ func selectNamespace (def , override string ) string {
263
+ if override != "" {
264
+ return override
265
+ }
266
+ return def
267
+ }
268
+
269
+ func combineSelectors (inherited , options Options , scheme * runtime.Scheme ) (SelectorsByObject , ObjectSelector , error ) {
270
+ // Selectors are combined via logical AND.
271
+ // - Combined label selector is a union of the selectors requirements from both sets of options.
272
+ // - Combined field selector uses fields.AndSelectors with the combined list of non-nil field selectors
273
+ // defined in both sets of options.
274
+ //
275
+ // There is a bunch of complexity here because we need to convert to SelectorsByGVK
276
+ // to be able to match keys between options and inherited and then convert back to SelectorsByObject
277
+ optionsSelectorsByGVK , err := convertToByGVK (options .SelectorsByObject , options .DefaultSelector , options .Scheme )
278
+ if err != nil {
279
+ return nil , ObjectSelector {}, err
280
+ }
281
+ inheritedSelectorsByGVK , err := convertToByGVK (inherited .SelectorsByObject , inherited .DefaultSelector , inherited .Scheme )
282
+ if err != nil {
283
+ return nil , ObjectSelector {}, err
284
+ }
285
+
286
+ for gvk , selector := range inheritedSelectorsByGVK {
287
+ optionsSelectorsByGVK [gvk ] = combineSelector (optionsSelectorsByGVK [gvk ], selector )
288
+ }
289
+ return convertToByObject (optionsSelectorsByGVK , scheme )
290
+ }
291
+
292
+ func combineSelector (selectors ... ObjectSelector ) ObjectSelector {
293
+ ls := make ([]labels.Selector , 0 , len (selectors ))
294
+ fs := make ([]fields.Selector , 0 , len (selectors ))
295
+ for _ , s := range selectors {
296
+ ls = append (ls , s .Label )
297
+ fs = append (fs , s .Field )
298
+ }
299
+ return ObjectSelector {
300
+ Label : combineLabelSelectors (ls ... ),
301
+ Field : combineFieldSelectors (fs ... ),
302
+ }
303
+ }
304
+
305
+ func combineLabelSelectors (ls ... labels.Selector ) labels.Selector {
306
+ var combined labels.Selector
307
+ for _ , l := range ls {
308
+ if l == nil {
309
+ continue
181
310
}
182
- if options . Mapper == nil {
183
- options . Mapper = opts . Mapper
311
+ if combined == nil {
312
+ combined = labels . NewSelector ()
184
313
}
185
- if options .Resync == nil {
186
- options .Resync = opts .Resync
314
+ reqs , _ := l .Requirements ()
315
+ combined = combined .Add (reqs ... )
316
+ }
317
+ return combined
318
+ }
319
+
320
+ func combineFieldSelectors (fs ... fields.Selector ) fields.Selector {
321
+ nonNil := fs [:0 ]
322
+ for _ , f := range fs {
323
+ if f == nil {
324
+ continue
187
325
}
188
- if options .Namespace == "" {
189
- options .Namespace = opts .Namespace
326
+ nonNil = append (nonNil , f )
327
+ }
328
+ if len (nonNil ) == 0 {
329
+ return nil
330
+ }
331
+ if len (nonNil ) == 1 {
332
+ return nonNil [0 ]
333
+ }
334
+ return fields .AndSelectors (nonNil ... )
335
+ }
336
+
337
+ func combineUnsafeDeepCopy (inherited , options Options , scheme * runtime.Scheme ) (DisableDeepCopyByObject , error ) {
338
+ // UnsafeDisableDeepCopyByObject is combined via precedence. Only if a value for a particular GVK is unset
339
+ // in options will a value from inherited be used.
340
+ optionsDisableDeepCopyByGVK , err := convertToDisableDeepCopyByGVK (options .UnsafeDisableDeepCopyByObject , options .Scheme )
341
+ if err != nil {
342
+ return nil , err
343
+ }
344
+ inheritedDisableDeepCopyByGVK , err := convertToDisableDeepCopyByGVK (inherited .UnsafeDisableDeepCopyByObject , inherited .Scheme )
345
+ if err != nil {
346
+ return nil , err
347
+ }
348
+
349
+ for gvk , inheritedDeepCopy := range inheritedDisableDeepCopyByGVK {
350
+ if _ , ok := optionsDisableDeepCopyByGVK [gvk ]; ! ok {
351
+ if optionsDisableDeepCopyByGVK == nil {
352
+ optionsDisableDeepCopyByGVK = map [schema.GroupVersionKind ]bool {}
353
+ }
354
+ optionsDisableDeepCopyByGVK [gvk ] = inheritedDeepCopy
190
355
}
191
- if opts .Resync == nil {
192
- opts .Resync = options .Resync
356
+ }
357
+ return convertToDisableDeepCopyByObject (optionsDisableDeepCopyByGVK , scheme )
358
+ }
359
+
360
+ func combineTransforms (inherited , options Options , scheme * runtime.Scheme ) (TransformByObject , toolscache.TransformFunc , error ) {
361
+ // Transform functions are combined via chaining. If both inherited and options define a transform
362
+ // function, the transform function from inherited will be called first, and the transform function from
363
+ // options will be called second.
364
+ optionsTransformByGVK , err := convertToByGVK (options .TransformByObject , options .DefaultTransform , options .Scheme )
365
+ if err != nil {
366
+ return nil , nil , err
367
+ }
368
+ inheritedTransformByGVK , err := convertToByGVK (inherited .TransformByObject , inherited .DefaultTransform , inherited .Scheme )
369
+ if err != nil {
370
+ return nil , nil , err
371
+ }
372
+
373
+ for gvk , inheritedTransform := range inheritedTransformByGVK {
374
+ if optionsTransformByGVK == nil {
375
+ optionsTransformByGVK = map [schema.GroupVersionKind ]toolscache.TransformFunc {}
193
376
}
377
+ optionsTransformByGVK [gvk ] = combineTransform (inheritedTransform , optionsTransformByGVK [gvk ])
378
+ }
379
+ return convertToByObject (optionsTransformByGVK , scheme )
380
+ }
194
381
195
- return New (config , options )
382
+ func combineTransform (inherited , current toolscache.TransformFunc ) toolscache.TransformFunc {
383
+ if inherited == nil {
384
+ return current
385
+ }
386
+ if current == nil {
387
+ return inherited
388
+ }
389
+ return func (in interface {}) (interface {}, error ) {
390
+ mid , err := inherited (in )
391
+ if err != nil {
392
+ return nil , err
393
+ }
394
+ return current (mid )
196
395
}
197
396
}
198
397
@@ -219,17 +418,40 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
219
418
return opts , nil
220
419
}
221
420
222
- func convertToSelectorsByGVK ( selectorsByObject SelectorsByObject , defaultSelector ObjectSelector , scheme * runtime.Scheme ) (internal. SelectorsByGVK , error ) {
223
- selectorsByGVK := internal. SelectorsByGVK {}
224
- for object , selector := range selectorsByObject {
421
+ func convertToByGVK [ T any ]( byObject map [client. Object ] T , def T , scheme * runtime.Scheme ) (map [schema. GroupVersionKind ] T , error ) {
422
+ byGVK := map [schema. GroupVersionKind ] T {}
423
+ for object , value := range byObject {
225
424
gvk , err := apiutil .GVKForObject (object , scheme )
226
425
if err != nil {
227
426
return nil , err
228
427
}
229
- selectorsByGVK [gvk ] = internal .Selector (selector )
428
+ byGVK [gvk ] = value
429
+ }
430
+ byGVK [schema.GroupVersionKind {}] = def
431
+ return byGVK , nil
432
+ }
433
+
434
+ func convertToByObject [T any ](byGVK map [schema.GroupVersionKind ]T , scheme * runtime.Scheme ) (map [client.Object ]T , T , error ) {
435
+ var byObject map [client.Object ]T
436
+ def := byGVK [schema.GroupVersionKind {}]
437
+ for gvk , value := range byGVK {
438
+ if gvk == (schema.GroupVersionKind {}) {
439
+ continue
440
+ }
441
+ obj , err := scheme .New (gvk )
442
+ if err != nil {
443
+ return nil , def , err
444
+ }
445
+ cObj , ok := obj .(client.Object )
446
+ if ! ok {
447
+ return nil , def , fmt .Errorf ("object %T for GVK %q does not implement client.Object" , obj , gvk )
448
+ }
449
+ if byObject == nil {
450
+ byObject = map [client.Object ]T {}
451
+ }
452
+ byObject [cObj ] = value
230
453
}
231
- selectorsByGVK [schema.GroupVersionKind {}] = internal .Selector (defaultSelector )
232
- return selectorsByGVK , nil
454
+ return byObject , def , nil
233
455
}
234
456
235
457
// DisableDeepCopyByObject associate a client.Object's GVK to disable DeepCopy during get or list from cache.
@@ -259,17 +481,30 @@ func convertToDisableDeepCopyByGVK(disableDeepCopyByObject DisableDeepCopyByObje
259
481
return disableDeepCopyByGVK , nil
260
482
}
261
483
262
- // TransformByObject associate a client.Object's GVK to a transformer function
263
- // to be applied when storing the object into the cache.
264
- type TransformByObject map [client.Object ]toolscache.TransformFunc
265
-
266
- func convertToTransformByKindAndGVK (t TransformByObject , defaultTransform toolscache.TransformFunc , scheme * runtime.Scheme ) (internal.TransformFuncByObject , error ) {
267
- result := internal .NewTransformFuncByObject ()
268
- for obj , transformation := range t {
269
- if err := result .Set (obj , scheme , transformation ); err != nil {
484
+ func convertToDisableDeepCopyByObject (byGVK internal.DisableDeepCopyByGVK , scheme * runtime.Scheme ) (DisableDeepCopyByObject , error ) {
485
+ var byObject DisableDeepCopyByObject
486
+ for gvk , value := range byGVK {
487
+ if byObject == nil {
488
+ byObject = DisableDeepCopyByObject {}
489
+ }
490
+ if gvk == (schema.GroupVersionKind {}) {
491
+ byObject [ObjectAll {}] = value
492
+ continue
493
+ }
494
+ obj , err := scheme .New (gvk )
495
+ if err != nil {
270
496
return nil , err
271
497
}
498
+ cObj , ok := obj .(client.Object )
499
+ if ! ok {
500
+ return nil , fmt .Errorf ("object %T for GVK %q does not implement client.Object" , obj , gvk )
501
+ }
502
+
503
+ byObject [cObj ] = value
272
504
}
273
- result .SetDefault (defaultTransform )
274
- return result , nil
505
+ return byObject , nil
275
506
}
507
+
508
+ // TransformByObject associate a client.Object's GVK to a transformer function
509
+ // to be applied when storing the object into the cache.
510
+ type TransformByObject map [client.Object ]toolscache.TransformFunc
0 commit comments