Skip to content

Commit 56df553

Browse files
authored
Merge pull request #3146 from k8s-infra-cherrypick-robot/cherry-pick-3143-to-release-0.19
[release-0.19] 🐛 Fakeclient: Fix dataraces when writing to the scheme
2 parents 13450ba + d2145c0 commit 56df553

File tree

2 files changed

+108
-4
lines changed

2 files changed

+108
-4
lines changed

Diff for: pkg/client/fake/client.go

+20-4
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ type fakeClient struct {
7474
trackerWriteLock sync.Mutex
7575
tracker versionedTracker
7676

77-
schemeWriteLock sync.Mutex
78-
scheme *runtime.Scheme
77+
schemeLock sync.RWMutex
78+
scheme *runtime.Scheme
7979

8080
restMapper meta.RESTMapper
8181
withStatusSubresource sets.Set[schema.GroupVersionKind]
@@ -509,6 +509,8 @@ func (t versionedTracker) updateObject(gvr schema.GroupVersionResource, obj runt
509509
}
510510

511511
func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
512+
c.schemeLock.RLock()
513+
defer c.schemeLock.RUnlock()
512514
gvr, err := getGVRFromObject(obj, c.scheme)
513515
if err != nil {
514516
return err
@@ -558,6 +560,8 @@ func (c *fakeClient) Watch(ctx context.Context, list client.ObjectList, opts ...
558560
}
559561

560562
func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error {
563+
c.schemeLock.RLock()
564+
defer c.schemeLock.RUnlock()
561565
gvk, err := apiutil.GVKForObject(obj, c.scheme)
562566
if err != nil {
563567
return err
@@ -570,9 +574,11 @@ func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...cl
570574
if _, isUnstructuredList := obj.(runtime.Unstructured); isUnstructuredList && !c.scheme.Recognizes(gvk) {
571575
// We need to register the ListKind with UnstructuredList:
572576
// https://github.com/kubernetes/kubernetes/blob/7b2776b89fb1be28d4e9203bdeec079be903c103/staging/src/k8s.io/client-go/dynamic/fake/simple.go#L44-L51
573-
c.schemeWriteLock.Lock()
577+
c.schemeLock.RUnlock()
578+
c.schemeLock.Lock()
574579
c.scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(gvk.Kind+"List"), &unstructured.UnstructuredList{})
575-
c.schemeWriteLock.Unlock()
580+
c.schemeLock.Unlock()
581+
c.schemeLock.RLock()
576582
}
577583

578584
listOpts := client.ListOptions{}
@@ -712,6 +718,8 @@ func (c *fakeClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
712718
}
713719

714720
func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
721+
c.schemeLock.RLock()
722+
defer c.schemeLock.RUnlock()
715723
createOptions := &client.CreateOptions{}
716724
createOptions.ApplyOptions(opts)
717725

@@ -748,6 +756,8 @@ func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...clie
748756
}
749757

750758
func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
759+
c.schemeLock.RLock()
760+
defer c.schemeLock.RUnlock()
751761
gvr, err := getGVRFromObject(obj, c.scheme)
752762
if err != nil {
753763
return err
@@ -793,6 +803,8 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie
793803
}
794804

795805
func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
806+
c.schemeLock.RLock()
807+
defer c.schemeLock.RUnlock()
796808
gvk, err := apiutil.GVKForObject(obj, c.scheme)
797809
if err != nil {
798810
return err
@@ -842,6 +854,8 @@ func (c *fakeClient) Update(ctx context.Context, obj client.Object, opts ...clie
842854
}
843855

844856
func (c *fakeClient) update(obj client.Object, isStatus bool, opts ...client.UpdateOption) error {
857+
c.schemeLock.RLock()
858+
defer c.schemeLock.RUnlock()
845859
updateOptions := &client.UpdateOptions{}
846860
updateOptions.ApplyOptions(opts)
847861

@@ -870,6 +884,8 @@ func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.
870884
}
871885

872886
func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
887+
c.schemeLock.RLock()
888+
defer c.schemeLock.RUnlock()
873889
patchOptions := &client.PatchOptions{}
874890
patchOptions.ApplyOptions(opts)
875891

Diff for: pkg/client/fake/client_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -2409,6 +2409,93 @@ var _ = Describe("Fake client", func() {
24092409
Expect(cl.SubResource(subResourceScale).Update(context.Background(), obj, client.WithSubResourceBody(scale)).Error()).To(Equal(expectedErr))
24102410
})
24112411

2412+
It("is threadsafe", func() {
2413+
cl := NewClientBuilder().Build()
2414+
2415+
u := func() *unstructured.Unstructured {
2416+
u := &unstructured.Unstructured{}
2417+
u.SetAPIVersion("custom/v1")
2418+
u.SetKind("Version")
2419+
u.SetName("foo")
2420+
return u
2421+
}
2422+
2423+
uList := func() *unstructured.UnstructuredList {
2424+
u := &unstructured.UnstructuredList{}
2425+
u.SetAPIVersion("custom/v1")
2426+
u.SetKind("Version")
2427+
2428+
return u
2429+
}
2430+
2431+
meta := func() *metav1.PartialObjectMetadata {
2432+
return &metav1.PartialObjectMetadata{
2433+
ObjectMeta: metav1.ObjectMeta{
2434+
Name: "foo",
2435+
Namespace: "default",
2436+
},
2437+
TypeMeta: metav1.TypeMeta{
2438+
APIVersion: "custom/v1",
2439+
Kind: "Version",
2440+
},
2441+
}
2442+
}
2443+
metaList := func() *metav1.PartialObjectMetadataList {
2444+
return &metav1.PartialObjectMetadataList{
2445+
TypeMeta: metav1.TypeMeta{
2446+
2447+
APIVersion: "custom/v1",
2448+
Kind: "Version",
2449+
},
2450+
}
2451+
}
2452+
2453+
pod := func() *corev1.Pod {
2454+
return &corev1.Pod{ObjectMeta: metav1.ObjectMeta{
2455+
Name: "foo",
2456+
Namespace: "default",
2457+
}}
2458+
}
2459+
2460+
ctx := context.Background()
2461+
ops := []func(){
2462+
func() { _ = cl.Create(ctx, u()) },
2463+
func() { _ = cl.Get(ctx, client.ObjectKeyFromObject(u()), u()) },
2464+
func() { _ = cl.Update(ctx, u()) },
2465+
func() { _ = cl.Patch(ctx, u(), client.RawPatch(types.StrategicMergePatchType, []byte("foo"))) },
2466+
func() { _ = cl.Delete(ctx, u()) },
2467+
func() { _ = cl.DeleteAllOf(ctx, u(), client.HasLabels{"foo"}) },
2468+
func() { _ = cl.List(ctx, uList()) },
2469+
2470+
func() { _ = cl.Create(ctx, meta()) },
2471+
func() { _ = cl.Get(ctx, client.ObjectKeyFromObject(meta()), meta()) },
2472+
func() { _ = cl.Update(ctx, meta()) },
2473+
func() { _ = cl.Patch(ctx, meta(), client.RawPatch(types.StrategicMergePatchType, []byte("foo"))) },
2474+
func() { _ = cl.Delete(ctx, meta()) },
2475+
func() { _ = cl.DeleteAllOf(ctx, meta(), client.HasLabels{"foo"}) },
2476+
func() { _ = cl.List(ctx, metaList()) },
2477+
2478+
func() { _ = cl.Create(ctx, pod()) },
2479+
func() { _ = cl.Get(ctx, client.ObjectKeyFromObject(pod()), pod()) },
2480+
func() { _ = cl.Update(ctx, pod()) },
2481+
func() { _ = cl.Patch(ctx, pod(), client.RawPatch(types.StrategicMergePatchType, []byte("foo"))) },
2482+
func() { _ = cl.Delete(ctx, pod()) },
2483+
func() { _ = cl.DeleteAllOf(ctx, pod(), client.HasLabels{"foo"}) },
2484+
func() { _ = cl.List(ctx, &corev1.PodList{}) },
2485+
}
2486+
2487+
wg := sync.WaitGroup{}
2488+
wg.Add(len(ops))
2489+
for _, op := range ops {
2490+
go func() {
2491+
defer wg.Done()
2492+
op()
2493+
}()
2494+
}
2495+
2496+
wg.Wait()
2497+
})
2498+
24122499
scalableObjs := []client.Object{
24132500
&appsv1.Deployment{
24142501
ObjectMeta: metav1.ObjectMeta{
@@ -2497,6 +2584,7 @@ var _ = Describe("Fake client", func() {
24972584
scaleExpected.ResourceVersion = scaleActual.ResourceVersion
24982585
Expect(cmp.Diff(scaleExpected, scaleActual)).To(BeEmpty())
24992586
})
2587+
25002588
}
25012589
})
25022590

0 commit comments

Comments
 (0)