@@ -22,8 +22,8 @@ import (
2222 "net/http"
2323 "time"
2424
25+ corev1 "k8s.io/api/core/v1"
2526 "k8s.io/apimachinery/pkg/api/meta"
26- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727 "k8s.io/apimachinery/pkg/fields"
2828 "k8s.io/apimachinery/pkg/labels"
2929 "k8s.io/apimachinery/pkg/runtime"
@@ -144,9 +144,13 @@ type Options struct {
144144 // instead of `reconcile.Result{}`.
145145 SyncPeriod * time.Duration
146146
147- // Namespaces restricts the cache's ListWatch to the desired namespaces
148- // Per default ListWatch watches all namespaces
149- Namespaces []string
147+ // DefaultNamespaces maps namespace names to cache settings. If set, only
148+ // the namespaces in here will be watched and it will by used to default
149+ // ByObject.Namespaces for all objects if that is nil.
150+ //
151+ // The options in the Config that are nil will be defaulted from
152+ // the respective Default* settings.
153+ DefaultNamespaces map [string ]Config
150154
151155 // DefaultLabelSelector will be used as a label selector for all object types
152156 // unless they have a more specific selector set in ByObject.
@@ -160,20 +164,40 @@ type Options struct {
160164 // unless they have a more specific transform set in ByObject.
161165 DefaultTransform toolscache.TransformFunc
162166
163- // UnsafeDisableDeepCopy indicates not to deep copy objects during get or
164- // list objects for EVERY object.
167+ // DefaultUnsafeDisableDeepCopy is the default for UnsafeDisableDeepCopy
168+ // for everything that doesn't specify this.
169+ //
165170 // Be very careful with this, when enabled you must DeepCopy any object before mutating it,
166171 // otherwise you will mutate the object in the cache.
167172 //
168173 // This is a global setting for all objects, and can be overridden by the ByObject setting.
169- UnsafeDisableDeepCopy * bool
174+ DefaultUnsafeDisableDeepCopy * bool
170175
171176 // ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
177+ // object, this will fall through to Default* settings.
172178 ByObject map [client.Object ]ByObject
173179}
174180
175181// ByObject offers more fine-grained control over the cache's ListWatch by object.
176182type ByObject struct {
183+ // Namespaces maps a namespace name to cache setting. If set, only the
184+ // namespaces in this map will be cached.
185+ //
186+ // Settings in the map value that are unset because either the value as a
187+ // whole is nil or because the specific setting is nil will be defaulted.
188+ // Use an empty value for the specific setting to prevent that.
189+ //
190+ // It is possible to have specific Config for just some namespaces
191+ // but cache all namespaces by using the AllNamespaces const as the map key.
192+ // This wil then include all namespaces that do not have a more specific
193+ // setting.
194+ //
195+ // A nil map allows to default this to the cache's DefaultNamespaces setting.
196+ // An empty map prevents this.
197+ //
198+ // This must be unset for cluster-scoped objects.
199+ Namespaces map [string ]Config
200+
177201 // Label represents a label selector for the object.
178202 Label labels.Selector
179203
@@ -194,48 +218,111 @@ type ByObject struct {
194218 UnsafeDisableDeepCopy * bool
195219}
196220
221+ // Config describes all potential options for a given watch.
222+ type Config struct {
223+ // LabelSelector specifies a label selector. A nil value allows to
224+ // default this.
225+ LabelSelector labels.Selector
226+
227+ // FieldSelector specifics a field selector. A nil value allows to
228+ // default this.
229+ FieldSelector fields.Selector
230+
231+ // Transform specifies a transform func. A nil value allows to default
232+ // this.
233+ Transform toolscache.TransformFunc
234+
235+ // UnsafeDisableDeepCopy specifies if List and Get requests against the
236+ // cache should not DeepCopy. A nil value allows to default this.
237+ UnsafeDisableDeepCopy * bool
238+ }
239+
197240// NewCacheFunc - Function for creating a new cache from the options and a rest config.
198241type NewCacheFunc func (config * rest.Config , opts Options ) (Cache , error )
199242
200243// New initializes and returns a new Cache.
201- func New (config * rest.Config , opts Options ) (Cache , error ) {
202- if len (opts .Namespaces ) == 0 {
203- opts .Namespaces = []string {metav1 .NamespaceAll }
244+ func New (cfg * rest.Config , opts Options ) (Cache , error ) {
245+ opts , err := defaultOpts (cfg , opts )
246+ if err != nil {
247+ return nil , err
204248 }
205- if len (opts .Namespaces ) > 1 {
206- return newMultiNamespaceCache (config , opts )
249+
250+ newCache := newCache (cfg , opts )
251+
252+ var defaultCache Cache
253+ if len (opts .DefaultNamespaces ) > 0 {
254+ defaultConfig := optionDefaultsToConfig (& opts )
255+ defaultCache = newMultiNamespaceCache (newCache , opts .Scheme , opts .Mapper , opts .DefaultNamespaces , & defaultConfig )
256+ } else {
257+ defaultCache = newCache (optionDefaultsToConfig (& opts ), corev1 .NamespaceAll )
207258 }
208259
209- opts , err := defaultOpts (config , opts )
210- if err != nil {
211- return nil , err
260+ if len (opts .ByObject ) == 0 {
261+ return defaultCache , nil
212262 }
213263
214- byGVK , err := convertToInformerOptsByGVK (opts .ByObject , opts .Scheme )
215- if err != nil {
216- return nil , err
264+ delegating := & delegatingByTypeCache {
265+ scheme : opts .Scheme ,
266+ caches : make (map [schema.GroupVersionKind ]Cache , len (opts .ByObject )),
267+ defaultCache : defaultCache ,
217268 }
218- // Set the default selector and transform.
219- byGVK [schema.GroupVersionKind {}] = internal.InformersOptsByGVK {
220- Selector : internal.Selector {
221- Label : opts .DefaultLabelSelector ,
222- Field : opts .DefaultFieldSelector ,
223- },
269+
270+ for tp , config := range opts .ByObject {
271+ gvk , err := apiutil .GVKForObject (tp , opts .Scheme )
272+ if err != nil {
273+ return nil , fmt .Errorf ("failed to get GVK for type %T: %w" , tp , err )
274+ }
275+ var cache Cache
276+ if len (config .Namespaces ) > 0 {
277+ cache = newMultiNamespaceCache (newCache , opts .Scheme , opts .Mapper , config .Namespaces , nil )
278+ } else {
279+ cache = newCache (byObjectToConfig (config ), corev1 .NamespaceAll )
280+ }
281+ delegating .caches [gvk ] = cache
282+ }
283+
284+ return delegating , nil
285+ }
286+
287+ func optionDefaultsToConfig (opts * Options ) Config {
288+ return Config {
289+ LabelSelector : opts .DefaultLabelSelector ,
290+ FieldSelector : opts .DefaultFieldSelector ,
224291 Transform : opts .DefaultTransform ,
225- UnsafeDisableDeepCopy : opts .UnsafeDisableDeepCopy ,
292+ UnsafeDisableDeepCopy : opts .DefaultUnsafeDisableDeepCopy ,
293+ }
294+ }
295+
296+ func byObjectToConfig (byObject ByObject ) Config {
297+ return Config {
298+ LabelSelector : byObject .Label ,
299+ FieldSelector : byObject .Field ,
300+ Transform : byObject .Transform ,
301+ UnsafeDisableDeepCopy : byObject .UnsafeDisableDeepCopy ,
226302 }
303+ }
227304
228- return & informerCache {
229- scheme : opts .Scheme ,
230- Informers : internal .NewInformers (config , & internal.InformersOpts {
231- HTTPClient : opts .HTTPClient ,
232- Scheme : opts .Scheme ,
233- Mapper : opts .Mapper ,
234- ResyncPeriod : * opts .SyncPeriod ,
235- Namespace : opts .Namespaces [0 ],
236- ByGVK : byGVK ,
237- }),
238- }, nil
305+ type newCacheFunc func (config Config , namespace string ) Cache
306+
307+ func newCache (cfg * rest.Config , opts Options ) newCacheFunc {
308+ return func (config Config , namespace string ) Cache {
309+ return & informerCache {
310+ scheme : opts .Scheme ,
311+ Informers : internal .NewInformers (cfg , & internal.InformersOpts {
312+ HTTPClient : opts .HTTPClient ,
313+ Scheme : opts .Scheme ,
314+ Mapper : opts .Mapper ,
315+ ResyncPeriod : * opts .SyncPeriod ,
316+ Namespace : namespace ,
317+ Selector : internal.Selector {
318+ Label : config .LabelSelector ,
319+ Field : config .FieldSelector ,
320+ },
321+ Transform : config .Transform ,
322+ UnsafeDisableDeepCopy : config .UnsafeDisableDeepCopy ,
323+ }),
324+ }
325+ }
239326}
240327
241328func defaultOpts (config * rest.Config , opts Options ) (Options , error ) {
@@ -262,31 +349,70 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
262349 }
263350 }
264351
352+ for namespace , cfg := range opts .DefaultNamespaces {
353+ cfg = defaultConfig (cfg , optionDefaultsToConfig (& opts ))
354+ opts .DefaultNamespaces [namespace ] = cfg
355+ }
356+
357+ for tp , cfg := range opts .ByObject {
358+ isNamespaced , err := apiutil .IsObjectNamespaced (tp , opts .Scheme , opts .Mapper )
359+ if err != nil {
360+ return opts , fmt .Errorf ("failed to determine if %T is namespaced: %w" , tp , err )
361+ }
362+ if ! isNamespaced && cfg .Namespaces != nil {
363+ return opts , fmt .Errorf ("type %T is not namespaced, but its ByObject.Namespaces setting is not nil" , tp )
364+ }
365+
366+ // Default the namespace-level configs first, because they need to use the undefaulted type-level config.
367+ for namespace , config := range cfg .Namespaces {
368+ // 1. Default from the undefaulted type-level config
369+ config = defaultConfig (config , byObjectToConfig (cfg ))
370+
371+ // 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
372+ // might not have an entry for the current namespace.
373+ if defaultNamespaceSettings , hasDefaultNamespace := opts .DefaultNamespaces [namespace ]; hasDefaultNamespace {
374+ config = defaultConfig (config , defaultNamespaceSettings )
375+ }
376+
377+ // 3. Default from the global defaults
378+ config = defaultConfig (config , optionDefaultsToConfig (& opts ))
379+
380+ cfg .Namespaces [namespace ] = config
381+ }
382+
383+ defaultedConfig := defaultConfig (byObjectToConfig (cfg ), optionDefaultsToConfig (& opts ))
384+ cfg .Label = defaultedConfig .LabelSelector
385+ cfg .Field = defaultedConfig .FieldSelector
386+ cfg .Transform = defaultedConfig .Transform
387+ cfg .UnsafeDisableDeepCopy = defaultedConfig .UnsafeDisableDeepCopy
388+
389+ if cfg .Namespaces == nil {
390+ cfg .Namespaces = opts .DefaultNamespaces
391+ }
392+
393+ opts .ByObject [tp ] = cfg
394+ }
395+
265396 // Default the resync period to 10 hours if unset
266397 if opts .SyncPeriod == nil {
267398 opts .SyncPeriod = & defaultSyncPeriod
268399 }
269400 return opts , nil
270401}
271402
272- func convertToInformerOptsByGVK (in map [client.Object ]ByObject , scheme * runtime.Scheme ) (map [schema.GroupVersionKind ]internal.InformersOptsByGVK , error ) {
273- out := map [schema.GroupVersionKind ]internal.InformersOptsByGVK {}
274- for object , byObject := range in {
275- gvk , err := apiutil .GVKForObject (object , scheme )
276- if err != nil {
277- return nil , err
278- }
279- if _ , ok := out [gvk ]; ok {
280- return nil , fmt .Errorf ("duplicate cache options for GVK %v, cache.Options.ByObject has multiple types with the same GroupVersionKind" , gvk )
281- }
282- out [gvk ] = internal.InformersOptsByGVK {
283- Selector : internal.Selector {
284- Field : byObject .Field ,
285- Label : byObject .Label ,
286- },
287- Transform : byObject .Transform ,
288- UnsafeDisableDeepCopy : byObject .UnsafeDisableDeepCopy ,
289- }
403+ func defaultConfig (toDefault , defaultFrom Config ) Config {
404+ if toDefault .LabelSelector == nil {
405+ toDefault .LabelSelector = defaultFrom .LabelSelector
406+ }
407+ if toDefault .FieldSelector == nil {
408+ toDefault .FieldSelector = defaultFrom .FieldSelector
290409 }
291- return out , nil
410+ if toDefault .Transform == nil {
411+ toDefault .Transform = defaultFrom .Transform
412+ }
413+ if toDefault .UnsafeDisableDeepCopy == nil {
414+ toDefault .UnsafeDisableDeepCopy = defaultFrom .UnsafeDisableDeepCopy
415+ }
416+
417+ return toDefault
292418}
0 commit comments