1
2
3
4 package resource_test
5
6 import (
7 "fmt"
8 "reflect"
9 "testing"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 "sigs.k8s.io/kustomize/api/internal/utils"
14 "sigs.k8s.io/kustomize/api/provider"
15 . "sigs.k8s.io/kustomize/api/resource"
16 "sigs.k8s.io/kustomize/api/types"
17 "sigs.k8s.io/kustomize/kyaml/resid"
18 kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
19 )
20
21 var factory = provider.NewDefaultDepProvider().GetResourceFactory()
22
23 var testConfigMap = factory.FromMap(
24 map[string]interface{}{
25 "apiVersion": "v1",
26 "kind": "ConfigMap",
27 "metadata": map[string]interface{}{
28 "name": "winnie",
29 "namespace": "hundred-acre-wood",
30 },
31 })
32
33
34 const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
35
36 var testDeployment = factory.FromMap(
37 map[string]interface{}{
38 "apiVersion": "apps/v1",
39 "kind": "Deployment",
40 "metadata": map[string]interface{}{
41 "name": "pooh",
42 },
43 })
44
45 const deploymentAsString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
46
47 func TestAsYAML(t *testing.T) {
48 expected := `apiVersion: apps/v1
49 kind: Deployment
50 metadata:
51 name: pooh
52 `
53 yaml, err := testDeployment.AsYAML()
54 if err != nil {
55 t.Fatal(err)
56 }
57 if string(yaml) != expected {
58 t.Fatalf("--- expected\n%s\n--- got\n%s\n", expected, string(yaml))
59 }
60 }
61
62 func TestResourceString(t *testing.T) {
63 tests := []struct {
64 in *Resource
65 s string
66 }{
67 {
68 in: testConfigMap,
69 s: configMapAsString,
70 },
71 {
72 in: testDeployment,
73 s: deploymentAsString,
74 },
75 }
76 for _, test := range tests {
77 assert.Equal(t, test.in.String(), test.s)
78 }
79 }
80
81 func TestResourceId(t *testing.T) {
82 tests := []struct {
83 in *Resource
84 id resid.ResId
85 }{
86 {
87 in: testConfigMap,
88 id: resid.NewResIdWithNamespace(
89 resid.NewGvk("", "v1", "ConfigMap"),
90 "winnie", "hundred-acre-wood"),
91 },
92 {
93 in: testDeployment,
94 id: resid.NewResId(
95 resid.NewGvk("apps", "v1", "Deployment"), "pooh"),
96 },
97 }
98 for _, test := range tests {
99 if test.in.OrgId() != test.id {
100 t.Fatalf("Expected %v, but got %v\n", test.id, test.in.OrgId())
101 }
102 }
103 }
104
105 func TestDeepCopy(t *testing.T) {
106 r := factory.FromMap(
107 map[string]interface{}{
108 "apiVersion": "apps/v1",
109 "kind": "Deployment",
110 "metadata": map[string]interface{}{
111 "name": "pooh",
112 },
113 })
114 r.AppendRefBy(resid.NewResId(resid.Gvk{Group: "somegroup", Kind: "MyKind"}, "random"))
115
116 var1 := types.Var{
117 Name: "SERVICE_ONE",
118 ObjRef: types.Target{
119 Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
120 Name: "backendOne"},
121 }
122 r.AppendRefVarName(var1)
123
124 cr := r.DeepCopy()
125 if !reflect.DeepEqual(r, cr) {
126 t.Errorf("expected %v\nbut got%v", r, cr)
127 }
128 }
129
130 func TestApplySmPatch_1(t *testing.T) {
131 resource, err := factory.FromBytes([]byte(`
132 apiVersion: apps/v1
133 kind: Deployment
134 metadata:
135 annotations:
136 baseAnno: This is a base annotation
137 labels:
138 app: mungebot
139 foo: bar
140 name: bingo
141 spec:
142 replicas: 1
143 selector:
144 matchLabels:
145 foo: bar
146 template:
147 metadata:
148 labels:
149 app: mungebot
150 spec:
151 containers:
152 - env:
153 - name: foo
154 value: bar
155 image: nginx
156 name: nginx
157 ports:
158 - containerPort: 80
159 `))
160 require.NoError(t, err)
161 patch, err := factory.FromBytes([]byte(`
162 apiVersion: apps/v1
163 kind: Deployment
164 metadata:
165 name: baseprefix-mungebot
166 spec:
167 template:
168 spec:
169 containers:
170 - image: nginx
171 name: nginx
172 ports:
173 - containerPort: 777
174 `))
175 require.NoError(t, err)
176
177 require.NoError(t, resource.ApplySmPatch(patch))
178 bytes, err := resource.AsYAML()
179 require.NoError(t, err)
180 assert.Equal(t, `apiVersion: apps/v1
181 kind: Deployment
182 metadata:
183 annotations:
184 baseAnno: This is a base annotation
185 labels:
186 app: mungebot
187 foo: bar
188 name: bingo
189 spec:
190 replicas: 1
191 selector:
192 matchLabels:
193 foo: bar
194 template:
195 metadata:
196 labels:
197 app: mungebot
198 spec:
199 containers:
200 - env:
201 - name: foo
202 value: bar
203 image: nginx
204 name: nginx
205 ports:
206 - containerPort: 777
207 - containerPort: 80
208 `, string(bytes))
209 }
210
211 func TestApplySmPatch_2(t *testing.T) {
212 resource, err := factory.FromBytes([]byte(`
213 apiVersion: example.com/v1
214 kind: Foo
215 metadata:
216 name: my-foo
217 spec:
218 bar:
219 A: X
220 B: Y
221 `))
222 require.NoError(t, err)
223 patch, err := factory.FromBytes([]byte(`
224 apiVersion: example.com/v1
225 kind: Foo
226 metadata:
227 name: my-foo
228 spec:
229 bar:
230 B:
231 C: Z
232 D: W
233 baz:
234 hello: world
235 `))
236 require.NoError(t, err)
237 require.NoError(t, resource.ApplySmPatch(patch))
238 bytes, err := resource.AsYAML()
239 require.NoError(t, err)
240 assert.Equal(t, `apiVersion: example.com/v1
241 kind: Foo
242 metadata:
243 name: my-foo
244 spec:
245 bar:
246 A: X
247 C: Z
248 D: W
249 baz:
250 hello: world
251 `, string(bytes))
252 }
253
254 func TestApplySmPatch_3(t *testing.T) {
255 resource, err := factory.FromBytes([]byte(`
256 apiVersion: v1
257 kind: Deployment
258 metadata:
259 name: clown
260 spec:
261 numReplicas: 1
262 `))
263 require.NoError(t, err)
264 patch, err := factory.FromBytes([]byte(`
265 apiVersion: v1
266 kind: Deployment
267 metadata:
268 name: clown
269 spec:
270 numReplicas: 999
271 `))
272 require.NoError(t, err)
273 require.NoError(t, resource.ApplySmPatch(patch))
274 bytes, err := resource.AsYAML()
275 require.NoError(t, err)
276 assert.Equal(t, `apiVersion: v1
277 kind: Deployment
278 metadata:
279 name: clown
280 spec:
281 numReplicas: 999
282 `, string(bytes))
283 }
284
285
286 func TestApplySmPatch_Idempotency(t *testing.T) {
287
288 patchApplyCount := 4
289 resourceYaml := `apiVersion: v1
290 kind: Deployment
291 metadata:
292 creationTimestamp: null
293 labels: null
294 name: my-deployment
295 `
296 resource, err := factory.FromBytes([]byte(resourceYaml))
297 require.NoError(t, err)
298
299 noOpPatch, err := factory.FromBytes([]byte(`
300 apiVersion: v1
301 kind: Deployment
302 metadata:
303 name: my-deployment
304 `))
305 require.NoError(t, err)
306
307 for i := 0; i < patchApplyCount; i++ {
308 require.NoError(t, resource.ApplySmPatch(noOpPatch))
309
310 bytes, err := resource.AsYAML()
311 require.NoError(t, err)
312
313 require.Equal(
314 t,
315 resourceYaml,
316 string(bytes),
317 "resource should be unchanged after re-application of patch",
318 )
319 }
320 }
321
322 func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) {
323 cases := []struct {
324 name string
325 skip bool
326 patch string
327 expectedOutput string
328 }{
329 {
330 name: "Order should not change when patch has foo only",
331 patch: `apiVersion: v1
332 kind: Pod
333 metadata:
334 name: test
335 spec:
336 initContainers:
337 - name: foo
338 `,
339 expectedOutput: `apiVersion: v1
340 kind: Pod
341 metadata:
342 name: test
343 spec:
344 initContainers:
345 - name: foo
346 - name: bar
347 `,
348 },
349 {
350 name: "Order changes when patch has bar only",
351 patch: `apiVersion: v1
352 kind: Pod
353 metadata:
354 name: test
355 spec:
356 initContainers:
357 - name: bar
358 `,
359
360
361 expectedOutput: `apiVersion: v1
362 kind: Pod
363 metadata:
364 name: test
365 spec:
366 initContainers:
367 - name: bar
368 - name: foo
369 `,
370 },
371 {
372 name: "Order should not change and should include a new item at the beginning when patch has a new list item",
373 patch: `apiVersion: v1
374 kind: Pod
375 metadata:
376 name: test
377 spec:
378 initContainers:
379 - name: baz
380 `,
381 expectedOutput: `apiVersion: v1
382 kind: Pod
383 metadata:
384 name: test
385 spec:
386 initContainers:
387 - name: baz
388 - name: foo
389 - name: bar
390 `,
391 },
392 {
393 name: "Order should not change when patch has foo and bar in same order",
394 patch: `apiVersion: v1
395 kind: Pod
396 metadata:
397 name: test
398 spec:
399 initContainers:
400 - name: foo
401 - name: bar
402 `,
403 expectedOutput: `apiVersion: v1
404 kind: Pod
405 metadata:
406 name: test
407 spec:
408 initContainers:
409 - name: foo
410 - name: bar
411 `,
412 },
413 {
414 name: "Order should change when patch has foo and bar in different order",
415 patch: `apiVersion: v1
416 kind: Pod
417 metadata:
418 name: test
419 spec:
420 initContainers:
421 - name: bar
422 - name: foo
423 `,
424 expectedOutput: `apiVersion: v1
425 kind: Pod
426 metadata:
427 name: test
428 spec:
429 initContainers:
430 - name: bar
431 - name: foo
432 `,
433 },
434 }
435 for _, tc := range cases {
436 t.Run(tc.name, func(t *testing.T) {
437 if tc.skip {
438 t.Skip()
439 }
440
441 resource, err := factory.FromBytes([]byte(`
442 apiVersion: v1
443 kind: Pod
444 metadata:
445 name: test
446 spec:
447 initContainers:
448 - name: foo
449 - name: bar
450 `))
451 require.NoError(t, err)
452
453 patch, err := factory.FromBytes([]byte(tc.patch))
454 require.NoError(t, err)
455 require.NoError(t, resource.ApplySmPatch(patch))
456 bytes, err := resource.AsYAML()
457 require.NoError(t, err)
458 assert.Equal(t, tc.expectedOutput, string(bytes))
459 })
460 }
461 }
462
463 func TestApplySmPatchShouldOutputPrimitiveListItemsInCorrectOrder(t *testing.T) {
464 cases := []struct {
465 name string
466 skip bool
467 patch string
468 expectedOutput string
469 }{
470 {
471 name: "Order should not change when patch has foo only",
472 patch: `apiVersion: v1
473 kind: Pod
474 metadata:
475 name: test
476 finalizers: ["foo"]
477 `,
478 expectedOutput: `apiVersion: v1
479 kind: Pod
480 metadata:
481 finalizers:
482 - foo
483 - bar
484 name: test
485 `,
486 },
487 {
488 name: "Order should not change when patch has bar only",
489 skip: true,
490 patch: `apiVersion: v1
491 kind: Pod
492 metadata:
493 name: test
494 finalizers: ["bar"]
495 `,
496 expectedOutput: `apiVersion: v1
497 kind: Pod
498 metadata:
499 finalizers:
500 - foo
501 - bar
502 name: test
503 `,
504 },
505 {
506 name: "Order should not change and should include a new item at the beginning when patch has a new list item",
507 patch: `apiVersion: v1
508 kind: Pod
509 metadata:
510 name: test
511 finalizers: ["baz"]
512 `,
513 expectedOutput: `apiVersion: v1
514 kind: Pod
515 metadata:
516 finalizers:
517 - baz
518 - foo
519 - bar
520 name: test
521 `,
522 },
523 {
524 name: "Order should not change when patch has foo and bar in same order",
525 patch: `apiVersion: v1
526 kind: Pod
527 metadata:
528 name: test
529 finalizers: ["foo", "bar"]
530 `,
531 expectedOutput: `apiVersion: v1
532 kind: Pod
533 metadata:
534 finalizers:
535 - foo
536 - bar
537 name: test
538 `,
539 },
540 {
541 name: "Order should change when patch has foo and bar in different order",
542 patch: `apiVersion: v1
543 kind: Pod
544 metadata:
545 name: test
546 finalizers: ["bar", "foo"]
547 `,
548 expectedOutput: `apiVersion: v1
549 kind: Pod
550 metadata:
551 finalizers:
552 - bar
553 - foo
554 name: test
555 `,
556 },
557 }
558 for _, tc := range cases {
559 t.Run(tc.name, func(t *testing.T) {
560 if tc.skip {
561 t.Skip()
562 }
563
564 resource, err := factory.FromBytes([]byte(`
565 kind: Pod
566 metadata:
567 name: test
568 finalizers: ["foo", "bar"]
569 `))
570 require.NoError(t, err)
571
572 patch, err := factory.FromBytes([]byte(tc.patch))
573 require.NoError(t, err)
574 require.NoError(t, resource.ApplySmPatch(patch))
575 bytes, err := resource.AsYAML()
576 require.NoError(t, err)
577 assert.Equal(t, tc.expectedOutput, string(bytes))
578 })
579 }
580 }
581
582 func TestMergeDataMapFrom(t *testing.T) {
583 resource, err := factory.FromBytes([]byte(`
584 apiVersion: v1
585 kind: BlahBlah
586 metadata:
587 name: clown
588 data:
589 fruit: pear
590 `))
591 if !assert.NoError(t, err) {
592 t.FailNow()
593 }
594 patch, err := factory.FromBytes([]byte(`
595 apiVersion: v1
596 kind: Whatever
597 metadata:
598 name: spaceship
599 data:
600 spaceship: enterprise
601 `))
602 if !assert.NoError(t, err) {
603 t.FailNow()
604 }
605 resource.MergeDataMapFrom(patch)
606 bytes, err := resource.AsYAML()
607 require.NoError(t, err)
608 assert.Equal(t, `apiVersion: v1
609 data:
610 fruit: pear
611 spaceship: enterprise
612 kind: BlahBlah
613 metadata:
614 name: clown
615 `, string(bytes))
616 }
617
618 func TestApplySmPatch_SwapOrder(t *testing.T) {
619 s1 := `
620 apiVersion: example.com/v1
621 kind: Foo
622 metadata:
623 name: my-foo
624 spec:
625 bar:
626 B:
627 C: Z
628 `
629 s2 := `
630 apiVersion: example.com/v1
631 kind: Foo
632 metadata:
633 name: my-foo
634 spec:
635 bar:
636 C: Z
637 D: W
638 baz:
639 hello: world
640 `
641 expected := `apiVersion: example.com/v1
642 kind: Foo
643 metadata:
644 name: my-foo
645 spec:
646 bar:
647 C: Z
648 D: W
649 baz:
650 hello: world
651 `
652 r1, err := factory.FromBytes([]byte(s1))
653 require.NoError(t, err)
654 r2, err := factory.FromBytes([]byte(s2))
655 require.NoError(t, err)
656 require.NoError(t, r1.ApplySmPatch(r2))
657 bytes, err := r1.AsYAML()
658 require.NoError(t, err)
659 assert.Equal(t, expected, string(bytes))
660
661 r1, _ = factory.FromBytes([]byte(s1))
662 r2, _ = factory.FromBytes([]byte(s2))
663 require.NoError(t, r2.ApplySmPatch(r1))
664 bytes, err = r2.AsYAML()
665 require.NoError(t, err)
666 assert.Equal(t, expected, string(bytes))
667 }
668
669 func TestApplySmPatch(t *testing.T) {
670 const (
671 myDeployment = "Deployment"
672 myCRD = "myCRD"
673 )
674
675 tests := map[string]struct {
676 base string
677 patch []string
678 expected string
679 errorExpected bool
680 errorMsg string
681 }{
682 "withschema-label-image-container": {
683 base: baseResource(myDeployment),
684 patch: []string{
685 addLabelAndEnvPatch(myDeployment),
686 changeImagePatch(myDeployment, "nginx:latest"),
687 addContainerAndEnvPatch(myDeployment),
688 },
689 errorExpected: false,
690 expected: expectedResultMultiPatch(myDeployment, false),
691 },
692 "withschema-image-container-label": {
693 base: baseResource(myDeployment),
694 patch: []string{
695 changeImagePatch(myDeployment, "nginx:latest"),
696 addContainerAndEnvPatch(myDeployment),
697 addLabelAndEnvPatch(myDeployment),
698 },
699 errorExpected: false,
700 expected: expectedResultMultiPatch(myDeployment, true),
701 },
702 "withschema-container-label-image": {
703 base: baseResource(myDeployment),
704 patch: []string{
705 addContainerAndEnvPatch(myDeployment),
706 addLabelAndEnvPatch(myDeployment),
707 changeImagePatch(myDeployment, "nginx:latest"),
708 },
709 errorExpected: false,
710 expected: expectedResultMultiPatch(myDeployment, true),
711 },
712 "noschema-label-image-container": {
713 base: baseResource(myCRD),
714 patch: []string{
715 addLabelAndEnvPatch(myCRD),
716 changeImagePatch(myCRD, "nginx:latest"),
717 addContainerAndEnvPatch(myCRD),
718 },
719
720
721 expected: `apiVersion: apps/v1
722 kind: myCRD
723 metadata:
724 name: deploy1
725 spec:
726 template:
727 metadata:
728 labels:
729 old-label: old-value
730 some-label: some-value
731 spec:
732 containers:
733 - env:
734 - name: ANOTHERENV
735 value: ANOTHERVALUE
736 name: nginx
737 - image: anotherimage
738 name: anothercontainer
739 `,
740 },
741 "noschema-image-container-label": {
742 base: baseResource(myCRD),
743 patch: []string{
744 changeImagePatch(myCRD, "nginx:latest"),
745 addContainerAndEnvPatch(myCRD),
746 addLabelAndEnvPatch(myCRD),
747 },
748
749 expected: `apiVersion: apps/v1
750 kind: myCRD
751 metadata:
752 name: deploy1
753 spec:
754 template:
755 metadata:
756 labels:
757 old-label: old-value
758 some-label: some-value
759 spec:
760 containers:
761 - env:
762 - name: SOMEENV
763 value: SOMEVALUE
764 name: nginx
765 `,
766 },
767 "noschema-container-label-image": {
768 base: baseResource(myCRD),
769 patch: []string{
770 addContainerAndEnvPatch(myCRD),
771 addLabelAndEnvPatch(myCRD),
772 changeImagePatch(myCRD, "nginx:latest"),
773 },
774
775 expected: `apiVersion: apps/v1
776 kind: myCRD
777 metadata:
778 name: deploy1
779 spec:
780 template:
781 metadata:
782 labels:
783 old-label: old-value
784 some-label: some-value
785 spec:
786 containers:
787 - image: nginx:latest
788 name: nginx
789 `,
790 },
791
792 "withschema-label-latest-someV-01": {
793 base: baseResource(myDeployment),
794 patch: []string{
795 addLabelAndEnvPatch(myDeployment),
796 changeImagePatch(myDeployment, "nginx:latest"),
797 changeImagePatch(myDeployment, "nginx:1.7.9"),
798 },
799 expected: `apiVersion: apps/v1
800 kind: Deployment
801 metadata:
802 name: deploy1
803 spec:
804 template:
805 metadata:
806 labels:
807 old-label: old-value
808 some-label: some-value
809 spec:
810 containers:
811 - env:
812 - name: SOMEENV
813 value: SOMEVALUE
814 image: nginx:1.7.9
815 name: nginx
816 `,
817 },
818 "withschema-latest-label-someV-02": {
819 base: baseResource(myDeployment),
820 patch: []string{
821 changeImagePatch(myDeployment, "nginx:latest"),
822 addLabelAndEnvPatch(myDeployment),
823 changeImagePatch(myDeployment, "nginx:1.7.9"),
824 },
825 expected: `apiVersion: apps/v1
826 kind: Deployment
827 metadata:
828 name: deploy1
829 spec:
830 template:
831 metadata:
832 labels:
833 old-label: old-value
834 some-label: some-value
835 spec:
836 containers:
837 - env:
838 - name: SOMEENV
839 value: SOMEVALUE
840 image: nginx:1.7.9
841 name: nginx
842 `,
843 },
844 "withschema-latest-label-someV-03": {
845 base: baseResource(myDeployment),
846 patch: []string{
847 changeImagePatch(myDeployment, "nginx:1.7.9"),
848 addLabelAndEnvPatch(myDeployment),
849 changeImagePatch(myDeployment, "nginx:latest"),
850 },
851 expected: `apiVersion: apps/v1
852 kind: Deployment
853 metadata:
854 name: deploy1
855 spec:
856 template:
857 metadata:
858 labels:
859 old-label: old-value
860 some-label: some-value
861 spec:
862 containers:
863 - env:
864 - name: SOMEENV
865 value: SOMEVALUE
866 image: nginx:latest
867 name: nginx
868 `,
869 },
870 "withschema-latest-label-someV-04": {
871 base: baseResource(myDeployment),
872 patch: []string{
873 changeImagePatch(myDeployment, "nginx:1.7.9"),
874 changeImagePatch(myDeployment, "nginx:latest"),
875 addLabelAndEnvPatch(myDeployment),
876 changeImagePatch(myDeployment, "nginx:nginx"),
877 },
878 expected: `apiVersion: apps/v1
879 kind: Deployment
880 metadata:
881 name: deploy1
882 spec:
883 template:
884 metadata:
885 labels:
886 old-label: old-value
887 some-label: some-value
888 spec:
889 containers:
890 - env:
891 - name: SOMEENV
892 value: SOMEVALUE
893 image: nginx:nginx
894 name: nginx
895 `,
896 },
897 "noschema-latest-label-someV-01": {
898 base: baseResource(myCRD),
899 patch: []string{
900 addLabelAndEnvPatch(myCRD),
901 changeImagePatch(myCRD, "nginx:latest"),
902 changeImagePatch(myCRD, "nginx:1.7.9"),
903 },
904 expected: `apiVersion: apps/v1
905 kind: myCRD
906 metadata:
907 name: deploy1
908 spec:
909 template:
910 metadata:
911 labels:
912 old-label: old-value
913 some-label: some-value
914 spec:
915 containers:
916 - image: nginx:1.7.9
917 name: nginx
918 `,
919 },
920 "noschema-latest-label-someV-02": {
921 base: baseResource(myCRD),
922 patch: []string{
923 changeImagePatch(myCRD, "nginx:latest"),
924 addLabelAndEnvPatch(myCRD),
925 changeImagePatch(myCRD, "nginx:1.7.9"),
926 },
927 expected: expectedResultJMP("nginx:1.7.9"),
928 },
929 "noschema-latest-label-someV-03": {
930 base: baseResource(myCRD),
931 patch: []string{
932 changeImagePatch(myCRD, "nginx:1.7.9"),
933 addLabelAndEnvPatch(myCRD),
934 changeImagePatch(myCRD, "nginx:latest"),
935 },
936 expected: `apiVersion: apps/v1
937 kind: myCRD
938 metadata:
939 name: deploy1
940 spec:
941 template:
942 metadata:
943 labels:
944 old-label: old-value
945 some-label: some-value
946 spec:
947 containers:
948 - image: nginx:latest
949 name: nginx
950 `,
951 },
952 "noschema-latest-label-someV-04": {
953 base: baseResource(myCRD),
954 patch: []string{
955 changeImagePatch(myCRD, "nginx:1.7.9"),
956 changeImagePatch(myCRD, "nginx:latest"),
957 addLabelAndEnvPatch(myCRD),
958 changeImagePatch(myCRD, "nginx:nginx"),
959 },
960 expected: `apiVersion: apps/v1
961 kind: myCRD
962 metadata:
963 name: deploy1
964 spec:
965 template:
966 metadata:
967 labels:
968 old-label: old-value
969 some-label: some-value
970 spec:
971 containers:
972 - image: nginx:nginx
973 name: nginx
974 `,
975 },
976 }
977
978 for name, test := range tests {
979 resource, err := factory.FromBytes([]byte(test.base))
980 require.NoError(t, err)
981 for _, p := range test.patch {
982 patch, err := factory.FromBytes([]byte(p))
983 require.NoError(t, err, name)
984 require.NoError(t, resource.ApplySmPatch(patch), name)
985 }
986 bytes, err := resource.AsYAML()
987 if test.errorExpected {
988 require.Error(t, err, name)
989 } else {
990 require.NoError(t, err, name)
991 assert.Equal(t, test.expected, string(bytes), name)
992 }
993 }
994 }
995
996 func TestResourceStorePreviousId(t *testing.T) {
997 tests := map[string]struct {
998 input string
999 newName string
1000 newNs string
1001 expected string
1002 }{
1003 "default namespace, first previous name": {
1004 input: `apiVersion: apps/v1
1005 kind: Secret
1006 metadata:
1007 name: oldName
1008 `,
1009 newName: "newName",
1010 newNs: "",
1011 expected: `apiVersion: apps/v1
1012 kind: Secret
1013 metadata:
1014 annotations:
1015 internal.config.kubernetes.io/previousKinds: Secret
1016 internal.config.kubernetes.io/previousNames: oldName
1017 internal.config.kubernetes.io/previousNamespaces: default
1018 name: newName
1019 `,
1020 },
1021
1022 "default namespace, second previous name": {
1023 input: `apiVersion: apps/v1
1024 kind: Secret
1025 metadata:
1026 annotations:
1027 internal.config.kubernetes.io/previousKinds: Secret
1028 internal.config.kubernetes.io/previousNames: oldName
1029 internal.config.kubernetes.io/previousNamespaces: default
1030 name: oldName2
1031 `,
1032 newName: "newName",
1033 newNs: "",
1034 expected: `apiVersion: apps/v1
1035 kind: Secret
1036 metadata:
1037 annotations:
1038 internal.config.kubernetes.io/previousKinds: Secret,Secret
1039 internal.config.kubernetes.io/previousNames: oldName,oldName2
1040 internal.config.kubernetes.io/previousNamespaces: default,default
1041 name: newName
1042 `,
1043 },
1044
1045 "non-default namespace": {
1046 input: `apiVersion: apps/v1
1047 kind: Secret
1048 metadata:
1049 annotations:
1050 internal.config.kubernetes.io/previousKinds: Secret
1051 internal.config.kubernetes.io/previousNames: oldName
1052 internal.config.kubernetes.io/previousNamespaces: default
1053 name: oldName2
1054 namespace: oldNamespace
1055 `,
1056 newName: "newName",
1057 newNs: "newNamespace",
1058 expected: `apiVersion: apps/v1
1059 kind: Secret
1060 metadata:
1061 annotations:
1062 internal.config.kubernetes.io/previousKinds: Secret,Secret
1063 internal.config.kubernetes.io/previousNames: oldName,oldName2
1064 internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
1065 name: newName
1066 namespace: newNamespace
1067 `,
1068 },
1069 }
1070 factory := provider.NewDefaultDepProvider().GetResourceFactory()
1071 for i := range tests {
1072 test := tests[i]
1073 t.Run(i, func(t *testing.T) {
1074 resources, err := factory.SliceFromBytes([]byte(test.input))
1075 if !assert.NoError(t, err) || len(resources) == 0 {
1076 t.FailNow()
1077 }
1078 r := resources[0]
1079 r.StorePreviousId()
1080 r.SetName(test.newName)
1081 if test.newNs != "" {
1082 r.SetNamespace(test.newNs)
1083 }
1084 bytes, err := r.AsYAML()
1085 if !assert.NoError(t, err) {
1086 t.FailNow()
1087 }
1088 assert.Equal(t, test.expected, string(bytes))
1089 })
1090 }
1091 }
1092
1093 func TestResource_PrevIds(t *testing.T) {
1094 tests := map[string]struct {
1095 input string
1096 expected []resid.ResId
1097 }{
1098 "no previous IDs": {
1099 input: `apiVersion: apps/v1
1100 kind: Secret
1101 metadata:
1102 name: name
1103 `,
1104 expected: nil,
1105 },
1106
1107 "one previous ID": {
1108 input: `apiVersion: apps/v1
1109 kind: Secret
1110 metadata:
1111 annotations:
1112 internal.config.kubernetes.io/previousKinds: Secret
1113 internal.config.kubernetes.io/previousNames: oldName
1114 internal.config.kubernetes.io/previousNamespaces: default
1115 name: newName
1116 `,
1117 expected: []resid.ResId{
1118 {
1119 Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
1120 Name: "oldName",
1121 Namespace: resid.DefaultNamespace,
1122 },
1123 },
1124 },
1125
1126 "two ids": {
1127 input: `apiVersion: apps/v1
1128 kind: Secret
1129 metadata:
1130 annotations:
1131 internal.config.kubernetes.io/previousKinds: Secret,Secret
1132 internal.config.kubernetes.io/previousNames: oldName,oldName2
1133 internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
1134 name: newName
1135 namespace: newNamespace
1136 `,
1137 expected: []resid.ResId{
1138 {
1139 Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
1140 Name: "oldName",
1141 Namespace: resid.DefaultNamespace,
1142 },
1143 {
1144 Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
1145 Name: "oldName2",
1146 Namespace: "oldNamespace",
1147 },
1148 },
1149 },
1150 }
1151 factory := provider.NewDefaultDepProvider().GetResourceFactory()
1152 for i := range tests {
1153 test := tests[i]
1154 t.Run(i, func(t *testing.T) {
1155 resources, err := factory.SliceFromBytes([]byte(test.input))
1156 if !assert.NoError(t, err) || len(resources) == 0 {
1157 t.FailNow()
1158 }
1159 r := resources[0]
1160 assert.Equal(t, test.expected, r.PrevIds())
1161 })
1162 }
1163 }
1164
1165
1166
1167
1168
1169
1170 func baseResource(kind string) string {
1171 res := `
1172 apiVersion: apps/v1
1173 kind: %s
1174 metadata:
1175 name: deploy1
1176 spec:
1177 template:
1178 metadata:
1179 labels:
1180 old-label: old-value
1181 spec:
1182 containers:
1183 - name: nginx
1184 image: nginx`
1185 return fmt.Sprintf(res, kind)
1186 }
1187
1188
1189
1190
1191
1192
1193 func addLabelAndEnvPatch(kind string) string {
1194 return fmt.Sprintf(`
1195 apiVersion: apps/v1
1196 kind: %s
1197 metadata:
1198 name: deploy1
1199 spec:
1200 template:
1201 metadata:
1202 labels:
1203 some-label: some-value
1204 spec:
1205 containers:
1206 - name: nginx
1207 env:
1208 - name: SOMEENV
1209 value: SOMEVALUE`, kind)
1210 }
1211
1212
1213
1214
1215
1216
1217 func addContainerAndEnvPatch(kind string) string {
1218 return fmt.Sprintf(`
1219 apiVersion: apps/v1
1220 kind: %s
1221 metadata:
1222 name: deploy1
1223 spec:
1224 template:
1225 spec:
1226 containers:
1227 - name: nginx
1228 env:
1229 - name: ANOTHERENV
1230 value: ANOTHERVALUE
1231 - name: anothercontainer
1232 image: anotherimage`, kind)
1233 }
1234
1235
1236
1237
1238
1239 func changeImagePatch(kind string, newImage string) string {
1240 return fmt.Sprintf(`
1241 apiVersion: apps/v1
1242 kind: %s
1243 metadata:
1244 name: deploy1
1245 spec:
1246 template:
1247 spec:
1248 containers:
1249 - name: nginx
1250 image: %s`, kind, newImage)
1251 }
1252
1253
1254
1255
1256 func expectedResultMultiPatch(kind string, reversed bool) string {
1257 pattern := `apiVersion: apps/v1
1258 kind: %s
1259 metadata:
1260 name: deploy1
1261 spec:
1262 template:
1263 metadata:
1264 labels:
1265 old-label: old-value
1266 some-label: some-value
1267 spec:
1268 containers:
1269 - env:
1270 %s
1271 image: nginx:latest
1272 name: nginx
1273 - image: anotherimage
1274 name: anothercontainer
1275 `
1276 if reversed {
1277 return fmt.Sprintf(pattern, kind, `- name: SOMEENV
1278 value: SOMEVALUE
1279 - name: ANOTHERENV
1280 value: ANOTHERVALUE`)
1281 }
1282 return fmt.Sprintf(pattern, kind, `- name: ANOTHERENV
1283 value: ANOTHERVALUE
1284 - name: SOMEENV
1285 value: SOMEVALUE`)
1286 }
1287
1288
1289
1290
1291
1292 func expectedResultJMP(imagename string) string {
1293 if imagename == "" {
1294 return `apiVersion: apps/v1
1295 kind: myCRD
1296 metadata:
1297 name: deploy1
1298 spec:
1299 template:
1300 metadata:
1301 labels:
1302 old-label: old-value
1303 some-label: some-value
1304 spec:
1305 containers:
1306 - env:
1307 - name: SOMEENV
1308 value: SOMEVALUE
1309 name: nginx
1310 `
1311 }
1312 return fmt.Sprintf(`apiVersion: apps/v1
1313 kind: myCRD
1314 metadata:
1315 name: deploy1
1316 spec:
1317 template:
1318 metadata:
1319 labels:
1320 old-label: old-value
1321 some-label: some-value
1322 spec:
1323 containers:
1324 - image: %s
1325 name: nginx
1326 `, imagename)
1327 }
1328
1329 func TestSameEndingSubarray(t *testing.T) {
1330 testCases := map[string]struct {
1331 a []string
1332 b []string
1333 expected bool
1334 }{
1335 "both nil": {
1336 expected: true,
1337 },
1338 "one nil": {
1339 b: []string{},
1340 expected: true,
1341 },
1342 "both empty": {
1343 a: []string{},
1344 b: []string{},
1345 expected: true,
1346 },
1347 "no1": {
1348 a: []string{"a"},
1349 b: []string{},
1350 expected: false,
1351 },
1352 "no2": {
1353 a: []string{"b", "a"},
1354 b: []string{"b"},
1355 expected: false,
1356 },
1357 "yes1": {
1358 a: []string{"a", "b"},
1359 b: []string{"b"},
1360 expected: true,
1361 },
1362 "yes2": {
1363 a: []string{"a", "b", "c"},
1364 b: []string{"b", "c"},
1365 expected: true,
1366 },
1367 "yes3": {
1368 a: []string{"a", "b", "c", "d", "e", "f"},
1369 b: []string{"f"},
1370 expected: true,
1371 },
1372 }
1373 for n := range testCases {
1374 tc := testCases[n]
1375 t.Run(n, func(t *testing.T) {
1376 assert.Equal(t, tc.expected, utils.SameEndingSubSlice(tc.a, tc.b))
1377 })
1378 }
1379 }
1380
1381 func TestGetGvk(t *testing.T) {
1382 r, err := factory.FromBytes([]byte(`
1383 apiVersion: apps/v1
1384 kind: Deployment
1385 metadata:
1386 name: clown
1387 spec:
1388 numReplicas: 1
1389 `))
1390 require.NoError(t, err)
1391
1392 gvk := r.GetGvk()
1393 expected := "apps"
1394 actual := gvk.Group
1395 if expected != actual {
1396 t.Fatalf("expected '%s', got '%s'", expected, actual)
1397 }
1398 expected = "v1"
1399 actual = gvk.Version
1400 if expected != actual {
1401 t.Fatalf("expected '%s', got '%s'", expected, actual)
1402 }
1403 expected = "Deployment"
1404 actual = gvk.Kind
1405 if expected != actual {
1406 t.Fatalf("expected '%s', got '%s'", expected, actual)
1407 }
1408 }
1409 func TestSetGvk(t *testing.T) {
1410 r, err := factory.FromBytes([]byte(`
1411 apiVersion: v1
1412 kind: Deployment
1413 metadata:
1414 name: clown
1415 spec:
1416 numReplicas: 1
1417 `))
1418 require.NoError(t, err)
1419 r.SetGvk(resid.GvkFromString("knd.ver.grp"))
1420 gvk := r.GetGvk()
1421 if expected, actual := "grp", gvk.Group; expected != actual {
1422 t.Fatalf("expected '%s', got '%s'", expected, actual)
1423 }
1424 if expected, actual := "ver", gvk.Version; expected != actual {
1425 t.Fatalf("expected '%s', got '%s'", expected, actual)
1426 }
1427 if expected, actual := "knd", gvk.Kind; expected != actual {
1428 t.Fatalf("expected '%s', got '%s'", expected, actual)
1429 }
1430 }
1431
1432 func TestRefBy(t *testing.T) {
1433 r, err := factory.FromBytes([]byte(`
1434 apiVersion: v1
1435 kind: Deployment
1436 metadata:
1437 name: clown
1438 spec:
1439 numReplicas: 1
1440 `))
1441 require.NoError(t, err)
1442 r.AppendRefBy(resid.FromString("knd1.ver1.gr1/name1.ns1"))
1443 assert.Equal(t, `apiVersion: v1
1444 kind: Deployment
1445 metadata:
1446 name: clown
1447 annotations:
1448 internal.config.kubernetes.io/refBy: 'knd1.ver1.gr1/name1.ns1'
1449 spec:
1450 numReplicas: 1
1451 `, r.RNode.MustString())
1452 assert.Equal(t, r.GetRefBy(), []resid.ResId{resid.FromString("knd1.ver1.gr1/name1.ns1")})
1453
1454 r.AppendRefBy(resid.FromString("knd2.ver2.gr2/name2.ns2"))
1455 assert.Equal(t, `apiVersion: v1
1456 kind: Deployment
1457 metadata:
1458 name: clown
1459 annotations:
1460 internal.config.kubernetes.io/refBy: 'knd1.ver1.gr1/name1.ns1,knd2.ver2.gr2/name2.ns2'
1461 spec:
1462 numReplicas: 1
1463 `, r.RNode.MustString())
1464 assert.Equal(t, []resid.ResId{
1465 resid.FromString("knd1.ver1.gr1/name1.ns1"),
1466 resid.FromString("knd2.ver2.gr2/name2.ns2"),
1467 }, r.GetRefBy())
1468 }
1469
1470 func TestOrigin(t *testing.T) {
1471 r, err := factory.FromBytes([]byte(`
1472 apiVersion: v1
1473 kind: Deployment
1474 metadata:
1475 name: clown
1476 spec:
1477 numReplicas: 1
1478 `))
1479 require.NoError(t, err)
1480 origin := &Origin{
1481 Path: "deployment.yaml",
1482 Repo: "github.com/myrepo",
1483 Ref: "master",
1484 }
1485 require.NoError(t, r.SetOrigin(origin))
1486 assert.Equal(t, `apiVersion: v1
1487 kind: Deployment
1488 metadata:
1489 name: clown
1490 annotations:
1491 config.kubernetes.io/origin: |
1492 path: deployment.yaml
1493 repo: github.com/myrepo
1494 ref: master
1495 spec:
1496 numReplicas: 1
1497 `, r.MustString())
1498 or, err := r.GetOrigin()
1499 require.NoError(t, err)
1500 assert.Equal(t, origin, or)
1501 }
1502
1503 func TestTransformations(t *testing.T) {
1504 r, err := factory.FromBytes([]byte(`
1505 apiVersion: v1
1506 kind: Deployment
1507 metadata:
1508 name: clown
1509 spec:
1510 numReplicas: 1
1511 `))
1512 require.NoError(t, err)
1513 origin1 := &Origin{
1514 Repo: "github.com/myrepo",
1515 Ref: "master",
1516 ConfiguredIn: "config.yaml",
1517 ConfiguredBy: kyaml.ResourceIdentifier{
1518 TypeMeta: kyaml.TypeMeta{
1519 APIVersion: "builtin",
1520 Kind: "Generator",
1521 },
1522 NameMeta: kyaml.NameMeta{
1523 Name: "my-name",
1524 Namespace: "my-namespace",
1525 },
1526 },
1527 }
1528 origin2 := &Origin{
1529 ConfiguredIn: "../base/config.yaml",
1530 ConfiguredBy: kyaml.ResourceIdentifier{
1531 TypeMeta: kyaml.TypeMeta{
1532 APIVersion: "builtin",
1533 Kind: "Generator",
1534 },
1535 NameMeta: kyaml.NameMeta{
1536 Name: "my-name",
1537 Namespace: "my-namespace",
1538 },
1539 },
1540 }
1541 require.NoError(t, r.AddTransformation(origin1))
1542 assert.Equal(t, `apiVersion: v1
1543 kind: Deployment
1544 metadata:
1545 name: clown
1546 annotations:
1547 alpha.config.kubernetes.io/transformations: |
1548 - repo: github.com/myrepo
1549 ref: master
1550 configuredIn: config.yaml
1551 configuredBy:
1552 apiVersion: builtin
1553 kind: Generator
1554 name: my-name
1555 namespace: my-namespace
1556 spec:
1557 numReplicas: 1
1558 `, r.MustString())
1559 require.NoError(t, r.AddTransformation(origin2))
1560 assert.Equal(t, `apiVersion: v1
1561 kind: Deployment
1562 metadata:
1563 name: clown
1564 annotations:
1565 alpha.config.kubernetes.io/transformations: |
1566 - repo: github.com/myrepo
1567 ref: master
1568 configuredIn: config.yaml
1569 configuredBy:
1570 apiVersion: builtin
1571 kind: Generator
1572 name: my-name
1573 namespace: my-namespace
1574 - configuredIn: ../base/config.yaml
1575 configuredBy:
1576 apiVersion: builtin
1577 kind: Generator
1578 name: my-name
1579 namespace: my-namespace
1580 spec:
1581 numReplicas: 1
1582 `, r.MustString())
1583 transformations, err := r.GetTransformations()
1584 require.NoError(t, err)
1585 assert.Equal(t, Transformations{origin1, origin2}, transformations)
1586 require.NoError(t, r.ClearTransformations())
1587 assert.Equal(t, `apiVersion: v1
1588 kind: Deployment
1589 metadata:
1590 name: clown
1591 spec:
1592 numReplicas: 1
1593 `, r.MustString())
1594 }
1595
View as plain text