1
16
17 package cache
18
19 import (
20 "reflect"
21 "testing"
22 "time"
23
24 "github.com/google/go-cmp/cmp"
25 "github.com/google/go-cmp/cmp/cmpopts"
26 fuzz "github.com/google/gofuzz"
27 corev1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/api/meta"
29 "k8s.io/apimachinery/pkg/fields"
30 "k8s.io/apimachinery/pkg/labels"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 "k8s.io/client-go/rest"
33 "k8s.io/client-go/tools/cache"
34 "k8s.io/utils/ptr"
35 "sigs.k8s.io/controller-runtime/pkg/client"
36 )
37
38 func TestDefaultOpts(t *testing.T) {
39 t.Parallel()
40
41 pod := &corev1.Pod{}
42
43 compare := func(a, b any) string {
44 return cmp.Diff(a, b,
45 cmpopts.IgnoreUnexported(Options{}),
46 cmpopts.IgnoreFields(Options{}, "HTTPClient", "Scheme", "Mapper", "SyncPeriod"),
47 cmp.Comparer(func(a, b fields.Selector) bool {
48 if (a != nil) != (b != nil) {
49 return false
50 }
51 if a == nil {
52 return true
53 }
54 return a.String() == b.String()
55 }),
56 )
57 }
58 testCases := []struct {
59 name string
60 in Options
61
62 verification func(Options) string
63 }{
64 {
65 name: "ByObject.Namespaces gets defaulted from ByObject",
66 in: Options{
67 ByObject: map[client.Object]ByObject{pod: {
68 Namespaces: map[string]Config{
69 "default": {},
70 },
71 Label: labels.SelectorFromSet(map[string]string{"from": "by-object"}),
72 }},
73 DefaultNamespaces: map[string]Config{
74 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-namespaces"})},
75 },
76 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
77 },
78
79 verification: func(o Options) string {
80 expected := map[string]Config{
81 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "by-object"})},
82 }
83 return cmp.Diff(expected, o.ByObject[pod].Namespaces)
84 },
85 },
86 {
87 name: "ByObject.Namespaces gets defaulted from DefaultNamespaces",
88 in: Options{
89 ByObject: map[client.Object]ByObject{pod: {
90 Namespaces: map[string]Config{
91 "default": {},
92 },
93 }},
94 DefaultNamespaces: map[string]Config{
95 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-namespaces"})},
96 },
97 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
98 },
99
100 verification: func(o Options) string {
101 expected := map[string]Config{
102 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-namespaces"})},
103 }
104 return cmp.Diff(expected, o.ByObject[pod].Namespaces)
105 },
106 },
107 {
108 name: "ByObject.Namespaces gets defaulted from DefaultLabelSelector",
109 in: Options{
110 ByObject: map[client.Object]ByObject{pod: {
111 Namespaces: map[string]Config{
112 "default": {},
113 },
114 }},
115 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
116 },
117
118 verification: func(o Options) string {
119 expected := map[string]Config{
120 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"})},
121 }
122 return cmp.Diff(expected, o.ByObject[pod].Namespaces)
123 },
124 },
125 {
126 name: "ByObject.Namespaces gets defaulted from DefaultNamespaces",
127 in: Options{
128 ByObject: map[client.Object]ByObject{pod: {}},
129 DefaultNamespaces: map[string]Config{
130 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-namespaces"})},
131 },
132 },
133
134 verification: func(o Options) string {
135 expected := map[string]Config{
136 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-namespaces"})},
137 }
138 return cmp.Diff(expected, o.ByObject[pod].Namespaces)
139 },
140 },
141 {
142 name: "ByObject.Namespaces doesn't get defaulted when its empty",
143 in: Options{
144 ByObject: map[client.Object]ByObject{pod: {Namespaces: map[string]Config{}}},
145 DefaultNamespaces: map[string]Config{
146 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-namespaces"})},
147 },
148 },
149
150 verification: func(o Options) string {
151 expected := map[string]Config{}
152 return cmp.Diff(expected, o.ByObject[pod].Namespaces)
153 },
154 },
155 {
156 name: "ByObject.Labels gets defaulted from DefautLabelSelector",
157 in: Options{
158 ByObject: map[client.Object]ByObject{pod: {}},
159 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
160 },
161
162 verification: func(o Options) string {
163 expected := labels.SelectorFromSet(map[string]string{"from": "default-label-selector"})
164 return cmp.Diff(expected, o.ByObject[pod].Label)
165 },
166 },
167 {
168 name: "ByObject.Labels doesn't get defaulted when set",
169 in: Options{
170 ByObject: map[client.Object]ByObject{pod: {Label: labels.SelectorFromSet(map[string]string{"from": "by-object"})}},
171 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
172 },
173
174 verification: func(o Options) string {
175 expected := labels.SelectorFromSet(map[string]string{"from": "by-object"})
176 return cmp.Diff(expected, o.ByObject[pod].Label)
177 },
178 },
179 {
180 name: "ByObject.Fields gets defaulted from DefaultFieldSelector",
181 in: Options{
182 ByObject: map[client.Object]ByObject{pod: {}},
183 DefaultFieldSelector: fields.SelectorFromSet(map[string]string{"from": "default-field-selector"}),
184 },
185
186 verification: func(o Options) string {
187 expected := fields.SelectorFromSet(map[string]string{"from": "default-field-selector"})
188 return cmp.Diff(expected, o.ByObject[pod].Field, cmp.Exporter(func(reflect.Type) bool { return true }))
189 },
190 },
191 {
192 name: "ByObject.Fields doesn't get defaulted when set",
193 in: Options{
194 ByObject: map[client.Object]ByObject{pod: {Field: fields.SelectorFromSet(map[string]string{"from": "by-object"})}},
195 DefaultFieldSelector: fields.SelectorFromSet(map[string]string{"from": "default-field-selector"}),
196 },
197
198 verification: func(o Options) string {
199 expected := fields.SelectorFromSet(map[string]string{"from": "by-object"})
200 return cmp.Diff(expected, o.ByObject[pod].Field, cmp.Exporter(func(reflect.Type) bool { return true }))
201 },
202 },
203 {
204 name: "ByObject.UnsafeDisableDeepCopy gets defaulted from DefaultUnsafeDisableDeepCopy",
205 in: Options{
206 ByObject: map[client.Object]ByObject{pod: {}},
207 DefaultUnsafeDisableDeepCopy: ptr.To(true),
208 },
209
210 verification: func(o Options) string {
211 expected := ptr.To(true)
212 return cmp.Diff(expected, o.ByObject[pod].UnsafeDisableDeepCopy)
213 },
214 },
215 {
216 name: "ByObject.UnsafeDisableDeepCopy doesn't get defaulted when set",
217 in: Options{
218 ByObject: map[client.Object]ByObject{pod: {UnsafeDisableDeepCopy: ptr.To(false)}},
219 DefaultUnsafeDisableDeepCopy: ptr.To(true),
220 },
221
222 verification: func(o Options) string {
223 expected := ptr.To(false)
224 return cmp.Diff(expected, o.ByObject[pod].UnsafeDisableDeepCopy)
225 },
226 },
227 {
228 name: "DefaultNamespace label selector gets defaulted from DefaultLabelSelector",
229 in: Options{
230 DefaultNamespaces: map[string]Config{"default": {}},
231 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
232 },
233
234 verification: func(o Options) string {
235 expected := map[string]Config{
236 "default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"})},
237 }
238 return cmp.Diff(expected, o.DefaultNamespaces)
239 },
240 },
241 {
242 name: "ByObject.Namespaces get selector from DefaultNamespaces before DefaultSelector",
243 in: Options{
244 ByObject: map[client.Object]ByObject{
245 pod: {Namespaces: map[string]Config{"default": {}}},
246 },
247 DefaultNamespaces: map[string]Config{"default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "namespace"})}},
248 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default"}),
249 },
250
251 verification: func(o Options) string {
252 expected := Options{
253 ByObject: map[client.Object]ByObject{
254 pod: {Namespaces: map[string]Config{"default": {
255 LabelSelector: labels.SelectorFromSet(map[string]string{"from": "namespace"}),
256 }}},
257 },
258 DefaultNamespaces: map[string]Config{"default": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "namespace"})}},
259 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default"}),
260 }
261
262 return compare(expected, o)
263 },
264 },
265 {
266 name: "Two namespaces in DefaultNamespaces with custom selection logic",
267 in: Options{DefaultNamespaces: map[string]Config{
268 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
269 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
270 "": {},
271 }},
272
273 verification: func(o Options) string {
274 expected := Options{
275 DefaultNamespaces: map[string]Config{
276 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
277 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
278 "": {FieldSelector: fields.ParseSelectorOrDie("metadata.namespace!=kube-public,metadata.namespace!=kube-system")},
279 },
280 }
281
282 return compare(expected, o)
283 },
284 },
285 {
286 name: "Two namespaces in DefaultNamespaces with custom selection logic and namespace default has its own field selector",
287 in: Options{DefaultNamespaces: map[string]Config{
288 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
289 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
290 "": {FieldSelector: fields.ParseSelectorOrDie("spec.nodeName=foo")},
291 }},
292
293 verification: func(o Options) string {
294 expected := Options{
295 DefaultNamespaces: map[string]Config{
296 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
297 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
298 "": {FieldSelector: fields.ParseSelectorOrDie(
299 "metadata.namespace!=kube-public,metadata.namespace!=kube-system,spec.nodeName=foo",
300 )},
301 },
302 }
303
304 return compare(expected, o)
305 },
306 },
307 {
308 name: "Two namespaces in ByObject.Namespaces with custom selection logic",
309 in: Options{ByObject: map[client.Object]ByObject{pod: {
310 Namespaces: map[string]Config{
311 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
312 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
313 "": {},
314 },
315 }}},
316
317 verification: func(o Options) string {
318 expected := Options{ByObject: map[client.Object]ByObject{pod: {
319 Namespaces: map[string]Config{
320 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
321 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
322 "": {FieldSelector: fields.ParseSelectorOrDie(
323 "metadata.namespace!=kube-public,metadata.namespace!=kube-system",
324 )},
325 },
326 }}}
327
328 return compare(expected, o)
329 },
330 },
331 {
332 name: "Two namespaces in ByObject.Namespaces with custom selection logic and namespace default has its own field selector",
333 in: Options{ByObject: map[client.Object]ByObject{pod: {
334 Namespaces: map[string]Config{
335 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
336 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
337 "": {FieldSelector: fields.ParseSelectorOrDie("spec.nodeName=foo")},
338 },
339 }}},
340
341 verification: func(o Options) string {
342 expected := Options{ByObject: map[client.Object]ByObject{pod: {
343 Namespaces: map[string]Config{
344 "kube-public": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-public"})},
345 "kube-system": {LabelSelector: labels.SelectorFromSet(map[string]string{"from": "kube-system"})},
346 "": {FieldSelector: fields.ParseSelectorOrDie(
347 "metadata.namespace!=kube-public,metadata.namespace!=kube-system,spec.nodeName=foo",
348 )},
349 },
350 }}}
351
352 return compare(expected, o)
353 },
354 },
355 {
356 name: "DefaultNamespace label selector doesn't get defaulted when set",
357 in: Options{
358 DefaultNamespaces: map[string]Config{"default": {LabelSelector: labels.Everything()}},
359 DefaultLabelSelector: labels.SelectorFromSet(map[string]string{"from": "default-label-selector"}),
360 },
361
362 verification: func(o Options) string {
363 expected := map[string]Config{
364 "default": {LabelSelector: labels.Everything()},
365 }
366 return cmp.Diff(expected, o.DefaultNamespaces)
367 },
368 },
369 {
370 name: "Defaulted namespaces in ByObject contain ByObject's selector",
371 in: Options{
372 ByObject: map[client.Object]ByObject{
373 pod: {Label: labels.SelectorFromSet(map[string]string{"from": "pod"})},
374 },
375 DefaultNamespaces: map[string]Config{"default": {}},
376 },
377 verification: func(o Options) string {
378 expected := Options{
379 ByObject: map[client.Object]ByObject{
380 pod: {
381 Label: labels.SelectorFromSet(map[string]string{"from": "pod"}),
382 Namespaces: map[string]Config{"default": {
383 LabelSelector: labels.SelectorFromSet(map[string]string{"from": "pod"}),
384 }},
385 },
386 },
387
388 DefaultNamespaces: map[string]Config{"default": {}},
389 }
390 return compare(expected, o)
391 },
392 },
393 }
394
395 for _, tc := range testCases {
396 t.Run(tc.name, func(t *testing.T) {
397 tc.in.Mapper = &fakeRESTMapper{}
398
399 defaulted, err := defaultOpts(&rest.Config{}, tc.in)
400 if err != nil {
401 t.Fatal(err)
402 }
403
404 if diff := tc.verification(defaulted); diff != "" {
405 t.Errorf("expected config differs from actual: %s", diff)
406 }
407 })
408 }
409 }
410
411 type fakeRESTMapper struct {
412 meta.RESTMapper
413 }
414
415 func (f *fakeRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
416 return &meta.RESTMapping{Scope: meta.RESTScopeNamespace}, nil
417 }
418
419 func TestDefaultConfigConsidersAllFields(t *testing.T) {
420 t.Parallel()
421 seed := time.Now().UnixNano()
422 t.Logf("Seed is %d", seed)
423 f := fuzz.NewWithSeed(seed).Funcs(
424 func(ls *labels.Selector, _ fuzz.Continue) {
425 *ls = labels.SelectorFromSet(map[string]string{"foo": "bar"})
426 },
427 func(fs *fields.Selector, _ fuzz.Continue) {
428 *fs = fields.SelectorFromSet(map[string]string{"foo": "bar"})
429 },
430 func(tf *cache.TransformFunc, _ fuzz.Continue) {
431
432 },
433 )
434
435 for i := 0; i < 100; i++ {
436 fuzzed := Config{}
437 f.Fuzz(&fuzzed)
438
439 defaulted := defaultConfig(Config{}, fuzzed)
440
441 if diff := cmp.Diff(fuzzed, defaulted, cmp.Exporter(func(reflect.Type) bool { return true })); diff != "" {
442 t.Errorf("Defaulted config doesn't match fuzzed one: %s", diff)
443 }
444 }
445 }
446
View as plain text