1
16
17 package apiserver
18
19 import (
20 "bytes"
21 "context"
22 "encoding/json"
23 "flag"
24 "fmt"
25 "net/http"
26 "reflect"
27 "strings"
28 "testing"
29 "time"
30
31 "github.com/google/go-cmp/cmp"
32 "github.com/stretchr/testify/require"
33 "sigs.k8s.io/yaml"
34
35 appsv1 "k8s.io/api/apps/v1"
36 v1 "k8s.io/api/core/v1"
37 apierrors "k8s.io/apimachinery/pkg/api/errors"
38 "k8s.io/apimachinery/pkg/api/meta"
39 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40 "k8s.io/apimachinery/pkg/runtime"
41 "k8s.io/apimachinery/pkg/types"
42 "k8s.io/apimachinery/pkg/util/wait"
43 yamlutil "k8s.io/apimachinery/pkg/util/yaml"
44 clientset "k8s.io/client-go/kubernetes"
45 restclient "k8s.io/client-go/rest"
46 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
47 "k8s.io/kubernetes/test/integration/framework"
48 )
49
50 func setup(t testing.TB) (clientset.Interface, kubeapiservertesting.TearDownFunc) {
51
52 server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
53
54 config := restclient.CopyConfig(server.ClientConfig)
55
56
57 config.ContentType = runtime.ContentTypeJSON
58 clientSet, err := clientset.NewForConfig(config)
59 if err != nil {
60 t.Fatalf("Error in create clientset: %v", err)
61 }
62 return clientSet, server.TearDownFn
63 }
64
65
66
67
68 func TestApplyAlsoCreates(t *testing.T) {
69 client, closeFn := setup(t)
70 defer closeFn()
71
72 testCases := []struct {
73 resource string
74 name string
75 body string
76 }{
77 {
78 resource: "pods",
79 name: "test-pod",
80 body: `{
81 "apiVersion": "v1",
82 "kind": "Pod",
83 "metadata": {
84 "name": "test-pod"
85 },
86 "spec": {
87 "containers": [{
88 "name": "test-container",
89 "image": "test-image"
90 }]
91 }
92 }`,
93 }, {
94 resource: "services",
95 name: "test-svc",
96 body: `{
97 "apiVersion": "v1",
98 "kind": "Service",
99 "metadata": {
100 "name": "test-svc"
101 },
102 "spec": {
103 "ports": [{
104 "port": 8080,
105 "protocol": "UDP"
106 }]
107 }
108 }`,
109 },
110 }
111
112 for _, tc := range testCases {
113 t.Run(tc.name, func(t *testing.T) {
114 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
115 Namespace("default").
116 Resource(tc.resource).
117 Name(tc.name).
118 Param("fieldManager", "apply_test").
119 Body([]byte(tc.body)).
120 Do(context.TODO()).
121 Get()
122 if err != nil {
123 t.Fatalf("Failed to create object using Apply patch: %v", err)
124 }
125
126 _, err = client.CoreV1().RESTClient().Get().Namespace("default").Resource(tc.resource).Name(tc.name).Do(context.TODO()).Get()
127 if err != nil {
128 t.Fatalf("Failed to retrieve object: %v", err)
129 }
130
131
132 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
133 Namespace("default").
134 Resource(tc.resource).
135 Name(tc.name).
136 Param("fieldManager", "apply_test_2").
137 Body([]byte(tc.body)).
138 Do(context.TODO()).
139 Get()
140 if err != nil {
141 t.Fatalf("Failed to re-apply object using Apply patch: %v", err)
142 }
143 })
144 }
145 }
146
147
148
149 func TestNoOpUpdateSameResourceVersion(t *testing.T) {
150 client, closeFn := setup(t)
151 defer closeFn()
152
153 podName := "no-op"
154 podResource := "pods"
155 podBytes := []byte(`{
156 "apiVersion": "v1",
157 "kind": "Pod",
158 "metadata": {
159 "name": "` + podName + `",
160 "labels": {
161 "a": "one",
162 "c": "two",
163 "b": "three"
164 }
165 },
166 "spec": {
167 "containers": [{
168 "name": "test-container-a",
169 "image": "test-image-one"
170 },{
171 "name": "test-container-c",
172 "image": "test-image-two"
173 },{
174 "name": "test-container-b",
175 "image": "test-image-three"
176 }]
177 }
178 }`)
179
180 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
181 Namespace("default").
182 Param("fieldManager", "apply_test").
183 Resource(podResource).
184 Name(podName).
185 Body(podBytes).
186 Do(context.TODO()).
187 Get()
188 if err != nil {
189 t.Fatalf("Failed to create object: %v", err)
190 }
191
192
193 time.Sleep(1 * time.Second)
194
195 createdObject, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource(podResource).Name(podName).Do(context.TODO()).Get()
196 if err != nil {
197 t.Fatalf("Failed to retrieve created object: %v", err)
198 }
199
200 createdAccessor, err := meta.Accessor(createdObject)
201 if err != nil {
202 t.Fatalf("Failed to get meta accessor for created object: %v", err)
203 }
204
205 createdBytes, err := json.MarshalIndent(createdObject, "\t", "\t")
206 if err != nil {
207 t.Fatalf("Failed to marshal created object: %v", err)
208 }
209
210
211 _, err = client.CoreV1().RESTClient().Put().
212 Namespace("default").
213 Resource(podResource).
214 Name(podName).
215 Body(createdBytes).
216 Do(context.TODO()).
217 Get()
218 if err != nil {
219 t.Fatalf("Failed to apply no-op update: %v", err)
220 }
221
222 updatedObject, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource(podResource).Name(podName).Do(context.TODO()).Get()
223 if err != nil {
224 t.Fatalf("Failed to retrieve updated object: %v", err)
225 }
226
227 updatedAccessor, err := meta.Accessor(updatedObject)
228 if err != nil {
229 t.Fatalf("Failed to get meta accessor for updated object: %v", err)
230 }
231
232 updatedBytes, err := json.MarshalIndent(updatedObject, "\t", "\t")
233 if err != nil {
234 t.Fatalf("Failed to marshal updated object: %v", err)
235 }
236
237 if createdAccessor.GetResourceVersion() != updatedAccessor.GetResourceVersion() {
238 t.Fatalf("Expected same resource version to be %v but got: %v\nold object:\n%v\nnew object:\n%v",
239 createdAccessor.GetResourceVersion(),
240 updatedAccessor.GetResourceVersion(),
241 string(createdBytes),
242 string(updatedBytes),
243 )
244 }
245 }
246
247 func getRV(obj runtime.Object) (string, error) {
248 acc, err := meta.Accessor(obj)
249 if err != nil {
250 return "", err
251 }
252 return acc.GetResourceVersion(), nil
253 }
254
255 func TestNoopChangeCreationTime(t *testing.T) {
256 client, closeFn := setup(t)
257 defer closeFn()
258
259 ssBytes := []byte(`{
260 "apiVersion": "v1",
261 "kind": "ConfigMap",
262 "metadata": {
263 "name": "myconfig",
264 "creationTimestamp": null,
265 "resourceVersion": null
266 },
267 "data": {
268 "key": "value"
269 }
270 }`)
271
272 obj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
273 Namespace("default").
274 Param("fieldManager", "apply_test").
275 Resource("configmaps").
276 Name("myconfig").
277 Body(ssBytes).
278 Do(context.TODO()).
279 Get()
280 if err != nil {
281 t.Fatalf("Failed to create object: %v", err)
282 }
283
284 require.NoError(t, err)
285
286 time.Sleep(1200 * time.Millisecond)
287
288 newObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
289 Namespace("default").
290 Param("fieldManager", "apply_test").
291 Resource("configmaps").
292 Name("myconfig").
293 Body(ssBytes).
294 Do(context.TODO()).
295 Get()
296 if err != nil {
297 t.Fatalf("Failed to create object: %v", err)
298 }
299
300 require.NoError(t, err)
301 require.Equal(t, obj, newObj)
302 }
303
304
305
306
307
308
309
310 func TestNoSemanticUpdateApplySameResourceVersion(t *testing.T) {
311 client, closeFn := setup(t)
312 defer closeFn()
313
314 ssBytes := []byte(`{
315 "apiVersion": "apps/v1",
316 "kind": "StatefulSet",
317 "metadata": {
318 "name": "nginx",
319 "labels": {"app": "nginx"}
320 },
321 "spec": {
322 "serviceName": "nginx",
323 "selector": { "matchLabels": {"app": "nginx"}},
324 "template": {
325 "metadata": {
326 "labels": {"app": "nginx"}
327 },
328 "spec": {
329 "containers": [{
330 "name": "nginx",
331 "image": "nginx",
332 "resources": {
333 "limits": {"memory": "2048Mi"}
334 }
335 }]
336 }
337 },
338 "volumeClaimTemplates": [{
339 "metadata": {"name": "nginx"},
340 "spec": {
341 "accessModes": ["ReadWriteOnce"],
342 "resources": {"requests": {"storage": "1Gi"}}
343 }
344 }]
345 }
346 }`)
347
348 obj, err := client.AppsV1().RESTClient().Patch(types.ApplyPatchType).
349 Namespace("default").
350 Param("fieldManager", "apply_test").
351 Resource("statefulsets").
352 Name("nginx").
353 Body(ssBytes).
354 Do(context.TODO()).
355 Get()
356 if err != nil {
357 t.Fatalf("Failed to create object: %v", err)
358 }
359
360 rvCreated, err := getRV(obj)
361 if err != nil {
362 t.Fatalf("Failed to get RV: %v", err)
363 }
364
365
366 time.Sleep(1200 * time.Millisecond)
367
368 obj, err = client.AppsV1().RESTClient().Patch(types.ApplyPatchType).
369 Namespace("default").
370 Param("fieldManager", "apply_test").
371 Resource("statefulsets").
372 Name("nginx").
373 Body(ssBytes).
374 Do(context.TODO()).
375 Get()
376 if err != nil {
377 t.Fatalf("Failed to create object: %v", err)
378 }
379 rvApplied, err := getRV(obj)
380 if err != nil {
381 t.Fatalf("Failed to get RV: %v", err)
382 }
383 if rvApplied != rvCreated {
384 t.Fatal("ResourceVersion changed after apply")
385 }
386 }
387
388
389
390
391
392
393
394 func TestNoSemanticUpdatePutSameResourceVersion(t *testing.T) {
395 client, closeFn := setup(t)
396 defer closeFn()
397
398 ssBytes := []byte(`{
399 "apiVersion": "apps/v1",
400 "kind": "StatefulSet",
401 "metadata": {
402 "name": "nginx",
403 "labels": {"app": "nginx"}
404 },
405 "spec": {
406 "serviceName": "nginx",
407 "selector": { "matchLabels": {"app": "nginx"}},
408 "template": {
409 "metadata": {
410 "labels": {"app": "nginx"}
411 },
412 "spec": {
413 "containers": [{
414 "name": "nginx",
415 "image": "nginx",
416 "resources": {
417 "limits": {"memory": "2048Mi"}
418 }
419 }]
420 }
421 },
422 "volumeClaimTemplates": [{
423 "metadata": {"name": "nginx"},
424 "spec": {
425 "accessModes": ["ReadWriteOnce"],
426 "resources": { "requests": { "storage": "1Gi"}}
427 }
428 }]
429 }
430 }`)
431
432 obj, err := client.AppsV1().RESTClient().Post().
433 Namespace("default").
434 Param("fieldManager", "apply_test").
435 Resource("statefulsets").
436 Body(ssBytes).
437 Do(context.TODO()).
438 Get()
439 if err != nil {
440 t.Fatalf("Failed to create object: %v", err)
441 }
442
443 rvCreated, err := getRV(obj)
444 if err != nil {
445 t.Fatalf("Failed to get RV: %v", err)
446 }
447
448
449 time.Sleep(1200 * time.Millisecond)
450
451 obj, err = client.AppsV1().RESTClient().Put().
452 Namespace("default").
453 Param("fieldManager", "apply_test").
454 Resource("statefulsets").
455 Name("nginx").
456 Body(ssBytes).
457 Do(context.TODO()).
458 Get()
459 if err != nil {
460 t.Fatalf("Failed to create object: %v", err)
461 }
462 rvApplied, err := getRV(obj)
463 if err != nil {
464 t.Fatalf("Failed to get RV: %v", err)
465 }
466 if rvApplied != rvCreated {
467 t.Fatal("ResourceVersion changed after similar PUT")
468 }
469 }
470
471
472
473 func TestCreateOnApplyFailsWithUID(t *testing.T) {
474 client, closeFn := setup(t)
475 defer closeFn()
476
477 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
478 Namespace("default").
479 Resource("pods").
480 Name("test-pod-uid").
481 Param("fieldManager", "apply_test").
482 Body([]byte(`{
483 "apiVersion": "v1",
484 "kind": "Pod",
485 "metadata": {
486 "name": "test-pod-uid",
487 "uid": "88e00824-7f0e-11e8-94a1-c8d3ffb15800"
488 },
489 "spec": {
490 "containers": [{
491 "name": "test-container",
492 "image": "test-image"
493 }]
494 }
495 }`)).
496 Do(context.TODO()).
497 Get()
498 if !apierrors.IsConflict(err) {
499 t.Fatalf("Expected conflict error but got: %v", err)
500 }
501 }
502
503 func TestApplyUpdateApplyConflictForced(t *testing.T) {
504 client, closeFn := setup(t)
505 defer closeFn()
506
507 obj := []byte(`{
508 "apiVersion": "apps/v1",
509 "kind": "Deployment",
510 "metadata": {
511 "name": "deployment",
512 "labels": {"app": "nginx"}
513 },
514 "spec": {
515 "replicas": 3,
516 "selector": {
517 "matchLabels": {
518 "app": "nginx"
519 }
520 },
521 "template": {
522 "metadata": {
523 "labels": {
524 "app": "nginx"
525 }
526 },
527 "spec": {
528 "containers": [{
529 "name": "nginx",
530 "image": "nginx:latest"
531 }]
532 }
533 }
534 }
535 }`)
536
537 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
538 AbsPath("/apis/apps/v1").
539 Namespace("default").
540 Resource("deployments").
541 Name("deployment").
542 Param("fieldManager", "apply_test").
543 Body(obj).Do(context.TODO()).Get()
544 if err != nil {
545 t.Fatalf("Failed to create object using Apply patch: %v", err)
546 }
547
548 _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
549 AbsPath("/apis/apps/v1").
550 Namespace("default").
551 Resource("deployments").
552 Name("deployment").
553 Body([]byte(`{"spec":{"replicas": 5}}`)).Do(context.TODO()).Get()
554 if err != nil {
555 t.Fatalf("Failed to patch object: %v", err)
556 }
557
558 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
559 AbsPath("/apis/apps/v1").
560 Namespace("default").
561 Resource("deployments").
562 Name("deployment").
563 Param("fieldManager", "apply_test").
564 Body([]byte(obj)).Do(context.TODO()).Get()
565 if err == nil {
566 t.Fatalf("Expecting to get conflicts when applying object")
567 }
568 status, ok := err.(*apierrors.StatusError)
569 if !ok {
570 t.Fatalf("Expecting to get conflicts as API error")
571 }
572 if len(status.Status().Details.Causes) < 1 {
573 t.Fatalf("Expecting to get at least one conflict when applying object, got: %v", status.Status().Details.Causes)
574 }
575
576 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
577 AbsPath("/apis/apps/v1").
578 Namespace("default").
579 Resource("deployments").
580 Name("deployment").
581 Param("force", "true").
582 Param("fieldManager", "apply_test").
583 Body([]byte(obj)).Do(context.TODO()).Get()
584 if err != nil {
585 t.Fatalf("Failed to apply object with force: %v", err)
586 }
587 }
588
589
590
591 func TestApplyGroupsManySeparateUpdates(t *testing.T) {
592 client, closeFn := setup(t)
593 defer closeFn()
594
595 obj := []byte(`{
596 "apiVersion": "admissionregistration.k8s.io/v1",
597 "kind": "ValidatingWebhookConfiguration",
598 "metadata": {
599 "name": "webhook",
600 "labels": {"applier":"true"},
601 },
602 }`)
603
604 object, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
605 AbsPath("/apis/admissionregistration.k8s.io/v1").
606 Resource("validatingwebhookconfigurations").
607 Name("webhook").
608 Param("fieldManager", "apply_test").
609 Body(obj).Do(context.TODO()).Get()
610 if err != nil {
611 t.Fatalf("Failed to create object using Apply patch: %v", err)
612 }
613
614 for i := 0; i < 20; i++ {
615 unique := fmt.Sprintf("updater%v", i)
616 object, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
617 AbsPath("/apis/admissionregistration.k8s.io/v1").
618 Resource("validatingwebhookconfigurations").
619 Name("webhook").
620 Param("fieldManager", unique).
621 Body([]byte(`{"metadata":{"labels":{"` + unique + `":"new"}}}`)).Do(context.TODO()).Get()
622 if err != nil {
623 t.Fatalf("Failed to patch object: %v", err)
624 }
625 }
626
627 accessor, err := meta.Accessor(object)
628 if err != nil {
629 t.Fatalf("Failed to get meta accessor: %v", err)
630 }
631
632
633 if actual, expected := len(accessor.GetManagedFields()), 11; actual != expected {
634 if b, err := json.MarshalIndent(object, "\t", "\t"); err == nil {
635 t.Fatalf("Object expected to contain %v entries in managedFields, but got %v:\n%v", expected, actual, string(b))
636 } else {
637 t.Fatalf("Object expected to contain %v entries in managedFields, but got %v: error marshalling object: %v", expected, actual, err)
638 }
639 }
640
641
642 if actual, expected := accessor.GetManagedFields()[0].Manager, "apply_test"; actual != expected {
643 t.Fatalf("Expected first manager to be named %v but got %v", expected, actual)
644 }
645
646
647 if actual, expected := accessor.GetManagedFields()[1].Manager, "ancient-changes"; actual != expected {
648 t.Fatalf("Expected first manager to be named %v but got %v", expected, actual)
649 }
650 }
651
652
653 func TestCreateVeryLargeObject(t *testing.T) {
654 client, closeFn := setup(t)
655 defer closeFn()
656
657 cfg := &v1.ConfigMap{
658 ObjectMeta: metav1.ObjectMeta{
659 Name: "large-create-test-cm",
660 Namespace: "default",
661 },
662 Data: map[string]string{},
663 }
664
665 for i := 0; i < 9999; i++ {
666 unique := fmt.Sprintf("this-key-is-very-long-so-as-to-create-a-very-large-serialized-fieldset-%v", i)
667 cfg.Data[unique] = "A"
668 }
669
670
671 if _, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(context.TODO(), cfg, metav1.CreateOptions{}); err != nil {
672 t.Errorf("unable to create large test configMap: %v", err)
673 }
674
675
676 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
677 Namespace(cfg.Namespace).
678 Resource("configmaps").
679 Name(cfg.Name).
680 Param("fieldManager", "apply_test").
681 Body([]byte(`{
682 "apiVersion": "v1",
683 "kind": "ConfigMap",
684 "metadata": {
685 "name": "large-create-test-cm",
686 "namespace": "default",
687 }
688 }`)).
689 Do(context.TODO()).
690 Get()
691 if err == nil {
692 t.Fatalf("expected to fail to update object using Apply patch, but succeeded")
693 }
694 }
695
696
697 func TestUpdateVeryLargeObject(t *testing.T) {
698 client, closeFn := setup(t)
699 defer closeFn()
700
701 cfg := &v1.ConfigMap{
702 ObjectMeta: metav1.ObjectMeta{
703 Name: "large-update-test-cm",
704 Namespace: "default",
705 },
706 Data: map[string]string{"k": "v"},
707 }
708
709
710 cfg, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(context.TODO(), cfg, metav1.CreateOptions{})
711 if err != nil {
712 t.Errorf("unable to create configMap: %v", err)
713 }
714
715
716 var updateErr error
717 pollErr := wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) {
718 updateCfg, err := client.CoreV1().ConfigMaps(cfg.Namespace).Get(context.TODO(), cfg.Name, metav1.GetOptions{})
719 if err != nil {
720 return false, err
721 }
722
723
724 for i := 0; i < 9999; i++ {
725 unique := fmt.Sprintf("this-key-is-very-long-so-as-to-create-a-very-large-serialized-fieldset-%v", i)
726 updateCfg.Data[unique] = "A"
727 }
728
729 if _, err = client.CoreV1().ConfigMaps(cfg.Namespace).Update(context.TODO(), updateCfg, metav1.UpdateOptions{}); err == nil {
730 return true, nil
731 }
732 updateErr = err
733 return false, nil
734 })
735 if pollErr == wait.ErrWaitTimeout {
736 t.Errorf("unable to update configMap: %v", updateErr)
737 }
738
739
740 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
741 Namespace(cfg.Namespace).
742 Resource("configmaps").
743 Name(cfg.Name).
744 Param("fieldManager", "apply_test").
745 Body([]byte(`{
746 "apiVersion": "v1",
747 "kind": "ConfigMap",
748 "metadata": {
749 "name": "large-update-test-cm",
750 "namespace": "default",
751 }
752 }`)).
753 Do(context.TODO()).
754 Get()
755 if err == nil {
756 t.Fatalf("expected to fail to update object using Apply patch, but succeeded")
757 }
758 }
759
760
761 func TestPatchVeryLargeObject(t *testing.T) {
762 client, closeFn := setup(t)
763 defer closeFn()
764
765 cfg := &v1.ConfigMap{
766 ObjectMeta: metav1.ObjectMeta{
767 Name: "large-patch-test-cm",
768 Namespace: "default",
769 },
770 Data: map[string]string{"k": "v"},
771 }
772
773
774 if _, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(context.TODO(), cfg, metav1.CreateOptions{}); err != nil {
775 t.Errorf("unable to create configMap: %v", err)
776 }
777
778 patchString := `{"data":{"k":"v"`
779 for i := 0; i < 9999; i++ {
780 unique := fmt.Sprintf("this-key-is-very-long-so-as-to-create-a-very-large-serialized-fieldset-%v", i)
781 patchString = fmt.Sprintf("%s,%q:%q", patchString, unique, "A")
782 }
783 patchString = fmt.Sprintf("%s}}", patchString)
784
785
786 _, err := client.CoreV1().RESTClient().Patch(types.MergePatchType).
787 AbsPath("/api/v1").
788 Namespace(cfg.Namespace).
789 Resource("configmaps").
790 Name(cfg.Name).
791 Body([]byte(patchString)).Do(context.TODO()).Get()
792 if err != nil {
793 t.Errorf("unable to patch configMap: %v", err)
794 }
795
796
797 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
798 Namespace("default").
799 Resource("configmaps").
800 Name("large-patch-test-cm").
801 Param("fieldManager", "apply_test").
802 Body([]byte(`{
803 "apiVersion": "v1",
804 "kind": "ConfigMap",
805 "metadata": {
806 "name": "large-patch-test-cm",
807 "namespace": "default",
808 }
809 }`)).
810 Do(context.TODO()).
811 Get()
812 if err == nil {
813 t.Fatalf("expected to fail to update object using Apply patch, but succeeded")
814 }
815 }
816
817
818 func TestApplyManagedFields(t *testing.T) {
819 client, closeFn := setup(t)
820 defer closeFn()
821
822 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
823 Namespace("default").
824 Resource("configmaps").
825 Name("test-cm").
826 Param("fieldManager", "apply_test").
827 Body([]byte(`{
828 "apiVersion": "v1",
829 "kind": "ConfigMap",
830 "metadata": {
831 "name": "test-cm",
832 "namespace": "default",
833 "labels": {
834 "test-label": "test"
835 }
836 },
837 "data": {
838 "key": "value"
839 }
840 }`)).
841 Do(context.TODO()).
842 Get()
843 if err != nil {
844 t.Fatalf("Failed to create object using Apply patch: %v", err)
845 }
846
847 _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
848 Namespace("default").
849 Resource("configmaps").
850 Name("test-cm").
851 Param("fieldManager", "updater").
852 Body([]byte(`{"data":{"new-key": "value"}}`)).Do(context.TODO()).Get()
853 if err != nil {
854 t.Fatalf("Failed to patch object: %v", err)
855 }
856
857
858
859
860 time.Sleep(1 * time.Second)
861
862 _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
863 Namespace("default").
864 Resource("configmaps").
865 Name("test-cm").
866 Param("fieldManager", "updater").
867 Body([]byte(`{"data":{"key": "new value"}}`)).Do(context.TODO()).Get()
868 if err != nil {
869 t.Fatalf("Failed to patch object: %v", err)
870 }
871
872 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
873 if err != nil {
874 t.Fatalf("Failed to retrieve object: %v", err)
875 }
876
877 accessor, err := meta.Accessor(object)
878 if err != nil {
879 t.Fatalf("Failed to get meta accessor: %v", err)
880 }
881
882 actual, err := json.MarshalIndent(object, "\t", "\t")
883 if err != nil {
884 t.Fatalf("Failed to marshal object: %v", err)
885 }
886
887 expected := []byte(`{
888 "metadata": {
889 "name": "test-cm",
890 "namespace": "default",
891 "uid": "` + string(accessor.GetUID()) + `",
892 "resourceVersion": "` + accessor.GetResourceVersion() + `",
893 "creationTimestamp": "` + accessor.GetCreationTimestamp().UTC().Format(time.RFC3339) + `",
894 "labels": {
895 "test-label": "test"
896 },
897 "managedFields": [
898 {
899 "manager": "apply_test",
900 "operation": "Apply",
901 "apiVersion": "v1",
902 "time": "` + accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) + `",
903 "fieldsType": "FieldsV1",
904 "fieldsV1": {
905 "f:metadata": {
906 "f:labels": {
907 "f:test-label": {}
908 }
909 }
910 }
911 },
912 {
913 "manager": "updater",
914 "operation": "Update",
915 "apiVersion": "v1",
916 "time": "` + accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) + `",
917 "fieldsType": "FieldsV1",
918 "fieldsV1": {
919 "f:data": {
920 "f:key": {},
921 "f:new-key": {}
922 }
923 }
924 }
925 ]
926 },
927 "data": {
928 "key": "new value",
929 "new-key": "value"
930 }
931 }`)
932
933 if string(expected) != string(actual) {
934 t.Fatalf("Expected:\n%v\nGot:\n%v", string(expected), string(actual))
935 }
936
937 if accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) == accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) {
938 t.Fatalf("Expected times to be different but got:\n%v", string(actual))
939 }
940 }
941
942
943 func TestApplyRemovesEmptyManagedFields(t *testing.T) {
944 client, closeFn := setup(t)
945 defer closeFn()
946
947 obj := []byte(`{
948 "apiVersion": "v1",
949 "kind": "ConfigMap",
950 "metadata": {
951 "name": "test-cm",
952 "namespace": "default"
953 }
954 }`)
955
956 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
957 Namespace("default").
958 Resource("configmaps").
959 Name("test-cm").
960 Param("fieldManager", "apply_test").
961 Body(obj).
962 Do(context.TODO()).
963 Get()
964 if err != nil {
965 t.Fatalf("Failed to create object using Apply patch: %v", err)
966 }
967
968 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
969 Namespace("default").
970 Resource("configmaps").
971 Name("test-cm").
972 Param("fieldManager", "apply_test").
973 Body(obj).Do(context.TODO()).Get()
974 if err != nil {
975 t.Fatalf("Failed to patch object: %v", err)
976 }
977
978 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
979 if err != nil {
980 t.Fatalf("Failed to retrieve object: %v", err)
981 }
982
983 accessor, err := meta.Accessor(object)
984 if err != nil {
985 t.Fatalf("Failed to get meta accessor: %v", err)
986 }
987
988 if managed := accessor.GetManagedFields(); managed != nil {
989 t.Fatalf("Object contains unexpected managedFields: %v", managed)
990 }
991 }
992
993 func TestApplyRequiresFieldManager(t *testing.T) {
994 client, closeFn := setup(t)
995 defer closeFn()
996
997 obj := []byte(`{
998 "apiVersion": "v1",
999 "kind": "ConfigMap",
1000 "metadata": {
1001 "name": "test-cm",
1002 "namespace": "default"
1003 }
1004 }`)
1005
1006 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1007 Namespace("default").
1008 Resource("configmaps").
1009 Name("test-cm").
1010 Body(obj).
1011 Do(context.TODO()).
1012 Get()
1013 if err == nil {
1014 t.Fatalf("Apply should fail to create without fieldManager")
1015 }
1016
1017 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1018 Namespace("default").
1019 Resource("configmaps").
1020 Name("test-cm").
1021 Param("fieldManager", "apply_test").
1022 Body(obj).
1023 Do(context.TODO()).
1024 Get()
1025 if err != nil {
1026 t.Fatalf("Apply failed to create with fieldManager: %v", err)
1027 }
1028 }
1029
1030
1031 func TestApplyRemoveContainerPort(t *testing.T) {
1032 client, closeFn := setup(t)
1033 defer closeFn()
1034
1035 obj := []byte(`{
1036 "apiVersion": "apps/v1",
1037 "kind": "Deployment",
1038 "metadata": {
1039 "name": "deployment",
1040 "labels": {"app": "nginx"}
1041 },
1042 "spec": {
1043 "replicas": 3,
1044 "selector": {
1045 "matchLabels": {
1046 "app": "nginx"
1047 }
1048 },
1049 "template": {
1050 "metadata": {
1051 "labels": {
1052 "app": "nginx"
1053 }
1054 },
1055 "spec": {
1056 "containers": [{
1057 "name": "nginx",
1058 "image": "nginx:latest",
1059 "ports": [{
1060 "containerPort": 80,
1061 "protocol": "TCP"
1062 }]
1063 }]
1064 }
1065 }
1066 }
1067 }`)
1068
1069 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1070 AbsPath("/apis/apps/v1").
1071 Namespace("default").
1072 Resource("deployments").
1073 Name("deployment").
1074 Param("fieldManager", "apply_test").
1075 Body(obj).Do(context.TODO()).Get()
1076 if err != nil {
1077 t.Fatalf("Failed to create object using Apply patch: %v", err)
1078 }
1079
1080 obj = []byte(`{
1081 "apiVersion": "apps/v1",
1082 "kind": "Deployment",
1083 "metadata": {
1084 "name": "deployment",
1085 "labels": {"app": "nginx"}
1086 },
1087 "spec": {
1088 "replicas": 3,
1089 "selector": {
1090 "matchLabels": {
1091 "app": "nginx"
1092 }
1093 },
1094 "template": {
1095 "metadata": {
1096 "labels": {
1097 "app": "nginx"
1098 }
1099 },
1100 "spec": {
1101 "containers": [{
1102 "name": "nginx",
1103 "image": "nginx:latest"
1104 }]
1105 }
1106 }
1107 }
1108 }`)
1109
1110 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1111 AbsPath("/apis/apps/v1").
1112 Namespace("default").
1113 Resource("deployments").
1114 Name("deployment").
1115 Param("fieldManager", "apply_test").
1116 Body(obj).Do(context.TODO()).Get()
1117 if err != nil {
1118 t.Fatalf("Failed to remove container port using Apply patch: %v", err)
1119 }
1120
1121 deployment, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
1122 if err != nil {
1123 t.Fatalf("Failed to retrieve object: %v", err)
1124 }
1125
1126 if len(deployment.Spec.Template.Spec.Containers[0].Ports) > 0 {
1127 t.Fatalf("Expected no container ports but got: %v, object: \n%#v", deployment.Spec.Template.Spec.Containers[0].Ports, deployment)
1128 }
1129 }
1130
1131
1132
1133 func TestApplyFailsWithVersionMismatch(t *testing.T) {
1134 client, closeFn := setup(t)
1135 defer closeFn()
1136
1137 obj := []byte(`{
1138 "apiVersion": "apps/v1",
1139 "kind": "Deployment",
1140 "metadata": {
1141 "name": "deployment",
1142 "labels": {"app": "nginx"}
1143 },
1144 "spec": {
1145 "replicas": 3,
1146 "selector": {
1147 "matchLabels": {
1148 "app": "nginx"
1149 }
1150 },
1151 "template": {
1152 "metadata": {
1153 "labels": {
1154 "app": "nginx"
1155 }
1156 },
1157 "spec": {
1158 "containers": [{
1159 "name": "nginx",
1160 "image": "nginx:latest"
1161 }]
1162 }
1163 }
1164 }
1165 }`)
1166
1167 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1168 AbsPath("/apis/apps/v1").
1169 Namespace("default").
1170 Resource("deployments").
1171 Name("deployment").
1172 Param("fieldManager", "apply_test").
1173 Body(obj).Do(context.TODO()).Get()
1174 if err != nil {
1175 t.Fatalf("Failed to create object using Apply patch: %v", err)
1176 }
1177
1178 obj = []byte(`{
1179 "apiVersion": "extensions/v1beta",
1180 "kind": "Deployment",
1181 "metadata": {
1182 "name": "deployment",
1183 "labels": {"app": "nginx"}
1184 },
1185 "spec": {
1186 "replicas": 100,
1187 "selector": {
1188 "matchLabels": {
1189 "app": "nginx"
1190 }
1191 },
1192 "template": {
1193 "metadata": {
1194 "labels": {
1195 "app": "nginx"
1196 }
1197 },
1198 "spec": {
1199 "containers": [{
1200 "name": "nginx",
1201 "image": "nginx:latest"
1202 }]
1203 }
1204 }
1205 }
1206 }`)
1207 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1208 AbsPath("/apis/apps/v1").
1209 Namespace("default").
1210 Resource("deployments").
1211 Name("deployment").
1212 Param("fieldManager", "apply_test").
1213 Body([]byte(obj)).Do(context.TODO()).Get()
1214 if err == nil {
1215 t.Fatalf("Expecting to get version mismatch when applying object")
1216 }
1217 status, ok := err.(*apierrors.StatusError)
1218 if !ok {
1219 t.Fatalf("Expecting to get version mismatch as API error")
1220 }
1221 if status.Status().Code != http.StatusBadRequest {
1222 t.Fatalf("expected status code to be %d but was %d", http.StatusBadRequest, status.Status().Code)
1223 }
1224 }
1225
1226
1227
1228 func TestApplyConvertsManagedFieldsVersion(t *testing.T) {
1229 client, closeFn := setup(t)
1230 defer closeFn()
1231
1232 obj := []byte(`{
1233 "apiVersion": "apps/v1",
1234 "kind": "Deployment",
1235 "metadata": {
1236 "name": "deployment",
1237 "labels": {"app": "nginx"},
1238 "managedFields": [
1239 {
1240 "manager": "sidecar_controller",
1241 "operation": "Apply",
1242 "apiVersion": "extensions/v1beta1",
1243 "fieldsV1": {
1244 "f:metadata": {
1245 "f:labels": {
1246 "f:sidecar_version": {}
1247 }
1248 },
1249 "f:spec": {
1250 "f:template": {
1251 "f: spec": {
1252 "f:containers": {
1253 "k:{\"name\":\"sidecar\"}": {
1254 ".": {},
1255 "f:image": {}
1256 }
1257 }
1258 }
1259 }
1260 }
1261 }
1262 }
1263 ]
1264 },
1265 "spec": {
1266 "selector": {
1267 "matchLabels": {
1268 "app": "nginx"
1269 }
1270 },
1271 "template": {
1272 "metadata": {
1273 "labels": {
1274 "app": "nginx"
1275 }
1276 },
1277 "spec": {
1278 "containers": [{
1279 "name": "nginx",
1280 "image": "nginx:latest"
1281 }]
1282 }
1283 }
1284 }
1285 }`)
1286
1287 _, err := client.CoreV1().RESTClient().Post().
1288 AbsPath("/apis/apps/v1").
1289 Namespace("default").
1290 Resource("deployments").
1291 Body(obj).Do(context.TODO()).Get()
1292 if err != nil {
1293 t.Fatalf("Failed to create object: %v", err)
1294 }
1295
1296 obj = []byte(`{
1297 "apiVersion": "apps/v1",
1298 "kind": "Deployment",
1299 "metadata": {
1300 "name": "deployment",
1301 "labels": {"sidecar_version": "release"}
1302 },
1303 "spec": {
1304 "template": {
1305 "spec": {
1306 "containers": [{
1307 "name": "sidecar",
1308 "image": "sidecar:latest"
1309 }]
1310 }
1311 }
1312 }
1313 }`)
1314 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1315 AbsPath("/apis/apps/v1").
1316 Namespace("default").
1317 Resource("deployments").
1318 Name("deployment").
1319 Param("fieldManager", "sidecar_controller").
1320 Body([]byte(obj)).Do(context.TODO()).Get()
1321 if err != nil {
1322 t.Fatalf("Failed to apply object: %v", err)
1323 }
1324
1325 object, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
1326 if err != nil {
1327 t.Fatalf("Failed to retrieve object: %v", err)
1328 }
1329
1330 accessor, err := meta.Accessor(object)
1331 if err != nil {
1332 t.Fatalf("Failed to get meta accessor: %v", err)
1333 }
1334
1335 managed := accessor.GetManagedFields()
1336 if len(managed) != 2 {
1337 t.Fatalf("Expected 2 field managers, but got managed fields: %v", managed)
1338 }
1339
1340 var actual *metav1.ManagedFieldsEntry
1341 for i := range managed {
1342 entry := &managed[i]
1343 if entry.Manager == "sidecar_controller" && entry.APIVersion == "apps/v1" {
1344 actual = entry
1345 }
1346 }
1347
1348 if actual == nil {
1349 t.Fatalf("Expected managed fields to contain entry with manager '%v' with converted api version '%v', but got managed fields:\n%v", "sidecar_controller", "apps/v1", managed)
1350 }
1351
1352 expected := &metav1.ManagedFieldsEntry{
1353 Manager: "sidecar_controller",
1354 Operation: metav1.ManagedFieldsOperationApply,
1355 APIVersion: "apps/v1",
1356 Time: actual.Time,
1357 FieldsType: "FieldsV1",
1358 FieldsV1: &metav1.FieldsV1{
1359 Raw: []byte(`{"f:metadata":{"f:labels":{"f:sidecar_version":{}}},"f:spec":{"f:template":{"f:spec":{"f:containers":{"k:{\"name\":\"sidecar\"}":{".":{},"f:image":{},"f:name":{}}}}}}}`),
1360 },
1361 }
1362
1363 if !reflect.DeepEqual(actual, expected) {
1364 t.Fatalf("expected:\n%v\nbut got:\n%v", expected, actual)
1365 }
1366 }
1367
1368
1369 func TestClearManagedFieldsWithMergePatch(t *testing.T) {
1370 client, closeFn := setup(t)
1371 defer closeFn()
1372
1373 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1374 Namespace("default").
1375 Resource("configmaps").
1376 Name("test-cm").
1377 Param("fieldManager", "apply_test").
1378 Body([]byte(`{
1379 "apiVersion": "v1",
1380 "kind": "ConfigMap",
1381 "metadata": {
1382 "name": "test-cm",
1383 "namespace": "default",
1384 "labels": {
1385 "test-label": "test"
1386 }
1387 },
1388 "data": {
1389 "key": "value"
1390 }
1391 }`)).
1392 Do(context.TODO()).
1393 Get()
1394 if err != nil {
1395 t.Fatalf("Failed to create object using Apply patch: %v", err)
1396 }
1397
1398 _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
1399 Namespace("default").
1400 Resource("configmaps").
1401 Name("test-cm").
1402 Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do(context.TODO()).Get()
1403 if err != nil {
1404 t.Fatalf("Failed to patch object: %v", err)
1405 }
1406
1407 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
1408 if err != nil {
1409 t.Fatalf("Failed to retrieve object: %v", err)
1410 }
1411
1412 accessor, err := meta.Accessor(object)
1413 if err != nil {
1414 t.Fatalf("Failed to get meta accessor: %v", err)
1415 }
1416
1417 if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
1418 t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
1419 }
1420 }
1421
1422
1423 func TestClearManagedFieldsWithStrategicMergePatch(t *testing.T) {
1424 client, closeFn := setup(t)
1425 defer closeFn()
1426
1427 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1428 Namespace("default").
1429 Resource("configmaps").
1430 Name("test-cm").
1431 Param("fieldManager", "apply_test").
1432 Body([]byte(`{
1433 "apiVersion": "v1",
1434 "kind": "ConfigMap",
1435 "metadata": {
1436 "name": "test-cm",
1437 "namespace": "default",
1438 "labels": {
1439 "test-label": "test"
1440 }
1441 },
1442 "data": {
1443 "key": "value"
1444 }
1445 }`)).
1446 Do(context.TODO()).
1447 Get()
1448 if err != nil {
1449 t.Fatalf("Failed to create object using Apply patch: %v", err)
1450 }
1451
1452 _, err = client.CoreV1().RESTClient().Patch(types.StrategicMergePatchType).
1453 Namespace("default").
1454 Resource("configmaps").
1455 Name("test-cm").
1456 Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do(context.TODO()).Get()
1457 if err != nil {
1458 t.Fatalf("Failed to patch object: %v", err)
1459 }
1460
1461 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
1462 if err != nil {
1463 t.Fatalf("Failed to retrieve object: %v", err)
1464 }
1465
1466 accessor, err := meta.Accessor(object)
1467 if err != nil {
1468 t.Fatalf("Failed to get meta accessor: %v", err)
1469 }
1470
1471 if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
1472 t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
1473 }
1474
1475 if labels := accessor.GetLabels(); len(labels) < 1 {
1476 t.Fatalf("Expected other fields to stay untouched, got: %v", object)
1477 }
1478 }
1479
1480
1481 func TestClearManagedFieldsWithJSONPatch(t *testing.T) {
1482 client, closeFn := setup(t)
1483 defer closeFn()
1484
1485 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1486 Namespace("default").
1487 Resource("configmaps").
1488 Name("test-cm").
1489 Param("fieldManager", "apply_test").
1490 Body([]byte(`{
1491 "apiVersion": "v1",
1492 "kind": "ConfigMap",
1493 "metadata": {
1494 "name": "test-cm",
1495 "namespace": "default",
1496 "labels": {
1497 "test-label": "test"
1498 }
1499 },
1500 "data": {
1501 "key": "value"
1502 }
1503 }`)).
1504 Do(context.TODO()).
1505 Get()
1506 if err != nil {
1507 t.Fatalf("Failed to create object using Apply patch: %v", err)
1508 }
1509
1510 _, err = client.CoreV1().RESTClient().Patch(types.JSONPatchType).
1511 Namespace("default").
1512 Resource("configmaps").
1513 Name("test-cm").
1514 Body([]byte(`[{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]`)).Do(context.TODO()).Get()
1515 if err != nil {
1516 t.Fatalf("Failed to patch object: %v", err)
1517 }
1518
1519 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
1520 if err != nil {
1521 t.Fatalf("Failed to retrieve object: %v", err)
1522 }
1523
1524 accessor, err := meta.Accessor(object)
1525 if err != nil {
1526 t.Fatalf("Failed to get meta accessor: %v", err)
1527 }
1528
1529 if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
1530 t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
1531 }
1532 }
1533
1534
1535 func TestClearManagedFieldsWithUpdate(t *testing.T) {
1536 client, closeFn := setup(t)
1537 defer closeFn()
1538
1539 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1540 Namespace("default").
1541 Resource("configmaps").
1542 Name("test-cm").
1543 Param("fieldManager", "apply_test").
1544 Body([]byte(`{
1545 "apiVersion": "v1",
1546 "kind": "ConfigMap",
1547 "metadata": {
1548 "name": "test-cm",
1549 "namespace": "default",
1550 "labels": {
1551 "test-label": "test"
1552 }
1553 },
1554 "data": {
1555 "key": "value"
1556 }
1557 }`)).
1558 Do(context.TODO()).
1559 Get()
1560 if err != nil {
1561 t.Fatalf("Failed to create object using Apply patch: %v", err)
1562 }
1563
1564 _, err = client.CoreV1().RESTClient().Put().
1565 Namespace("default").
1566 Resource("configmaps").
1567 Name("test-cm").
1568 Body([]byte(`{
1569 "apiVersion": "v1",
1570 "kind": "ConfigMap",
1571 "metadata": {
1572 "name": "test-cm",
1573 "namespace": "default",
1574 "managedFields": [{}],
1575 "labels": {
1576 "test-label": "test"
1577 }
1578 },
1579 "data": {
1580 "key": "value"
1581 }
1582 }`)).Do(context.TODO()).Get()
1583 if err != nil {
1584 t.Fatalf("Failed to patch object: %v", err)
1585 }
1586
1587 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
1588 if err != nil {
1589 t.Fatalf("Failed to retrieve object: %v", err)
1590 }
1591
1592 accessor, err := meta.Accessor(object)
1593 if err != nil {
1594 t.Fatalf("Failed to get meta accessor: %v", err)
1595 }
1596
1597 if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
1598 t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
1599 }
1600
1601 if labels := accessor.GetLabels(); len(labels) < 1 {
1602 t.Fatalf("Expected other fields to stay untouched, got: %v", object)
1603 }
1604 }
1605
1606
1607 func TestErrorsDontFail(t *testing.T) {
1608 client, closeFn := setup(t)
1609 defer closeFn()
1610
1611
1612 _, err := client.CoreV1().RESTClient().Post().
1613 Namespace("default").
1614 Resource("configmaps").
1615 Param("fieldManager", "apply_test").
1616 Body([]byte(`{
1617 "apiVersion": "v1",
1618 "kind": "ConfigMap",
1619 "metadata": {
1620 "name": "test-cm",
1621 "namespace": "default",
1622 "managedFields": [{
1623 "manager": "apply_test",
1624 "operation": "Apply",
1625 "apiVersion": "v1",
1626 "time": "2019-07-08T09:31:18Z",
1627 "fieldsType": "",
1628 "fieldsV1": {}
1629 }],
1630 "labels": {
1631 "test-label": "test"
1632 }
1633 },
1634 "data": {
1635 "key": "value"
1636 }
1637 }`)).
1638 Do(context.TODO()).
1639 Get()
1640 if err != nil {
1641 t.Fatalf("Failed to create object with empty fieldsType: %v", err)
1642 }
1643 }
1644
1645 func TestErrorsDontFailUpdate(t *testing.T) {
1646 client, closeFn := setup(t)
1647 defer closeFn()
1648
1649 _, err := client.CoreV1().RESTClient().Post().
1650 Namespace("default").
1651 Resource("configmaps").
1652 Param("fieldManager", "apply_test").
1653 Body([]byte(`{
1654 "apiVersion": "v1",
1655 "kind": "ConfigMap",
1656 "metadata": {
1657 "name": "test-cm",
1658 "namespace": "default",
1659 "labels": {
1660 "test-label": "test"
1661 }
1662 },
1663 "data": {
1664 "key": "value"
1665 }
1666 }`)).
1667 Do(context.TODO()).
1668 Get()
1669 if err != nil {
1670 t.Fatalf("Failed to create object: %v", err)
1671 }
1672
1673 _, err = client.CoreV1().RESTClient().Put().
1674 Namespace("default").
1675 Resource("configmaps").
1676 Name("test-cm").
1677 Param("fieldManager", "apply_test").
1678 Body([]byte(`{
1679 "apiVersion": "v1",
1680 "kind": "ConfigMap",
1681 "metadata": {
1682 "name": "test-cm",
1683 "namespace": "default",
1684 "managedFields": [{
1685 "manager": "apply_test",
1686 "operation": "Apply",
1687 "apiVersion": "v1",
1688 "time": "2019-07-08T09:31:18Z",
1689 "fieldsType": "",
1690 "fieldsV1": {}
1691 }],
1692 "labels": {
1693 "test-label": "test"
1694 }
1695 },
1696 "data": {
1697 "key": "value"
1698 }
1699 }`)).
1700 Do(context.TODO()).
1701 Get()
1702 if err != nil {
1703 t.Fatalf("Failed to update object with empty fieldsType: %v", err)
1704 }
1705 }
1706
1707 func TestErrorsDontFailPatch(t *testing.T) {
1708 client, closeFn := setup(t)
1709 defer closeFn()
1710
1711 _, err := client.CoreV1().RESTClient().Post().
1712 Namespace("default").
1713 Resource("configmaps").
1714 Param("fieldManager", "apply_test").
1715 Body([]byte(`{
1716 "apiVersion": "v1",
1717 "kind": "ConfigMap",
1718 "metadata": {
1719 "name": "test-cm",
1720 "namespace": "default",
1721 "labels": {
1722 "test-label": "test"
1723 }
1724 },
1725 "data": {
1726 "key": "value"
1727 }
1728 }`)).
1729 Do(context.TODO()).
1730 Get()
1731 if err != nil {
1732 t.Fatalf("Failed to create object: %v", err)
1733 }
1734
1735 _, err = client.CoreV1().RESTClient().Patch(types.JSONPatchType).
1736 Namespace("default").
1737 Resource("configmaps").
1738 Name("test-cm").
1739 Param("fieldManager", "apply_test").
1740 Body([]byte(`[{"op": "replace", "path": "/metadata/managedFields", "value": [{
1741 "manager": "apply_test",
1742 "operation": "Apply",
1743 "apiVersion": "v1",
1744 "time": "2019-07-08T09:31:18Z",
1745 "fieldsType": "",
1746 "fieldsV1": {}
1747 }]}]`)).
1748 Do(context.TODO()).
1749 Get()
1750 if err != nil {
1751 t.Fatalf("Failed to patch object with empty FieldsType: %v", err)
1752 }
1753 }
1754
1755 func TestApplyDoesNotChangeManagedFieldsViaSubresources(t *testing.T) {
1756 client, closeFn := setup(t)
1757 defer closeFn()
1758
1759 podBytes := []byte(`{
1760 "apiVersion": "v1",
1761 "kind": "Pod",
1762 "metadata": {
1763 "name": "just-a-pod"
1764 },
1765 "spec": {
1766 "containers": [{
1767 "name": "test-container-a",
1768 "image": "test-image-one"
1769 }]
1770 }
1771 }`)
1772
1773 liveObj, err := client.CoreV1().RESTClient().
1774 Patch(types.ApplyPatchType).
1775 Namespace("default").
1776 Param("fieldManager", "apply_test").
1777 Resource("pods").
1778 Name("just-a-pod").
1779 Body(podBytes).
1780 Do(context.TODO()).
1781 Get()
1782 if err != nil {
1783 t.Fatalf("Failed to create object: %v", err)
1784 }
1785
1786 updateBytes := []byte(`{
1787 "metadata": {
1788 "managedFields": [{
1789 "manager":"testing",
1790 "operation":"Update",
1791 "apiVersion":"v1",
1792 "fieldsType":"FieldsV1",
1793 "fieldsV1":{
1794 "f:spec":{
1795 "f:containers":{
1796 "k:{\"name\":\"testing\"}":{
1797 ".":{},
1798 "f:image":{},
1799 "f:name":{}
1800 }
1801 }
1802 }
1803 }
1804 }]
1805 },
1806 "status": {
1807 "conditions": [{"type": "MyStatus", "status":"true"}]
1808 }
1809 }`)
1810
1811 updateActor := "update_managedfields_test"
1812 newObj, err := client.CoreV1().RESTClient().
1813 Patch(types.MergePatchType).
1814 Namespace("default").
1815 Param("fieldManager", updateActor).
1816 Name("just-a-pod").
1817 Resource("pods").
1818 SubResource("status").
1819 Body(updateBytes).
1820 Do(context.TODO()).
1821 Get()
1822
1823 if err != nil {
1824 t.Fatalf("Error updating subresource: %v ", err)
1825 }
1826
1827 liveAccessor, err := meta.Accessor(liveObj)
1828 if err != nil {
1829 t.Fatalf("Failed to get meta accessor for live object: %v", err)
1830 }
1831 newAccessor, err := meta.Accessor(newObj)
1832 if err != nil {
1833 t.Fatalf("Failed to get meta accessor for new object: %v", err)
1834 }
1835
1836 liveManagedFields := liveAccessor.GetManagedFields()
1837 if len(liveManagedFields) != 1 {
1838 t.Fatalf("Expected managedFields in the live object to have exactly one entry, got %d: %v", len(liveManagedFields), liveManagedFields)
1839 }
1840
1841 newManagedFields := newAccessor.GetManagedFields()
1842 if len(newManagedFields) != 2 {
1843 t.Fatalf("Expected managedFields in the new object to have exactly two entries, got %d: %v", len(newManagedFields), newManagedFields)
1844 }
1845
1846 if !reflect.DeepEqual(liveManagedFields[0], newManagedFields[0]) {
1847 t.Fatalf("managedFields updated via subresource:\n\nlive managedFields: %v\nnew managedFields: %v\n\n", liveManagedFields, newManagedFields)
1848 }
1849
1850 if newManagedFields[1].Manager != updateActor {
1851 t.Fatalf(`Expected managerFields to have an entry with manager set to %q`, updateActor)
1852 }
1853 }
1854
1855
1856 func TestClearManagedFieldsWithUpdateEmptyList(t *testing.T) {
1857 client, closeFn := setup(t)
1858 defer closeFn()
1859
1860 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1861 Namespace("default").
1862 Resource("configmaps").
1863 Name("test-cm").
1864 Param("fieldManager", "apply_test").
1865 Body([]byte(`{
1866 "apiVersion": "v1",
1867 "kind": "ConfigMap",
1868 "metadata": {
1869 "name": "test-cm",
1870 "namespace": "default",
1871 "labels": {
1872 "test-label": "test"
1873 }
1874 },
1875 "data": {
1876 "key": "value"
1877 }
1878 }`)).
1879 Do(context.TODO()).
1880 Get()
1881 if err != nil {
1882 t.Fatalf("Failed to create object using Apply patch: %v", err)
1883 }
1884
1885 _, err = client.CoreV1().RESTClient().Put().
1886 Namespace("default").
1887 Resource("configmaps").
1888 Name("test-cm").
1889 Body([]byte(`{
1890 "apiVersion": "v1",
1891 "kind": "ConfigMap",
1892 "metadata": {
1893 "name": "test-cm",
1894 "namespace": "default",
1895 "managedFields": [],
1896 "labels": {
1897 "test-label": "test"
1898 }
1899 },
1900 "data": {
1901 "key": "value"
1902 }
1903 }`)).Do(context.TODO()).Get()
1904 if err != nil {
1905 t.Fatalf("Failed to patch object: %v", err)
1906 }
1907
1908 _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
1909 Namespace("default").
1910 Resource("configmaps").
1911 Name("test-cm").
1912 Body([]byte(`{"metadata":{"labels": { "test-label": "v1" }}}`)).Do(context.TODO()).Get()
1913
1914 if err != nil {
1915 t.Fatalf("Failed to patch object: %v", err)
1916 }
1917
1918 object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
1919 if err != nil {
1920 t.Fatalf("Failed to retrieve object: %v", err)
1921 }
1922
1923 accessor, err := meta.Accessor(object)
1924 if err != nil {
1925 t.Fatalf("Failed to get meta accessor: %v", err)
1926 }
1927
1928 if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
1929 t.Fatalf("Failed to stop tracking managedFields, got: %v", managedFields)
1930 }
1931
1932 if labels := accessor.GetLabels(); len(labels) < 1 {
1933 t.Fatalf("Expected other fields to stay untouched, got: %v", object)
1934 }
1935 }
1936
1937
1938
1939 func TestApplyUnsetExclusivelyOwnedFields(t *testing.T) {
1940 client, closeFn := setup(t)
1941 defer closeFn()
1942
1943
1944
1945 apply := []byte(`{
1946 "apiVersion": "apps/v1",
1947 "kind": "Deployment",
1948 "metadata": {
1949 "name": "deployment-exclusive-unset",
1950 "labels": {"app": "nginx"}
1951 },
1952 "spec": {
1953 "replicas": 3,
1954 "selector": {
1955 "matchLabels": {
1956 "app": "nginx"
1957 }
1958 },
1959 "template": {
1960 "metadata": {
1961 "labels": {
1962 "app": "nginx"
1963 }
1964 },
1965 "spec": {
1966 "hostname": "test-hostname",
1967 "containers": [{
1968 "name": "nginx",
1969 "image": "nginx:latest"
1970 }]
1971 }
1972 }
1973 }
1974 }`)
1975
1976 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
1977 AbsPath("/apis/apps/v1").
1978 Namespace("default").
1979 Resource("deployments").
1980 Name("deployment-exclusive-unset").
1981 Param("fieldManager", "apply_test").
1982 Body(apply).
1983 Do(context.TODO()).
1984 Get()
1985 if err != nil {
1986 t.Fatalf("Failed to create object using Apply patch: %v", err)
1987 }
1988
1989
1990 apply = []byte(`{
1991 "apiVersion": "apps/v1",
1992 "kind": "Deployment",
1993 "metadata": {
1994 "name": "deployment-exclusive-unset",
1995 "labels": {"app": "nginx"}
1996 },
1997 "spec": {
1998 "selector": {
1999 "matchLabels": {
2000 "app": "nginx"
2001 }
2002 },
2003 "template": {
2004 "metadata": {
2005 "labels": {
2006 "app": "nginx"
2007 }
2008 },
2009 "spec": {
2010 "containers": [{
2011 "name": "nginx",
2012 "image": "nginx:latest"
2013 }]
2014 }
2015 }
2016 }
2017 }`)
2018
2019 patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2020 AbsPath("/apis/apps/v1").
2021 Namespace("default").
2022 Resource("deployments").
2023 Name("deployment-exclusive-unset").
2024 Param("fieldManager", "apply_test").
2025 Body(apply).
2026 Do(context.TODO()).
2027 Get()
2028 if err != nil {
2029 t.Fatalf("Failed to create object using Apply patch: %v", err)
2030 }
2031
2032 deployment, ok := patched.(*appsv1.Deployment)
2033 if !ok {
2034 t.Fatalf("Failed to convert response object to Deployment")
2035 }
2036 if *deployment.Spec.Replicas != 1 {
2037 t.Errorf("Expected deployment.spec.replicas to be 1 (default value), but got %d", deployment.Spec.Replicas)
2038 }
2039 if len(deployment.Spec.Template.Spec.Hostname) != 0 {
2040 t.Errorf("Expected deployment.spec.template.spec.hostname to be unset, but got %s", deployment.Spec.Template.Spec.Hostname)
2041 }
2042 }
2043
2044
2045
2046 func TestApplyUnsetSharedFields(t *testing.T) {
2047 client, closeFn := setup(t)
2048 defer closeFn()
2049
2050
2051
2052 apply := []byte(`{
2053 "apiVersion": "apps/v1",
2054 "kind": "Deployment",
2055 "metadata": {
2056 "name": "deployment-shared-unset",
2057 "labels": {"app": "nginx"}
2058 },
2059 "spec": {
2060 "replicas": 3,
2061 "selector": {
2062 "matchLabels": {
2063 "app": "nginx"
2064 }
2065 },
2066 "template": {
2067 "metadata": {
2068 "labels": {
2069 "app": "nginx"
2070 }
2071 },
2072 "spec": {
2073 "hostname": "test-hostname",
2074 "containers": [{
2075 "name": "nginx",
2076 "image": "nginx:latest"
2077 }]
2078 }
2079 }
2080 }
2081 }`)
2082
2083 for _, fieldManager := range []string{"shared_owner_1", "shared_owner_2"} {
2084 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2085 AbsPath("/apis/apps/v1").
2086 Namespace("default").
2087 Resource("deployments").
2088 Name("deployment-shared-unset").
2089 Param("fieldManager", fieldManager).
2090 Body(apply).
2091 Do(context.TODO()).
2092 Get()
2093 if err != nil {
2094 t.Fatalf("Failed to create object using Apply patch: %v", err)
2095 }
2096 }
2097
2098
2099 apply = []byte(`{
2100 "apiVersion": "apps/v1",
2101 "kind": "Deployment",
2102 "metadata": {
2103 "name": "deployment-shared-unset",
2104 "labels": {"app": "nginx"}
2105 },
2106 "spec": {
2107 "selector": {
2108 "matchLabels": {
2109 "app": "nginx"
2110 }
2111 },
2112 "template": {
2113 "metadata": {
2114 "labels": {
2115 "app": "nginx"
2116 }
2117 },
2118 "spec": {
2119 "containers": [{
2120 "name": "nginx",
2121 "image": "nginx:latest"
2122 }]
2123 }
2124 }
2125 }
2126 }`)
2127
2128 patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2129 AbsPath("/apis/apps/v1").
2130 Namespace("default").
2131 Resource("deployments").
2132 Name("deployment-shared-unset").
2133 Param("fieldManager", "shared_owner_1").
2134 Body(apply).
2135 Do(context.TODO()).
2136 Get()
2137 if err != nil {
2138 t.Fatalf("Failed to create object using Apply patch: %v", err)
2139 }
2140
2141 deployment, ok := patched.(*appsv1.Deployment)
2142 if !ok {
2143 t.Fatalf("Failed to convert response object to Deployment")
2144 }
2145 if *deployment.Spec.Replicas != 3 {
2146 t.Errorf("Expected deployment.spec.replicas to be 3, but got %d", deployment.Spec.Replicas)
2147 }
2148 if deployment.Spec.Template.Spec.Hostname != "test-hostname" {
2149 t.Errorf("Expected deployment.spec.template.spec.hostname to be \"test-hostname\", but got %s", deployment.Spec.Template.Spec.Hostname)
2150 }
2151 }
2152
2153
2154
2155
2156 func TestApplyCanTransferFieldOwnershipToController(t *testing.T) {
2157 client, closeFn := setup(t)
2158 defer closeFn()
2159
2160
2161 apply := []byte(`{
2162 "apiVersion": "apps/v1",
2163 "kind": "Deployment",
2164 "metadata": {
2165 "name": "deployment-shared-map-item-removal",
2166 "labels": {"app": "nginx"}
2167 },
2168 "spec": {
2169 "replicas": 3,
2170 "selector": {
2171 "matchLabels": {
2172 "app": "nginx"
2173 }
2174 },
2175 "template": {
2176 "metadata": {
2177 "labels": {
2178 "app": "nginx"
2179 }
2180 },
2181 "spec": {
2182 "containers": [{
2183 "name": "nginx",
2184 "image": "nginx:latest",
2185 }]
2186 }
2187 }
2188 }
2189 }`)
2190
2191 appliedObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2192 AbsPath("/apis/apps/v1").
2193 Namespace("default").
2194 Resource("deployments").
2195 Name("deployment-shared-map-item-removal").
2196 Param("fieldManager", "test_applier").
2197 Body(apply).
2198 Do(context.TODO()).
2199 Get()
2200 if err != nil {
2201 t.Fatalf("Failed to create object using Apply patch: %v", err)
2202 }
2203
2204
2205 applied, ok := appliedObj.(*appsv1.Deployment)
2206 if !ok {
2207 t.Fatalf("Failed to convert response object to Deployment")
2208 }
2209 replicas := int32(4)
2210 applied.Spec.Replicas = &replicas
2211 _, err = client.AppsV1().Deployments("default").
2212 Update(context.TODO(), applied, metav1.UpdateOptions{FieldManager: "test_updater"})
2213 if err != nil {
2214 t.Fatalf("Failed to create object using Apply patch: %v", err)
2215 }
2216
2217
2218 apply = []byte(`{
2219 "apiVersion": "apps/v1",
2220 "kind": "Deployment",
2221 "metadata": {
2222 "name": "deployment-shared-map-item-removal",
2223 "labels": {"app": "nginx"}
2224 },
2225 "spec": {
2226 "selector": {
2227 "matchLabels": {
2228 "app": "nginx"
2229 }
2230 },
2231 "template": {
2232 "metadata": {
2233 "labels": {
2234 "app": "nginx"
2235 }
2236 },
2237 "spec": {
2238 "containers": [{
2239 "name": "nginx",
2240 "image": "nginx:latest",
2241 }]
2242 }
2243 }
2244 }
2245 }`)
2246
2247 patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2248 AbsPath("/apis/apps/v1").
2249 Namespace("default").
2250 Resource("deployments").
2251 Name("deployment-shared-map-item-removal").
2252 Param("fieldManager", "test_applier").
2253 Body(apply).
2254 Do(context.TODO()).
2255 Get()
2256 if err != nil {
2257 t.Fatalf("Failed to create object using Apply patch: %v", err)
2258 }
2259
2260
2261 deployment, ok := patched.(*appsv1.Deployment)
2262 if !ok {
2263 t.Fatalf("Failed to convert response object to Deployment")
2264 }
2265 if *deployment.Spec.Replicas != 4 {
2266 t.Errorf("Expected deployment.spec.replicas to be 4, but got %d", deployment.Spec.Replicas)
2267 }
2268 }
2269
2270
2271
2272
2273 func TestApplyCanRemoveMapItemsContributedToByControllers(t *testing.T) {
2274 client, closeFn := setup(t)
2275 defer closeFn()
2276
2277
2278 apply := []byte(`{
2279 "apiVersion": "apps/v1",
2280 "kind": "Deployment",
2281 "metadata": {
2282 "name": "deployment-shared-map-item-removal",
2283 "labels": {"app": "nginx"}
2284 },
2285 "spec": {
2286 "selector": {
2287 "matchLabels": {
2288 "app": "nginx"
2289 }
2290 },
2291 "template": {
2292 "metadata": {
2293 "labels": {
2294 "app": "nginx"
2295 }
2296 },
2297 "spec": {
2298 "containers": [{
2299 "name": "nginx",
2300 "image": "nginx:latest",
2301 }]
2302 }
2303 }
2304 }
2305 }`)
2306
2307 appliedObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2308 AbsPath("/apis/apps/v1").
2309 Namespace("default").
2310 Resource("deployments").
2311 Name("deployment-shared-map-item-removal").
2312 Param("fieldManager", "test_applier").
2313 Body(apply).
2314 Do(context.TODO()).
2315 Get()
2316 if err != nil {
2317 t.Fatalf("Failed to create object using Apply patch: %v", err)
2318 }
2319
2320
2321 applied, ok := appliedObj.(*appsv1.Deployment)
2322 if !ok {
2323 t.Fatalf("Failed to convert response object to Deployment")
2324 }
2325 applied.Spec.Template.Spec.Containers[0].WorkingDir = "/home/replacement"
2326 _, err = client.AppsV1().Deployments("default").
2327 Update(context.TODO(), applied, metav1.UpdateOptions{FieldManager: "test_updater"})
2328 if err != nil {
2329 t.Fatalf("Failed to create object using Apply patch: %v", err)
2330 }
2331
2332
2333 apply = []byte(`{
2334 "apiVersion": "apps/v1",
2335 "kind": "Deployment",
2336 "metadata": {
2337 "name": "deployment-shared-map-item-removal",
2338 "labels": {"app": "nginx"}
2339 },
2340 "spec": {
2341 "replicas": 3,
2342 "selector": {
2343 "matchLabels": {
2344 "app": "nginx"
2345 }
2346 },
2347 "template": {
2348 "metadata": {
2349 "labels": {
2350 "app": "nginx"
2351 }
2352 },
2353 "spec": {
2354 "hostname": "test-hostname",
2355 "containers": [{
2356 "name": "other-container",
2357 "image": "nginx:latest",
2358 }]
2359 }
2360 }
2361 }
2362 }`)
2363
2364 patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2365 AbsPath("/apis/apps/v1").
2366 Namespace("default").
2367 Resource("deployments").
2368 Name("deployment-shared-map-item-removal").
2369 Param("fieldManager", "test_applier").
2370 Body(apply).
2371 Do(context.TODO()).
2372 Get()
2373 if err != nil {
2374 t.Fatalf("Failed to create object using Apply patch: %v", err)
2375 }
2376
2377
2378 deployment, ok := patched.(*appsv1.Deployment)
2379 if !ok {
2380 t.Fatalf("Failed to convert response object to Deployment")
2381 }
2382 if len(deployment.Spec.Template.Spec.Containers) != 1 {
2383 t.Fatalf("Expected 1 container after apply, got %d", len(deployment.Spec.Template.Spec.Containers))
2384 }
2385 if deployment.Spec.Template.Spec.Containers[0].Name != "other-container" {
2386 t.Fatalf("Expected container to be named \"other-container\" but got %s", deployment.Spec.Template.Spec.Containers[0].Name)
2387 }
2388 }
2389
2390
2391 func TestDefaultMissingKeys(t *testing.T) {
2392 client, closeFn := setup(t)
2393 defer closeFn()
2394
2395
2396 apply := []byte(`{
2397 "apiVersion": "apps/v1",
2398 "kind": "Deployment",
2399 "metadata": {
2400 "name": "deployment-shared-map-item-removal",
2401 "labels": {"app": "nginx"}
2402 },
2403 "spec": {
2404 "selector": {
2405 "matchLabels": {
2406 "app": "nginx"
2407 }
2408 },
2409 "template": {
2410 "metadata": {
2411 "labels": {
2412 "app": "nginx"
2413 }
2414 },
2415 "spec": {
2416 "containers": [{
2417 "name": "nginx",
2418 "image": "nginx:latest",
2419 "ports": [{
2420 "name": "foo",
2421 "containerPort": 80
2422 }]
2423 }]
2424 }
2425 }
2426 }
2427 }`)
2428
2429 _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2430 AbsPath("/apis/apps/v1").
2431 Namespace("default").
2432 Resource("deployments").
2433 Name("deployment-shared-map-item-removal").
2434 Param("fieldManager", "test_applier").
2435 Body(apply).
2436 Do(context.TODO()).
2437 Get()
2438 if err != nil {
2439 t.Fatalf("Failed to create object using Apply patch: %v", err)
2440 }
2441
2442
2443 apply = []byte(`{
2444 "apiVersion": "apps/v1",
2445 "kind": "Deployment",
2446 "metadata": {
2447 "name": "deployment-shared-map-item-removal",
2448 "labels": {"app": "nginx"}
2449 },
2450 "spec": {
2451 "selector": {
2452 "matchLabels": {
2453 "app": "nginx"
2454 }
2455 },
2456 "template": {
2457 "metadata": {
2458 "labels": {
2459 "app": "nginx"
2460 }
2461 },
2462 "spec": {
2463 "containers": [{
2464 "name": "nginx",
2465 "image": "nginx:latest",
2466 "ports": [{
2467 "name": "bar",
2468 "containerPort": 80,
2469 "protocol": "TCP"
2470 }]
2471 }]
2472 }
2473 }
2474 }
2475 }`)
2476 patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2477 AbsPath("/apis/apps/v1").
2478 Namespace("default").
2479 Resource("deployments").
2480 Name("deployment-shared-map-item-removal").
2481 Param("fieldManager", "test_applier_conflict").
2482 Body(apply).
2483 Do(context.TODO()).
2484 Get()
2485 if err == nil {
2486 t.Fatalf("Expecting to get conflicts when a different applier updates existing list item, got no error: %s", patched)
2487 }
2488 status, ok := err.(*apierrors.StatusError)
2489 if !ok {
2490 t.Fatalf("Expecting to get conflicts as API error")
2491 }
2492 if len(status.Status().Details.Causes) != 1 {
2493 t.Fatalf("Expecting to get one conflict when a different applier updates existing list item, got: %v", status.Status().Details.Causes)
2494 }
2495 }
2496
2497 var podBytes = []byte(`
2498 apiVersion: v1
2499 kind: Pod
2500 metadata:
2501 labels:
2502 app: some-app
2503 plugin1: some-value
2504 plugin2: some-value
2505 plugin3: some-value
2506 plugin4: some-value
2507 name: some-name
2508 namespace: default
2509 ownerReferences:
2510 - apiVersion: apps/v1
2511 blockOwnerDeletion: true
2512 controller: true
2513 kind: ReplicaSet
2514 name: some-name
2515 uid: 0a9d2b9e-779e-11e7-b422-42010a8001be
2516 spec:
2517 containers:
2518 - args:
2519 - one
2520 - two
2521 - three
2522 - four
2523 - five
2524 - six
2525 - seven
2526 - eight
2527 - nine
2528 env:
2529 - name: VAR_3
2530 valueFrom:
2531 secretKeyRef:
2532 key: some-other-key
2533 name: some-oher-name
2534 - name: VAR_2
2535 valueFrom:
2536 secretKeyRef:
2537 key: other-key
2538 name: other-name
2539 - name: VAR_1
2540 valueFrom:
2541 secretKeyRef:
2542 key: some-key
2543 name: some-name
2544 image: some-image-name
2545 imagePullPolicy: IfNotPresent
2546 name: some-name
2547 resources:
2548 requests:
2549 cpu: "0"
2550 terminationMessagePath: /dev/termination-log
2551 terminationMessagePolicy: File
2552 volumeMounts:
2553 - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
2554 name: default-token-hu5jz
2555 readOnly: true
2556 dnsPolicy: ClusterFirst
2557 nodeName: node-name
2558 priority: 0
2559 restartPolicy: Always
2560 schedulerName: default-scheduler
2561 securityContext: {}
2562 serviceAccount: default
2563 serviceAccountName: default
2564 terminationGracePeriodSeconds: 30
2565 tolerations:
2566 - effect: NoExecute
2567 key: node.kubernetes.io/not-ready
2568 operator: Exists
2569 tolerationSeconds: 300
2570 - effect: NoExecute
2571 key: node.kubernetes.io/unreachable
2572 operator: Exists
2573 tolerationSeconds: 300
2574 volumes:
2575 - name: default-token-hu5jz
2576 secret:
2577 defaultMode: 420
2578 secretName: default-token-hu5jz
2579 status:
2580 conditions:
2581 - lastProbeTime: null
2582 lastTransitionTime: "2019-07-08T09:31:18Z"
2583 status: "True"
2584 type: Initialized
2585 - lastProbeTime: null
2586 lastTransitionTime: "2019-07-08T09:41:59Z"
2587 status: "True"
2588 type: Ready
2589 - lastProbeTime: null
2590 lastTransitionTime: null
2591 status: "True"
2592 type: ContainersReady
2593 - lastProbeTime: null
2594 lastTransitionTime: "2019-07-08T09:31:18Z"
2595 status: "True"
2596 type: PodScheduled
2597 containerStatuses:
2598 - containerID: docker://885e82a1ed0b7356541bb410a0126921ac42439607c09875cd8097dd5d7b5376
2599 image: some-image-name
2600 imageID: docker-pullable://some-image-id
2601 lastState:
2602 terminated:
2603 containerID: docker://d57290f9e00fad626b20d2dd87a3cf69bbc22edae07985374f86a8b2b4e39565
2604 exitCode: 255
2605 finishedAt: "2019-07-08T09:39:09Z"
2606 reason: Error
2607 startedAt: "2019-07-08T09:38:54Z"
2608 name: name
2609 ready: true
2610 restartCount: 6
2611 state:
2612 running:
2613 startedAt: "2019-07-08T09:41:59Z"
2614 hostIP: 10.0.0.1
2615 phase: Running
2616 podIP: 10.0.0.1
2617 qosClass: BestEffort
2618 startTime: "2019-07-08T09:31:18Z"
2619 `)
2620
2621 func decodePod(podBytes []byte) v1.Pod {
2622 pod := v1.Pod{}
2623 err := yaml.Unmarshal(podBytes, &pod)
2624 if err != nil {
2625 panic(err)
2626 }
2627 return pod
2628 }
2629
2630 func encodePod(pod v1.Pod) []byte {
2631 podBytes, err := yaml.Marshal(pod)
2632 if err != nil {
2633 panic(err)
2634 }
2635 return podBytes
2636 }
2637
2638 func getPodBytesWhenEnabled(b *testing.B, pod v1.Pod, format string) []byte {
2639 client, closeFn := setup(b)
2640 defer closeFn()
2641 flag.Lookup("v").Value.Set("0")
2642
2643 pod.Name = "size-pod"
2644 podB, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2645 Name(pod.Name).
2646 Namespace("default").
2647 Param("fieldManager", "apply_test").
2648 Resource("pods").
2649 SetHeader("Accept", format).
2650 Body(encodePod(pod)).DoRaw(context.TODO())
2651 if err != nil {
2652 b.Fatalf("Failed to create object: %#v", err)
2653 }
2654 return podB
2655 }
2656
2657 func BenchmarkServerSideApply(b *testing.B) {
2658 podBytesWhenEnabled := getPodBytesWhenEnabled(b, decodePod(podBytes), "application/yaml")
2659
2660 client, closeFn := setup(b)
2661 defer closeFn()
2662 flag.Lookup("v").Value.Set("0")
2663
2664 benchAll(b, client, decodePod(podBytesWhenEnabled))
2665 }
2666
2667 func benchAll(b *testing.B, client clientset.Interface, pod v1.Pod) {
2668
2669 pod.ObjectMeta.CreationTimestamp = metav1.Time{}
2670 pod.ObjectMeta.ResourceVersion = ""
2671 pod.ObjectMeta.UID = ""
2672
2673
2674 pod.Name = "repeated-pod"
2675 _, err := client.CoreV1().RESTClient().Post().
2676 Namespace("default").
2677 Resource("pods").
2678 SetHeader("Content-Type", "application/yaml").
2679 Body(encodePod(pod)).Do(context.TODO()).Get()
2680 if err != nil {
2681 b.Fatalf("Failed to create object: %v", err)
2682 }
2683
2684 b.Run("List1", benchListPod(client, pod, 1))
2685 b.Run("List20", benchListPod(client, pod, 20))
2686 b.Run("List200", benchListPod(client, pod, 200))
2687 b.Run("List2000", benchListPod(client, pod, 2000))
2688
2689 b.Run("RepeatedUpdates", benchRepeatedUpdate(client, "repeated-pod"))
2690 b.Run("Post1", benchPostPod(client, pod, 1))
2691 b.Run("Post10", benchPostPod(client, pod, 10))
2692 b.Run("Post50", benchPostPod(client, pod, 50))
2693 }
2694
2695 func benchPostPod(client clientset.Interface, pod v1.Pod, parallel int) func(*testing.B) {
2696 return func(b *testing.B) {
2697 b.ResetTimer()
2698 b.ReportAllocs()
2699 for i := 0; i < b.N; i++ {
2700 c := make(chan error)
2701 for j := 0; j < parallel; j++ {
2702 j := j
2703 i := i
2704 go func(pod v1.Pod) {
2705 pod.Name = fmt.Sprintf("post%d-%d-%d-%d", parallel, b.N, j, i)
2706 _, err := client.CoreV1().RESTClient().Post().
2707 Namespace("default").
2708 Resource("pods").
2709 SetHeader("Content-Type", "application/yaml").
2710 Body(encodePod(pod)).Do(context.TODO()).Get()
2711 c <- err
2712 }(pod)
2713 }
2714 for j := 0; j < parallel; j++ {
2715 err := <-c
2716 if err != nil {
2717 b.Fatal(err)
2718 }
2719 }
2720 close(c)
2721 }
2722 }
2723 }
2724
2725 func createNamespace(client clientset.Interface, name string) error {
2726 namespace := v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
2727 namespaceBytes, err := yaml.Marshal(namespace)
2728 if err != nil {
2729 return fmt.Errorf("Failed to marshal namespace: %v", err)
2730 }
2731 _, err = client.CoreV1().RESTClient().Get().
2732 Resource("namespaces").
2733 SetHeader("Content-Type", "application/yaml").
2734 Body(namespaceBytes).Do(context.TODO()).Get()
2735 if err != nil {
2736 return fmt.Errorf("Failed to create namespace: %v", err)
2737 }
2738 return nil
2739 }
2740
2741 func benchListPod(client clientset.Interface, pod v1.Pod, num int) func(*testing.B) {
2742 return func(b *testing.B) {
2743 namespace := fmt.Sprintf("get-%d-%d", num, b.N)
2744 if err := createNamespace(client, namespace); err != nil {
2745 b.Fatal(err)
2746 }
2747
2748 for i := 0; i < num; i++ {
2749 pod.Name = fmt.Sprintf("get-%d-%d", b.N, i)
2750 pod.Namespace = namespace
2751 _, err := client.CoreV1().RESTClient().Post().
2752 Namespace(namespace).
2753 Resource("pods").
2754 SetHeader("Content-Type", "application/yaml").
2755 Body(encodePod(pod)).Do(context.TODO()).Get()
2756 if err != nil {
2757 b.Fatalf("Failed to create object: %v", err)
2758 }
2759 }
2760
2761 b.ResetTimer()
2762 b.ReportAllocs()
2763 for i := 0; i < b.N; i++ {
2764 _, err := client.CoreV1().RESTClient().Get().
2765 Namespace(namespace).
2766 Resource("pods").
2767 SetHeader("Accept", "application/vnd.kubernetes.protobuf").
2768 Do(context.TODO()).Get()
2769 if err != nil {
2770 b.Fatalf("Failed to patch object: %v", err)
2771 }
2772 }
2773 }
2774 }
2775
2776 func benchRepeatedUpdate(client clientset.Interface, podName string) func(*testing.B) {
2777 return func(b *testing.B) {
2778 b.ResetTimer()
2779 b.ReportAllocs()
2780 for i := 0; i < b.N; i++ {
2781 _, err := client.CoreV1().RESTClient().Patch(types.JSONPatchType).
2782 Namespace("default").
2783 Resource("pods").
2784 Name(podName).
2785 Body([]byte(fmt.Sprintf(`[{"op": "replace", "path": "/spec/containers/0/image", "value": "image%d"}]`, i))).Do(context.TODO()).Get()
2786 if err != nil {
2787 b.Fatalf("Failed to patch object: %v", err)
2788 }
2789 }
2790 }
2791 }
2792
2793 func TestUpgradeClientSideToServerSideApply(t *testing.T) {
2794 client, closeFn := setup(t)
2795 defer closeFn()
2796
2797 obj := []byte(`
2798 apiVersion: apps/v1
2799 kind: Deployment
2800 metadata:
2801 name: my-deployment
2802 annotations:
2803 "kubectl.kubernetes.io/last-applied-configuration": |
2804 {"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"my-deployment","labels":{"app":"my-app"}},"spec":{"replicas": 3,"template":{"metadata":{"labels":{"app":"my-app"}},"spec":{"containers":[{"name":"my-c","image":"my-image"}]}}}}
2805 labels:
2806 app: my-app
2807 spec:
2808 replicas: 100000
2809 selector:
2810 matchLabels:
2811 app: my-app
2812 template:
2813 metadata:
2814 labels:
2815 app: my-app
2816 spec:
2817 containers:
2818 - name: my-c
2819 image: my-image
2820 `)
2821
2822 deployment, err := yamlutil.ToJSON(obj)
2823 if err != nil {
2824 t.Fatalf("Failed marshal yaml: %v", err)
2825 }
2826
2827 _, err = client.CoreV1().RESTClient().Post().
2828 AbsPath("/apis/apps/v1").
2829 Namespace("default").
2830 Resource("deployments").
2831 Body(deployment).Do(context.TODO()).Get()
2832 if err != nil {
2833 t.Fatalf("Failed to create object: %v", err)
2834 }
2835
2836 obj = []byte(`
2837 apiVersion: apps/v1
2838 kind: Deployment
2839 metadata:
2840 name: my-deployment
2841 labels:
2842 app: my-new-label
2843 spec:
2844 replicas: 3 # expect conflict
2845 template:
2846 metadata:
2847 labels:
2848 app: my-app
2849 spec:
2850 containers:
2851 - name: my-c
2852 image: my-image
2853 `)
2854
2855 deployment, err = yamlutil.ToJSON(obj)
2856 if err != nil {
2857 t.Fatalf("Failed marshal yaml: %v", err)
2858 }
2859
2860 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2861 AbsPath("/apis/apps/v1").
2862 Namespace("default").
2863 Resource("deployments").
2864 Name("my-deployment").
2865 Param("fieldManager", "kubectl").
2866 Body(deployment).
2867 Do(context.TODO()).
2868 Get()
2869 if !apierrors.IsConflict(err) {
2870 t.Fatalf("Expected conflict error but got: %v", err)
2871 }
2872
2873 obj = []byte(`
2874 apiVersion: apps/v1
2875 kind: Deployment
2876 metadata:
2877 name: my-deployment
2878 labels:
2879 app: my-new-label
2880 spec:
2881 template:
2882 metadata:
2883 labels:
2884 app: my-app
2885 spec:
2886 containers:
2887 - name: my-c
2888 image: my-image-new
2889 `)
2890
2891 deployment, err = yamlutil.ToJSON(obj)
2892 if err != nil {
2893 t.Fatalf("Failed marshal yaml: %v", err)
2894 }
2895
2896 _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
2897 AbsPath("/apis/apps/v1").
2898 Namespace("default").
2899 Resource("deployments").
2900 Name("my-deployment").
2901 Param("fieldManager", "kubectl").
2902 Body(deployment).
2903 Do(context.TODO()).
2904 Get()
2905 if err != nil {
2906 t.Fatalf("Failed to apply object: %v", err)
2907 }
2908
2909 deploymentObj, err := client.AppsV1().Deployments("default").Get(context.TODO(), "my-deployment", metav1.GetOptions{})
2910 if err != nil {
2911 t.Fatalf("Failed to get object: %v", err)
2912 }
2913 if *deploymentObj.Spec.Replicas != 100000 {
2914 t.Fatalf("expected to get obj with replicas %d, but got %d", 100000, *deploymentObj.Spec.Replicas)
2915 }
2916 if deploymentObj.Spec.Template.Spec.Containers[0].Image != "my-image-new" {
2917 t.Fatalf("expected to get obj with image %s, but got %s", "my-image-new", deploymentObj.Spec.Template.Spec.Containers[0].Image)
2918 }
2919 }
2920
2921 func TestRenamingAppliedFieldManagers(t *testing.T) {
2922 client, closeFn := setup(t)
2923 defer closeFn()
2924
2925
2926 podBytes := []byte(`{
2927 "apiVersion": "v1",
2928 "kind": "Pod",
2929 "metadata": {
2930 "name": "just-a-pod",
2931 "labels": {
2932 "a": "one"
2933 }
2934 },
2935 "spec": {
2936 "containers": [{
2937 "name": "test-container-a",
2938 "image": "test-image-one"
2939 }]
2940 }
2941 }`)
2942 _, err := client.CoreV1().RESTClient().
2943 Patch(types.ApplyPatchType).
2944 Namespace("default").
2945 Param("fieldManager", "multi_manager_one").
2946 Resource("pods").
2947 Name("just-a-pod").
2948 Body(podBytes).
2949 Do(context.TODO()).
2950 Get()
2951 if err != nil {
2952 t.Fatalf("Failed to apply: %v", err)
2953 }
2954 _, err = client.CoreV1().RESTClient().
2955 Patch(types.ApplyPatchType).
2956 Namespace("default").
2957 Param("fieldManager", "multi_manager_two").
2958 Resource("pods").
2959 Name("just-a-pod").
2960 Body([]byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"labels":{"b":"two"}}}`)).
2961 Do(context.TODO()).
2962 Get()
2963 if err != nil {
2964 t.Fatalf("Failed to apply: %v", err)
2965 }
2966
2967 pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
2968 if err != nil {
2969 t.Fatalf("Failed to get object: %v", err)
2970 }
2971 expectedLabels := map[string]string{
2972 "a": "one",
2973 "b": "two",
2974 }
2975 if !reflect.DeepEqual(pod.Labels, expectedLabels) {
2976 t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
2977 }
2978
2979 managedFields := pod.GetManagedFields()
2980 for i := range managedFields {
2981 managedFields[i].Manager = "multi_manager"
2982 }
2983 pod.SetManagedFields(managedFields)
2984
2985 obj, err := client.CoreV1().RESTClient().
2986 Put().
2987 Namespace("default").
2988 Resource("pods").
2989 Name("just-a-pod").
2990 Body(pod).
2991 Do(context.TODO()).
2992 Get()
2993 if err != nil {
2994 t.Fatalf("Failed to update: %v", err)
2995 }
2996
2997 accessor, err := meta.Accessor(obj)
2998 if err != nil {
2999 t.Fatalf("Failed to get meta accessor for object: %v", err)
3000 }
3001 managedFields = accessor.GetManagedFields()
3002 if len(managedFields) != 1 {
3003 t.Fatalf("Expected object to have 1 managed fields entry, got: %d", len(managedFields))
3004 }
3005 entry := managedFields[0]
3006 if entry.Manager != "multi_manager" || entry.Operation != "Apply" || string(entry.FieldsV1.Raw) != `{"f:metadata":{"f:labels":{"f:b":{}}}}` {
3007 t.Fatalf(`Unexpected entry, got: %v`, entry)
3008 }
3009 }
3010
3011 func TestRenamingUpdatedFieldManagers(t *testing.T) {
3012 client, closeFn := setup(t)
3013 defer closeFn()
3014
3015
3016 podBytes := []byte(`{
3017 "apiVersion": "v1",
3018 "kind": "Pod",
3019 "metadata": {
3020 "name": "just-a-pod"
3021 },
3022 "spec": {
3023 "containers": [{
3024 "name": "test-container-a",
3025 "image": "test-image-one"
3026 }]
3027 }
3028 }`)
3029 _, err := client.CoreV1().RESTClient().
3030 Patch(types.ApplyPatchType).
3031 Namespace("default").
3032 Param("fieldManager", "first").
3033 Resource("pods").
3034 Name("just-a-pod").
3035 Body(podBytes).
3036 Do(context.TODO()).
3037 Get()
3038 if err != nil {
3039 t.Fatalf("Failed to create object: %v", err)
3040 }
3041
3042 _, err = client.CoreV1().RESTClient().
3043 Patch(types.MergePatchType).
3044 Namespace("default").
3045 Param("fieldManager", "multi_manager_one").
3046 Resource("pods").
3047 Name("just-a-pod").
3048 Body([]byte(`{"metadata":{"labels":{"a":"one"}}}`)).
3049 Do(context.TODO()).
3050 Get()
3051 if err != nil {
3052 t.Fatalf("Failed to update: %v", err)
3053 }
3054 _, err = client.CoreV1().RESTClient().
3055 Patch(types.MergePatchType).
3056 Namespace("default").
3057 Param("fieldManager", "multi_manager_two").
3058 Resource("pods").
3059 Name("just-a-pod").
3060 Body([]byte(`{"metadata":{"labels":{"b":"two"}}}`)).
3061 Do(context.TODO()).
3062 Get()
3063 if err != nil {
3064 t.Fatalf("Failed to update: %v", err)
3065 }
3066
3067 pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
3068 if err != nil {
3069 t.Fatalf("Failed to get object: %v", err)
3070 }
3071 expectedLabels := map[string]string{
3072 "a": "one",
3073 "b": "two",
3074 }
3075 if !reflect.DeepEqual(pod.Labels, expectedLabels) {
3076 t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
3077 }
3078
3079 managedFields := pod.GetManagedFields()
3080 for i := range managedFields {
3081 managedFields[i].Manager = "multi_manager"
3082 }
3083 pod.SetManagedFields(managedFields)
3084
3085 obj, err := client.CoreV1().RESTClient().
3086 Put().
3087 Namespace("default").
3088 Resource("pods").
3089 Name("just-a-pod").
3090 Body(pod).
3091 Do(context.TODO()).
3092 Get()
3093 if err != nil {
3094 t.Fatalf("Failed to update: %v", err)
3095 }
3096
3097 accessor, err := meta.Accessor(obj)
3098 if err != nil {
3099 t.Fatalf("Failed to get meta accessor for object: %v", err)
3100 }
3101 managedFields = accessor.GetManagedFields()
3102 if len(managedFields) != 2 {
3103 t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
3104 }
3105 entry := managedFields[1]
3106 if entry.Manager != "multi_manager" || entry.Operation != "Update" || string(entry.FieldsV1.Raw) != `{"f:metadata":{"f:labels":{"f:b":{}}}}` {
3107 t.Fatalf(`Unexpected entry, got: %v`, entry)
3108 }
3109 }
3110
3111 func TestDroppingSubresourceField(t *testing.T) {
3112 client, closeFn := setup(t)
3113 defer closeFn()
3114
3115
3116 podBytes := []byte(`{
3117 "apiVersion": "v1",
3118 "kind": "Pod",
3119 "metadata": {
3120 "name": "just-a-pod"
3121 },
3122 "spec": {
3123 "containers": [{
3124 "name": "test-container-a",
3125 "image": "test-image-one"
3126 }]
3127 }
3128 }`)
3129 _, err := client.CoreV1().RESTClient().
3130 Patch(types.ApplyPatchType).
3131 Namespace("default").
3132 Param("fieldManager", "first").
3133 Resource("pods").
3134 Name("just-a-pod").
3135 Body(podBytes).
3136 Do(context.TODO()).
3137 Get()
3138 if err != nil {
3139 t.Fatalf("Failed to create object: %v", err)
3140 }
3141
3142 _, err = client.CoreV1().RESTClient().
3143 Patch(types.ApplyPatchType).
3144 Namespace("default").
3145 Param("fieldManager", "label_manager").
3146 Resource("pods").
3147 Name("just-a-pod").
3148 Body([]byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"labels":{"a":"one"}}}`)).
3149 Do(context.TODO()).
3150 Get()
3151 if err != nil {
3152 t.Fatalf("Failed to apply: %v", err)
3153 }
3154 _, err = client.CoreV1().RESTClient().
3155 Patch(types.ApplyPatchType).
3156 Namespace("default").
3157 Param("fieldManager", "label_manager").
3158 Resource("pods").
3159 Name("just-a-pod").
3160 SubResource("status").
3161 Body([]byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"labels":{"b":"two"}}}`)).
3162 Do(context.TODO()).
3163 Get()
3164 if err != nil {
3165 t.Fatalf("Failed to apply: %v", err)
3166 }
3167
3168 pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
3169 if err != nil {
3170 t.Fatalf("Failed to get object: %v", err)
3171 }
3172 expectedLabels := map[string]string{
3173 "a": "one",
3174 "b": "two",
3175 }
3176 if !reflect.DeepEqual(pod.Labels, expectedLabels) {
3177 t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
3178 }
3179
3180 managedFields := pod.GetManagedFields()
3181 if len(managedFields) != 3 {
3182 t.Fatalf("Expected object to have 3 managed fields entries, got: %d", len(managedFields))
3183 }
3184 if managedFields[1].Manager != "label_manager" || managedFields[1].Operation != "Apply" || managedFields[1].Subresource != "" {
3185 t.Fatalf(`Unexpected entry, got: %v`, managedFields[1])
3186 }
3187 if managedFields[2].Manager != "label_manager" || managedFields[2].Operation != "Apply" || managedFields[2].Subresource != "status" {
3188 t.Fatalf(`Unexpected entry, got: %v`, managedFields[2])
3189 }
3190
3191 for i := range managedFields {
3192 managedFields[i].Subresource = ""
3193 }
3194 pod.SetManagedFields(managedFields)
3195
3196 obj, err := client.CoreV1().RESTClient().
3197 Put().
3198 Namespace("default").
3199 Resource("pods").
3200 Name("just-a-pod").
3201 Body(pod).
3202 Do(context.TODO()).
3203 Get()
3204 if err != nil {
3205 t.Fatalf("Failed to update: %v", err)
3206 }
3207
3208 accessor, err := meta.Accessor(obj)
3209 if err != nil {
3210 t.Fatalf("Failed to get meta accessor for object: %v", err)
3211 }
3212 managedFields = accessor.GetManagedFields()
3213 if len(managedFields) != 2 {
3214 t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
3215 }
3216 entry := managedFields[1]
3217 if entry.Manager != "label_manager" || entry.Operation != "Apply" || string(entry.FieldsV1.Raw) != `{"f:metadata":{"f:labels":{"f:b":{}}}}` {
3218 t.Fatalf(`Unexpected entry, got: %v`, entry)
3219 }
3220 }
3221
3222 func TestDroppingSubresourceFromSpecField(t *testing.T) {
3223 client, closeFn := setup(t)
3224 defer closeFn()
3225
3226
3227 podBytes := []byte(`{
3228 "apiVersion": "v1",
3229 "kind": "Pod",
3230 "metadata": {
3231 "name": "just-a-pod"
3232 },
3233 "spec": {
3234 "containers": [{
3235 "name": "test-container-a",
3236 "image": "test-image-one"
3237 }]
3238 }
3239 }`)
3240 _, err := client.CoreV1().RESTClient().
3241 Patch(types.ApplyPatchType).
3242 Namespace("default").
3243 Param("fieldManager", "first").
3244 Resource("pods").
3245 Name("just-a-pod").
3246 Body(podBytes).
3247 Do(context.TODO()).
3248 Get()
3249 if err != nil {
3250 t.Fatalf("Failed to create object: %v", err)
3251 }
3252
3253 _, err = client.CoreV1().RESTClient().
3254 Patch(types.MergePatchType).
3255 Namespace("default").
3256 Param("fieldManager", "manager").
3257 Resource("pods").
3258 Name("just-a-pod").
3259 Body([]byte(`{"metadata":{"labels":{"a":"two"}}}`)).
3260 Do(context.TODO()).
3261 Get()
3262 if err != nil {
3263 t.Fatalf("Failed to update: %v", err)
3264 }
3265
3266 _, err = client.CoreV1().RESTClient().
3267 Patch(types.MergePatchType).
3268 Namespace("default").
3269 Param("fieldManager", "manager").
3270 Resource("pods").
3271 SubResource("status").
3272 Name("just-a-pod").
3273 Body([]byte(`{"status":{"phase":"Running"}}`)).
3274 Do(context.TODO()).
3275 Get()
3276 if err != nil {
3277 t.Fatalf("Failed to apply: %v", err)
3278 }
3279
3280 pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
3281 if err != nil {
3282 t.Fatalf("Failed to get object: %v", err)
3283 }
3284 expectedLabels := map[string]string{"a": "two"}
3285 if !reflect.DeepEqual(pod.Labels, expectedLabels) {
3286 t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
3287 }
3288 if pod.Status.Phase != v1.PodRunning {
3289 t.Fatalf("Expected phase to be %q, but got %q", v1.PodRunning, pod.Status.Phase)
3290 }
3291
3292 managedFields := pod.GetManagedFields()
3293 if len(managedFields) != 3 {
3294 t.Fatalf("Expected object to have 3 managed fields entries, got: %d", len(managedFields))
3295 }
3296 if managedFields[1].Manager != "manager" || managedFields[1].Operation != "Update" || managedFields[1].Subresource != "" {
3297 t.Fatalf(`Unexpected entry, got: %v`, managedFields[1])
3298 }
3299 if managedFields[2].Manager != "manager" || managedFields[2].Operation != "Update" || managedFields[2].Subresource != "status" {
3300 t.Fatalf(`Unexpected entry, got: %v`, managedFields[2])
3301 }
3302
3303 for i := range managedFields {
3304 managedFields[i].Subresource = ""
3305 }
3306 pod.SetManagedFields(managedFields)
3307
3308 obj, err := client.CoreV1().RESTClient().
3309 Put().
3310 Namespace("default").
3311 Resource("pods").
3312 Name("just-a-pod").
3313 Body(pod).
3314 Do(context.TODO()).
3315 Get()
3316 if err != nil {
3317 t.Fatalf("Failed to update: %v", err)
3318 }
3319
3320 accessor, err := meta.Accessor(obj)
3321 if err != nil {
3322 t.Fatalf("Failed to get meta accessor for object: %v", err)
3323 }
3324 managedFields = accessor.GetManagedFields()
3325 if len(managedFields) != 2 {
3326 t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
3327 }
3328 entry := managedFields[1]
3329 if entry.Manager != "manager" || entry.Operation != "Update" || string(entry.FieldsV1.Raw) != `{"f:status":{"f:phase":{}}}` {
3330 t.Fatalf(`Unexpected entry, got: %v`, entry)
3331 }
3332 }
3333
3334 func TestSubresourceField(t *testing.T) {
3335 client, closeFn := setup(t)
3336 defer closeFn()
3337
3338
3339 deploymentBytes := []byte(`{
3340 "apiVersion": "apps/v1",
3341 "kind": "Deployment",
3342 "metadata": {
3343 "name": "deployment",
3344 "labels": {"app": "nginx"}
3345 },
3346 "spec": {
3347 "replicas": 3,
3348 "selector": {
3349 "matchLabels": {
3350 "app": "nginx"
3351 }
3352 },
3353 "template": {
3354 "metadata": {
3355 "labels": {
3356 "app": "nginx"
3357 }
3358 },
3359 "spec": {
3360 "containers": [{
3361 "name": "nginx",
3362 "image": "nginx:latest"
3363 }]
3364 }
3365 }
3366 }
3367 }`)
3368 _, err := client.CoreV1().RESTClient().
3369 Patch(types.ApplyPatchType).
3370 AbsPath("/apis/apps/v1").
3371 Namespace("default").
3372 Resource("deployments").
3373 Name("deployment").
3374 Param("fieldManager", "manager").
3375 Body(deploymentBytes).Do(context.TODO()).Get()
3376 if err != nil {
3377 t.Fatalf("Failed to apply object: %v", err)
3378 }
3379
3380 _, err = client.CoreV1().RESTClient().
3381 Patch(types.MergePatchType).
3382 AbsPath("/apis/apps/v1").
3383 Namespace("default").
3384 Resource("deployments").
3385 SubResource("scale").
3386 Name("deployment").
3387 Body([]byte(`{"spec":{"replicas":32}}`)).
3388 Param("fieldManager", "manager").
3389 Do(context.TODO()).
3390 Get()
3391 if err != nil {
3392 t.Fatalf("Failed to update status: %v", err)
3393 }
3394
3395 deployment, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
3396 if err != nil {
3397 t.Fatalf("Failed to get object: %v", err)
3398 }
3399
3400 managedFields := deployment.GetManagedFields()
3401 if len(managedFields) != 2 {
3402 t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
3403 }
3404 if managedFields[0].Manager != "manager" || managedFields[0].Operation != "Apply" || managedFields[0].Subresource != "" {
3405 t.Fatalf(`Unexpected entry, got: %v`, managedFields[0])
3406 }
3407 if managedFields[1].Manager != "manager" ||
3408 managedFields[1].Operation != "Update" ||
3409 managedFields[1].Subresource != "scale" ||
3410 string(managedFields[1].FieldsV1.Raw) != `{"f:spec":{"f:replicas":{}}}` {
3411 t.Fatalf(`Unexpected entry, got: %v`, managedFields[1])
3412 }
3413 }
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423 func TestApplyFormerlyAtomicFields(t *testing.T) {
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436 client, closeFn := setup(t)
3437 defer closeFn()
3438
3439
3440
3441 oldPersistentVolume := []byte(`
3442 {
3443 "apiVersion": "v1",
3444 "kind": "PersistentVolume",
3445 "metadata": {
3446 "creationTimestamp": "2022-06-08T23:46:32Z",
3447 "finalizers": [
3448 "kubernetes.io/pv-protection"
3449 ],
3450 "labels": {
3451 "type": "local"
3452 },
3453 "name": "pv-storage",
3454 "uid": "112b18f7-fde6-4e48-aa61-f5168bd576b8"
3455 },
3456 "spec": {
3457 "accessModes": [
3458 "ReadWriteOnce"
3459 ],
3460 "capacity": {
3461 "storage": "16Mi"
3462 },
3463 "claimRef": {
3464 "apiVersion": "v1",
3465 "kind": "PersistentVolumeClaim",
3466 "name": "pvc-storage",
3467 "namespace": "default",
3468 "resourceVersion": "15499",
3469 "uid": "2018e302-7b12-406c-9fa2-e52535d29e48"
3470 },
3471 "hostPath": {
3472 "path": "“/tmp/mydata",
3473 "type": ""
3474 },
3475 "persistentVolumeReclaimPolicy": "Retain",
3476 "volumeMode": "Filesystem"
3477 },
3478 "status": {
3479 "phase": "Bound"
3480 }
3481 }`)
3482
3483 managedFieldsUpdate := []byte(`{
3484 "apiVersion": "v1",
3485 "kind": "PersistentVolume",
3486 "metadata": {
3487 "name": "pv-storage",
3488 "managedFields": [
3489 {
3490 "apiVersion": "v1",
3491 "fieldsType": "FieldsV1",
3492 "fieldsV1": {
3493 "f:metadata": {
3494 "f:labels": {
3495 "f:type": {}
3496 }
3497 },
3498 "f:spec": {
3499 "f:accessModes": {},
3500 "f:capacity": {
3501 "f:storage": {}
3502 },
3503 "f:hostPath": {
3504 "f:path": {}
3505 },
3506 "f:storageClassName": {}
3507 }
3508 },
3509 "manager": "apply_test",
3510 "operation": "Apply",
3511 "time": "2022-06-08T23:46:32Z"
3512 },
3513 {
3514 "apiVersion": "v1",
3515 "fieldsType": "FieldsV1",
3516 "fieldsV1": {
3517 "f:status": {
3518 "f:phase": {}
3519 }
3520 },
3521 "manager": "kube-controller-manager",
3522 "operation": "Update",
3523 "subresource": "status",
3524 "time": "2022-06-08T23:46:32Z"
3525 },
3526 {
3527 "apiVersion": "v1",
3528 "fieldsType": "FieldsV1",
3529 "fieldsV1": {
3530 "f:spec": {
3531 "f:claimRef": {}
3532 }
3533 },
3534 "manager": "kube-controller-manager",
3535 "operation": "Update",
3536 "time": "2022-06-08T23:46:37Z"
3537 }
3538 ]
3539 }
3540 }`)
3541
3542
3543 originalPV := []byte(`{
3544 "kind": "PersistentVolume",
3545 "apiVersion": "v1",
3546 "metadata": {
3547 "labels": {
3548 "type": "local"
3549 },
3550 "name": "pv-storage",
3551 },
3552 "spec": {
3553 "storageClassName": "",
3554 "capacity": {
3555 "storage": "16Mi"
3556 },
3557 "accessModes": [
3558 "ReadWriteOnce"
3559 ],
3560 "hostPath": {
3561 "path": "“/tmp/mydata"
3562 },
3563 "claimRef": {
3564 "name": "pvc-storage",
3565 "namespace": "default"
3566 }
3567 }
3568 }`)
3569
3570
3571 originalObj, err := client.CoreV1().RESTClient().
3572 Post().
3573 Param("fieldManager", "apply_test").
3574 Resource("persistentvolumes").
3575 Body(oldPersistentVolume).
3576 Do(context.TODO()).
3577 Get()
3578
3579 if err != nil {
3580 t.Fatalf("Failed to apply object: %v", err)
3581 } else if _, ok := originalObj.(*v1.PersistentVolume); !ok {
3582 t.Fatalf("returned object is incorrect type: %t", originalObj)
3583 }
3584
3585
3586 newObj, err := client.CoreV1().RESTClient().
3587 Patch(types.StrategicMergePatchType).
3588 Name("pv-storage").
3589 Param("fieldManager", "apply_test").
3590 Resource("persistentvolumes").
3591 Body(managedFieldsUpdate).
3592 Do(context.TODO()).
3593 Get()
3594
3595 if err != nil {
3596 t.Fatalf("Failed to apply object: %v", err)
3597 } else if _, ok := newObj.(*v1.PersistentVolume); !ok {
3598 t.Fatalf("returned object is incorrect type: %t", newObj)
3599 }
3600
3601
3602
3603 newObj, err = client.CoreV1().RESTClient().
3604 Patch(types.ApplyPatchType).
3605 Name("pv-storage").
3606 Param("fieldManager", "apply_test").
3607 Resource("persistentvolumes").
3608 Body(originalPV).
3609 Do(context.TODO()).
3610 Get()
3611
3612 if err != nil {
3613 t.Fatalf("Failed to apply object: %v", err)
3614 } else if _, ok := newObj.(*v1.PersistentVolume); !ok {
3615 t.Fatalf("returned object is incorrect type: %t", newObj)
3616 }
3617
3618
3619 if !reflect.DeepEqual(originalObj.(*v1.PersistentVolume).Spec.ClaimRef, newObj.(*v1.PersistentVolume).Spec.ClaimRef) {
3620 t.Fatalf("claimRef changed unexpectedly")
3621 }
3622
3623
3624
3625
3626
3627 managedFields := newObj.(*v1.PersistentVolume).ManagedFields
3628 var expectedManagedFields []metav1.ManagedFieldsEntry
3629 expectedManagedFieldsString := []byte(`[
3630 {
3631 "apiVersion": "v1",
3632 "fieldsType": "FieldsV1",
3633 "fieldsV1": {"f:metadata":{"f:labels":{"f:type":{}}},"f:spec":{"f:accessModes":{},"f:capacity":{"f:storage":{}},"f:claimRef":{"f:name":{},"f:namespace":{}},"f:hostPath":{"f:path":{}},"f:storageClassName":{}}},
3634 "manager": "apply_test",
3635 "operation": "Apply",
3636 "time": "2022-06-08T23:46:32Z"
3637 },
3638 {
3639 "apiVersion": "v1",
3640 "fieldsType": "FieldsV1",
3641 "fieldsV1": {"f:status":{"f:phase":{}}},
3642 "manager": "kube-controller-manager",
3643 "operation": "Update",
3644 "subresource": "status",
3645 "time": "2022-06-08T23:46:32Z"
3646 },
3647 {
3648 "apiVersion": "v1",
3649 "fieldsType": "FieldsV1",
3650 "fieldsV1": {"f:spec":{"f:claimRef":{}}},
3651 "manager": "kube-controller-manager",
3652 "operation": "Update",
3653 "time": "2022-06-08T23:46:37Z"
3654 }
3655 ]`)
3656
3657 err = json.Unmarshal(expectedManagedFieldsString, &expectedManagedFields)
3658 if err != nil {
3659 t.Fatalf("unexpectly failed to decode expected managed fields")
3660 }
3661
3662
3663 for i := range expectedManagedFields {
3664 expectedManagedFields[i].Time = nil
3665 }
3666
3667 for i := range managedFields {
3668 managedFields[i].Time = nil
3669 }
3670
3671 if !reflect.DeepEqual(expectedManagedFields, managedFields) {
3672 t.Fatalf("unexpected managed fields: %v", cmp.Diff(expectedManagedFields, managedFields))
3673 }
3674 }
3675
3676 func TestDuplicatesInAssociativeLists(t *testing.T) {
3677 client, closeFn := setup(t)
3678 defer closeFn()
3679
3680 ds := []byte(`{
3681 "apiVersion": "apps/v1",
3682 "kind": "DaemonSet",
3683 "metadata": {
3684 "name": "example-daemonset",
3685 "labels": {
3686 "app": "example"
3687 }
3688 },
3689 "spec": {
3690 "selector": {
3691 "matchLabels": {
3692 "app": "example"
3693 }
3694 },
3695 "template": {
3696 "metadata": {
3697 "labels": {
3698 "app": "example"
3699 }
3700 },
3701 "spec": {
3702 "containers": [
3703 {
3704 "name": "nginx",
3705 "image": "nginx",
3706 "ports": [
3707 {
3708 "name": "port0",
3709 "containerPort": 1
3710 },
3711 {
3712 "name": "port1",
3713 "containerPort": 80
3714 },
3715 {
3716 "name": "port2",
3717 "containerPort": 80
3718 }
3719 ],
3720 "env": [
3721 {
3722 "name": "ENV0",
3723 "value": "/env0value"
3724 },
3725 {
3726 "name": "PATH",
3727 "value": "/bin"
3728 },
3729 {
3730 "name": "PATH",
3731 "value": "$PATH:/usr/bin"
3732 }
3733 ]
3734 }
3735 ]
3736 }
3737 }
3738 }
3739 }`)
3740
3741 obj, err := client.AppsV1().RESTClient().
3742 Post().
3743 Namespace("default").
3744 Param("fieldManager", "create").
3745 Resource("daemonsets").
3746 Body(ds).
3747 Do(context.TODO()).
3748 Get()
3749 if err != nil {
3750 t.Fatalf("Failed to create the object: %v", err)
3751 }
3752 daemon := obj.(*appsv1.DaemonSet)
3753 if want, got := 3, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
3754 t.Fatalf("Expected %v EnvVars, got %v", want, got)
3755 }
3756 if want, got := 3, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
3757 t.Fatalf("Expected %v Ports, got %v", want, got)
3758 }
3759
3760 expectManagedFields(t, daemon.ManagedFields, `
3761 [
3762 {
3763 "manager": "create",
3764 "operation": "Update",
3765 "apiVersion": "apps/v1",
3766 "time": null,
3767 "fieldsType": "FieldsV1",
3768 "fieldsV1": {
3769 "f:metadata": {
3770 "f:annotations": {
3771 ".": {},
3772 "f:deprecated.daemonset.template.generation": {}
3773 },
3774 "f:labels": {
3775 ".": {},
3776 "f:app": {}
3777 }
3778 },
3779 "f:spec": {
3780 "f:revisionHistoryLimit": {},
3781 "f:selector": {},
3782 "f:template": {
3783 "f:metadata": {
3784 "f:labels": {
3785 ".": {},
3786 "f:app": {}
3787 }
3788 },
3789 "f:spec": {
3790 "f:containers": {
3791 "k:{\"name\":\"nginx\"}": {
3792 ".": {},
3793 "f:env": {
3794 ".": {},
3795 "k:{\"name\":\"ENV0\"}": {
3796 ".": {},
3797 "f:name": {},
3798 "f:value": {}
3799 },
3800 "k:{\"name\":\"PATH\"}": {}
3801 },
3802 "f:image": {},
3803 "f:imagePullPolicy": {},
3804 "f:name": {},
3805 "f:ports": {
3806 ".": {},
3807 "k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
3808 ".": {},
3809 "f:containerPort": {},
3810 "f:name": {},
3811 "f:protocol": {}
3812 },
3813 "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {}
3814 },
3815 "f:resources": {},
3816 "f:terminationMessagePath": {},
3817 "f:terminationMessagePolicy": {}
3818 }
3819 },
3820 "f:dnsPolicy": {},
3821 "f:restartPolicy": {},
3822 "f:schedulerName": {},
3823 "f:securityContext": {},
3824 "f:terminationGracePeriodSeconds": {}
3825 }
3826 },
3827 "f:updateStrategy": {
3828 "f:rollingUpdate": {
3829 ".": {},
3830 "f:maxSurge": {},
3831 "f:maxUnavailable": {}
3832 },
3833 "f:type": {}
3834 }
3835 }
3836 }
3837 }
3838 ]`)
3839
3840
3841 ds = []byte(`
3842 apiVersion: apps/v1
3843 kind: DaemonSet
3844 metadata:
3845 name: example-daemonset
3846 labels:
3847 app: example
3848 spec:
3849 selector:
3850 matchLabels:
3851 app: example
3852 template:
3853 spec:
3854 containers:
3855 - name: nginx
3856 image: nginx
3857 ports:
3858 - name: port3
3859 containerPort: 443
3860 env:
3861 - name: HOME
3862 value: "/usr/home"
3863 `)
3864 obj, err = client.AppsV1().RESTClient().
3865 Patch(types.ApplyPatchType).
3866 Namespace("default").
3867 Name("example-daemonset").
3868 Param("fieldManager", "apply").
3869 Resource("daemonsets").
3870 Body(ds).
3871 Do(context.TODO()).
3872 Get()
3873 if err != nil {
3874 t.Fatalf("Failed to aply the first object: %v", err)
3875 }
3876 daemon = obj.(*appsv1.DaemonSet)
3877 if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
3878 t.Fatalf("Expected %v EnvVars, got %v", want, got)
3879 }
3880 if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
3881 t.Fatalf("Expected %v Ports, got %v", want, got)
3882 }
3883
3884 expectManagedFields(t, daemon.ManagedFields, `
3885 [
3886 {
3887 "manager": "apply",
3888 "operation": "Apply",
3889 "apiVersion": "apps/v1",
3890 "time": null,
3891 "fieldsType": "FieldsV1",
3892 "fieldsV1": {
3893 "f:metadata": {
3894 "f:labels": {
3895 "f:app": {}
3896 }
3897 },
3898 "f:spec": {
3899 "f:selector": {},
3900 "f:template": {
3901 "f:spec": {
3902 "f:containers": {
3903 "k:{\"name\":\"nginx\"}": {
3904 ".": {},
3905 "f:env": {
3906 "k:{\"name\":\"HOME\"}": {
3907 ".": {},
3908 "f:name": {},
3909 "f:value": {}
3910 }
3911 },
3912 "f:image": {},
3913 "f:name": {},
3914 "f:ports": {
3915 "k:{\"containerPort\":443,\"protocol\":\"TCP\"}": {
3916 ".": {},
3917 "f:containerPort": {},
3918 "f:name": {}
3919 }
3920 }
3921 }
3922 }
3923 }
3924 }
3925 }
3926 }
3927 },
3928 {
3929 "manager": "create",
3930 "operation": "Update",
3931 "apiVersion": "apps/v1",
3932 "time": null,
3933 "fieldsType": "FieldsV1",
3934 "fieldsV1": {
3935 "f:metadata": {
3936 "f:annotations": {
3937 ".": {},
3938 "f:deprecated.daemonset.template.generation": {}
3939 },
3940 "f:labels": {
3941 ".": {},
3942 "f:app": {}
3943 }
3944 },
3945 "f:spec": {
3946 "f:revisionHistoryLimit": {},
3947 "f:selector": {},
3948 "f:template": {
3949 "f:metadata": {
3950 "f:labels": {
3951 ".": {},
3952 "f:app": {}
3953 }
3954 },
3955 "f:spec": {
3956 "f:containers": {
3957 "k:{\"name\":\"nginx\"}": {
3958 ".": {},
3959 "f:env": {
3960 ".": {},
3961 "k:{\"name\":\"ENV0\"}": {
3962 ".": {},
3963 "f:name": {},
3964 "f:value": {}
3965 },
3966 "k:{\"name\":\"PATH\"}": {}
3967 },
3968 "f:image": {},
3969 "f:imagePullPolicy": {},
3970 "f:name": {},
3971 "f:ports": {
3972 ".": {},
3973 "k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
3974 ".": {},
3975 "f:containerPort": {},
3976 "f:name": {},
3977 "f:protocol": {}
3978 },
3979 "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {}
3980 },
3981 "f:resources": {},
3982 "f:terminationMessagePath": {},
3983 "f:terminationMessagePolicy": {}
3984 }
3985 },
3986 "f:dnsPolicy": {},
3987 "f:restartPolicy": {},
3988 "f:schedulerName": {},
3989 "f:securityContext": {},
3990 "f:terminationGracePeriodSeconds": {}
3991 }
3992 },
3993 "f:updateStrategy": {
3994 "f:rollingUpdate": {
3995 ".": {},
3996 "f:maxSurge": {},
3997 "f:maxUnavailable": {}
3998 },
3999 "f:type": {}
4000 }
4001 }
4002 }
4003 }
4004 ]
4005 `)
4006
4007
4008 ds = []byte(`{
4009 "apiVersion": "apps/v1",
4010 "kind": "DaemonSet",
4011 "metadata": {
4012 "name": "example-daemonset",
4013 "labels": {
4014 "app": "example"
4015 }
4016 },
4017 "spec": {
4018 "selector": {
4019 "matchLabels": {
4020 "app": "example"
4021 }
4022 },
4023 "template": {
4024 "metadata": {
4025 "labels": {
4026 "app": "example"
4027 }
4028 },
4029 "spec": {
4030 "containers": [
4031 {
4032 "name": "nginx",
4033 "image": "nginx",
4034 "ports": [
4035 {
4036 "name": "port0",
4037 "containerPort": 1
4038 },
4039 {
4040 "name": "port3",
4041 "containerPort": 443
4042 },
4043 {
4044 "name": "port4",
4045 "containerPort": 80
4046 },
4047 {
4048 "name": "port5",
4049 "containerPort": 80
4050 }
4051 ],
4052 "env": [
4053 {
4054 "name": "ENV0",
4055 "value": "/env0value"
4056 },
4057 {
4058 "name": "PATH",
4059 "value": "/bin"
4060 },
4061 {
4062 "name": "PATH",
4063 "value": "$PATH:/usr/bin:/usr/local/bin"
4064 },
4065 {
4066 "name": "HOME",
4067 "value": "/usr/home"
4068 }
4069 ]
4070 }
4071 ]
4072 }
4073 }
4074 }
4075 }`)
4076 obj, err = client.AppsV1().RESTClient().
4077 Put().
4078 Namespace("default").
4079 Name("example-daemonset").
4080 Param("fieldManager", "update").
4081 Resource("daemonsets").
4082 Body(ds).
4083 Do(context.TODO()).
4084 Get()
4085 if err != nil {
4086 t.Fatalf("Failed to update the object: %v", err)
4087 }
4088 daemon = obj.(*appsv1.DaemonSet)
4089 if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
4090 t.Fatalf("Expected %v EnvVars, got %v", want, got)
4091 }
4092 if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
4093 t.Fatalf("Expected %v Ports, got %v", want, got)
4094 }
4095
4096 expectManagedFields(t, daemon.ManagedFields, `
4097 [
4098 {
4099 "manager": "apply",
4100 "operation": "Apply",
4101 "apiVersion": "apps/v1",
4102 "time": null,
4103 "fieldsType": "FieldsV1",
4104 "fieldsV1": {
4105 "f:metadata": {
4106 "f:labels": {
4107 "f:app": {}
4108 }
4109 },
4110 "f:spec": {
4111 "f:selector": {},
4112 "f:template": {
4113 "f:spec": {
4114 "f:containers": {
4115 "k:{\"name\":\"nginx\"}": {
4116 ".": {},
4117 "f:env": {
4118 "k:{\"name\":\"HOME\"}": {
4119 ".": {},
4120 "f:name": {},
4121 "f:value": {}
4122 }
4123 },
4124 "f:image": {},
4125 "f:name": {},
4126 "f:ports": {
4127 "k:{\"containerPort\":443,\"protocol\":\"TCP\"}": {
4128 ".": {},
4129 "f:containerPort": {},
4130 "f:name": {}
4131 }
4132 }
4133 }
4134 }
4135 }
4136 }
4137 }
4138 }
4139 },
4140 {
4141 "manager": "create",
4142 "operation": "Update",
4143 "apiVersion": "apps/v1",
4144 "time": null,
4145 "fieldsType": "FieldsV1",
4146 "fieldsV1": {
4147 "f:metadata": {
4148 "f:annotations": {},
4149 "f:labels": {
4150 ".": {},
4151 "f:app": {}
4152 }
4153 },
4154 "f:spec": {
4155 "f:revisionHistoryLimit": {},
4156 "f:selector": {},
4157 "f:template": {
4158 "f:metadata": {
4159 "f:labels": {
4160 ".": {},
4161 "f:app": {}
4162 }
4163 },
4164 "f:spec": {
4165 "f:containers": {
4166 "k:{\"name\":\"nginx\"}": {
4167 ".": {},
4168 "f:env": {
4169 ".": {},
4170 "k:{\"name\":\"ENV0\"}": {
4171 ".": {},
4172 "f:name": {},
4173 "f:value": {}
4174 }
4175 },
4176 "f:image": {},
4177 "f:imagePullPolicy": {},
4178 "f:name": {},
4179 "f:ports": {
4180 ".": {},
4181 "k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
4182 ".": {},
4183 "f:containerPort": {},
4184 "f:name": {},
4185 "f:protocol": {}
4186 }
4187 },
4188 "f:resources": {},
4189 "f:terminationMessagePath": {},
4190 "f:terminationMessagePolicy": {}
4191 }
4192 },
4193 "f:dnsPolicy": {},
4194 "f:restartPolicy": {},
4195 "f:schedulerName": {},
4196 "f:securityContext": {},
4197 "f:terminationGracePeriodSeconds": {}
4198 }
4199 },
4200 "f:updateStrategy": {
4201 "f:rollingUpdate": {
4202 ".": {},
4203 "f:maxSurge": {},
4204 "f:maxUnavailable": {}
4205 },
4206 "f:type": {}
4207 }
4208 }
4209 }
4210 },
4211 {
4212 "manager": "update",
4213 "operation": "Update",
4214 "apiVersion": "apps/v1",
4215 "time": null,
4216 "fieldsType": "FieldsV1",
4217 "fieldsV1": {
4218 "f:metadata": {
4219 "f:annotations": {
4220 "f:deprecated.daemonset.template.generation": {}
4221 }
4222 },
4223 "f:spec": {
4224 "f:template": {
4225 "f:spec": {
4226 "f:containers": {
4227 "k:{\"name\":\"nginx\"}": {
4228 "f:env": {
4229 "k:{\"name\":\"PATH\"}": {}
4230 },
4231 "f:ports": {
4232 "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {}
4233 }
4234 }
4235 }
4236 }
4237 }
4238 }
4239 }
4240 }
4241 ]
4242 `)
4243
4244
4245 ds = []byte(`
4246 apiVersion: apps/v1
4247 kind: DaemonSet
4248 metadata:
4249 name: example-daemonset
4250 labels:
4251 app: example
4252 spec:
4253 selector:
4254 matchLabels:
4255 app: example
4256 template:
4257 spec:
4258 containers:
4259 - name: nginx
4260 image: nginx
4261 ports:
4262 - name: port80
4263 containerPort: 80
4264 env:
4265 - name: PATH
4266 value: "/bin:/usr/bin:/usr/local/bin"
4267 `)
4268 obj, err = client.AppsV1().RESTClient().
4269 Patch(types.ApplyPatchType).
4270 Namespace("default").
4271 Name("example-daemonset").
4272 Param("fieldManager", "apply").
4273 Param("force", "true").
4274 Resource("daemonsets").
4275 Body(ds).
4276 Do(context.TODO()).
4277 Get()
4278 if err != nil {
4279 t.Fatalf("Failed to apply the second object: %v", err)
4280 }
4281
4282 daemon = obj.(*appsv1.DaemonSet)
4283
4284 if want, got := 2, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
4285 t.Fatalf("Expected %v EnvVars, got %v", want, got)
4286 }
4287 if want, got := 2, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
4288 t.Fatalf("Expected %v Ports, got %v", want, got)
4289 }
4290
4291 expectManagedFields(t, daemon.ManagedFields, `
4292 [
4293 {
4294 "manager": "apply",
4295 "operation": "Apply",
4296 "apiVersion": "apps/v1",
4297 "time": null,
4298 "fieldsType": "FieldsV1",
4299 "fieldsV1": {
4300 "f:metadata": {
4301 "f:labels": {
4302 "f:app": {}
4303 }
4304 },
4305 "f:spec": {
4306 "f:selector": {},
4307 "f:template": {
4308 "f:spec": {
4309 "f:containers": {
4310 "k:{\"name\":\"nginx\"}": {
4311 ".": {},
4312 "f:env": {
4313 "k:{\"name\":\"PATH\"}": {
4314 ".": {},
4315 "f:name": {},
4316 "f:value": {}
4317 }
4318 },
4319 "f:image": {},
4320 "f:name": {},
4321 "f:ports": {
4322 "k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
4323 ".": {},
4324 "f:containerPort": {},
4325 "f:name": {}
4326 }
4327 }
4328 }
4329 }
4330 }
4331 }
4332 }
4333 }
4334 },
4335 {
4336 "manager": "create",
4337 "operation": "Update",
4338 "apiVersion": "apps/v1",
4339 "time": null,
4340 "fieldsType": "FieldsV1",
4341 "fieldsV1": {
4342 "f:metadata": {
4343 "f:annotations": {},
4344 "f:labels": {
4345 ".": {},
4346 "f:app": {}
4347 }
4348 },
4349 "f:spec": {
4350 "f:revisionHistoryLimit": {},
4351 "f:selector": {},
4352 "f:template": {
4353 "f:metadata": {
4354 "f:labels": {
4355 ".": {},
4356 "f:app": {}
4357 }
4358 },
4359 "f:spec": {
4360 "f:containers": {
4361 "k:{\"name\":\"nginx\"}": {
4362 ".": {},
4363 "f:env": {
4364 ".": {},
4365 "k:{\"name\":\"ENV0\"}": {
4366 ".": {},
4367 "f:name": {},
4368 "f:value": {}
4369 }
4370 },
4371 "f:image": {},
4372 "f:imagePullPolicy": {},
4373 "f:name": {},
4374 "f:ports": {
4375 ".": {},
4376 "k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
4377 ".": {},
4378 "f:containerPort": {},
4379 "f:name": {},
4380 "f:protocol": {}
4381 }
4382 },
4383 "f:resources": {},
4384 "f:terminationMessagePath": {},
4385 "f:terminationMessagePolicy": {}
4386 }
4387 },
4388 "f:dnsPolicy": {},
4389 "f:restartPolicy": {},
4390 "f:schedulerName": {},
4391 "f:securityContext": {},
4392 "f:terminationGracePeriodSeconds": {}
4393 }
4394 },
4395 "f:updateStrategy": {
4396 "f:rollingUpdate": {
4397 ".": {},
4398 "f:maxSurge": {},
4399 "f:maxUnavailable": {}
4400 },
4401 "f:type": {}
4402 }
4403 }
4404 }
4405 },
4406 {
4407 "manager": "update",
4408 "operation": "Update",
4409 "apiVersion": "apps/v1",
4410 "time": null,
4411 "fieldsType": "FieldsV1",
4412 "fieldsV1": {
4413 "f:metadata": {
4414 "f:annotations": {
4415 "f:deprecated.daemonset.template.generation": {}
4416 }
4417 }
4418 }
4419 }
4420 ]
4421 `)
4422 }
4423
4424 func expectManagedFields(t *testing.T, managedFields []metav1.ManagedFieldsEntry, expect string) {
4425 t.Helper()
4426 for i := range managedFields {
4427 managedFields[i].Time = &metav1.Time{}
4428 }
4429 got, err := json.MarshalIndent(managedFields, "", " ")
4430 if err != nil {
4431 t.Fatalf("Failed to marshal managed fields: %v", err)
4432 }
4433 b := &bytes.Buffer{}
4434 err = json.Indent(b, []byte(expect), "", " ")
4435 if err != nil {
4436 t.Fatalf("Failed to indent json: %v", err)
4437 }
4438 want := b.String()
4439 diff := cmp.Diff(strings.Split(strings.TrimSpace(string(got)), "\n"), strings.Split(strings.TrimSpace(want), "\n"))
4440 if len(diff) > 0 {
4441 t.Fatalf("Want:\n%s\nGot:\n%s\nDiff:\n%s", string(want), string(got), diff)
4442 }
4443 }
4444
View as plain text