@@ -18,7 +18,9 @@ package apiserver
18
18
19
19
import (
20
20
"encoding/json"
21
+ "net/http"
21
22
"net/http/httptest"
23
+ "reflect"
22
24
"testing"
23
25
"time"
24
26
@@ -217,7 +219,7 @@ func TestApplyUpdateApplyConflictForced(t *testing.T) {
217
219
}
218
220
219
221
_ , err = client .CoreV1 ().RESTClient ().Patch (types .MergePatchType ).
220
- AbsPath ("/apis/extensions/v1beta1 " ).
222
+ AbsPath ("/apis/apps/v1 " ).
221
223
Namespace ("default" ).
222
224
Resource ("deployments" ).
223
225
Name ("deployment" ).
@@ -555,3 +557,276 @@ func TestApplyRemoveContainerPort(t *testing.T) {
555
557
t .Fatalf ("Expected no container ports but got: %v" , deployment .Spec .Template .Spec .Containers [0 ].Ports )
556
558
}
557
559
}
560
+
561
+ // TestApplyFailsWithVersionMismatch ensures that a version mismatch between the
562
+ // patch object and the live object will error
563
+ func TestApplyFailsWithVersionMismatch (t * testing.T ) {
564
+ defer utilfeaturetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , genericfeatures .ServerSideApply , true )()
565
+
566
+ _ , client , closeFn := setup (t )
567
+ defer closeFn ()
568
+
569
+ obj := []byte (`{
570
+ "apiVersion": "apps/v1",
571
+ "kind": "Deployment",
572
+ "metadata": {
573
+ "name": "deployment",
574
+ "labels": {"app": "nginx"}
575
+ },
576
+ "spec": {
577
+ "replicas": 3,
578
+ "selector": {
579
+ "matchLabels": {
580
+ "app": "nginx"
581
+ }
582
+ },
583
+ "template": {
584
+ "metadata": {
585
+ "labels": {
586
+ "app": "nginx"
587
+ }
588
+ },
589
+ "spec": {
590
+ "containers": [{
591
+ "name": "nginx",
592
+ "image": "nginx:latest"
593
+ }]
594
+ }
595
+ }
596
+ }
597
+ }` )
598
+
599
+ _ , err := client .CoreV1 ().RESTClient ().Patch (types .ApplyPatchType ).
600
+ AbsPath ("/apis/apps/v1" ).
601
+ Namespace ("default" ).
602
+ Resource ("deployments" ).
603
+ Name ("deployment" ).
604
+ Param ("fieldManager" , "apply_test" ).
605
+ Body (obj ).Do ().Get ()
606
+ if err != nil {
607
+ t .Fatalf ("Failed to create object using Apply patch: %v" , err )
608
+ }
609
+
610
+ obj = []byte (`{
611
+ "apiVersion": "extensions/v1beta",
612
+ "kind": "Deployment",
613
+ "metadata": {
614
+ "name": "deployment",
615
+ "labels": {"app": "nginx"}
616
+ },
617
+ "spec": {
618
+ "replicas": 100,
619
+ "selector": {
620
+ "matchLabels": {
621
+ "app": "nginx"
622
+ }
623
+ },
624
+ "template": {
625
+ "metadata": {
626
+ "labels": {
627
+ "app": "nginx"
628
+ }
629
+ },
630
+ "spec": {
631
+ "containers": [{
632
+ "name": "nginx",
633
+ "image": "nginx:latest"
634
+ }]
635
+ }
636
+ }
637
+ }
638
+ }` )
639
+ _ , err = client .CoreV1 ().RESTClient ().Patch (types .ApplyPatchType ).
640
+ AbsPath ("/apis/apps/v1" ).
641
+ Namespace ("default" ).
642
+ Resource ("deployments" ).
643
+ Name ("deployment" ).
644
+ Param ("fieldManager" , "apply_test" ).
645
+ Body ([]byte (obj )).Do ().Get ()
646
+ if err == nil {
647
+ t .Fatalf ("Expecting to get version mismatch when applying object" )
648
+ }
649
+ status , ok := err .(* errors.StatusError )
650
+ if ! ok {
651
+ t .Fatalf ("Expecting to get version mismatch as API error" )
652
+ }
653
+ if status .Status ().Code != http .StatusBadRequest {
654
+ t .Fatalf ("expected status code to be %d but was %d" , http .StatusBadRequest , status .Status ().Code )
655
+ }
656
+ }
657
+
658
+ // TestApplyConvertsManagedFieldsVersion checks that the apply
659
+ // converts the API group-version in the field manager
660
+ func TestApplyConvertsManagedFieldsVersion (t * testing.T ) {
661
+ defer utilfeaturetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , genericfeatures .ServerSideApply , true )()
662
+
663
+ _ , client , closeFn := setup (t )
664
+ defer closeFn ()
665
+
666
+ obj := []byte (`{
667
+ "apiVersion": "apps/v1",
668
+ "kind": "Deployment",
669
+ "metadata": {
670
+ "name": "deployment",
671
+ "labels": {"app": "nginx"},
672
+ "managedFields": [
673
+ {
674
+ "manager": "sidecar_controller",
675
+ "operation": "Apply",
676
+ "apiVersion": "extensions/v1beta1",
677
+ "fields": {
678
+ "f:metadata": {
679
+ "f:labels": {
680
+ "f:sidecar_version": {}
681
+ }
682
+ },
683
+ "f:spec": {
684
+ "f:template": {
685
+ "f: spec": {
686
+ "f:containers": {
687
+ "k:{\"name\":\"sidecar\"}": {
688
+ ".": {},
689
+ "f:image": {}
690
+ }
691
+ }
692
+ }
693
+ }
694
+ }
695
+ }
696
+ }
697
+ ]
698
+ },
699
+ "spec": {
700
+ "selector": {
701
+ "matchLabels": {
702
+ "app": "nginx"
703
+ }
704
+ },
705
+ "template": {
706
+ "metadata": {
707
+ "labels": {
708
+ "app": "nginx"
709
+ }
710
+ },
711
+ "spec": {
712
+ "containers": [{
713
+ "name": "nginx",
714
+ "image": "nginx:latest"
715
+ }]
716
+ }
717
+ }
718
+ }
719
+ }` )
720
+
721
+ _ , err := client .CoreV1 ().RESTClient ().Post ().
722
+ AbsPath ("/apis/apps/v1" ).
723
+ Namespace ("default" ).
724
+ Resource ("deployments" ).
725
+ Body (obj ).Do ().Get ()
726
+ if err != nil {
727
+ t .Fatalf ("Failed to create object: %v" , err )
728
+ }
729
+
730
+ obj = []byte (`{
731
+ "apiVersion": "apps/v1",
732
+ "kind": "Deployment",
733
+ "metadata": {
734
+ "name": "deployment",
735
+ "labels": {"sidecar_version": "release"}
736
+ },
737
+ "spec": {
738
+ "template": {
739
+ "spec": {
740
+ "containers": [{
741
+ "name": "sidecar",
742
+ "image": "sidecar:latest"
743
+ }]
744
+ }
745
+ }
746
+ }
747
+ }` )
748
+ _ , err = client .CoreV1 ().RESTClient ().Patch (types .ApplyPatchType ).
749
+ AbsPath ("/apis/apps/v1" ).
750
+ Namespace ("default" ).
751
+ Resource ("deployments" ).
752
+ Name ("deployment" ).
753
+ Param ("fieldManager" , "sidecar_controller" ).
754
+ Body ([]byte (obj )).Do ().Get ()
755
+ if err != nil {
756
+ t .Fatalf ("Failed to apply object: %v" , err )
757
+ }
758
+
759
+ object , err := client .AppsV1 ().Deployments ("default" ).Get ("deployment" , metav1.GetOptions {})
760
+ if err != nil {
761
+ t .Fatalf ("Failed to retrieve object: %v" , err )
762
+ }
763
+
764
+ accessor , err := meta .Accessor (object )
765
+ if err != nil {
766
+ t .Fatalf ("Failed to get meta accessor: %v" , err )
767
+ }
768
+
769
+ managed := accessor .GetManagedFields ()
770
+ if len (managed ) != 2 {
771
+ t .Fatalf ("Expected 2 field managers, but got managed fields: %v" , managed )
772
+ }
773
+
774
+ var actual * metav1.ManagedFieldsEntry
775
+ for i := range managed {
776
+ entry := & managed [i ]
777
+ if entry .Manager == "sidecar_controller" && entry .APIVersion == "apps/v1" {
778
+ actual = entry
779
+ }
780
+ }
781
+
782
+ if actual == nil {
783
+ 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 )
784
+ }
785
+
786
+ expected := & metav1.ManagedFieldsEntry {
787
+ Manager : "sidecar_controller" ,
788
+ Operation : metav1 .ManagedFieldsOperationApply ,
789
+ APIVersion : "apps/v1" ,
790
+ Time : actual .Time ,
791
+ Fields : & metav1.Fields {
792
+ Map : map [string ]metav1.Fields {
793
+ "f:metadata" : {
794
+ Map : map [string ]metav1.Fields {
795
+ "f:labels" : {
796
+ Map : map [string ]metav1.Fields {
797
+ "f:sidecar_version" : {Map : map [string ]metav1.Fields {}},
798
+ },
799
+ },
800
+ },
801
+ },
802
+ "f:spec" : {
803
+ Map : map [string ]metav1.Fields {
804
+ "f:template" : {
805
+ Map : map [string ]metav1.Fields {
806
+ "f:spec" : {
807
+ Map : map [string ]metav1.Fields {
808
+ "f:containers" : {
809
+ Map : map [string ]metav1.Fields {
810
+ "k:{\" name\" :\" sidecar\" }" : {
811
+ Map : map [string ]metav1.Fields {
812
+ "." : {Map : map [string ]metav1.Fields {}},
813
+ "f:image" : {Map : map [string ]metav1.Fields {}},
814
+ "f:name" : {Map : map [string ]metav1.Fields {}},
815
+ },
816
+ },
817
+ },
818
+ },
819
+ },
820
+ },
821
+ },
822
+ },
823
+ },
824
+ },
825
+ },
826
+ },
827
+ }
828
+
829
+ if ! reflect .DeepEqual (actual , expected ) {
830
+ t .Fatalf ("expected:\n %v\n but got:\n %v" , expected , actual )
831
+ }
832
+ }
0 commit comments