1
2
3
4 package fieldspec_test
5
6 import (
7 "bytes"
8 "strings"
9 "testing"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 "sigs.k8s.io/kustomize/api/filters/fieldspec"
14 "sigs.k8s.io/kustomize/api/filters/filtersutil"
15 "sigs.k8s.io/kustomize/kyaml/kio"
16 "sigs.k8s.io/kustomize/kyaml/yaml"
17 )
18
19 func TestFilter_Filter(t *testing.T) {
20 testCases := map[string]struct {
21 input string
22 expected string
23 filter fieldspec.Filter
24 fieldSpec string
25 error string
26 }{
27 "path not found": {
28 fieldSpec: `
29 path: a/b
30 group: foo
31 kind: Bar
32 `,
33 input: `
34 apiVersion: foo
35 kind: Bar
36 xxx:
37 `,
38 expected: `
39 apiVersion: foo
40 kind: Bar
41 xxx:
42 `,
43 filter: fieldspec.Filter{
44 SetValue: filtersutil.SetScalar("e"),
45 },
46 },
47 "empty path": {
48 fieldSpec: `
49 group: foo
50 version: v1
51 kind: Bar
52 `,
53 input: `
54 apiVersion: foo/v1
55 kind: Bar
56 xxx:
57 `,
58 expected: `
59 apiVersion: foo
60 kind: Bar
61 xxx:
62 `,
63 error: `considering field '' of object Bar.v1.foo/[noName].[noNs]: cannot set or create an empty field name`,
64 filter: fieldspec.Filter{
65 SetValue: filtersutil.SetScalar("e"),
66 },
67 },
68
69 "update": {
70 fieldSpec: `
71 path: a/b
72 group: foo
73 kind: Bar
74 `,
75 input: `
76 apiVersion: foo/v1beta1
77 kind: Bar
78 a:
79 b: c
80 `,
81 expected: `
82 apiVersion: foo/v1beta1
83 kind: Bar
84 a:
85 b: e
86 `,
87 filter: fieldspec.Filter{
88 SetValue: filtersutil.SetScalar("e"),
89 },
90 },
91
92 "update-kind-not-match": {
93 fieldSpec: `
94 path: a/b
95 group: foo
96 kind: Bar1
97 `,
98 input: `
99 apiVersion: foo/v1beta1
100 kind: Bar2
101 a:
102 b: c
103 `,
104 expected: `
105 apiVersion: foo/v1beta1
106 kind: Bar2
107 a:
108 b: c
109 `,
110 filter: fieldspec.Filter{
111 SetValue: filtersutil.SetScalar("e"),
112 },
113 },
114
115 "update-group-not-match": {
116 fieldSpec: `
117 path: a/b
118 group: foo1
119 kind: Bar
120 `,
121 input: `
122 apiVersion: foo2/v1beta1
123 kind: Bar
124 a:
125 b: c
126 `,
127 expected: `
128 apiVersion: foo2/v1beta1
129 kind: Bar
130 a:
131 b: c
132 `,
133 filter: fieldspec.Filter{
134 SetValue: filtersutil.SetScalar("e"),
135 },
136 },
137
138 "update-version-not-match": {
139 fieldSpec: `
140 path: a/b
141 group: foo
142 version: v1beta1
143 kind: Bar
144 `,
145 input: `
146 apiVersion: foo/v1beta2
147 kind: Bar
148 a:
149 b: c
150 `,
151 expected: `
152 apiVersion: foo/v1beta2
153 kind: Bar
154 a:
155 b: c
156 `,
157 filter: fieldspec.Filter{
158 SetValue: filtersutil.SetScalar("e"),
159 },
160 },
161
162 "bad-version": {
163 fieldSpec: `
164 path: a/b
165 group: foo
166 version: v1beta1
167 kind: Bar
168 `,
169 input: `
170 apiVersion: foo/v1beta2/something
171 kind: Bar
172 a:
173 b: c
174 `,
175 expected: `
176 apiVersion: foo/v1beta2/something
177 kind: Bar
178 a:
179 b: c
180 `,
181 filter: fieldspec.Filter{
182 SetValue: filtersutil.SetScalar("e"),
183 },
184 },
185
186 "bad-meta": {
187 fieldSpec: `
188 path: a/b
189 group: foo
190 version: v1beta1
191 kind: Bar
192 `,
193 input: `
194 a:
195 b: c
196 `,
197 expected: `
198 a:
199 b: c
200 `,
201 filter: fieldspec.Filter{
202 SetValue: filtersutil.SetScalar("e"),
203 },
204 },
205
206 "miss-match-type": {
207 fieldSpec: `
208 path: a/b/c
209 kind: Bar
210 `,
211 input: `
212 kind: Bar
213 a:
214 b: a
215 `,
216 error: `considering field 'a/b/c' of object Bar.[noVer].[noGrp]/[noName].[noNs]: expected sequence or mapping node`,
217 filter: fieldspec.Filter{
218 SetValue: filtersutil.SetScalar("e"),
219 },
220 },
221
222 "add": {
223 fieldSpec: `
224 path: a/b/c/d
225 group: foo
226 create: true
227 kind: Bar
228 `,
229 input: `
230 apiVersion: foo/v1beta1
231 kind: Bar
232 a: {}
233 `,
234 expected: `
235 apiVersion: foo/v1beta1
236 kind: Bar
237 a: {b: {c: {d: e}}}
238 `,
239 filter: fieldspec.Filter{
240 SetValue: filtersutil.SetScalar("e"),
241 CreateKind: yaml.ScalarNode,
242 },
243 },
244
245 "update-in-sequence": {
246 fieldSpec: `
247 path: a/b[]/c/d
248 group: foo
249 kind: Bar
250 `,
251 input: `
252 apiVersion: foo/v1beta1
253 kind: Bar
254 a:
255 b:
256 - c:
257 d: a
258 `,
259 expected: `
260 apiVersion: foo/v1beta1
261 kind: Bar
262 a:
263 b:
264 - c:
265 d: e
266 `,
267 filter: fieldspec.Filter{
268 SetValue: filtersutil.SetScalar("e"),
269 },
270 },
271
272
273 "empty-sequence-no-create": {
274 fieldSpec: `
275 path: a/b[]/c/d
276 group: foo
277 create: true
278 kind: Bar
279 `,
280 input: `
281 apiVersion: foo/v1beta1
282 kind: Bar
283 a: {}
284 `,
285 expected: `
286 apiVersion: foo/v1beta1
287 kind: Bar
288 a: {}
289 `,
290 filter: fieldspec.Filter{
291 SetValue: filtersutil.SetScalar("e"),
292 CreateKind: yaml.ScalarNode,
293 },
294 },
295
296
297 "empty-sequence-create": {
298 fieldSpec: `
299 path: a/b[]/c/d
300 group: foo
301 create: true
302 kind: Bar
303 `,
304 input: `
305 apiVersion: foo/v1beta1
306 kind: Bar
307 a:
308 b:
309 - c: {}
310 `,
311 expected: `
312 apiVersion: foo/v1beta1
313 kind: Bar
314 a:
315 b:
316 - c: {d: e}
317 `,
318 filter: fieldspec.Filter{
319 SetValue: filtersutil.SetScalar("e"),
320 CreateKind: yaml.ScalarNode,
321 },
322 },
323
324 "group v1": {
325 fieldSpec: `
326 path: a/b
327 group: v1
328 create: true
329 kind: Bar
330 `,
331 input: `
332 apiVersion: v1
333 kind: Bar
334 `,
335 expected: `
336 apiVersion: v1
337 kind: Bar
338 `,
339 filter: fieldspec.Filter{
340 SetValue: filtersutil.SetScalar("e"),
341 CreateKind: yaml.ScalarNode,
342 },
343 },
344
345 "version v1": {
346 fieldSpec: `
347 path: a/b
348 version: v1
349 create: true
350 kind: Bar
351 `,
352 input: `
353 apiVersion: v1
354 kind: Bar
355 `,
356 expected: `
357 apiVersion: v1
358 kind: Bar
359 a:
360 b: e
361 `,
362 filter: fieldspec.Filter{
363 SetValue: filtersutil.SetScalar("e"),
364 CreateKind: yaml.ScalarNode,
365 },
366 },
367
368 "successfully set field on array entry no sequence hint": {
369 fieldSpec: `
370 path: spec/containers/image
371 version: v1
372 kind: Bar
373 `,
374 input: `
375 apiVersion: v1
376 kind: Bar
377 spec:
378 containers:
379 - image: foo
380 `,
381 expected: `
382 apiVersion: v1
383 kind: Bar
384 spec:
385 containers:
386 - image: bar
387 `,
388 filter: fieldspec.Filter{
389 SetValue: filtersutil.SetScalar("bar"),
390 CreateKind: yaml.ScalarNode,
391 },
392 },
393
394 "successfully set field on array entry with sequence hint": {
395 fieldSpec: `
396 path: spec/containers[]/image
397 version: v1
398 kind: Bar
399 `,
400 input: `
401 apiVersion: v1
402 kind: Bar
403 spec:
404 containers:
405 - image: foo
406 `,
407 expected: `
408 apiVersion: v1
409 kind: Bar
410 spec:
411 containers:
412 - image: bar
413 `,
414 filter: fieldspec.Filter{
415 SetValue: filtersutil.SetScalar("bar"),
416 CreateKind: yaml.ScalarNode,
417 },
418 },
419 "failure to set field on array entry with sequence hint in path": {
420 fieldSpec: `
421 path: spec/containers[]/image
422 version: v1
423 kind: Bar
424 `,
425 input: `
426 apiVersion: v1
427 kind: Bar
428 spec:
429 containers:
430 `,
431 expected: `
432 apiVersion: v1
433 kind: Bar
434 spec:
435 containers: []
436 `,
437 filter: fieldspec.Filter{
438 SetValue: filtersutil.SetScalar("bar"),
439 CreateKind: yaml.ScalarNode,
440 },
441 },
442
443 "failure to set field on array entry, no sequence hint in path": {
444 fieldSpec: `
445 path: spec/containers/image
446 version: v1
447 kind: Bar
448 `,
449 input: `
450 apiVersion: v1
451 kind: Bar
452 spec:
453 containers:
454 `,
455 expected: `
456 apiVersion: v1
457 kind: Bar
458 spec:
459 containers:
460 `,
461 filter: fieldspec.Filter{
462 SetValue: filtersutil.SetScalar("bar"),
463 CreateKind: yaml.ScalarNode,
464 },
465 },
466 "fieldname with slash '/'": {
467 fieldSpec: `
468 path: a/b\/c/d
469 version: v1
470 kind: Bar
471 `,
472 input: `
473 apiVersion: v1
474 kind: Bar
475 a:
476 b/c:
477 d: foo
478 `,
479 expected: `
480 apiVersion: v1
481 kind: Bar
482 a:
483 b/c:
484 d: bar
485 `,
486 filter: fieldspec.Filter{
487 SetValue: filtersutil.SetScalar("bar"),
488 CreateKind: yaml.ScalarNode,
489 },
490 },
491 "fieldname with multiple '/'": {
492 fieldSpec: `
493 path: a/b\/c/d\/e/f
494 version: v1
495 kind: Bar
496 `,
497 input: `
498 apiVersion: v1
499 kind: Bar
500 a:
501 b/c:
502 d/e:
503 f: foo
504 `,
505 expected: `
506 apiVersion: v1
507 kind: Bar
508 a:
509 b/c:
510 d/e:
511 f: bar
512 `,
513 filter: fieldspec.Filter{
514 SetValue: filtersutil.SetScalar("bar"),
515 CreateKind: yaml.ScalarNode,
516 },
517 },
518 }
519
520 for n := range testCases {
521 tc := testCases[n]
522 t.Run(n, func(t *testing.T) {
523 err := yaml.Unmarshal([]byte(tc.fieldSpec), &tc.filter.FieldSpec)
524 if !assert.NoError(t, err) {
525 t.FailNow()
526 }
527
528 out := &bytes.Buffer{}
529 rw := &kio.ByteReadWriter{
530 Reader: bytes.NewBufferString(tc.input),
531 Writer: out,
532 OmitReaderAnnotations: true,
533 }
534
535
536 err = kio.Pipeline{
537 Inputs: []kio.Reader{rw},
538 Filters: []kio.Filter{kio.FilterAll(tc.filter)},
539 Outputs: []kio.Writer{rw},
540 }.Execute()
541 if tc.error != "" {
542 if !assert.EqualError(t, err, tc.error) {
543 t.FailNow()
544 }
545
546 return
547 }
548
549 if !assert.NoError(t, err) {
550 t.FailNow()
551 }
552
553
554 if !assert.Equal(t,
555 strings.TrimSpace(tc.expected),
556 strings.TrimSpace(out.String())) {
557 t.FailNow()
558 }
559 })
560 }
561 }
562
563 func TestFilter_FieldPaths(t *testing.T) {
564 testCases := map[string]struct {
565 input string
566 fieldSpec string
567 expected []string
568 }{
569 "fieldpath containing SequenceNode": {
570 input: `
571 apiVersion: v1
572 kind: Pod
573 metadata:
574 name: app
575 spec:
576 containers:
577 - name: store
578 image: redis:6.2.6
579 - name: server
580 image: nginx:latest
581 `,
582 fieldSpec: `
583 path: spec/containers[]/image
584 kind: Pod
585 `,
586 expected: []string{
587 "spec.containers.image",
588 "spec.containers.image",
589 },
590 },
591 "fieldpath with MappingNode": {
592 input: `
593 apiVersion: v1
594 kind: Pod
595 metadata:
596 name: app
597 spec:
598 containers:
599 - name: store
600 image: redis:6.2.6
601 - name: server
602 image: nginx:latest
603 `,
604 fieldSpec: `
605 path: metadata/name
606 kind: Pod
607 `,
608 expected: []string{
609 "metadata.name",
610 },
611 },
612 }
613 for name, tc := range testCases {
614 var fieldPaths []string
615 trackableSetter := filtersutil.TrackableSetter{}
616 trackableSetter.WithMutationTracker(func(key, value, tag string, node *yaml.RNode) {
617 fieldPaths = append(fieldPaths, strings.Join(node.FieldPath(), "."))
618 })
619 filter := fieldspec.Filter{
620 SetValue: trackableSetter.SetScalar("foo"),
621 }
622
623 t.Run(name, func(t *testing.T) {
624 err := yaml.Unmarshal([]byte(tc.fieldSpec), &filter.FieldSpec)
625 require.NoError(t, err)
626 rw := &kio.ByteReadWriter{
627 Reader: bytes.NewBufferString(tc.input),
628 Writer: &bytes.Buffer{},
629 OmitReaderAnnotations: true,
630 }
631
632
633 err = kio.Pipeline{
634 Inputs: []kio.Reader{rw},
635 Filters: []kio.Filter{kio.FilterAll(filter)},
636 Outputs: []kio.Writer{rw},
637 }.Execute()
638
639 require.NoError(t, err)
640 assert.Equal(t, tc.expected, fieldPaths)
641 })
642 }
643 }
644
View as plain text