diff --git a/pkg/client/client.go b/pkg/client/client.go index 730e0ba910..d7ff54a2d4 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -288,40 +288,45 @@ func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) e } // Status implements client.StatusClient. -func (c *client) Status() StatusWriter { - return &statusWriter{client: c} +func (c *client) Status() SubResourceWriter { + return c.SubResource("status") } -// statusWriter is client.StatusWriter that writes status subresource. -type statusWriter struct { - client *client +func (c *client) SubResource(subResource string) SubResourceWriter { + return &subResourceWriter{client: c, subResource: subResource} } -// ensure statusWriter implements client.StatusWriter. -var _ StatusWriter = &statusWriter{} +// subResourceWriter is client.SubResourceWriter that writes to subresources. +type subResourceWriter struct { + client *client + subResource string +} + +// ensure subResourceWriter implements client.SubResourceWriter. +var _ SubResourceWriter = &subResourceWriter{} -// Update implements client.StatusWriter. -func (sw *statusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { +// Update implements client.SubResourceWriter. +func (sw *subResourceWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind()) switch obj.(type) { case *unstructured.Unstructured: - return sw.client.unstructuredClient.UpdateStatus(ctx, obj, opts...) + return sw.client.unstructuredClient.UpdateSubResource(ctx, obj, sw.subResource, opts...) case *metav1.PartialObjectMetadata: return fmt.Errorf("cannot update status using only metadata -- did you mean to patch?") default: - return sw.client.typedClient.UpdateStatus(ctx, obj, opts...) + return sw.client.typedClient.UpdateSubResource(ctx, obj, sw.subResource, opts...) } } -// Patch implements client.Client. -func (sw *statusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { +// Patch implements client.SubResourceWriter. +func (sw *subResourceWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind()) switch obj.(type) { case *unstructured.Unstructured: - return sw.client.unstructuredClient.PatchStatus(ctx, obj, patch, opts...) + return sw.client.unstructuredClient.PatchSubResource(ctx, obj, sw.subResource, patch, opts...) case *metav1.PartialObjectMetadata: - return sw.client.metadataClient.PatchStatus(ctx, obj, patch, opts...) + return sw.client.metadataClient.PatchSubResource(ctx, obj, sw.subResource, patch, opts...) default: - return sw.client.typedClient.PatchStatus(ctx, obj, patch, opts...) + return sw.client.typedClient.PatchSubResource(ctx, obj, sw.subResource, patch, opts...) } } diff --git a/pkg/client/dryrun.go b/pkg/client/dryrun.go index 14606a5794..0481920da8 100644 --- a/pkg/client/dryrun.go +++ b/pkg/client/dryrun.go @@ -82,25 +82,30 @@ func (c *dryRunClient) List(ctx context.Context, obj ObjectList, opts ...ListOpt } // Status implements client.StatusClient. -func (c *dryRunClient) Status() StatusWriter { - return &dryRunStatusWriter{client: c.client.Status()} +func (c *dryRunClient) Status() SubResourceWriter { + return c.SubResource("status") } -// ensure dryRunStatusWriter implements client.StatusWriter. -var _ StatusWriter = &dryRunStatusWriter{} +// SubResource implements client.SubResourceClient. +func (c *dryRunClient) SubResource(subResource string) SubResourceWriter { + return &dryRunSubResourceWriter{client: c.client.SubResource(subResource)} +} + +// ensure dryRunSubResourceWriter implements client.SubResourceWriter. +var _ SubResourceWriter = &dryRunSubResourceWriter{} -// dryRunStatusWriter is client.StatusWriter that writes status subresource with dryRun mode +// dryRunSubResourceWriter is client.SubResourceWriter that writes status subresource with dryRun mode // enforced. -type dryRunStatusWriter struct { - client StatusWriter +type dryRunSubResourceWriter struct { + client SubResourceWriter } -// Update implements client.StatusWriter. -func (sw *dryRunStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { +// Update implements client.SubResourceWriter. +func (sw *dryRunSubResourceWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { return sw.client.Update(ctx, obj, append(opts, DryRunAll)...) } -// Patch implements client.StatusWriter. -func (sw *dryRunStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { +// Patch implements client.SubResourceWriter. +func (sw *dryRunSubResourceWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { return sw.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...) } diff --git a/pkg/client/fake/client.go b/pkg/client/fake/client.go index b7ca2de47a..65077d5eda 100644 --- a/pkg/client/fake/client.go +++ b/pkg/client/fake/client.go @@ -634,8 +634,12 @@ func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client. return err } -func (c *fakeClient) Status() client.StatusWriter { - return &fakeStatusWriter{client: c} +func (c *fakeClient) Status() client.SubResourceWriter { + return c.SubResource("status") +} + +func (c *fakeClient) SubResource(subResource string) client.SubResourceWriter { + return &fakeSubResourceWriter{client: c} } func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor metav1.Object) error { @@ -664,19 +668,19 @@ func getGVRFromObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupV return gvr, nil } -type fakeStatusWriter struct { +type fakeSubResourceWriter struct { client *fakeClient } -func (sw *fakeStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { - // TODO(droot): This results in full update of the obj (spec + status). Need - // a way to update status field only. +func (sw *fakeSubResourceWriter) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + // TODO(droot): This results in full update of the obj (spec + subresources). Need + // a way to update subresource only. return sw.client.Update(ctx, obj, opts...) } -func (sw *fakeStatusWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { - // TODO(droot): This results in full update of the obj (spec + status). Need - // a way to update status field only. +func (sw *fakeSubResourceWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + // TODO(droot): This results in full update of the obj (spec + subresources). Need + // a way to update subresource only. return sw.client.Patch(ctx, obj, patch, opts...) } diff --git a/pkg/client/interfaces.go b/pkg/client/interfaces.go index 7f8f8f31c6..113730a87d 100644 --- a/pkg/client/interfaces.go +++ b/pkg/client/interfaces.go @@ -81,11 +81,20 @@ type Writer interface { // StatusClient knows how to create a client which can update status subresource // for kubernetes objects. type StatusClient interface { - Status() StatusWriter + Status() SubResourceWriter } -// StatusWriter knows how to update status subresource of a Kubernetes object. -type StatusWriter interface { +// SubResourceClient knows how to create a client which can update subresource +// for kubernetes objects. +type SubResourceClient interface { + SubResource(subResource string) SubResourceWriter +} + +// StatusWriter is kept for backward compatibility. +type StatusWriter = SubResourceWriter + +// SubResourceWriter knows how to update subresource of a Kubernetes object. +type SubResourceWriter interface { // Update updates the fields corresponding to the status subresource for the // given obj. obj must be a struct pointer so that obj can be updated // with the content returned by the Server. @@ -102,6 +111,7 @@ type Client interface { Reader Writer StatusClient + SubResourceClient // Scheme returns the scheme this client is using. Scheme() *runtime.Scheme diff --git a/pkg/client/metadata_client.go b/pkg/client/metadata_client.go index 2854556f32..3e433cc02a 100644 --- a/pkg/client/metadata_client.go +++ b/pkg/client/metadata_client.go @@ -168,7 +168,7 @@ func (mc *metadataClient) List(ctx context.Context, obj ObjectList, opts ...List return nil } -func (mc *metadataClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { +func (mc *metadataClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...PatchOption) error { metadata, ok := obj.(*metav1.PartialObjectMetadata) if !ok { return fmt.Errorf("metadata client did not understand object: %T", obj) @@ -186,7 +186,7 @@ func (mc *metadataClient) PatchStatus(ctx context.Context, obj Object, patch Pat } patchOpts := &PatchOptions{} - res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions(), "status") + res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions(), subResource) if err != nil { return err } diff --git a/pkg/client/namespaced_client.go b/pkg/client/namespaced_client.go index 674fe253d8..a714cfc2de 100644 --- a/pkg/client/namespaced_client.go +++ b/pkg/client/namespaced_client.go @@ -161,21 +161,26 @@ func (n *namespacedClient) List(ctx context.Context, obj ObjectList, opts ...Lis } // Status implements client.StatusClient. -func (n *namespacedClient) Status() StatusWriter { - return &namespacedClientStatusWriter{StatusClient: n.client.Status(), namespace: n.namespace, namespacedclient: n} +func (n *namespacedClient) Status() SubResourceWriter { + return n.SubResource("status") } -// ensure namespacedClientStatusWriter implements client.StatusWriter. -var _ StatusWriter = &namespacedClientStatusWriter{} +// SubResource implements client.SubResourceClient. +func (n *namespacedClient) SubResource(subResource string) SubResourceWriter { + return &namespacedClientSubResourceWriter{StatusClient: n.client.SubResource(subResource), namespace: n.namespace, namespacedclient: n} +} + +// ensure namespacedClientSubResourceWriter implements client.SubResourceWriter. +var _ SubResourceWriter = &namespacedClientSubResourceWriter{} -type namespacedClientStatusWriter struct { - StatusClient StatusWriter +type namespacedClientSubResourceWriter struct { + StatusClient SubResourceWriter namespace string namespacedclient Client } -// Update implements client.StatusWriter. -func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { +// Update implements client.SubResourceWriter. +func (nsw *namespacedClientSubResourceWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper()) if err != nil { @@ -193,8 +198,8 @@ func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj Object, return nsw.StatusClient.Update(ctx, obj, opts...) } -// Patch implements client.StatusWriter. -func (nsw *namespacedClientStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { +// Patch implements client.SubResourceWriter. +func (nsw *namespacedClientSubResourceWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper()) if err != nil { diff --git a/pkg/client/namespaced_client_test.go b/pkg/client/namespaced_client_test.go index 648c8a4c62..1692b7fcd9 100644 --- a/pkg/client/namespaced_client_test.go +++ b/pkg/client/namespaced_client_test.go @@ -480,7 +480,7 @@ var _ = Describe("NamespacedClient", func() { }) }) - Describe("StatusWriter", func() { + Describe("SubResourceWriter", func() { var err error BeforeEach(func() { dep, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{}) @@ -495,7 +495,7 @@ var _ = Describe("NamespacedClient", func() { changedDep := dep.DeepCopy() changedDep.Status.Replicas = 99 - Expect(getClient().Status().Update(ctx, changedDep)).NotTo(HaveOccurred()) + Expect(getClient().SubResource("status").Update(ctx, changedDep)).NotTo(HaveOccurred()) actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) @@ -509,14 +509,14 @@ var _ = Describe("NamespacedClient", func() { changedDep.SetNamespace("test") changedDep.Status.Replicas = 99 - Expect(getClient().Status().Update(ctx, changedDep)).To(HaveOccurred()) + Expect(getClient().SubResource("status").Update(ctx, changedDep)).To(HaveOccurred()) }) It("should change objects via status patch", func() { changedDep := dep.DeepCopy() changedDep.Status.Replicas = 99 - Expect(getClient().Status().Patch(ctx, changedDep, client.MergeFrom(dep))).NotTo(HaveOccurred()) + Expect(getClient().SubResource("status").Patch(ctx, changedDep, client.MergeFrom(dep))).NotTo(HaveOccurred()) actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) @@ -530,7 +530,7 @@ var _ = Describe("NamespacedClient", func() { changedDep.Status.Replicas = 99 changedDep.SetNamespace("test") - Expect(getClient().Status().Patch(ctx, changedDep, client.MergeFrom(dep))).To(HaveOccurred()) + Expect(getClient().SubResource("status").Patch(ctx, changedDep, client.MergeFrom(dep))).To(HaveOccurred()) }) }) diff --git a/pkg/client/split.go b/pkg/client/split.go index 8717345349..42b61152bd 100644 --- a/pkg/client/split.go +++ b/pkg/client/split.go @@ -61,8 +61,9 @@ func NewDelegatingClient(in NewDelegatingClientInput) (Client, error) { uncachedGVKs: uncachedGVKs, cacheUnstructured: in.CacheUnstructured, }, - Writer: in.Client, - StatusClient: in.Client, + Writer: in.Client, + StatusClient: in.Client, + SubResourceClient: in.Client, }, nil } @@ -70,6 +71,7 @@ type delegatingClient struct { Reader Writer StatusClient + SubResourceClient scheme *runtime.Scheme mapper meta.RESTMapper diff --git a/pkg/client/typed_client.go b/pkg/client/typed_client.go index c2f8aa1efd..675df6da73 100644 --- a/pkg/client/typed_client.go +++ b/pkg/client/typed_client.go @@ -24,7 +24,7 @@ import ( var _ Reader = &typedClient{} var _ Writer = &typedClient{} -var _ StatusWriter = &typedClient{} +var _ SubResourceWriter = &typedClient{} // client is a client.Client that reads and writes directly from/to an API server. It lazily initializes // new clients at the time they are used, and caches the client. @@ -168,8 +168,8 @@ func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOpti Into(obj) } -// UpdateStatus used by StatusWriter to write status. -func (c *typedClient) UpdateStatus(ctx context.Context, obj Object, opts ...UpdateOption) error { +// UpdateSubResource used by SubResourceWriter to write status. +func (c *typedClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...UpdateOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -185,15 +185,15 @@ func (c *typedClient) UpdateStatus(ctx context.Context, obj Object, opts ...Upda NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). Resource(o.resource()). Name(o.GetName()). - SubResource("status"). + SubResource(subResource). Body(obj). VersionedParams(updateOpts.AsUpdateOptions(), c.paramCodec). Do(ctx). Into(obj) } -// PatchStatus used by StatusWriter to write status. -func (c *typedClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { +// PatchSubResource used by SubResourceWriter to write subresource. +func (c *typedClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...PatchOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -211,7 +211,7 @@ func (c *typedClient) PatchStatus(ctx context.Context, obj Object, patch Patch, NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). Resource(o.resource()). Name(o.GetName()). - SubResource("status"). + SubResource(subResource). Body(data). VersionedParams(patchOpts.AsPatchOptions(), c.paramCodec). Do(ctx). diff --git a/pkg/client/unstructured_client.go b/pkg/client/unstructured_client.go index 25e10946a6..d92c18347b 100644 --- a/pkg/client/unstructured_client.go +++ b/pkg/client/unstructured_client.go @@ -27,7 +27,7 @@ import ( var _ Reader = &unstructuredClient{} var _ Writer = &unstructuredClient{} -var _ StatusWriter = &unstructuredClient{} +var _ SubResourceWriter = &unstructuredClient{} // client is a client.Client that reads and writes directly from/to an API server. It lazily initializes // new clients at the time they are used, and caches the client. @@ -226,7 +226,7 @@ func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ... Into(obj) } -func (uc *unstructuredClient) UpdateStatus(ctx context.Context, obj Object, opts ...UpdateOption) error { +func (uc *unstructuredClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...UpdateOption) error { if _, ok := obj.(*unstructured.Unstructured); !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) } @@ -243,14 +243,14 @@ func (uc *unstructuredClient) UpdateStatus(ctx context.Context, obj Object, opts NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). Resource(o.resource()). Name(o.GetName()). - SubResource("status"). + SubResource(subResource). Body(obj). VersionedParams(updateOpts.AsUpdateOptions(), uc.paramCodec). Do(ctx). Into(obj) } -func (uc *unstructuredClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { +func (uc *unstructuredClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...PatchOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -275,7 +275,7 @@ func (uc *unstructuredClient) PatchStatus(ctx context.Context, obj Object, patch NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). Resource(o.resource()). Name(o.GetName()). - SubResource("status"). + SubResource(subResource). Body(data). VersionedParams(patchOpts.AsPatchOptions(), uc.paramCodec). Do(ctx).