@@ -19,14 +19,18 @@ package cache
1919import (
2020 "context"
2121 "fmt"
22+ "reflect"
2223 "time"
2324
2425 "k8s.io/apimachinery/pkg/api/meta"
26+ "k8s.io/apimachinery/pkg/fields"
27+ "k8s.io/apimachinery/pkg/labels"
2528 "k8s.io/apimachinery/pkg/runtime"
2629 "k8s.io/apimachinery/pkg/runtime/schema"
2730 "k8s.io/client-go/kubernetes/scheme"
2831 "k8s.io/client-go/rest"
2932 toolscache "k8s.io/client-go/tools/cache"
33+
3034 "sigs.k8s.io/controller-runtime/pkg/cache/internal"
3135 "sigs.k8s.io/controller-runtime/pkg/client"
3236 "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
@@ -150,49 +154,242 @@ func New(config *rest.Config, opts Options) (Cache, error) {
150154 if err != nil {
151155 return nil , err
152156 }
153- selectorsByGVK , err := convertToSelectorsByGVK (opts .SelectorsByObject , opts .DefaultSelector , opts .Scheme )
157+ selectorsByGVK , err := convertToByGVK (opts .SelectorsByObject , opts .DefaultSelector , opts .Scheme )
154158 if err != nil {
155159 return nil , err
156160 }
157161 disableDeepCopyByGVK , err := convertToDisableDeepCopyByGVK (opts .UnsafeDisableDeepCopyByObject , opts .Scheme )
158162 if err != nil {
159163 return nil , err
160164 }
161- transformByGVK , err := convertToTransformByKindAndGVK (opts .TransformByObject , opts .DefaultTransform , opts .Scheme )
165+ transformByGVK , err := convertToByGVK (opts .TransformByObject , opts .DefaultTransform , opts .Scheme )
162166 if err != nil {
163167 return nil , err
164168 }
169+ transformByObj := internal .TransformFuncByObjectFromMap (transformByGVK )
170+
171+ internalSelectorsByGVK := internal.SelectorsByGVK {}
172+ for gvk , selector := range selectorsByGVK {
173+ internalSelectorsByGVK [gvk ] = internal .Selector (selector )
174+ }
165175
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 )
167177 return & informerCache {InformersMap : im }, nil
168178}
169179
170- // BuilderWithOptions returns a Cache constructor that will build the a cache
180+ // BuilderWithOptions returns a Cache constructor that will build a cache
171181// honoring the options argument, this is useful to specify options like
172182// SelectorsByObject
173183// WARNING: If SelectorsByObject is specified, filtered out resources are not
174184// returned.
175185// WARNING: If UnsafeDisableDeepCopy is enabled, you must DeepCopy any object
176186// returned from cache get/list before mutating it.
177187func 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
181193 }
182- if options .Mapper == nil {
183- options .Mapper = opts .Mapper
194+ options , err = defaultOpts (config , options )
195+ if err != nil {
196+ return nil , err
184197 }
185- if options .Resync == nil {
186- options .Resync = opts .Resync
198+ combined , err := options .inheritFrom (inherited )
199+ if err != nil {
200+ return nil , err
187201 }
188- if options .Namespace == "" {
189- options .Namespace = opts .Namespace
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 , err
218+ }
219+ combined .UnsafeDisableDeepCopyByObject , err = combineUnsafeDeepCopy (inherited , options , combined .Scheme )
220+ if err != nil {
221+ return nil , err
222+ }
223+ combined .TransformByObject , combined .DefaultTransform , err = combineTransforms (inherited , options , combined .Scheme )
224+ if err != nil {
225+ return nil , err
226+ }
227+ return & combined , nil
228+ }
229+
230+ func combineScheme (schemes ... * runtime.Scheme ) * runtime.Scheme {
231+ var out * runtime.Scheme
232+ for _ , sch := range schemes {
233+ if sch == nil {
234+ continue
190235 }
191- if opts .Resync == nil {
192- opts .Resync = options .Resync
236+ for gvk , t := range sch .AllKnownTypes () {
237+ if out == nil {
238+ out = runtime .NewScheme ()
239+ }
240+ out .AddKnownTypeWithName (gvk , reflect .New (t ).Interface ().(runtime.Object ))
193241 }
242+ }
243+ return out
244+ }
194245
195- return New (config , options )
246+ func selectMapper (def , override meta.RESTMapper ) meta.RESTMapper {
247+ if override != nil {
248+ return override
249+ }
250+ return def
251+ }
252+
253+ func selectResync (def , override * time.Duration ) * time.Duration {
254+ if override != nil {
255+ return override
256+ }
257+ return def
258+ }
259+
260+ func selectNamespace (def , override string ) string {
261+ if override != "" {
262+ return override
263+ }
264+ return def
265+ }
266+
267+ func combineSelectors (inherited , options Options , scheme * runtime.Scheme ) (SelectorsByObject , ObjectSelector , error ) {
268+ // Selectors are combined via logical AND.
269+ // - Combined label selector is a union of the selectors requirements from both sets of options.
270+ // - Combined field selector uses fields.AndSelectors with the combined list of non-nil field selectors
271+ // defined in both sets of options.
272+ //
273+ // There is a bunch of complexity here because we need to convert to SelectorsByGVK
274+ // to be able to match keys between options and inherited and then convert back to SelectorsByObject
275+ optionsSelectorsByGVK , err := convertToByGVK (options .SelectorsByObject , options .DefaultSelector , options .Scheme )
276+ if err != nil {
277+ return nil , ObjectSelector {}, err
278+ }
279+ inheritedSelectorsByGVK , err := convertToByGVK (inherited .SelectorsByObject , inherited .DefaultSelector , inherited .Scheme )
280+ if err != nil {
281+ return nil , ObjectSelector {}, err
282+ }
283+
284+ for gvk , inheritedSelector := range inheritedSelectorsByGVK {
285+ optionsSelectorsByGVK [gvk ] = combineSelector (inheritedSelector , optionsSelectorsByGVK [gvk ])
286+ }
287+ return convertToByObject (optionsSelectorsByGVK , scheme )
288+ }
289+
290+ func combineSelector (selectors ... ObjectSelector ) ObjectSelector {
291+ ls := make ([]labels.Selector , 0 , len (selectors ))
292+ fs := make ([]fields.Selector , 0 , len (selectors ))
293+ for _ , s := range selectors {
294+ ls = append (ls , s .Label )
295+ fs = append (fs , s .Field )
296+ }
297+ return ObjectSelector {
298+ Label : combineLabelSelectors (ls ... ),
299+ Field : combineFieldSelectors (fs ... ),
300+ }
301+ }
302+
303+ func combineLabelSelectors (ls ... labels.Selector ) labels.Selector {
304+ var combined labels.Selector
305+ for _ , l := range ls {
306+ if l == nil {
307+ continue
308+ }
309+ if combined == nil {
310+ combined = labels .NewSelector ()
311+ }
312+ reqs , _ := l .Requirements ()
313+ combined = combined .Add (reqs ... )
314+ }
315+ return combined
316+ }
317+
318+ func combineFieldSelectors (fs ... fields.Selector ) fields.Selector {
319+ nonNil := fs [:0 ]
320+ for _ , f := range fs {
321+ if f == nil {
322+ continue
323+ }
324+ nonNil = append (nonNil , f )
325+ }
326+ if len (nonNil ) == 0 {
327+ return nil
328+ }
329+ if len (nonNil ) == 1 {
330+ return nonNil [0 ]
331+ }
332+ return fields .AndSelectors (nonNil ... )
333+ }
334+
335+ func combineUnsafeDeepCopy (inherited , options Options , scheme * runtime.Scheme ) (DisableDeepCopyByObject , error ) {
336+ // UnsafeDisableDeepCopyByObject is combined via precedence. Only if a value for a particular GVK is unset
337+ // in options will a value from inherited be used.
338+ optionsDisableDeepCopyByGVK , err := convertToDisableDeepCopyByGVK (options .UnsafeDisableDeepCopyByObject , options .Scheme )
339+ if err != nil {
340+ return nil , err
341+ }
342+ inheritedDisableDeepCopyByGVK , err := convertToDisableDeepCopyByGVK (inherited .UnsafeDisableDeepCopyByObject , inherited .Scheme )
343+ if err != nil {
344+ return nil , err
345+ }
346+
347+ for gvk , inheritedDeepCopy := range inheritedDisableDeepCopyByGVK {
348+ if _ , ok := optionsDisableDeepCopyByGVK [gvk ]; ! ok {
349+ if optionsDisableDeepCopyByGVK == nil {
350+ optionsDisableDeepCopyByGVK = map [schema.GroupVersionKind ]bool {}
351+ }
352+ optionsDisableDeepCopyByGVK [gvk ] = inheritedDeepCopy
353+ }
354+ }
355+ return convertToDisableDeepCopyByObject (optionsDisableDeepCopyByGVK , scheme )
356+ }
357+
358+ func combineTransforms (inherited , options Options , scheme * runtime.Scheme ) (TransformByObject , toolscache.TransformFunc , error ) {
359+ // Transform functions are combined via chaining. If both inherited and options define a transform
360+ // function, the transform function from inherited will be called first, and the transform function from
361+ // options will be called second.
362+ optionsTransformByGVK , err := convertToByGVK (options .TransformByObject , options .DefaultTransform , options .Scheme )
363+ if err != nil {
364+ return nil , nil , err
365+ }
366+ inheritedTransformByGVK , err := convertToByGVK (inherited .TransformByObject , inherited .DefaultTransform , inherited .Scheme )
367+ if err != nil {
368+ return nil , nil , err
369+ }
370+
371+ for gvk , inheritedTransform := range inheritedTransformByGVK {
372+ if optionsTransformByGVK == nil {
373+ optionsTransformByGVK = map [schema.GroupVersionKind ]toolscache.TransformFunc {}
374+ }
375+ optionsTransformByGVK [gvk ] = combineTransform (inheritedTransform , optionsTransformByGVK [gvk ])
376+ }
377+ return convertToByObject (optionsTransformByGVK , scheme )
378+ }
379+
380+ func combineTransform (inherited , current toolscache.TransformFunc ) toolscache.TransformFunc {
381+ if inherited == nil {
382+ return current
383+ }
384+ if current == nil {
385+ return inherited
386+ }
387+ return func (in interface {}) (interface {}, error ) {
388+ mid , err := inherited (in )
389+ if err != nil {
390+ return nil , err
391+ }
392+ return current (mid )
196393 }
197394}
198395
@@ -219,17 +416,40 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
219416 return opts , nil
220417}
221418
222- func convertToSelectorsByGVK ( selectorsByObject SelectorsByObject , defaultSelector ObjectSelector , scheme * runtime.Scheme ) (internal. SelectorsByGVK , error ) {
223- selectorsByGVK := internal. SelectorsByGVK {}
224- for object , selector := range selectorsByObject {
419+ func convertToByGVK [ T any ]( byObject map [client. Object ] T , def T , scheme * runtime.Scheme ) (map [schema. GroupVersionKind ] T , error ) {
420+ byGVK := map [schema. GroupVersionKind ] T {}
421+ for object , value := range byObject {
225422 gvk , err := apiutil .GVKForObject (object , scheme )
226423 if err != nil {
227424 return nil , err
228425 }
229- selectorsByGVK [gvk ] = internal .Selector (selector )
426+ byGVK [gvk ] = value
427+ }
428+ byGVK [schema.GroupVersionKind {}] = def
429+ return byGVK , nil
430+ }
431+
432+ func convertToByObject [T any ](byGVK map [schema.GroupVersionKind ]T , scheme * runtime.Scheme ) (map [client.Object ]T , T , error ) {
433+ var byObject map [client.Object ]T
434+ def := byGVK [schema.GroupVersionKind {}]
435+ for gvk , value := range byGVK {
436+ if gvk == (schema.GroupVersionKind {}) {
437+ continue
438+ }
439+ obj , err := scheme .New (gvk )
440+ if err != nil {
441+ return nil , def , err
442+ }
443+ cObj , ok := obj .(client.Object )
444+ if ! ok {
445+ return nil , def , fmt .Errorf ("object %T for GVK %q does not implement client.Object" , obj , gvk )
446+ }
447+ if byObject == nil {
448+ byObject = map [client.Object ]T {}
449+ }
450+ byObject [cObj ] = value
230451 }
231- selectorsByGVK [schema.GroupVersionKind {}] = internal .Selector (defaultSelector )
232- return selectorsByGVK , nil
452+ return byObject , def , nil
233453}
234454
235455// DisableDeepCopyByObject associate a client.Object's GVK to disable DeepCopy during get or list from cache.
@@ -259,17 +479,30 @@ func convertToDisableDeepCopyByGVK(disableDeepCopyByObject DisableDeepCopyByObje
259479 return disableDeepCopyByGVK , nil
260480}
261481
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 {
482+ func convertToDisableDeepCopyByObject (byGVK internal.DisableDeepCopyByGVK , scheme * runtime.Scheme ) (DisableDeepCopyByObject , error ) {
483+ var byObject DisableDeepCopyByObject
484+ for gvk , value := range byGVK {
485+ if byObject == nil {
486+ byObject = DisableDeepCopyByObject {}
487+ }
488+ if gvk == (schema.GroupVersionKind {}) {
489+ byObject [ObjectAll {}] = value
490+ continue
491+ }
492+ obj , err := scheme .New (gvk )
493+ if err != nil {
270494 return nil , err
271495 }
496+ cObj , ok := obj .(client.Object )
497+ if ! ok {
498+ return nil , fmt .Errorf ("object %T for GVK %q does not implement client.Object" , obj , gvk )
499+ }
500+
501+ byObject [cObj ] = value
272502 }
273- result .SetDefault (defaultTransform )
274- return result , nil
503+ return byObject , nil
275504}
505+
506+ // TransformByObject associate a client.Object's GVK to a transformer function
507+ // to be applied when storing the object into the cache.
508+ type TransformByObject map [client.Object ]toolscache.TransformFunc
0 commit comments