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