1
2
3
4 package kioutil_test
5
6 import (
7 "bytes"
8 "math/rand"
9 "strings"
10 "testing"
11 "time"
12
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15 "sigs.k8s.io/kustomize/kyaml/kio"
16 "sigs.k8s.io/kustomize/kyaml/kio/kioutil"
17 "sigs.k8s.io/kustomize/kyaml/yaml"
18 )
19
20 func TestSortNodes_moreThan10(t *testing.T) {
21 input := `
22 a: b
23 ---
24 c: d
25 ---
26 e: f
27 ---
28 g: h
29 ---
30 i: j
31 ---
32 k: l
33 ---
34 m: n
35 ---
36 o: p
37 ---
38 q: r
39 ---
40 s: t
41 ---
42 u: v
43 ---
44 w: x
45 ---
46 y: z
47 `
48 actual := &bytes.Buffer{}
49 rw := kio.ByteReadWriter{Reader: bytes.NewBufferString(input), Writer: actual}
50 nodes, err := rw.Read()
51 if !assert.NoError(t, err) {
52 t.Fail()
53 }
54
55
56 rand.Seed(time.Now().UnixNano())
57 rand.Shuffle(len(nodes), func(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] })
58
59
60 if !assert.NoError(t, kioutil.SortNodes(nodes)) {
61 t.Fail()
62 }
63
64
65 expected := strings.Split(input, "---")
66 for i := range nodes {
67 a := strings.TrimSpace(nodes[i].MustString())
68 b := strings.TrimSpace(expected[i])
69 if !assert.Contains(t, a, b) {
70 t.Fail()
71 }
72 }
73
74 if !assert.NoError(t, rw.Write(nodes)) {
75 t.Fail()
76 }
77
78 assert.Equal(t, strings.TrimSpace(input), strings.TrimSpace(actual.String()))
79 }
80
81 func TestDefaultPathAnnotation(t *testing.T) {
82 var tests = []struct {
83 dir string
84 input string
85 expected string
86 name string
87 }{
88 {
89 `foo`,
90 `apiVersion: v1
91 kind: Bar
92 metadata:
93 name: a
94 namespace: b
95 `,
96 `apiVersion: v1
97 kind: Bar
98 metadata:
99 name: a
100 namespace: b
101 annotations:
102 internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
103 config.kubernetes.io/path: 'foo/b/bar_a.yaml'
104 `, `with namespace`},
105 {
106 `foo`,
107 `apiVersion: v1
108 kind: Bar
109 metadata:
110 name: a
111 `,
112 `apiVersion: v1
113 kind: Bar
114 metadata:
115 name: a
116 annotations:
117 internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
118 config.kubernetes.io/path: 'foo/bar_a.yaml'
119 `, `without namespace`},
120
121 {
122 ``,
123 `apiVersion: v1
124 kind: Bar
125 metadata:
126 name: a
127 namespace: b
128 `,
129 `apiVersion: v1
130 kind: Bar
131 metadata:
132 name: a
133 namespace: b
134 annotations:
135 internal.config.kubernetes.io/path: 'b/bar_a.yaml'
136 config.kubernetes.io/path: 'b/bar_a.yaml'
137 `, `without dir`},
138 {
139 ``,
140 `apiVersion: v1
141 kind: Bar
142 metadata:
143 name: a
144 namespace: b
145 annotations:
146 internal.config.kubernetes.io/path: 'a/b.yaml'
147 config.kubernetes.io/path: 'a/b.yaml'
148 `,
149 `apiVersion: v1
150 kind: Bar
151 metadata:
152 name: a
153 namespace: b
154 annotations:
155 internal.config.kubernetes.io/path: 'a/b.yaml'
156 config.kubernetes.io/path: 'a/b.yaml'
157 `, `skip`},
158 }
159
160 for _, s := range tests {
161 n := yaml.MustParse(s.input)
162 err := kioutil.DefaultPathAnnotation(s.dir, []*yaml.RNode{n})
163 if !assert.NoError(t, err, s.name) {
164 t.FailNow()
165 }
166 if !assert.Equal(t, s.expected, n.MustString(), s.name) {
167 t.FailNow()
168 }
169 }
170 }
171
172 func TestDefaultPathAndIndexAnnotation(t *testing.T) {
173 var tests = []struct {
174 dir string
175 input string
176 expected string
177 name string
178 }{
179 {
180 `foo`,
181 `apiVersion: v1
182 kind: Bar
183 metadata:
184 name: a
185 namespace: b
186 `,
187 `apiVersion: v1
188 kind: Bar
189 metadata:
190 name: a
191 namespace: b
192 annotations:
193 internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
194 config.kubernetes.io/path: 'foo/b/bar_a.yaml'
195 internal.config.kubernetes.io/index: '0'
196 config.kubernetes.io/index: '0'
197 `, `with namespace`},
198 {
199 `foo`,
200 `apiVersion: v1
201 kind: Bar
202 metadata:
203 name: a
204 `,
205 `apiVersion: v1
206 kind: Bar
207 metadata:
208 name: a
209 annotations:
210 internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
211 config.kubernetes.io/path: 'foo/bar_a.yaml'
212 internal.config.kubernetes.io/index: '0'
213 config.kubernetes.io/index: '0'
214 `, `without namespace`},
215
216 {
217 ``,
218 `apiVersion: v1
219 kind: Bar
220 metadata:
221 name: a
222 namespace: b
223 `,
224 `apiVersion: v1
225 kind: Bar
226 metadata:
227 name: a
228 namespace: b
229 annotations:
230 internal.config.kubernetes.io/path: 'b/bar_a.yaml'
231 config.kubernetes.io/path: 'b/bar_a.yaml'
232 internal.config.kubernetes.io/index: '0'
233 config.kubernetes.io/index: '0'
234 `, `without dir`},
235 {
236 ``,
237 `apiVersion: v1
238 kind: Bar
239 metadata:
240 name: a
241 namespace: b
242 annotations:
243 internal.config.kubernetes.io/path: 'a/b.yaml'
244 config.kubernetes.io/path: 'a/b.yaml'
245 internal.config.kubernetes.io/index: '5'
246 config.kubernetes.io/index: '5'
247 `,
248 `apiVersion: v1
249 kind: Bar
250 metadata:
251 name: a
252 namespace: b
253 annotations:
254 internal.config.kubernetes.io/path: 'a/b.yaml'
255 config.kubernetes.io/path: 'a/b.yaml'
256 internal.config.kubernetes.io/index: '5'
257 config.kubernetes.io/index: '5'
258 `, `skip`},
259 }
260
261 for _, s := range tests {
262 out := &bytes.Buffer{}
263 r := kio.ByteReadWriter{
264 Reader: bytes.NewBufferString(s.input),
265 Writer: out,
266 KeepReaderAnnotations: true,
267 OmitReaderAnnotations: true,
268 }
269 n, err := r.Read()
270 if !assert.NoError(t, err, s.name) {
271 t.FailNow()
272 }
273 if !assert.NoError(t, kioutil.DefaultPathAndIndexAnnotation(s.dir, n), s.name) {
274 t.FailNow()
275 }
276 if !assert.NoError(t, r.Write(n), s.name) {
277 t.FailNow()
278 }
279 if !assert.Equal(t, s.expected, out.String(), s.name) {
280 t.FailNow()
281 }
282 }
283 }
284
285 func TestCreatePathAnnotationValue(t *testing.T) {
286 var tests = []struct {
287 dir string
288 meta yaml.ResourceMeta
289 expected string
290 name string
291 }{
292 {
293 `dir`,
294 yaml.ResourceMeta{
295 TypeMeta: yaml.TypeMeta{
296 APIVersion: "apps/v1",
297 Kind: "foo",
298 },
299 ObjectMeta: yaml.ObjectMeta{
300 NameMeta: yaml.NameMeta{
301 Name: "bar", Namespace: "baz",
302 },
303 },
304 },
305 `dir/baz/foo_bar.yaml`, `with namespace`,
306 },
307 {
308 ``,
309 yaml.ResourceMeta{
310 TypeMeta: yaml.TypeMeta{
311 APIVersion: "apps/v1",
312 Kind: "foo",
313 },
314 ObjectMeta: yaml.ObjectMeta{
315 NameMeta: yaml.NameMeta{
316 Name: "bar", Namespace: "baz",
317 },
318 },
319 },
320 `baz/foo_bar.yaml`, `without dir`,
321 },
322 {
323 `dir`,
324 yaml.ResourceMeta{
325 TypeMeta: yaml.TypeMeta{
326 APIVersion: "apps/v1",
327 Kind: "foo",
328 },
329 ObjectMeta: yaml.ObjectMeta{
330 NameMeta: yaml.NameMeta{Name: "bar"},
331 },
332 },
333 `dir/foo_bar.yaml`, `without namespace`,
334 },
335 {
336 ``,
337 yaml.ResourceMeta{
338 TypeMeta: yaml.TypeMeta{
339 APIVersion: "apps/v1",
340 Kind: "foo",
341 },
342 ObjectMeta: yaml.ObjectMeta{
343 NameMeta: yaml.NameMeta{Name: "bar"},
344 },
345 },
346 `foo_bar.yaml`, `without namespace or dir`,
347 },
348 {
349 ``,
350 yaml.ResourceMeta{
351 TypeMeta: yaml.TypeMeta{
352 APIVersion: "apps/v1",
353 Kind: "foo",
354 },
355 ObjectMeta: yaml.ObjectMeta{},
356 },
357 `foo_.yaml`, `without namespace, dir or name`,
358 },
359 {
360 ``,
361 yaml.ResourceMeta{
362 TypeMeta: yaml.TypeMeta{
363 APIVersion: "apps/v1",
364 },
365 ObjectMeta: yaml.ObjectMeta{},
366 },
367 `_.yaml`, `without any`,
368 },
369 }
370
371 for _, s := range tests {
372 p := kioutil.CreatePathAnnotationValue(s.dir, s.meta)
373 if !assert.Equal(t, s.expected, p, s.name) {
374 t.FailNow()
375 }
376 }
377 }
378
379 func TestCopyLegacyAnnotations(t *testing.T) {
380 var tests = []struct {
381 input string
382 expected string
383 }{
384 {
385 input: `apiVersion: v1
386 kind: Foo
387 metadata:
388 name: foobar
389 annotations:
390 config.kubernetes.io/path: 'a/b.yaml'
391 config.kubernetes.io/index: '5'
392 `,
393 expected: `apiVersion: v1
394 kind: Foo
395 metadata:
396 name: foobar
397 annotations:
398 config.kubernetes.io/path: 'a/b.yaml'
399 config.kubernetes.io/index: '5'
400 internal.config.kubernetes.io/path: 'a/b.yaml'
401 internal.config.kubernetes.io/index: '5'
402 `,
403 },
404 {
405 input: `apiVersion: v1
406 kind: Foo
407 metadata:
408 name: foobar
409 annotations:
410 internal.config.kubernetes.io/path: 'a/b.yaml'
411 internal.config.kubernetes.io/index: '5'
412 `,
413 expected: `apiVersion: v1
414 kind: Foo
415 metadata:
416 name: foobar
417 annotations:
418 internal.config.kubernetes.io/path: 'a/b.yaml'
419 internal.config.kubernetes.io/index: '5'
420 config.kubernetes.io/path: 'a/b.yaml'
421 config.kubernetes.io/index: '5'
422 `,
423 },
424 {
425 input: `apiVersion: v1
426 kind: Foo
427 metadata:
428 name: foobar
429 annotations:
430 internal.config.kubernetes.io/path: 'a/b.yaml'
431 config.kubernetes.io/path: 'c/d.yaml'
432 `,
433 expected: `apiVersion: v1
434 kind: Foo
435 metadata:
436 name: foobar
437 annotations:
438 internal.config.kubernetes.io/path: 'a/b.yaml'
439 config.kubernetes.io/path: 'c/d.yaml'
440 `,
441 },
442 }
443
444 for _, tc := range tests {
445 rw := kio.ByteReadWriter{
446 Reader: bytes.NewBufferString(tc.input),
447 OmitReaderAnnotations: true,
448 }
449 nodes, err := rw.Read()
450 require.NoError(t, err)
451 require.NoError(t, kioutil.CopyLegacyAnnotations(nodes[0]))
452 assert.Equal(t, tc.expected, nodes[0].MustString())
453 }
454 }
455
456 func TestCopyInternalAnnotations(t *testing.T) {
457 var tests = []struct {
458 input string
459 exclusions []kioutil.AnnotationKey
460 expected string
461 }{
462 {
463 input: `apiVersion: v1
464 kind: Foo
465 metadata:
466 name: src
467 annotations:
468 internal.config.kubernetes.io/path: 'a/b.yaml'
469 internal.config.kubernetes.io/index: '5'
470 internal.config.kubernetes.io/foo: 'bar'
471 ---
472 apiVersion: v1
473 kind: Foo
474 metadata:
475 name: dst
476 annotations:
477 internal.config.kubernetes.io/path: 'c/d.yaml'
478 internal.config.kubernetes.io/index: '10'
479 `,
480 expected: `apiVersion: v1
481 kind: Foo
482 metadata:
483 name: dst
484 annotations:
485 internal.config.kubernetes.io/path: 'a/b.yaml'
486 internal.config.kubernetes.io/index: '5'
487 internal.config.kubernetes.io/foo: 'bar'
488 `,
489 },
490 {
491 input: `apiVersion: v1
492 kind: Foo
493 metadata:
494 name: src
495 annotations:
496 internal.config.kubernetes.io/path: 'a/b.yaml'
497 internal.config.kubernetes.io/index: '5'
498 internal.config.kubernetes.io/foo: 'bar-src'
499 ---
500 apiVersion: v1
501 kind: Foo
502 metadata:
503 name: dst
504 annotations:
505 internal.config.kubernetes.io/path: 'c/d.yaml'
506 internal.config.kubernetes.io/index: '10'
507 internal.config.kubernetes.io/foo: 'bar-dst'
508 `,
509 exclusions: []kioutil.AnnotationKey{
510 kioutil.PathAnnotation,
511 kioutil.IndexAnnotation,
512 },
513 expected: `apiVersion: v1
514 kind: Foo
515 metadata:
516 name: dst
517 annotations:
518 internal.config.kubernetes.io/path: 'c/d.yaml'
519 internal.config.kubernetes.io/index: '10'
520 internal.config.kubernetes.io/foo: 'bar-src'
521 `,
522 },
523 }
524
525 for _, tc := range tests {
526 rw := kio.ByteReadWriter{
527 Reader: bytes.NewBufferString(tc.input),
528 OmitReaderAnnotations: true,
529 }
530 nodes, err := rw.Read()
531 require.NoError(t, err)
532 require.NoError(t, kioutil.CopyInternalAnnotations(nodes[0], nodes[1], tc.exclusions...))
533 assert.Equal(t, tc.expected, nodes[1].MustString())
534 }
535 }
536
537 func TestConfirmInternalAnnotationUnchanged(t *testing.T) {
538 var tests = []struct {
539 input string
540 exclusions []kioutil.AnnotationKey
541 expectedErr string
542 }{
543 {
544 input: `apiVersion: v1
545 kind: Foo
546 metadata:
547 name: foo-1
548 annotations:
549 internal.config.kubernetes.io/path: 'a/b.yaml'
550 internal.config.kubernetes.io/index: '5'
551 ---
552 apiVersion: v1
553 kind: Foo
554 metadata:
555 name: foo-2
556 annotations:
557 internal.config.kubernetes.io/path: 'c/d.yaml'
558 internal.config.kubernetes.io/index: '10'
559 `,
560 expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
561 },
562 {
563 input: `apiVersion: v1
564 kind: Foo
565 metadata:
566 name: foo-1
567 annotations:
568 internal.config.kubernetes.io/path: 'a/b.yaml'
569 internal.config.kubernetes.io/index: '5'
570 ---
571 apiVersion: v1
572 kind: Foo
573 metadata:
574 name: foo-2
575 annotations:
576 internal.config.kubernetes.io/path: 'c/d.yaml'
577 internal.config.kubernetes.io/index: '10'
578 `,
579 exclusions: []kioutil.AnnotationKey{
580 kioutil.PathAnnotation,
581 kioutil.IndexAnnotation,
582 },
583 expectedErr: ``,
584 },
585 {
586 input: `apiVersion: v1
587 kind: Foo
588 metadata:
589 name: foo-1
590 annotations:
591 internal.config.kubernetes.io/path: 'a/b.yaml'
592 internal.config.kubernetes.io/index: '5'
593 internal.config.kubernetes.io/foo: 'bar-1'
594 ---
595 apiVersion: v1
596 kind: Foo
597 metadata:
598 name: foo-2
599 annotations:
600 internal.config.kubernetes.io/path: 'c/d.yaml'
601 internal.config.kubernetes.io/index: '10'
602 internal.config.kubernetes.io/foo: 'bar-2'
603 `,
604 exclusions: []kioutil.AnnotationKey{
605 kioutil.PathAnnotation,
606 kioutil.IndexAnnotation,
607 },
608 expectedErr: `internal annotations differ: internal.config.kubernetes.io/foo`,
609 },
610 {
611 input: `apiVersion: v1
612 kind: Foo
613 metadata:
614 name: foo-1
615 annotations:
616 internal.config.kubernetes.io/path: 'a/b.yaml'
617 internal.config.kubernetes.io/index: '5'
618 internal.config.kubernetes.io/foo: 'bar-1'
619 ---
620 apiVersion: v1
621 kind: Foo
622 metadata:
623 name: foo-2
624 annotations:
625 internal.config.kubernetes.io/path: 'c/d.yaml'
626 internal.config.kubernetes.io/index: '10'
627 internal.config.kubernetes.io/foo: 'bar-1'
628 `,
629 expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
630 },
631 {
632 input: `apiVersion: v1
633 kind: Foo
634 metadata:
635 name: foo-1
636 annotations:
637 internal.config.kubernetes.io/a: 'b'
638 internal.config.kubernetes.io/c: 'd'
639 ---
640 apiVersion: v1
641 kind: Foo
642 metadata:
643 name: foo-2
644 annotations:
645 internal.config.kubernetes.io/e: 'f'
646 internal.config.kubernetes.io/g: 'h'
647 `,
648 expectedErr: `internal annotations differ: ` +
649 `internal.config.kubernetes.io/a, internal.config.kubernetes.io/c, ` +
650 `internal.config.kubernetes.io/e, internal.config.kubernetes.io/g`,
651 },
652 }
653
654 for _, tc := range tests {
655 rw := kio.ByteReadWriter{
656 Reader: bytes.NewBufferString(tc.input),
657 OmitReaderAnnotations: true,
658 }
659 nodes, err := rw.Read()
660 require.NoError(t, err)
661 err = kioutil.ConfirmInternalAnnotationUnchanged(nodes[0], nodes[1], tc.exclusions...)
662 if tc.expectedErr == "" {
663 require.NoError(t, err)
664 } else {
665 if err == nil {
666 t.Fatalf("expected error: %s\n", tc.expectedErr)
667 }
668 assert.Equal(t, tc.expectedErr, err.Error())
669 }
670 }
671 }
672
673 func TestGetInternalAnnotations(t *testing.T) {
674 var tests = []struct {
675 input string
676 expected map[string]string
677 }{
678 {
679 input: `apiVersion: v1
680 kind: Foo
681 metadata:
682 name: foobar
683 annotations:
684 foo: bar
685 internal.config.kubernetes.io/path: 'a/b.yaml'
686 internal.config.kubernetes.io/index: '5'
687 internal.config.kubernetes.io/foo: 'bar'
688 `,
689 expected: map[string]string{
690 "internal.config.kubernetes.io/path": "a/b.yaml",
691 "internal.config.kubernetes.io/index": "5",
692 "internal.config.kubernetes.io/foo": "bar",
693 },
694 },
695 }
696
697 for _, tc := range tests {
698 rw := kio.ByteReadWriter{
699 Reader: bytes.NewBufferString(tc.input),
700 OmitReaderAnnotations: true,
701 }
702 nodes, err := rw.Read()
703 require.NoError(t, err)
704 assert.Equal(t, tc.expected, kioutil.GetInternalAnnotations(nodes[0]))
705 }
706 }
707
View as plain text