@@ -22,15 +22,16 @@ 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"
3030 "k8s.io/apimachinery/pkg/runtime/schema"
3131 "k8s.io/client-go/kubernetes/scheme"
3232 "k8s.io/client-go/rest"
3333 toolscache "k8s.io/client-go/tools/cache"
34+ "k8s.io/utils/pointer"
3435
3536 "sigs.k8s.io/controller-runtime/pkg/cache/internal"
3637 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -144,36 +145,60 @@ type Options struct {
144145 // instead of `reconcile.Result{}`.
145146 SyncPeriod * time.Duration
146147
147- // Namespaces restricts the cache's ListWatch to the desired namespaces
148- // Per default ListWatch watches all namespaces
149- Namespaces []string
148+ // DefaultNamespaces maps namespace names to cache configs. If set, only
149+ // the namespaces in here will be watched and it will by used to default
150+ // ByObject.Namespaces for all objects if that is nil.
151+ //
152+ // The options in the Config that are nil will be defaulted from
153+ // the respective Default* settings.
154+ DefaultNamespaces map [string ]Config
150155
151- // DefaultLabelSelector will be used as a label selector for all object types
152- // unless they have a more specific selector set in ByObject.
156+ // DefaultLabelSelector will be used as a label selector for all objects
157+ // unless there is already one set in ByObject or DefaultNamespaces .
153158 DefaultLabelSelector labels.Selector
154159
155160 // DefaultFieldSelector will be used as a field selector for all object types
156- // unless they have a more specific selector set in ByObject.
161+ // unless there is already one set in ByObject or DefaultNamespaces .
157162 DefaultFieldSelector fields.Selector
158163
159164 // DefaultTransform will be used as transform for all object types
160- // unless they have a more specific transform set in ByObject.
165+ // unless there is already one set in ByObject or DefaultNamespaces .
161166 DefaultTransform toolscache.TransformFunc
162167
163- // UnsafeDisableDeepCopy indicates not to deep copy objects during get or
164- // list objects for EVERY object.
168+ // DefaultUnsafeDisableDeepCopy is the default for UnsafeDisableDeepCopy
169+ // for everything that doesn't specify this.
170+ //
165171 // Be very careful with this, when enabled you must DeepCopy any object before mutating it,
166172 // otherwise you will mutate the object in the cache.
167173 //
168- // This is a global setting for all objects, and can be overridden by the ByObject setting.
169- UnsafeDisableDeepCopy * bool
174+ // This will be used for all object types, unless it is set in ByObject or
175+ // DefaultNamespaces.
176+ DefaultUnsafeDisableDeepCopy * bool
170177
171178 // ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
179+ // object, this will fall through to Default* settings.
172180 ByObject map [client.Object ]ByObject
173181}
174182
175183// ByObject offers more fine-grained control over the cache's ListWatch by object.
176184type ByObject struct {
185+ // Namespaces maps a namespace name to cache configs. If set, only the
186+ // namespaces in this map will be cached.
187+ //
188+ // Settings in the map value that are unset will be defaulted.
189+ // Use an empty value for the specific setting to prevent that.
190+ //
191+ // A nil map allows to default this to the cache's DefaultNamespaces setting.
192+ // An empty map prevents this and means that all namespaces will be cached.
193+ //
194+ // The defaulting follows the following precedence order:
195+ // 1. ByObject
196+ // 2. DefaultNamespaces[namespace]
197+ // 3. Default*
198+ //
199+ // This must be unset for cluster-scoped objects.
200+ Namespaces map [string ]Config
201+
177202 // Label represents a label selector for the object.
178203 Label labels.Selector
179204
@@ -194,48 +219,118 @@ type ByObject struct {
194219 UnsafeDisableDeepCopy * bool
195220}
196221
222+ // Config describes all potential options for a given watch.
223+ type Config struct {
224+ // LabelSelector specifies a label selector. A nil value allows to
225+ // default this.
226+ //
227+ // Set to labels.Everything() if you don't want this defaulted.
228+ LabelSelector labels.Selector
229+
230+ // FieldSelector specifics a field selector. A nil value allows to
231+ // default this.
232+ //
233+ // Set to fields.Everything() if you don't want this defaulted.
234+ FieldSelector fields.Selector
235+
236+ // Transform specifies a transform func. A nil value allows to default
237+ // this.
238+ //
239+ // Set to an empty func to prevent this:
240+ // func(in interface{}) (interface{}, error) { return in, nil }
241+ Transform toolscache.TransformFunc
242+
243+ // UnsafeDisableDeepCopy specifies if List and Get requests against the
244+ // cache should not DeepCopy. A nil value allows to default this.
245+ UnsafeDisableDeepCopy * bool
246+ }
247+
197248// NewCacheFunc - Function for creating a new cache from the options and a rest config.
198249type NewCacheFunc func (config * rest.Config , opts Options ) (Cache , error )
199250
200251// 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 }
252+ func New (cfg * rest.Config , opts Options ) (Cache , error ) {
253+ opts , err := defaultOpts (cfg , opts )
254+ if err != nil {
255+ return nil , err
204256 }
205- if len (opts .Namespaces ) > 1 {
206- return newMultiNamespaceCache (config , opts )
257+
258+ newCacheFunc := newCache (cfg , opts )
259+
260+ var defaultCache Cache
261+ if len (opts .DefaultNamespaces ) > 0 {
262+ defaultConfig := optionDefaultsToConfig (& opts )
263+ defaultCache = newMultiNamespaceCache (newCacheFunc , opts .Scheme , opts .Mapper , opts .DefaultNamespaces , & defaultConfig )
264+ } else {
265+ defaultCache = newCacheFunc (optionDefaultsToConfig (& opts ), corev1 .NamespaceAll )
207266 }
208267
209- opts , err := defaultOpts (config , opts )
210- if err != nil {
211- return nil , err
268+ if len (opts .ByObject ) == 0 {
269+ return defaultCache , nil
212270 }
213271
214- byGVK , err := convertToInformerOptsByGVK (opts .ByObject , opts .Scheme )
215- if err != nil {
216- return nil , err
272+ delegating := & delegatingByGVKCache {
273+ scheme : opts .Scheme ,
274+ caches : make (map [schema.GroupVersionKind ]Cache , len (opts .ByObject )),
275+ defaultCache : defaultCache ,
276+ }
277+
278+ for obj , config := range opts .ByObject {
279+ gvk , err := apiutil .GVKForObject (obj , opts .Scheme )
280+ if err != nil {
281+ return nil , fmt .Errorf ("failed to get GVK for type %T: %w" , obj , err )
282+ }
283+ var cache Cache
284+ if len (config .Namespaces ) > 0 {
285+ cache = newMultiNamespaceCache (newCacheFunc , opts .Scheme , opts .Mapper , config .Namespaces , nil )
286+ } else {
287+ cache = newCacheFunc (byObjectToConfig (config ), corev1 .NamespaceAll )
288+ }
289+ delegating .caches [gvk ] = cache
217290 }
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- },
291+
292+ return delegating , nil
293+ }
294+
295+ func optionDefaultsToConfig (opts * Options ) Config {
296+ return Config {
297+ LabelSelector : opts .DefaultLabelSelector ,
298+ FieldSelector : opts .DefaultFieldSelector ,
224299 Transform : opts .DefaultTransform ,
225- UnsafeDisableDeepCopy : opts .UnsafeDisableDeepCopy ,
300+ UnsafeDisableDeepCopy : opts .DefaultUnsafeDisableDeepCopy ,
301+ }
302+ }
303+
304+ func byObjectToConfig (byObject ByObject ) Config {
305+ return Config {
306+ LabelSelector : byObject .Label ,
307+ FieldSelector : byObject .Field ,
308+ Transform : byObject .Transform ,
309+ UnsafeDisableDeepCopy : byObject .UnsafeDisableDeepCopy ,
226310 }
311+ }
227312
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
313+ type newCacheFunc func (config Config , namespace string ) Cache
314+
315+ func newCache (restConfig * rest.Config , opts Options ) newCacheFunc {
316+ return func (config Config , namespace string ) Cache {
317+ return & informerCache {
318+ scheme : opts .Scheme ,
319+ Informers : internal .NewInformers (restConfig , & internal.InformersOpts {
320+ HTTPClient : opts .HTTPClient ,
321+ Scheme : opts .Scheme ,
322+ Mapper : opts .Mapper ,
323+ ResyncPeriod : * opts .SyncPeriod ,
324+ Namespace : namespace ,
325+ Selector : internal.Selector {
326+ Label : config .LabelSelector ,
327+ Field : config .FieldSelector ,
328+ },
329+ Transform : config .Transform ,
330+ UnsafeDisableDeepCopy : pointer .BoolDeref (config .UnsafeDisableDeepCopy , false ),
331+ }),
332+ }
333+ }
239334}
240335
241336func defaultOpts (config * rest.Config , opts Options ) (Options , error ) {
@@ -262,31 +357,70 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
262357 }
263358 }
264359
360+ for namespace , cfg := range opts .DefaultNamespaces {
361+ cfg = defaultConfig (cfg , optionDefaultsToConfig (& opts ))
362+ opts .DefaultNamespaces [namespace ] = cfg
363+ }
364+
365+ for obj , byObject := range opts .ByObject {
366+ isNamespaced , err := apiutil .IsObjectNamespaced (obj , opts .Scheme , opts .Mapper )
367+ if err != nil {
368+ return opts , fmt .Errorf ("failed to determine if %T is namespaced: %w" , obj , err )
369+ }
370+ if ! isNamespaced && byObject .Namespaces != nil {
371+ return opts , fmt .Errorf ("type %T is not namespaced, but its ByObject.Namespaces setting is not nil" , obj )
372+ }
373+
374+ // Default the namespace-level configs first, because they need to use the undefaulted type-level config.
375+ for namespace , config := range byObject .Namespaces {
376+ // 1. Default from the undefaulted type-level config
377+ config = defaultConfig (config , byObjectToConfig (byObject ))
378+
379+ // 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
380+ // might not have an entry for the current namespace.
381+ if defaultNamespaceSettings , hasDefaultNamespace := opts .DefaultNamespaces [namespace ]; hasDefaultNamespace {
382+ config = defaultConfig (config , defaultNamespaceSettings )
383+ }
384+
385+ // 3. Default from the global defaults
386+ config = defaultConfig (config , optionDefaultsToConfig (& opts ))
387+
388+ byObject .Namespaces [namespace ] = config
389+ }
390+
391+ defaultedConfig := defaultConfig (byObjectToConfig (byObject ), optionDefaultsToConfig (& opts ))
392+ byObject .Label = defaultedConfig .LabelSelector
393+ byObject .Field = defaultedConfig .FieldSelector
394+ byObject .Transform = defaultedConfig .Transform
395+ byObject .UnsafeDisableDeepCopy = defaultedConfig .UnsafeDisableDeepCopy
396+
397+ if byObject .Namespaces == nil {
398+ byObject .Namespaces = opts .DefaultNamespaces
399+ }
400+
401+ opts .ByObject [obj ] = byObject
402+ }
403+
265404 // Default the resync period to 10 hours if unset
266405 if opts .SyncPeriod == nil {
267406 opts .SyncPeriod = & defaultSyncPeriod
268407 }
269408 return opts , nil
270409}
271410
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- }
411+ func defaultConfig (toDefault , defaultFrom Config ) Config {
412+ if toDefault .LabelSelector == nil {
413+ toDefault .LabelSelector = defaultFrom .LabelSelector
290414 }
291- return out , nil
415+ if toDefault .FieldSelector == nil {
416+ toDefault .FieldSelector = defaultFrom .FieldSelector
417+ }
418+ if toDefault .Transform == nil {
419+ toDefault .Transform = defaultFrom .Transform
420+ }
421+ if toDefault .UnsafeDisableDeepCopy == nil {
422+ toDefault .UnsafeDisableDeepCopy = defaultFrom .UnsafeDisableDeepCopy
423+ }
424+
425+ return toDefault
292426}
0 commit comments