@@ -45,12 +45,14 @@ import (
45
45
"k8s.io/apimachinery/pkg/labels"
46
46
"k8s.io/apimachinery/pkg/runtime"
47
47
"k8s.io/apimachinery/pkg/runtime/schema"
48
+ "k8s.io/apimachinery/pkg/runtime/serializer"
48
49
"k8s.io/apimachinery/pkg/types"
49
50
utilrand "k8s.io/apimachinery/pkg/util/rand"
50
51
"k8s.io/apimachinery/pkg/util/sets"
51
52
"k8s.io/apimachinery/pkg/util/strategicpatch"
52
53
"k8s.io/apimachinery/pkg/util/validation/field"
53
54
"k8s.io/apimachinery/pkg/watch"
55
+ clientgoapplyconfigurations "k8s.io/client-go/applyconfigurations"
54
56
"k8s.io/client-go/kubernetes/scheme"
55
57
"k8s.io/client-go/testing"
56
58
"k8s.io/utils/ptr"
@@ -230,7 +232,11 @@ func (f *ClientBuilder) Build() client.WithWatch {
230
232
}
231
233
232
234
if f .objectTracker == nil {
233
- tracker = versionedTracker {ObjectTracker : testing .NewObjectTracker (f .scheme , scheme .Codecs .UniversalDecoder ()), scheme : f .scheme , withStatusSubresource : withStatusSubResource }
235
+ tracker = versionedTracker {ObjectTracker : testing .NewFieldManagedObjectTracker (
236
+ f .scheme ,
237
+ serializer .NewCodecFactory (f .scheme ).UniversalDecoder (),
238
+ clientgoapplyconfigurations .NewTypeConverter (f .scheme ),
239
+ )}
234
240
} else {
235
241
tracker = versionedTracker {ObjectTracker : f .objectTracker , scheme : f .scheme , withStatusSubresource : withStatusSubResource }
236
242
}
@@ -868,6 +874,12 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client
868
874
if err != nil {
869
875
return err
870
876
}
877
+
878
+ // otherwise the merge logic in the tracker complains
879
+ if patch .Type () == types .ApplyPatchType {
880
+ obj .SetManagedFields (nil )
881
+ }
882
+
871
883
data , err := patch .Data (obj )
872
884
if err != nil {
873
885
return err
@@ -880,7 +892,11 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client
880
892
881
893
oldObj , err := c .tracker .Get (gvr , accessor .GetNamespace (), accessor .GetName ())
882
894
if err != nil {
883
- return err
895
+ if patch .Type () == types .ApplyPatchType {
896
+ oldObj = & unstructured.Unstructured {}
897
+ } else {
898
+ return err
899
+ }
884
900
}
885
901
oldAccessor , err := meta .Accessor (oldObj )
886
902
if err != nil {
@@ -895,7 +911,7 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client
895
911
// This ensures that the patch may be rejected if a deletionTimestamp is modified, prior
896
912
// to updating the object.
897
913
action := testing .NewPatchAction (gvr , accessor .GetNamespace (), accessor .GetName (), patch .Type (), data )
898
- o , err := dryPatch (action , c .tracker )
914
+ o , err := dryPatch (action , c .tracker , obj )
899
915
if err != nil {
900
916
return err
901
917
}
@@ -954,12 +970,15 @@ func deletionTimestampEqual(newObj metav1.Object, obj metav1.Object) bool {
954
970
// This results in some code duplication, but was found to be a cleaner alternative than unmarshalling and introspecting the patch data
955
971
// and easier than refactoring the k8s client-go method upstream.
956
972
// Duplicate of upstream: https://github.com/kubernetes/client-go/blob/783d0d33626e59d55d52bfd7696b775851f92107/testing/fixture.go#L146-L194
957
- func dryPatch (action testing.PatchActionImpl , tracker testing.ObjectTracker ) (runtime.Object , error ) {
973
+ func dryPatch (action testing.PatchActionImpl , tracker testing.ObjectTracker , newObj runtime. Object ) (runtime.Object , error ) {
958
974
ns := action .GetNamespace ()
959
975
gvr := action .GetResource ()
960
976
961
977
obj , err := tracker .Get (gvr , ns , action .GetName ())
962
978
if err != nil {
979
+ if action .GetPatchType () == types .ApplyPatchType {
980
+ return & unstructured.Unstructured {}, nil
981
+ }
963
982
return nil , err
964
983
}
965
984
@@ -1005,7 +1024,18 @@ func dryPatch(action testing.PatchActionImpl, tracker testing.ObjectTracker) (ru
1005
1024
return nil , err
1006
1025
}
1007
1026
case types .ApplyPatchType :
1008
- return nil , errors .New ("apply patches are not supported in the fake client. Follow https://github.com/kubernetes/kubernetes/issues/115598 for the current status" )
1027
+ // There doesn't seem to be a way to test this without actually applying it as apply is implemented in the tracker.
1028
+ // We have to make sure no reader sees this and we can not handle errors resetting the obj to the original state.
1029
+ defer tracker .Add (obj )
1030
+ defer func () {
1031
+ if err := tracker .Add (obj ); err != nil {
1032
+ panic (err )
1033
+ }
1034
+ }()
1035
+ if err := tracker .Apply (gvr , newObj , ns , action .PatchOptions ); err != nil {
1036
+ return nil , err
1037
+ }
1038
+ return tracker .Get (gvr , ns , action .GetName ())
1009
1039
default :
1010
1040
return nil , fmt .Errorf ("%s PatchType is not supported" , action .GetPatchType ())
1011
1041
}
0 commit comments