diff --git a/pkg/client/fake/client.go b/pkg/client/fake/client.go index d70237e950..b3819926c0 100644 --- a/pkg/client/fake/client.go +++ b/pkg/client/fake/client.go @@ -361,7 +361,7 @@ func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Ob isStatus := false // We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change // that reaction, we use the callstack to figure out if this originated from the status client. - if bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).Patch")) { + if bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).statusPatch")) { isStatus = true } return t.update(gvr, obj, ns, isStatus, false) @@ -1090,6 +1090,15 @@ func (sw *fakeSubResourceClient) Patch(ctx context.Context, obj client.Object, p body = patchOptions.SubResourceBody } + // this is necessary to identify that last call was made for status patch, through stack trace. + if sw.subResource == "status" { + return sw.statusPatch(body, patch, patchOptions) + } + + return sw.client.patch(body, patch, &patchOptions.PatchOptions) +} + +func (sw *fakeSubResourceClient) statusPatch(body client.Object, patch client.Patch, patchOptions client.SubResourcePatchOptions) error { return sw.client.patch(body, patch, &patchOptions.PatchOptions) } diff --git a/pkg/client/fake/client_test.go b/pkg/client/fake/client_test.go index bc857d7be8..810cf5499c 100644 --- a/pkg/client/fake/client_test.go +++ b/pkg/client/fake/client_test.go @@ -40,6 +40,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes/fake" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/interceptor" @@ -1590,6 +1591,28 @@ var _ = Describe("Fake client", func() { Expect(objOriginal.Status.Phase).ToNot(Equal(actual.Status.Phase)) }) + It("should be able to change typed objects that have a scale subresource on patch", func() { + obj := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deploy", + }, + } + cl := NewClientBuilder().WithObjects(obj).Build() + objOriginal := obj.DeepCopy() + + patch := []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, 2)) + Expect(cl.SubResource("scale").Patch(context.Background(), obj, client.RawPatch(types.MergePatchType, patch))).NotTo(HaveOccurred()) + + actual := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}} + Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).To(Succeed()) + + objOriginal.APIVersion = actual.APIVersion + objOriginal.Kind = actual.Kind + objOriginal.ResourceVersion = actual.ResourceVersion + objOriginal.Spec.Replicas = pointer.Int32(2) + Expect(cmp.Diff(objOriginal, actual)).To(BeEmpty()) + }) + It("should not change the status of typed objects that have a status subresource on patch", func() { obj := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{