1
16
17 package csaupgrade_test
18
19 import (
20 "encoding/json"
21 "reflect"
22 "testing"
23
24 jsonpatch "github.com/evanphx/json-patch"
25 "github.com/google/go-cmp/cmp"
26 "github.com/stretchr/testify/require"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29 "k8s.io/apimachinery/pkg/util/sets"
30 "k8s.io/apimachinery/pkg/util/yaml"
31 "k8s.io/client-go/util/csaupgrade"
32 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
33 )
34
35 func TestFindOwners(t *testing.T) {
36 testCases := []struct {
37 Name string
38 ManagedFieldsYAML string
39 Operation metav1.ManagedFieldsOperationType
40 Fields *fieldpath.Set
41 Expectation []string
42 }{
43 {
44
45 Name: "Basic",
46 ManagedFieldsYAML: `
47 managedFields:
48 - apiVersion: v1
49 fieldsType: FieldsV1
50 fieldsV1:
51 f:data:
52 .: {}
53 f:key: {}
54 f:legacy: {}
55 f:metadata:
56 f:annotations:
57 .: {}
58 f:kubectl.kubernetes.io/last-applied-configuration: {}
59 manager: kubectl-client-side-apply
60 operation: Update
61 time: "2022-08-22T23:08:23Z"
62 `,
63 Operation: metav1.ManagedFieldsOperationUpdate,
64 Fields: fieldpath.NewSet(fieldpath.MakePathOrDie("data")),
65 Expectation: []string{"kubectl-client-side-apply"},
66 },
67 {
68
69 Name: "Nested",
70 ManagedFieldsYAML: `
71 managedFields:
72 - apiVersion: v1
73 fieldsType: FieldsV1
74 fieldsV1:
75 f:data:
76 .: {}
77 f:key: {}
78 f:legacy: {}
79 f:metadata:
80 f:annotations:
81 .: {}
82 f:kubectl.kubernetes.io/last-applied-configuration: {}
83 manager: kubectl-client-side-apply
84 operation: Update
85 time: "2022-08-22T23:08:23Z"
86 `,
87 Operation: metav1.ManagedFieldsOperationUpdate,
88 Fields: fieldpath.NewSet(fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration")),
89 Expectation: []string{"kubectl-client-side-apply"},
90 },
91 {
92
93
94 Name: "NotFound",
95 ManagedFieldsYAML: `
96 managedFields:
97 - apiVersion: v1
98 fieldsType: FieldsV1
99 fieldsV1:
100 f:data:
101 .: {}
102 f:key: {}
103 f:legacy: {}
104 f:metadata:
105 f:annotations:
106 .: {}
107 f:kubectl.kubernetes.io/last-applied-configuration: {}
108 manager: kubectl
109 operation: Apply
110 time: "2022-08-23T23:08:23Z"
111 `,
112 Operation: metav1.ManagedFieldsOperationUpdate,
113 Fields: fieldpath.NewSet(fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration")),
114 Expectation: []string{},
115 },
116 {
117
118 Name: "ApplyOperation",
119 ManagedFieldsYAML: `
120 managedFields:
121 - apiVersion: v1
122 fieldsType: FieldsV1
123 fieldsV1:
124 f:data:
125 .: {}
126 f:key: {}
127 f:legacy: {}
128 f:metadata:
129 f:annotations:
130 .: {}
131 f:kubectl.kubernetes.io/last-applied-configuration: {}
132 manager: kubectl
133 operation: Apply
134 time: "2022-08-23T23:08:23Z"
135 `,
136 Operation: metav1.ManagedFieldsOperationApply,
137 Fields: fieldpath.NewSet(fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration")),
138 Expectation: []string{"kubectl"},
139 },
140 {
141
142 Name: "OneOfMultiple",
143 ManagedFieldsYAML: `
144 managedFields:
145 - apiVersion: v1
146 fieldsType: FieldsV1
147 fieldsV1:
148 f:metadata:
149 f:annotations:
150 .: {}
151 f:kubectl.kubernetes.io/last-applied-configuration: {}
152 manager: kubectl-client-side-apply
153 operation: Update
154 time: "2022-08-23T23:08:23Z"
155 - apiVersion: v1
156 fieldsType: FieldsV1
157 fieldsV1:
158 f:data:
159 .: {}
160 f:key: {}
161 f:legacy: {}
162 manager: kubectl
163 operation: Apply
164 time: "2022-08-23T23:08:23Z"
165 `,
166 Operation: metav1.ManagedFieldsOperationUpdate,
167 Fields: fieldpath.NewSet(fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration")),
168 Expectation: []string{"kubectl-client-side-apply"},
169 },
170 {
171
172 Name: "ManyOfMultiple",
173 ManagedFieldsYAML: `
174 managedFields:
175 - apiVersion: v1
176 fieldsType: FieldsV1
177 fieldsV1:
178 f:metadata:
179 f:annotations:
180 .: {}
181 f:kubectl.kubernetes.io/last-applied-configuration: {}
182 manager: kubectl-client-side-apply
183 operation: Update
184 time: "2022-08-23T23:08:23Z"
185 - apiVersion: v1
186 fieldsType: FieldsV1
187 fieldsV1:
188 f:metadata:
189 f:annotations:
190 .: {}
191 f:kubectl.kubernetes.io/last-applied-configuration: {}
192 f:data:
193 .: {}
194 f:key: {}
195 f:legacy: {}
196 manager: kubectl
197 operation: Apply
198 time: "2022-08-23T23:08:23Z"
199 - apiVersion: v1
200 fieldsType: FieldsV1
201 fieldsV1:
202 f:metadata:
203 f:annotations:
204 .: {}
205 f:kubectl.kubernetes.io/last-applied-configuration: {}
206 manager: kubectl-client-side-apply2
207 operation: Update
208 time: "2022-08-23T23:08:23Z"
209 `,
210 Operation: metav1.ManagedFieldsOperationUpdate,
211 Fields: fieldpath.NewSet(fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration")),
212 Expectation: []string{"kubectl-client-side-apply", "kubectl-client-side-apply2"},
213 },
214 {
215
216 Name: "BasicMultipleFields",
217 ManagedFieldsYAML: `
218 managedFields:
219 - apiVersion: v1
220 fieldsType: FieldsV1
221 fieldsV1:
222 f:metadata:
223 f:annotations:
224 .: {}
225 f:kubectl.kubernetes.io/last-applied-configuration: {}
226 f:data:
227 .: {}
228 f:key: {}
229 f:legacy: {}
230 manager: kubectl-client-side-apply
231 operation: Update
232 time: "2022-08-23T23:08:23Z"
233 `,
234 Operation: metav1.ManagedFieldsOperationUpdate,
235 Fields: fieldpath.NewSet(
236 fieldpath.MakePathOrDie("data", "key"),
237 fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration"),
238 ),
239 Expectation: []string{"kubectl-client-side-apply"},
240 },
241 {
242
243
244 Name: "MissingOneField",
245 ManagedFieldsYAML: `
246 managedFields:
247 - apiVersion: v1
248 fieldsType: FieldsV1
249 fieldsV1:
250 f:metadata:
251 f:annotations:
252 .: {}
253 f:kubectl.kubernetes.io/last-applied-configuration: {}
254 f:data:
255 .: {}
256 f:legacy: {}
257 manager: kubectl-client-side-apply
258 operation: Update
259 time: "2022-08-23T23:08:23Z"
260 `,
261 Operation: metav1.ManagedFieldsOperationUpdate,
262 Fields: fieldpath.NewSet(
263 fieldpath.MakePathOrDie("data", "key"),
264 fieldpath.MakePathOrDie("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration"),
265 ),
266 Expectation: []string{},
267 },
268 }
269 for _, tcase := range testCases {
270 t.Run(tcase.Name, func(t *testing.T) {
271 var entries struct {
272 ManagedFields []metav1.ManagedFieldsEntry `json:"managedFields"`
273 }
274 err := yaml.Unmarshal([]byte(tcase.ManagedFieldsYAML), &entries)
275 require.NoError(t, err)
276
277 result := csaupgrade.FindFieldsOwners(entries.ManagedFields, tcase.Operation, tcase.Fields)
278
279
280
281 ownerNames := []string{}
282 for _, entry := range result {
283 ownerNames = append(ownerNames, entry.Manager)
284 require.Equal(t, tcase.Operation, entry.Operation)
285 }
286 require.ElementsMatch(t, tcase.Expectation, ownerNames)
287 })
288 }
289 }
290
291 func TestUpgradeCSA(t *testing.T) {
292
293 cases := []struct {
294 Name string
295 CSAManagers []string
296 SSAManager string
297 Options []csaupgrade.Option
298 OriginalObject []byte
299 ExpectedObject []byte
300 }{
301 {
302
303
304
305 Name: "csa-basic-direct-conversion",
306 CSAManagers: []string{"kubectl-client-side-apply"},
307 SSAManager: "kubectl",
308 OriginalObject: []byte(`
309 apiVersion: v1
310 data: {}
311 kind: ConfigMap
312 metadata:
313 resourceVersion: "1"
314 annotations:
315 kubectl.kubernetes.io/last-applied-configuration: |
316 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
317 creationTimestamp: "2022-08-22T23:08:23Z"
318 managedFields:
319 - apiVersion: v1
320 fieldsType: FieldsV1
321 fieldsV1:
322 f:data:
323 .: {}
324 f:key: {}
325 f:legacy: {}
326 f:metadata:
327 f:annotations:
328 .: {}
329 f:kubectl.kubernetes.io/last-applied-configuration: {}
330 manager: kubectl-client-side-apply
331 operation: Update
332 time: "2022-08-22T23:08:23Z"
333 name: test
334 namespace: default
335 `),
336 ExpectedObject: []byte(`
337 apiVersion: v1
338 data: {}
339 kind: ConfigMap
340 metadata:
341 resourceVersion: "1"
342 annotations:
343 kubectl.kubernetes.io/last-applied-configuration: |
344 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
345 creationTimestamp: "2022-08-22T23:08:23Z"
346 managedFields:
347 - apiVersion: v1
348 fieldsType: FieldsV1
349 fieldsV1:
350 f:data:
351 .: {}
352 f:key: {}
353 f:legacy: {}
354 f:metadata:
355 f:annotations:
356 .: {}
357 f:kubectl.kubernetes.io/last-applied-configuration: {}
358 manager: kubectl
359 operation: Apply
360 time: "2022-08-22T23:08:23Z"
361 name: test
362 namespace: default
363 `),
364 },
365 {
366
367
368
369
370 Name: "csa-combine-with-ssa-duplicate-keys",
371 CSAManagers: []string{"kubectl-client-side-apply"},
372 SSAManager: "kubectl",
373 OriginalObject: []byte(`
374 apiVersion: v1
375 data: {}
376 kind: ConfigMap
377 metadata:
378 resourceVersion: "1"
379 annotations:
380 kubectl.kubernetes.io/last-applied-configuration: |
381 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
382 creationTimestamp: "2022-08-22T23:08:23Z"
383 managedFields:
384 - apiVersion: v1
385 fieldsType: FieldsV1
386 fieldsV1:
387 f:data:
388 .: {}
389 f:key: {}
390 f:legacy: {}
391 f:metadata:
392 f:annotations:
393 .: {}
394 f:kubectl.kubernetes.io/last-applied-configuration: {}
395 manager: kubectl
396 operation: Apply
397 time: "2022-08-23T23:08:23Z"
398 - apiVersion: v1
399 fieldsType: FieldsV1
400 fieldsV1:
401 f:data:
402 .: {}
403 f:key: {}
404 f:legacy: {}
405 f:metadata:
406 f:annotations:
407 .: {}
408 f:kubectl.kubernetes.io/last-applied-configuration: {}
409 manager: kubectl-client-side-apply
410 operation: Update
411 time: "2022-08-22T23:08:23Z"
412 name: test
413 namespace: default
414 `),
415 ExpectedObject: []byte(`
416 apiVersion: v1
417 data: {}
418 kind: ConfigMap
419 metadata:
420 resourceVersion: "1"
421 annotations:
422 kubectl.kubernetes.io/last-applied-configuration: |
423 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
424 creationTimestamp: "2022-08-22T23:08:23Z"
425 managedFields:
426 - apiVersion: v1
427 fieldsType: FieldsV1
428 fieldsV1:
429 f:data:
430 .: {}
431 f:key: {}
432 f:legacy: {}
433 f:metadata:
434 f:annotations:
435 .: {}
436 f:kubectl.kubernetes.io/last-applied-configuration: {}
437 manager: kubectl
438 operation: Apply
439 time: "2022-08-23T23:08:23Z"
440 name: test
441 namespace: default
442 `),
443 },
444 {
445
446
447
448
449
450
451 Name: "csa-combine-with-ssa-additional-keys",
452 CSAManagers: []string{"kubectl-client-side-apply"},
453 SSAManager: "kubectl",
454 OriginalObject: []byte(`
455 apiVersion: v1
456 data: {}
457 kind: ConfigMap
458 metadata:
459 resourceVersion: "1"
460 annotations:
461 kubectl.kubernetes.io/last-applied-configuration: |
462 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
463 creationTimestamp: "2022-08-22T23:08:23Z"
464 managedFields:
465 - apiVersion: v1
466 fieldsType: FieldsV1
467 fieldsV1:
468 f:data:
469 .: {}
470 f:key: {}
471 f:metadata:
472 f:annotations:
473 .: {}
474 f:kubectl.kubernetes.io/last-applied-configuration: {}
475 manager: kubectl
476 operation: Apply
477 time: "2022-08-23T23:08:23Z"
478 - apiVersion: v1
479 fieldsType: FieldsV1
480 fieldsV1:
481 f:data:
482 .: {}
483 f:key: {}
484 f:legacy: {}
485 f:metadata:
486 f:annotations:
487 .: {}
488 f:kubectl.kubernetes.io/last-applied-configuration: {}
489 manager: kubectl-client-side-apply
490 operation: Update
491 time: "2022-08-22T23:08:23Z"
492 name: test
493 namespace: default
494 `),
495 ExpectedObject: []byte(`
496 apiVersion: v1
497 data: {}
498 kind: ConfigMap
499 metadata:
500 resourceVersion: "1"
501 annotations:
502 kubectl.kubernetes.io/last-applied-configuration: |
503 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
504 creationTimestamp: "2022-08-22T23:08:23Z"
505 managedFields:
506 - apiVersion: v1
507 fieldsType: FieldsV1
508 fieldsV1:
509 f:data:
510 .: {}
511 f:key: {}
512 f:legacy: {}
513 f:metadata:
514 f:annotations:
515 .: {}
516 f:kubectl.kubernetes.io/last-applied-configuration: {}
517 manager: kubectl
518 operation: Apply
519 time: "2022-08-23T23:08:23Z"
520 name: test
521 namespace: default
522 `),
523 },
524 {
525
526
527
528 Name: "csa-no-applicable-version",
529 CSAManagers: []string{"kubectl-client-side-apply"},
530 SSAManager: "kubectl",
531 OriginalObject: []byte(`
532 apiVersion: v1
533 data: {}
534 kind: ConfigMap
535 metadata:
536 resourceVersion: "1"
537 annotations:
538 kubectl.kubernetes.io/last-applied-configuration: |
539 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
540 creationTimestamp: "2022-08-22T23:08:23Z"
541 managedFields:
542 - apiVersion: v5
543 fieldsType: FieldsV1
544 fieldsV1:
545 f:data:
546 .: {}
547 f:key: {}
548 f:legacy: {}
549 f:metadata:
550 f:annotations:
551 .: {}
552 f:kubectl.kubernetes.io/last-applied-configuration: {}
553 manager: kubectl
554 operation: Apply
555 time: "2022-08-23T23:08:23Z"
556 - apiVersion: v1
557 fieldsType: FieldsV1
558 fieldsV1:
559 f:data:
560 f:key2: {}
561 f:metadata:
562 f:annotations:
563 f:hello2: {}
564 manager: kubectl-client-side-apply
565 operation: Update
566 time: "2022-08-22T23:08:23Z"
567 - apiVersion: v2
568 fieldsType: FieldsV1
569 fieldsV1:
570 f:data:
571 f:key3: {}
572 f:metadata:
573 f:annotations:
574 f:hello3: {}
575 manager: kubectl-client-side-apply
576 operation: Update
577 time: "2022-08-22T23:08:23Z"
578 - apiVersion: v3
579 fieldsType: FieldsV1
580 fieldsV1:
581 f:data:
582 f:key4: {}
583 f:metadata:
584 f:annotations:
585 f:hello3: {}
586 manager: kubectl-client-side-apply
587 operation: Update
588 time: "2022-08-22T23:08:23Z"
589 - apiVersion: v4
590 fieldsType: FieldsV1
591 fieldsV1:
592 f:data:
593 f:key5: {}
594 f:metadata:
595 f:annotations:
596 f:hello4: {}
597 manager: kubectl-client-side-apply
598 operation: Update
599 time: "2022-08-22T23:08:23Z"
600 name: test
601 namespace: default
602 `),
603 ExpectedObject: []byte(`
604 apiVersion: v1
605 data: {}
606 kind: ConfigMap
607 metadata:
608 resourceVersion: "1"
609 annotations:
610 kubectl.kubernetes.io/last-applied-configuration: |
611 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
612 creationTimestamp: "2022-08-22T23:08:23Z"
613 managedFields:
614 - apiVersion: v5
615 fieldsType: FieldsV1
616 fieldsV1:
617 f:data:
618 .: {}
619 f:key: {}
620 f:legacy: {}
621 f:metadata:
622 f:annotations:
623 .: {}
624 f:kubectl.kubernetes.io/last-applied-configuration: {}
625 manager: kubectl
626 operation: Apply
627 time: "2022-08-23T23:08:23Z"
628 name: test
629 namespace: default
630 `),
631 },
632 {
633
634
635
636 Name: "csa-single-applicable-version",
637 CSAManagers: []string{"kubectl-client-side-apply"},
638 SSAManager: "kubectl",
639 OriginalObject: []byte(`
640 apiVersion: v1
641 data: {}
642 kind: ConfigMap
643 metadata:
644 resourceVersion: "1"
645 annotations:
646 kubectl.kubernetes.io/last-applied-configuration: |
647 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
648 creationTimestamp: "2022-08-22T23:08:23Z"
649 managedFields:
650 - apiVersion: v5
651 fieldsType: FieldsV1
652 fieldsV1:
653 f:data:
654 .: {}
655 f:key: {}
656 f:legacy: {}
657 f:metadata:
658 f:annotations:
659 .: {}
660 f:kubectl.kubernetes.io/last-applied-configuration: {}
661 manager: kubectl
662 operation: Apply
663 time: "2022-08-23T23:08:23Z"
664 - apiVersion: v5
665 fieldsType: FieldsV1
666 fieldsV1:
667 f:data:
668 f:key2: {}
669 f:metadata:
670 f:annotations:
671 f:hello2: {}
672 manager: kubectl-client-side-apply
673 operation: Update
674 time: "2022-08-22T23:08:23Z"
675 - apiVersion: v2
676 fieldsType: FieldsV1
677 fieldsV1:
678 f:data:
679 f:key3: {}
680 f:metadata:
681 f:annotations:
682 f:hello3: {}
683 manager: kubectl-client-side-apply
684 operation: Update
685 time: "2022-08-22T23:08:23Z"
686 - apiVersion: v3
687 fieldsType: FieldsV1
688 fieldsV1:
689 f:data:
690 f:key4: {}
691 f:metadata:
692 f:annotations:
693 f:hello4: {}
694 manager: kubectl-client-side-apply
695 operation: Update
696 time: "2022-08-22T23:08:23Z"
697 - apiVersion: v4
698 fieldsType: FieldsV1
699 fieldsV1:
700 f:data:
701 f:key5: {}
702 f:metadata:
703 f:annotations:
704 f:hello5: {}
705 manager: kubectl-client-side-apply
706 operation: Update
707 time: "2022-08-22T23:08:23Z"
708 name: test
709 namespace: default
710 `),
711 ExpectedObject: []byte(`
712 apiVersion: v1
713 data: {}
714 kind: ConfigMap
715 metadata:
716 resourceVersion: "1"
717 annotations:
718 kubectl.kubernetes.io/last-applied-configuration: |
719 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
720 creationTimestamp: "2022-08-22T23:08:23Z"
721 managedFields:
722 - apiVersion: v5
723 fieldsType: FieldsV1
724 fieldsV1:
725 f:data:
726 .: {}
727 f:key: {}
728 f:key2: {}
729 f:legacy: {}
730 f:metadata:
731 f:annotations:
732 .: {}
733 f:hello2: {}
734 f:kubectl.kubernetes.io/last-applied-configuration: {}
735 manager: kubectl
736 operation: Apply
737 time: "2022-08-23T23:08:23Z"
738 name: test
739 namespace: default
740 `),
741 },
742 {
743
744 Name: "noop",
745 CSAManagers: []string{"kubectl-client-side-apply"},
746 SSAManager: "not-already-in-object",
747 OriginalObject: []byte(`
748 apiVersion: v1
749 data: {}
750 kind: ConfigMap
751 metadata:
752 resourceVersion: "1"
753 annotations:
754 kubectl.kubernetes.io/last-applied-configuration: |
755 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
756 creationTimestamp: "2022-08-22T23:08:23Z"
757 managedFields:
758 - apiVersion: v5
759 fieldsType: FieldsV1
760 fieldsV1:
761 f:data:
762 .: {}
763 f:key: {}
764 f:legacy: {}
765 f:metadata:
766 f:annotations:
767 .: {}
768 f:kubectl.kubernetes.io/last-applied-configuration: {}
769 manager: kubectl
770 operation: Apply
771 time: "2022-08-23T23:08:23Z"
772 name: test
773 namespace: default
774 `),
775 ExpectedObject: []byte(`
776 apiVersion: v1
777 data: {}
778 kind: ConfigMap
779 metadata:
780 resourceVersion: "1"
781 annotations:
782 kubectl.kubernetes.io/last-applied-configuration: |
783 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
784 creationTimestamp: "2022-08-22T23:08:23Z"
785 managedFields:
786 - apiVersion: v5
787 fieldsType: FieldsV1
788 fieldsV1:
789 f:data:
790 .: {}
791 f:key: {}
792 f:legacy: {}
793 f:metadata:
794 f:annotations:
795 .: {}
796 f:kubectl.kubernetes.io/last-applied-configuration: {}
797 manager: kubectl
798 operation: Apply
799 time: "2022-08-23T23:08:23Z"
800 name: test
801 namespace: default
802 `),
803 },
804 {
805
806 Name: "multipleTargetsExisting",
807 CSAManagers: []string{"kube-scheduler", "kubectl-client-side-apply"},
808 SSAManager: "kubectl",
809 OriginalObject: []byte(`
810 apiVersion: v1
811 data: {}
812 kind: ConfigMap
813 metadata:
814 resourceVersion: "1"
815 annotations:
816 kubectl.kubernetes.io/last-applied-configuration: |
817 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
818 creationTimestamp: "2022-08-22T23:08:23Z"
819 managedFields:
820 - apiVersion: v1
821 fieldsType: FieldsV1
822 fieldsV1:
823 f:metadata:
824 f:labels:
825 f:name: {}
826 f:spec:
827 f:containers:
828 k:{"name":"kubernetes-pause"}:
829 .: {}
830 f:image: {}
831 f:name: {}
832 manager: kubectl
833 operation: Apply
834 - apiVersion: v1
835 fieldsType: FieldsV1
836 fieldsV1:
837 f:status:
838 f:conditions:
839 .: {}
840 k:{"type":"PodScheduled"}:
841 .: {}
842 f:lastProbeTime: {}
843 f:lastTransitionTime: {}
844 f:message: {}
845 f:reason: {}
846 f:status: {}
847 f:type: {}
848 manager: kube-scheduler
849 operation: Update
850 time: "2022-11-03T23:22:40Z"
851 - apiVersion: v1
852 fieldsType: FieldsV1
853 fieldsV1:
854 f:metadata:
855 f:annotations:
856 .: {}
857 f:kubectl.kubernetes.io/last-applied-configuration: {}
858 f:labels:
859 .: {}
860 f:name: {}
861 f:spec:
862 f:containers:
863 k:{"name":"kubernetes-pause"}:
864 .: {}
865 f:image: {}
866 f:imagePullPolicy: {}
867 f:name: {}
868 f:resources: {}
869 f:terminationMessagePath: {}
870 f:terminationMessagePolicy: {}
871 f:dnsPolicy: {}
872 f:enableServiceLinks: {}
873 f:restartPolicy: {}
874 f:schedulerName: {}
875 f:securityContext: {}
876 f:terminationGracePeriodSeconds: {}
877 manager: kubectl-client-side-apply
878 operation: Update
879 time: "2022-11-03T23:22:40Z"
880 name: test
881 namespace: default
882 `),
883 ExpectedObject: []byte(`
884 apiVersion: v1
885 data: {}
886 kind: ConfigMap
887 metadata:
888 resourceVersion: "1"
889 annotations:
890 kubectl.kubernetes.io/last-applied-configuration: |
891 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
892 creationTimestamp: "2022-08-22T23:08:23Z"
893 managedFields:
894 - apiVersion: v1
895 fieldsType: FieldsV1
896 fieldsV1:
897 f:status:
898 f:conditions:
899 .: {}
900 k:{"type":"PodScheduled"}:
901 .: {}
902 f:lastProbeTime: {}
903 f:lastTransitionTime: {}
904 f:message: {}
905 f:reason: {}
906 f:status: {}
907 f:type: {}
908 f:metadata:
909 f:annotations:
910 .: {}
911 f:kubectl.kubernetes.io/last-applied-configuration: {}
912 f:labels:
913 .: {}
914 f:name: {}
915 f:spec:
916 f:containers:
917 k:{"name":"kubernetes-pause"}:
918 .: {}
919 f:image: {}
920 f:imagePullPolicy: {}
921 f:name: {}
922 f:resources: {}
923 f:terminationMessagePath: {}
924 f:terminationMessagePolicy: {}
925 f:dnsPolicy: {}
926 f:enableServiceLinks: {}
927 f:restartPolicy: {}
928 f:schedulerName: {}
929 f:securityContext: {}
930 f:terminationGracePeriodSeconds: {}
931 manager: kubectl
932 operation: Apply
933 name: test
934 namespace: default
935 `),
936 },
937 {
938
939 Name: "multipleTargetsNewInsertion",
940 CSAManagers: []string{"kubectl-client-side-apply", "kube-scheduler"},
941 SSAManager: "newly-inserted-manager",
942 OriginalObject: []byte(`
943 apiVersion: v1
944 data: {}
945 kind: ConfigMap
946 metadata:
947 resourceVersion: "1"
948 annotations:
949 kubectl.kubernetes.io/last-applied-configuration: |
950 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
951 creationTimestamp: "2022-08-22T23:08:23Z"
952 managedFields:
953 - apiVersion: v1
954 fieldsType: FieldsV1
955 fieldsV1:
956 f:metadata:
957 f:labels:
958 f:name: {}
959 f:spec:
960 f:containers:
961 k:{"name":"kubernetes-pause"}:
962 .: {}
963 f:image: {}
964 f:name: {}
965 manager: kubectl
966 operation: Apply
967 - apiVersion: v1
968 fieldsType: FieldsV1
969 fieldsV1:
970 f:status:
971 f:conditions:
972 .: {}
973 k:{"type":"PodScheduled"}:
974 .: {}
975 f:lastProbeTime: {}
976 f:lastTransitionTime: {}
977 f:message: {}
978 f:reason: {}
979 f:status: {}
980 f:type: {}
981 manager: kube-scheduler
982 operation: Update
983 time: "2022-11-03T23:22:40Z"
984 - apiVersion: v1
985 fieldsType: FieldsV1
986 fieldsV1:
987 f:metadata:
988 f:annotations:
989 .: {}
990 f:kubectl.kubernetes.io/last-applied-configuration: {}
991 f:labels:
992 .: {}
993 f:name: {}
994 f:spec:
995 f:containers:
996 k:{"name":"kubernetes-pause"}:
997 .: {}
998 f:image: {}
999 f:imagePullPolicy: {}
1000 f:name: {}
1001 f:resources: {}
1002 f:terminationMessagePath: {}
1003 f:terminationMessagePolicy: {}
1004 f:dnsPolicy: {}
1005 f:enableServiceLinks: {}
1006 f:restartPolicy: {}
1007 f:schedulerName: {}
1008 f:securityContext: {}
1009 f:terminationGracePeriodSeconds: {}
1010 manager: kubectl-client-side-apply
1011 operation: Update
1012 time: "2022-11-03T23:22:40Z"
1013 name: test
1014 namespace: default
1015 `),
1016 ExpectedObject: []byte(`
1017 apiVersion: v1
1018 data: {}
1019 kind: ConfigMap
1020 metadata:
1021 resourceVersion: "1"
1022 annotations:
1023 kubectl.kubernetes.io/last-applied-configuration: |
1024 {"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
1025 creationTimestamp: "2022-08-22T23:08:23Z"
1026 managedFields:
1027 - apiVersion: v1
1028 fieldsType: FieldsV1
1029 fieldsV1:
1030 f:metadata:
1031 f:labels:
1032 f:name: {}
1033 f:spec:
1034 f:containers:
1035 k:{"name":"kubernetes-pause"}:
1036 .: {}
1037 f:image: {}
1038 f:name: {}
1039 manager: kubectl
1040 operation: Apply
1041 - apiVersion: v1
1042 fieldsType: FieldsV1
1043 fieldsV1:
1044 f:metadata:
1045 f:annotations:
1046 .: {}
1047 f:kubectl.kubernetes.io/last-applied-configuration: {}
1048 f:labels:
1049 .: {}
1050 f:name: {}
1051 f:spec:
1052 f:containers:
1053 k:{"name":"kubernetes-pause"}:
1054 .: {}
1055 f:image: {}
1056 f:imagePullPolicy: {}
1057 f:name: {}
1058 f:resources: {}
1059 f:terminationMessagePath: {}
1060 f:terminationMessagePolicy: {}
1061 f:dnsPolicy: {}
1062 f:enableServiceLinks: {}
1063 f:restartPolicy: {}
1064 f:schedulerName: {}
1065 f:securityContext: {}
1066 f:terminationGracePeriodSeconds: {}
1067 f:status:
1068 f:conditions:
1069 .: {}
1070 k:{"type":"PodScheduled"}:
1071 .: {}
1072 f:lastProbeTime: {}
1073 f:lastTransitionTime: {}
1074 f:message: {}
1075 f:reason: {}
1076 f:status: {}
1077 f:type: {}
1078 manager: newly-inserted-manager
1079 operation: Apply
1080 time: "2022-11-03T23:22:40Z"
1081 name: test
1082 namespace: default
1083 `),
1084 },
1085 {
1086
1087 Name: "subresource",
1088 CSAManagers: []string{"kube-controller-manager"},
1089 SSAManager: "kube-controller-manager",
1090 Options: []csaupgrade.Option{csaupgrade.Subresource("status")},
1091 OriginalObject: []byte(`
1092 apiVersion: v1
1093 kind: PersistentVolumeClaim
1094 metadata:
1095 annotations:
1096 pv.kubernetes.io/bind-completed: "yes"
1097 pv.kubernetes.io/bound-by-controller: "yes"
1098 volume.beta.kubernetes.io/storage-provisioner: openshift-storage.cephfs.csi.ceph.com
1099 volume.kubernetes.io/storage-provisioner: openshift-storage.cephfs.csi.ceph.com
1100 creationTimestamp: "2024-02-24T15:24:31Z"
1101 finalizers:
1102 - kubernetes.io/pvc-protection
1103 managedFields:
1104 - apiVersion: v1
1105 fieldsType: FieldsV1
1106 fieldsV1:
1107 f:spec:
1108 f:accessModes: {}
1109 f:resources:
1110 f:requests:
1111 .: {}
1112 f:storage: {}
1113 f:storageClassName: {}
1114 f:volumeMode: {}
1115 manager: Mozilla
1116 operation: Update
1117 time: "2024-02-24T15:24:31Z"
1118 - apiVersion: v1
1119 fieldsType: FieldsV1
1120 fieldsV1:
1121 f:metadata:
1122 f:annotations:
1123 .: {}
1124 f:pv.kubernetes.io/bind-completed: {}
1125 f:pv.kubernetes.io/bound-by-controller: {}
1126 f:volume.beta.kubernetes.io/storage-provisioner: {}
1127 f:volume.kubernetes.io/storage-provisioner: {}
1128 f:spec:
1129 f:volumeName: {}
1130 manager: kube-controller-manager
1131 operation: Update
1132 time: "2024-02-24T15:24:32Z"
1133 - apiVersion: v1
1134 fieldsType: FieldsV1
1135 fieldsV1:
1136 f:status:
1137 f:accessModes: {}
1138 f:capacity:
1139 .: {}
1140 f:storage: {}
1141 f:phase: {}
1142 manager: kube-controller-manager
1143 operation: Update
1144 subresource: status
1145 time: "2024-02-24T15:24:32Z"
1146 name: test
1147 namespace: default
1148 resourceVersion: "948647140"
1149 uid: f0692a61-0ffe-4fd5-b00f-0b95f3654fb9
1150 spec:
1151 accessModes:
1152 - ReadWriteOnce
1153 resources:
1154 requests:
1155 storage: 1Gi
1156 storageClassName: ocs-storagecluster-cephfs
1157 volumeMode: Filesystem
1158 volumeName: pvc-f0692a61-0ffe-4fd5-b00f-0b95f3654fb9
1159 status:
1160 accessModes:
1161 - ReadWriteOnce
1162 capacity:
1163 storage: 1Gi
1164 phase: Bound
1165 `),
1166 ExpectedObject: []byte(`
1167 apiVersion: v1
1168 kind: PersistentVolumeClaim
1169 metadata:
1170 annotations:
1171 pv.kubernetes.io/bind-completed: "yes"
1172 pv.kubernetes.io/bound-by-controller: "yes"
1173 volume.beta.kubernetes.io/storage-provisioner: openshift-storage.cephfs.csi.ceph.com
1174 volume.kubernetes.io/storage-provisioner: openshift-storage.cephfs.csi.ceph.com
1175 creationTimestamp: "2024-02-24T15:24:31Z"
1176 finalizers:
1177 - kubernetes.io/pvc-protection
1178 managedFields:
1179 - apiVersion: v1
1180 fieldsType: FieldsV1
1181 fieldsV1:
1182 f:spec:
1183 f:accessModes: {}
1184 f:resources:
1185 f:requests:
1186 .: {}
1187 f:storage: {}
1188 f:storageClassName: {}
1189 f:volumeMode: {}
1190 manager: Mozilla
1191 operation: Update
1192 time: "2024-02-24T15:24:31Z"
1193 - apiVersion: v1
1194 fieldsType: FieldsV1
1195 fieldsV1:
1196 f:metadata:
1197 f:annotations:
1198 .: {}
1199 f:pv.kubernetes.io/bind-completed: {}
1200 f:pv.kubernetes.io/bound-by-controller: {}
1201 f:volume.beta.kubernetes.io/storage-provisioner: {}
1202 f:volume.kubernetes.io/storage-provisioner: {}
1203 f:spec:
1204 f:volumeName: {}
1205 manager: kube-controller-manager
1206 operation: Update
1207 time: "2024-02-24T15:24:32Z"
1208 - apiVersion: v1
1209 fieldsType: FieldsV1
1210 fieldsV1:
1211 f:status:
1212 f:accessModes: {}
1213 f:capacity:
1214 .: {}
1215 f:storage: {}
1216 f:phase: {}
1217 manager: kube-controller-manager
1218 operation: Apply
1219 subresource: status
1220 time: "2024-02-24T15:24:32Z"
1221 name: test
1222 namespace: default
1223 resourceVersion: "948647140"
1224 uid: f0692a61-0ffe-4fd5-b00f-0b95f3654fb9
1225 spec:
1226 accessModes:
1227 - ReadWriteOnce
1228 resources:
1229 requests:
1230 storage: 1Gi
1231 storageClassName: ocs-storagecluster-cephfs
1232 volumeMode: Filesystem
1233 volumeName: pvc-f0692a61-0ffe-4fd5-b00f-0b95f3654fb9
1234 status:
1235 accessModes:
1236 - ReadWriteOnce
1237 capacity:
1238 storage: 1Gi
1239 phase: Bound
1240 `),
1241 },
1242 }
1243
1244 for _, testCase := range cases {
1245 t.Run(testCase.Name, func(t *testing.T) {
1246 initialObject := unstructured.Unstructured{}
1247 err := yaml.Unmarshal(testCase.OriginalObject, &initialObject.Object)
1248 if err != nil {
1249 t.Fatal(err)
1250 }
1251
1252 upgraded := initialObject.DeepCopy()
1253 err = csaupgrade.UpgradeManagedFields(
1254 upgraded,
1255 sets.New(testCase.CSAManagers...),
1256 testCase.SSAManager,
1257 testCase.Options...,
1258 )
1259
1260 if err != nil {
1261 t.Fatal(err)
1262 }
1263
1264 expectedObject := unstructured.Unstructured{}
1265 err = yaml.Unmarshal(testCase.ExpectedObject, &expectedObject.Object)
1266 if err != nil {
1267 t.Fatal(err)
1268 }
1269
1270 if !reflect.DeepEqual(&expectedObject, upgraded) {
1271 t.Fatal(cmp.Diff(&expectedObject, upgraded))
1272 }
1273
1274
1275
1276
1277
1278 initialCopy := initialObject.DeepCopyObject()
1279 patchBytes, err := csaupgrade.UpgradeManagedFieldsPatch(
1280 initialCopy, sets.New(testCase.CSAManagers...), testCase.SSAManager, testCase.Options...)
1281
1282 if err != nil {
1283 t.Fatal(err)
1284 } else if patchBytes != nil {
1285 patch, err := jsonpatch.DecodePatch(patchBytes)
1286 if err != nil {
1287 t.Fatal(err)
1288 }
1289
1290 initialJSON, err := json.Marshal(initialObject.Object)
1291 if err != nil {
1292 t.Fatal(err)
1293 }
1294
1295 patchedBytes, err := patch.Apply(initialJSON)
1296 if err != nil {
1297 t.Fatal(err)
1298 }
1299
1300 var patched unstructured.Unstructured
1301 if err := json.Unmarshal(patchedBytes, &patched.Object); err != nil {
1302 t.Fatal(err)
1303 }
1304
1305 if !reflect.DeepEqual(&patched, upgraded) {
1306 t.Fatalf("expected patch to produce an upgraded object: %v", cmp.Diff(patched, upgraded))
1307 }
1308 }
1309 })
1310 }
1311 }
1312
View as plain text