@@ -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,36 +145,60 @@ 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
- // 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 .
153
158
DefaultLabelSelector labels.Selector
154
159
155
160
// 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 .
157
162
DefaultFieldSelector fields.Selector
158
163
159
164
// 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 .
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
- // 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
170
177
171
178
// 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.
172
180
ByObject map [client.Object ]ByObject
173
181
}
174
182
175
183
// ByObject offers more fine-grained control over the cache's ListWatch by object.
176
184
type 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
+
177
202
// Label represents a label selector for the object.
178
203
Label labels.Selector
179
204
@@ -194,48 +219,118 @@ type ByObject struct {
194
219
UnsafeDisableDeepCopy * bool
195
220
}
196
221
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
+
197
248
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
198
249
type NewCacheFunc func (config * rest.Config , opts Options ) (Cache , error )
199
250
200
251
// 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
204
256
}
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 )
207
266
}
208
267
209
- opts , err := defaultOpts (config , opts )
210
- if err != nil {
211
- return nil , err
268
+ if len (opts .ByObject ) == 0 {
269
+ return defaultCache , nil
212
270
}
213
271
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
217
290
}
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 ,
224
299
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 ,
226
310
}
311
+ }
227
312
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
+ }
239
334
}
240
335
241
336
func defaultOpts (config * rest.Config , opts Options ) (Options , error ) {
@@ -262,31 +357,70 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
262
357
}
263
358
}
264
359
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
+
265
404
// Default the resync period to 10 hours if unset
266
405
if opts .SyncPeriod == nil {
267
406
opts .SyncPeriod = & defaultSyncPeriod
268
407
}
269
408
return opts , nil
270
409
}
271
410
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
290
414
}
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
292
426
}
0 commit comments