@@ -22,15 +22,16 @@ import (
22
22
"net/http"
23
23
"time"
24
24
25
+ corev1 "k8s.io/api/core/v1"
25
26
"k8s.io/apimachinery/pkg/api/meta"
26
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
27
"k8s.io/apimachinery/pkg/fields"
28
28
"k8s.io/apimachinery/pkg/labels"
29
29
"k8s.io/apimachinery/pkg/runtime"
30
30
"k8s.io/apimachinery/pkg/runtime/schema"
31
31
"k8s.io/client-go/kubernetes/scheme"
32
32
"k8s.io/client-go/rest"
33
33
toolscache "k8s.io/client-go/tools/cache"
34
+ "k8s.io/utils/pointer"
34
35
35
36
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
36
37
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -144,9 +145,13 @@ type Options struct {
144
145
// instead of `reconcile.Result{}`.
145
146
SyncPeriod * time.Duration
146
147
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
150
155
151
156
// DefaultLabelSelector will be used as a label selector for all object types
152
157
// unless they have a more specific selector set in ByObject.
@@ -160,20 +165,39 @@ type Options struct {
160
165
// unless they have a more specific transform set in ByObject.
161
166
DefaultTransform toolscache.TransformFunc
162
167
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
+ //
165
171
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
166
172
// otherwise you will mutate the object in the cache.
167
173
//
168
174
// This is a global setting for all objects, and can be overridden by the ByObject setting.
169
- UnsafeDisableDeepCopy * bool
175
+ DefaultUnsafeDisableDeepCopy * bool
170
176
171
177
// ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
178
+ // object, this will fall through to Default* settings.
172
179
ByObject map [client.Object ]ByObject
173
180
}
174
181
175
182
// ByObject offers more fine-grained control over the cache's ListWatch by object.
176
183
type ByObject struct {
184
+ // Namespaces maps a namespace name to cache configs. If set, only the
185
+ // namespaces in this map will be cached.
186
+ //
187
+ // Settings in the map value that are unset will be defaulted.
188
+ // Use an empty value for the specific setting to prevent that.
189
+ //
190
+ // A nil map allows to default this to the cache's DefaultNamespaces setting.
191
+ // An empty map prevents this and means that all namespaces will be cached.
192
+ //
193
+ // The defaulting follows the following precedence order:
194
+ // 1. ByObject
195
+ // 2. DefaultNamespaces[namespace]
196
+ // 3. Default*
197
+ //
198
+ // This must be unset for cluster-scoped objects.
199
+ Namespaces map [string ]Config
200
+
177
201
// Label represents a label selector for the object.
178
202
Label labels.Selector
179
203
@@ -194,48 +218,118 @@ type ByObject struct {
194
218
UnsafeDisableDeepCopy * bool
195
219
}
196
220
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
+ //
226
+ // Set to labels.Everything() if you don't want this defaulted.
227
+ LabelSelector labels.Selector
228
+
229
+ // FieldSelector specifics a field selector. A nil value allows to
230
+ // default this.
231
+ //
232
+ // Set to fields.Everything() if you don't want this defaulted.
233
+ FieldSelector fields.Selector
234
+
235
+ // Transform specifies a transform func. A nil value allows to default
236
+ // this.
237
+ //
238
+ // Set to an empty func to prevent this:
239
+ // func(in interface{}) (interface{}, error) { return in, nil }
240
+ Transform toolscache.TransformFunc
241
+
242
+ // UnsafeDisableDeepCopy specifies if List and Get requests against the
243
+ // cache should not DeepCopy. A nil value allows to default this.
244
+ UnsafeDisableDeepCopy * bool
245
+ }
246
+
197
247
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
198
248
type NewCacheFunc func (config * rest.Config , opts Options ) (Cache , error )
199
249
200
250
// 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 }
251
+ func New (cfg * rest.Config , opts Options ) (Cache , error ) {
252
+ opts , err := defaultOpts (cfg , opts )
253
+ if err != nil {
254
+ return nil , err
204
255
}
205
- if len (opts .Namespaces ) > 1 {
206
- return newMultiNamespaceCache (config , opts )
256
+
257
+ newCacheFunc := newCache (cfg , opts )
258
+
259
+ var defaultCache Cache
260
+ if len (opts .DefaultNamespaces ) > 0 {
261
+ defaultConfig := optionDefaultsToConfig (& opts )
262
+ defaultCache = newMultiNamespaceCache (newCacheFunc , opts .Scheme , opts .Mapper , opts .DefaultNamespaces , & defaultConfig )
263
+ } else {
264
+ defaultCache = newCacheFunc (optionDefaultsToConfig (& opts ), corev1 .NamespaceAll )
207
265
}
208
266
209
- opts , err := defaultOpts (config , opts )
210
- if err != nil {
211
- return nil , err
267
+ if len (opts .ByObject ) == 0 {
268
+ return defaultCache , nil
212
269
}
213
270
214
- byGVK , err := convertToInformerOptsByGVK (opts .ByObject , opts .Scheme )
215
- if err != nil {
216
- return nil , err
271
+ delegating := & delegatingByTypeCache {
272
+ scheme : opts .Scheme ,
273
+ caches : make (map [schema.GroupVersionKind ]Cache , len (opts .ByObject )),
274
+ defaultCache : defaultCache ,
275
+ }
276
+
277
+ for obj , config := range opts .ByObject {
278
+ gvk , err := apiutil .GVKForObject (obj , opts .Scheme )
279
+ if err != nil {
280
+ return nil , fmt .Errorf ("failed to get GVK for type %T: %w" , obj , err )
281
+ }
282
+ var cache Cache
283
+ if len (config .Namespaces ) > 0 {
284
+ cache = newMultiNamespaceCache (newCacheFunc , opts .Scheme , opts .Mapper , config .Namespaces , nil )
285
+ } else {
286
+ cache = newCacheFunc (byObjectToConfig (config ), corev1 .NamespaceAll )
287
+ }
288
+ delegating .caches [gvk ] = cache
217
289
}
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
- },
290
+
291
+ return delegating , nil
292
+ }
293
+
294
+ func optionDefaultsToConfig (opts * Options ) Config {
295
+ return Config {
296
+ LabelSelector : opts .DefaultLabelSelector ,
297
+ FieldSelector : opts .DefaultFieldSelector ,
224
298
Transform : opts .DefaultTransform ,
225
- UnsafeDisableDeepCopy : opts .UnsafeDisableDeepCopy ,
299
+ UnsafeDisableDeepCopy : opts .DefaultUnsafeDisableDeepCopy ,
300
+ }
301
+ }
302
+
303
+ func byObjectToConfig (byObject ByObject ) Config {
304
+ return Config {
305
+ LabelSelector : byObject .Label ,
306
+ FieldSelector : byObject .Field ,
307
+ Transform : byObject .Transform ,
308
+ UnsafeDisableDeepCopy : byObject .UnsafeDisableDeepCopy ,
226
309
}
310
+ }
227
311
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
312
+ type newCacheFunc func (config Config , namespace string ) Cache
313
+
314
+ func newCache (restConfig * rest.Config , opts Options ) newCacheFunc {
315
+ return func (config Config , namespace string ) Cache {
316
+ return & informerCache {
317
+ scheme : opts .Scheme ,
318
+ Informers : internal .NewInformers (restConfig , & internal.InformersOpts {
319
+ HTTPClient : opts .HTTPClient ,
320
+ Scheme : opts .Scheme ,
321
+ Mapper : opts .Mapper ,
322
+ ResyncPeriod : * opts .SyncPeriod ,
323
+ Namespace : namespace ,
324
+ Selector : internal.Selector {
325
+ Label : config .LabelSelector ,
326
+ Field : config .FieldSelector ,
327
+ },
328
+ Transform : config .Transform ,
329
+ UnsafeDisableDeepCopy : pointer .BoolDeref (config .UnsafeDisableDeepCopy , false ),
330
+ }),
331
+ }
332
+ }
239
333
}
240
334
241
335
func defaultOpts (config * rest.Config , opts Options ) (Options , error ) {
@@ -262,31 +356,70 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
262
356
}
263
357
}
264
358
359
+ for namespace , cfg := range opts .DefaultNamespaces {
360
+ cfg = defaultConfig (cfg , optionDefaultsToConfig (& opts ))
361
+ opts .DefaultNamespaces [namespace ] = cfg
362
+ }
363
+
364
+ for obj , byObject := range opts .ByObject {
365
+ isNamespaced , err := apiutil .IsObjectNamespaced (obj , opts .Scheme , opts .Mapper )
366
+ if err != nil {
367
+ return opts , fmt .Errorf ("failed to determine if %T is namespaced: %w" , obj , err )
368
+ }
369
+ if ! isNamespaced && byObject .Namespaces != nil {
370
+ return opts , fmt .Errorf ("type %T is not namespaced, but its ByObject.Namespaces setting is not nil" , obj )
371
+ }
372
+
373
+ // Default the namespace-level configs first, because they need to use the undefaulted type-level config.
374
+ for namespace , config := range byObject .Namespaces {
375
+ // 1. Default from the undefaulted type-level config
376
+ config = defaultConfig (config , byObjectToConfig (byObject ))
377
+
378
+ // 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
379
+ // might not have an entry for the current namespace.
380
+ if defaultNamespaceSettings , hasDefaultNamespace := opts .DefaultNamespaces [namespace ]; hasDefaultNamespace {
381
+ config = defaultConfig (config , defaultNamespaceSettings )
382
+ }
383
+
384
+ // 3. Default from the global defaults
385
+ config = defaultConfig (config , optionDefaultsToConfig (& opts ))
386
+
387
+ byObject .Namespaces [namespace ] = config
388
+ }
389
+
390
+ defaultedConfig := defaultConfig (byObjectToConfig (byObject ), optionDefaultsToConfig (& opts ))
391
+ byObject .Label = defaultedConfig .LabelSelector
392
+ byObject .Field = defaultedConfig .FieldSelector
393
+ byObject .Transform = defaultedConfig .Transform
394
+ byObject .UnsafeDisableDeepCopy = defaultedConfig .UnsafeDisableDeepCopy
395
+
396
+ if byObject .Namespaces == nil {
397
+ byObject .Namespaces = opts .DefaultNamespaces
398
+ }
399
+
400
+ opts .ByObject [obj ] = byObject
401
+ }
402
+
265
403
// Default the resync period to 10 hours if unset
266
404
if opts .SyncPeriod == nil {
267
405
opts .SyncPeriod = & defaultSyncPeriod
268
406
}
269
407
return opts , nil
270
408
}
271
409
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
- }
410
+ func defaultConfig (toDefault , defaultFrom Config ) Config {
411
+ if toDefault .LabelSelector == nil {
412
+ toDefault .LabelSelector = defaultFrom .LabelSelector
290
413
}
291
- return out , nil
414
+ if toDefault .FieldSelector == nil {
415
+ toDefault .FieldSelector = defaultFrom .FieldSelector
416
+ }
417
+ if toDefault .Transform == nil {
418
+ toDefault .Transform = defaultFrom .Transform
419
+ }
420
+ if toDefault .UnsafeDisableDeepCopy == nil {
421
+ toDefault .UnsafeDisableDeepCopy = defaultFrom .UnsafeDisableDeepCopy
422
+ }
423
+
424
+ return toDefault
292
425
}
0 commit comments