@@ -68,16 +68,21 @@ type versionedTracker struct {
68
68
}
69
69
70
70
type fakeClient struct {
71
- tracker versionedTracker
72
- scheme * runtime.Scheme
71
+ // trackerWriteLock must be acquired before writing to
72
+ // the tracker or performing reads that affect a following
73
+ // write.
74
+ trackerWriteLock sync.Mutex
75
+ tracker versionedTracker
76
+
77
+ schemeWriteLock sync.Mutex
78
+ scheme * runtime.Scheme
79
+
73
80
restMapper meta.RESTMapper
74
81
withStatusSubresource sets.Set [schema.GroupVersionKind ]
75
82
76
83
// indexes maps each GroupVersionKind (GVK) to the indexes registered for that GVK.
77
84
// The inner map maps from index name to IndexerFunc.
78
85
indexes map [schema.GroupVersionKind ]map [string ]client.IndexerFunc
79
-
80
- schemeWriteLock sync.Mutex
81
86
}
82
87
83
88
var _ client.WithWatch = & fakeClient {}
@@ -467,6 +472,11 @@ func (t versionedTracker) updateObject(gvr schema.GroupVersionResource, obj runt
467
472
switch {
468
473
case allowsUnconditionalUpdate (gvk ):
469
474
accessor .SetResourceVersion (oldAccessor .GetResourceVersion ())
475
+ // This is needed because if the patch explicitly sets the RV to null, the client-go reaction we use
476
+ // to apply it and whose output we process here will have it unset. It is not clear why the Kubernetes
477
+ // apiserver accepts such a patch, but it does so we just copy that behavior.
478
+ // Kubernetes apiserver behavior can be checked like this:
479
+ // `kubectl patch configmap foo --patch '{"metadata":{"annotations":{"foo":"bar"},"resourceVersion":null}}' -v=9`
470
480
case bytes .
471
481
Contains (debug .Stack (), []byte ("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeClient).Patch" )):
472
482
// We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change
@@ -732,6 +742,8 @@ func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...clie
732
742
accessor .SetDeletionTimestamp (nil )
733
743
}
734
744
745
+ c .trackerWriteLock .Lock ()
746
+ defer c .trackerWriteLock .Unlock ()
735
747
return c .tracker .Create (gvr , obj , accessor .GetNamespace ())
736
748
}
737
749
@@ -753,6 +765,8 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie
753
765
}
754
766
}
755
767
768
+ c .trackerWriteLock .Lock ()
769
+ defer c .trackerWriteLock .Unlock ()
756
770
// Check the ResourceVersion if that Precondition was specified.
757
771
if delOptions .Preconditions != nil && delOptions .Preconditions .ResourceVersion != nil {
758
772
name := accessor .GetName ()
@@ -775,7 +789,7 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie
775
789
}
776
790
}
777
791
778
- return c .deleteObject (gvr , accessor )
792
+ return c .deleteObjectLocked (gvr , accessor )
779
793
}
780
794
781
795
func (c * fakeClient ) DeleteAllOf (ctx context.Context , obj client.Object , opts ... client.DeleteAllOfOption ) error {
@@ -793,6 +807,9 @@ func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ..
793
807
}
794
808
}
795
809
810
+ c .trackerWriteLock .Lock ()
811
+ defer c .trackerWriteLock .Unlock ()
812
+
796
813
gvr , _ := meta .UnsafeGuessKindToResource (gvk )
797
814
o , err := c .tracker .List (gvr , gvk , dcOptions .Namespace )
798
815
if err != nil {
@@ -812,7 +829,7 @@ func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ..
812
829
if err != nil {
813
830
return err
814
831
}
815
- err = c .deleteObject (gvr , accessor )
832
+ err = c .deleteObjectLocked (gvr , accessor )
816
833
if err != nil {
817
834
return err
818
835
}
@@ -842,6 +859,9 @@ func (c *fakeClient) update(obj client.Object, isStatus bool, opts ...client.Upd
842
859
if err != nil {
843
860
return err
844
861
}
862
+
863
+ c .trackerWriteLock .Lock ()
864
+ defer c .trackerWriteLock .Unlock ()
845
865
return c .tracker .update (gvr , obj , accessor .GetNamespace (), isStatus , false , * updateOptions .AsUpdateOptions ())
846
866
}
847
867
@@ -877,6 +897,8 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client
877
897
return err
878
898
}
879
899
900
+ c .trackerWriteLock .Lock ()
901
+ defer c .trackerWriteLock .Unlock ()
880
902
oldObj , err := c .tracker .Get (gvr , accessor .GetNamespace (), accessor .GetName ())
881
903
if err != nil {
882
904
return err
@@ -1085,7 +1107,7 @@ func (c *fakeClient) SubResource(subResource string) client.SubResourceClient {
1085
1107
return & fakeSubResourceClient {client : c , subResource : subResource }
1086
1108
}
1087
1109
1088
- func (c * fakeClient ) deleteObject (gvr schema.GroupVersionResource , accessor metav1.Object ) error {
1110
+ func (c * fakeClient ) deleteObjectLocked (gvr schema.GroupVersionResource , accessor metav1.Object ) error {
1089
1111
old , err := c .tracker .Get (gvr , accessor .GetNamespace (), accessor .GetName ())
1090
1112
if err == nil {
1091
1113
oldAccessor , err := meta .Accessor (old )
@@ -1167,7 +1189,7 @@ func (sw *fakeSubResourceClient) Update(ctx context.Context, obj client.Object,
1167
1189
1168
1190
switch sw .subResource {
1169
1191
case subResourceScale :
1170
- if err := sw .client .Get (ctx , client .ObjectKeyFromObject (obj ), obj ); err != nil {
1192
+ if err := sw .client .Get (ctx , client .ObjectKeyFromObject (obj ), obj . DeepCopyObject ().(client. Object ) ); err != nil {
1171
1193
return err
1172
1194
}
1173
1195
if updateOptions .SubResourceBody == nil {
0 commit comments