@@ -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,35 @@ 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 because either the value as a
188
+ // whole is nil or because the specific setting is nil 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
+ // This must be unset for cluster-scoped objects.
195
+ Namespaces map [string ]Config
196
+
177
197
// Label represents a label selector for the object.
178
198
Label labels.Selector
179
199
@@ -194,48 +214,111 @@ type ByObject struct {
194
214
UnsafeDisableDeepCopy * bool
195
215
}
196
216
217
+ // Config describes all potential options for a given watch.
218
+ type Config struct {
219
+ // LabelSelector specifies a label selector. A nil value allows to
220
+ // default this.
221
+ LabelSelector labels.Selector
222
+
223
+ // FieldSelector specifics a field selector. A nil value allows to
224
+ // default this.
225
+ FieldSelector fields.Selector
226
+
227
+ // Transform specifies a transform func. A nil value allows to default
228
+ // this.
229
+ Transform toolscache.TransformFunc
230
+
231
+ // UnsafeDisableDeepCopy specifies if List and Get requests against the
232
+ // cache should not DeepCopy. A nil value allows to default this.
233
+ UnsafeDisableDeepCopy * bool
234
+ }
235
+
197
236
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
198
237
type NewCacheFunc func (config * rest.Config , opts Options ) (Cache , error )
199
238
200
239
// 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 }
240
+ func New (cfg * rest.Config , opts Options ) (Cache , error ) {
241
+ opts , err := defaultOpts (cfg , opts )
242
+ if err != nil {
243
+ return nil , err
204
244
}
205
- if len (opts .Namespaces ) > 1 {
206
- return newMultiNamespaceCache (config , opts )
245
+
246
+ newCache := newCache (cfg , opts )
247
+
248
+ var defaultCache Cache
249
+ if len (opts .DefaultNamespaces ) > 0 {
250
+ defaultConfig := optionDefaultsToConfig (& opts )
251
+ defaultCache = newMultiNamespaceCache (newCache , opts .Scheme , opts .Mapper , opts .DefaultNamespaces , & defaultConfig )
252
+ } else {
253
+ defaultCache = newCache (optionDefaultsToConfig (& opts ), corev1 .NamespaceAll )
207
254
}
208
255
209
- opts , err := defaultOpts (config , opts )
210
- if err != nil {
211
- return nil , err
256
+ if len (opts .ByObject ) == 0 {
257
+ return defaultCache , nil
212
258
}
213
259
214
- byGVK , err := convertToInformerOptsByGVK (opts .ByObject , opts .Scheme )
215
- if err != nil {
216
- return nil , err
260
+ delegating := & delegatingByTypeCache {
261
+ scheme : opts .Scheme ,
262
+ caches : make (map [schema.GroupVersionKind ]Cache , len (opts .ByObject )),
263
+ defaultCache : defaultCache ,
217
264
}
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
- },
265
+
266
+ for obj , config := range opts .ByObject {
267
+ gvk , err := apiutil .GVKForObject (obj , opts .Scheme )
268
+ if err != nil {
269
+ return nil , fmt .Errorf ("failed to get GVK for type %T: %w" , obj , err )
270
+ }
271
+ var cache Cache
272
+ if len (config .Namespaces ) > 0 {
273
+ cache = newMultiNamespaceCache (newCache , opts .Scheme , opts .Mapper , config .Namespaces , nil )
274
+ } else {
275
+ cache = newCache (byObjectToConfig (config ), corev1 .NamespaceAll )
276
+ }
277
+ delegating .caches [gvk ] = cache
278
+ }
279
+
280
+ return delegating , nil
281
+ }
282
+
283
+ func optionDefaultsToConfig (opts * Options ) Config {
284
+ return Config {
285
+ LabelSelector : opts .DefaultLabelSelector ,
286
+ FieldSelector : opts .DefaultFieldSelector ,
224
287
Transform : opts .DefaultTransform ,
225
- UnsafeDisableDeepCopy : opts .UnsafeDisableDeepCopy ,
288
+ UnsafeDisableDeepCopy : opts .DefaultUnsafeDisableDeepCopy ,
226
289
}
290
+ }
291
+
292
+ func byObjectToConfig (byObject ByObject ) Config {
293
+ return Config {
294
+ LabelSelector : byObject .Label ,
295
+ FieldSelector : byObject .Field ,
296
+ Transform : byObject .Transform ,
297
+ UnsafeDisableDeepCopy : byObject .UnsafeDisableDeepCopy ,
298
+ }
299
+ }
227
300
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
301
+ type newCacheFunc func (config Config , namespace string ) Cache
302
+
303
+ func newCache (restConfig * rest.Config , opts Options ) newCacheFunc {
304
+ return func (config Config , namespace string ) Cache {
305
+ return & informerCache {
306
+ scheme : opts .Scheme ,
307
+ Informers : internal .NewInformers (restConfig , & internal.InformersOpts {
308
+ HTTPClient : opts .HTTPClient ,
309
+ Scheme : opts .Scheme ,
310
+ Mapper : opts .Mapper ,
311
+ ResyncPeriod : * opts .SyncPeriod ,
312
+ Namespace : namespace ,
313
+ Selector : internal.Selector {
314
+ Label : config .LabelSelector ,
315
+ Field : config .FieldSelector ,
316
+ },
317
+ Transform : config .Transform ,
318
+ UnsafeDisableDeepCopy : pointer .BoolDeref (config .UnsafeDisableDeepCopy , false ),
319
+ }),
320
+ }
321
+ }
239
322
}
240
323
241
324
func defaultOpts (config * rest.Config , opts Options ) (Options , error ) {
@@ -262,31 +345,70 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
262
345
}
263
346
}
264
347
348
+ for namespace , cfg := range opts .DefaultNamespaces {
349
+ cfg = defaultConfig (cfg , optionDefaultsToConfig (& opts ))
350
+ opts .DefaultNamespaces [namespace ] = cfg
351
+ }
352
+
353
+ for obj , cfg := range opts .ByObject {
354
+ isNamespaced , err := apiutil .IsObjectNamespaced (obj , opts .Scheme , opts .Mapper )
355
+ if err != nil {
356
+ return opts , fmt .Errorf ("failed to determine if %T is namespaced: %w" , obj , err )
357
+ }
358
+ if ! isNamespaced && cfg .Namespaces != nil {
359
+ return opts , fmt .Errorf ("type %T is not namespaced, but its ByObject.Namespaces setting is not nil" , obj )
360
+ }
361
+
362
+ // Default the namespace-level configs first, because they need to use the undefaulted type-level config.
363
+ for namespace , config := range cfg .Namespaces {
364
+ // 1. Default from the undefaulted type-level config
365
+ config = defaultConfig (config , byObjectToConfig (cfg ))
366
+
367
+ // 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
368
+ // might not have an entry for the current namespace.
369
+ if defaultNamespaceSettings , hasDefaultNamespace := opts .DefaultNamespaces [namespace ]; hasDefaultNamespace {
370
+ config = defaultConfig (config , defaultNamespaceSettings )
371
+ }
372
+
373
+ // 3. Default from the global defaults
374
+ config = defaultConfig (config , optionDefaultsToConfig (& opts ))
375
+
376
+ cfg .Namespaces [namespace ] = config
377
+ }
378
+
379
+ defaultedConfig := defaultConfig (byObjectToConfig (cfg ), optionDefaultsToConfig (& opts ))
380
+ cfg .Label = defaultedConfig .LabelSelector
381
+ cfg .Field = defaultedConfig .FieldSelector
382
+ cfg .Transform = defaultedConfig .Transform
383
+ cfg .UnsafeDisableDeepCopy = defaultedConfig .UnsafeDisableDeepCopy
384
+
385
+ if cfg .Namespaces == nil {
386
+ cfg .Namespaces = opts .DefaultNamespaces
387
+ }
388
+
389
+ opts .ByObject [obj ] = cfg
390
+ }
391
+
265
392
// Default the resync period to 10 hours if unset
266
393
if opts .SyncPeriod == nil {
267
394
opts .SyncPeriod = & defaultSyncPeriod
268
395
}
269
396
return opts , nil
270
397
}
271
398
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
- }
399
+ func defaultConfig (toDefault , defaultFrom Config ) Config {
400
+ if toDefault .LabelSelector == nil {
401
+ toDefault .LabelSelector = defaultFrom .LabelSelector
402
+ }
403
+ if toDefault .FieldSelector == nil {
404
+ toDefault .FieldSelector = defaultFrom .FieldSelector
290
405
}
291
- return out , nil
406
+ if toDefault .Transform == nil {
407
+ toDefault .Transform = defaultFrom .Transform
408
+ }
409
+ if toDefault .UnsafeDisableDeepCopy == nil {
410
+ toDefault .UnsafeDisableDeepCopy = defaultFrom .UnsafeDisableDeepCopy
411
+ }
412
+
413
+ return toDefault
292
414
}
0 commit comments