From f78d61e7c263392f31560b90c08c57765ceae482 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Fri, 5 May 2017 14:25:26 -0400 Subject: [PATCH 1/2] [client-go] Add dynamic.Interface This adds an interface form of dynamic.Client and dynamic.ResourceClient, making those two follow the general client conventions: `Interface` is an interface, and `Client` is the concrete implementation. `ClientPool` retains it's interface status. This allows us to create a fake implemenation of dyanmic.Interface, dynamic.ResourceInterface, and dynamic.ClientPool for testing. --- .../garbagecollector/graph_builder.go | 2 +- .../garbagecollector/rate_limiter_helper.go | 2 +- .../deletion/namespaced_resources_deleter.go | 6 +-- pkg/printers/internalversion/describe.go | 4 +- .../test/integration/basic_test.go | 4 +- .../test/integration/registration_test.go | 4 +- .../test/integration/testserver/resources.go | 4 +- .../src/k8s.io/client-go/dynamic/client.go | 44 ++++++++++++++++--- .../k8s.io/client-go/dynamic/client_pool.go | 8 ++-- .../k8s.io/client-go/dynamic/client_test.go | 2 +- 10 files changed, 57 insertions(+), 23 deletions(-) diff --git a/pkg/controller/garbagecollector/graph_builder.go b/pkg/controller/garbagecollector/graph_builder.go index c656fde07bd..726d0224a3a 100644 --- a/pkg/controller/garbagecollector/graph_builder.go +++ b/pkg/controller/garbagecollector/graph_builder.go @@ -97,7 +97,7 @@ type GraphBuilder struct { ignoredResources map[schema.GroupResource]struct{} } -func listWatcher(client *dynamic.Client, resource schema.GroupVersionResource) *cache.ListWatch { +func listWatcher(client dynamic.Interface, resource schema.GroupVersionResource) *cache.ListWatch { return &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { // APIResource.Kind is not used by the dynamic client, so diff --git a/pkg/controller/garbagecollector/rate_limiter_helper.go b/pkg/controller/garbagecollector/rate_limiter_helper.go index e9b855b664e..8d43d28be40 100644 --- a/pkg/controller/garbagecollector/rate_limiter_helper.go +++ b/pkg/controller/garbagecollector/rate_limiter_helper.go @@ -46,7 +46,7 @@ func NewRegisteredRateLimiter(resources map[schema.GroupVersionResource]struct{} return &RegisteredRateLimiter{rateLimiters: rateLimiters} } -func (r *RegisteredRateLimiter) registerIfNotPresent(gv schema.GroupVersion, client *dynamic.Client, prefix string) { +func (r *RegisteredRateLimiter) registerIfNotPresent(gv schema.GroupVersion, client dynamic.Interface, prefix string) { once, found := r.rateLimiters[gv] if !found { return diff --git a/pkg/controller/namespace/deletion/namespaced_resources_deleter.go b/pkg/controller/namespace/deletion/namespaced_resources_deleter.go index 31b89a89d52..d76d67bc78e 100644 --- a/pkg/controller/namespace/deletion/namespaced_resources_deleter.go +++ b/pkg/controller/namespace/deletion/namespaced_resources_deleter.go @@ -325,7 +325,7 @@ func (d *namespacedResourcesDeleter) finalizeNamespace(namespace *v1.Namespace) // it returns true if the operation was supported on the server. // it returns an error if the operation was supported on the server but was unable to complete. func (d *namespacedResourcesDeleter) deleteCollection( - dynamicClient *dynamic.Client, gvr schema.GroupVersionResource, + dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string) (bool, error) { glog.V(5).Infof("namespace controller - deleteCollection - namespace: %s, gvr: %v", namespace, gvr) @@ -370,7 +370,7 @@ func (d *namespacedResourcesDeleter) deleteCollection( // a boolean if the operation is supported // an error if the operation is supported but could not be completed. func (d *namespacedResourcesDeleter) listCollection( - dynamicClient *dynamic.Client, gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, bool, error) { + dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, bool, error) { glog.V(5).Infof("namespace controller - listCollection - namespace: %s, gvr: %v", namespace, gvr) key := operationKey{operation: operationList, gvr: gvr} @@ -406,7 +406,7 @@ func (d *namespacedResourcesDeleter) listCollection( // deleteEachItem is a helper function that will list the collection of resources and delete each item 1 by 1. func (d *namespacedResourcesDeleter) deleteEachItem( - dynamicClient *dynamic.Client, gvr schema.GroupVersionResource, namespace string) error { + dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string) error { glog.V(5).Infof("namespace controller - deleteEachItem - namespace: %s, gvr: %v", namespace, gvr) unstructuredList, listSupported, err := d.listCollection(dynamicClient, gvr, namespace) diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index caf0b557eb0..2911fd00a64 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -176,13 +176,13 @@ func DescriberFor(kind schema.GroupKind, c clientset.Interface) (printers.Descri // GenericDescriberFor returns a generic describer for the specified mapping // that uses only information available from runtime.Unstructured -func GenericDescriberFor(mapping *meta.RESTMapping, dynamic *dynamic.Client, events coreclient.EventsGetter) printers.Describer { +func GenericDescriberFor(mapping *meta.RESTMapping, dynamic dynamic.Interface, events coreclient.EventsGetter) printers.Describer { return &genericDescriber{mapping, dynamic, events} } type genericDescriber struct { mapping *meta.RESTMapping - dynamic *dynamic.Client + dynamic dynamic.Interface events coreclient.EventsGetter } diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go index 5e2dbdd9892..659b118d1ca 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go @@ -75,7 +75,7 @@ func TestClusterScopedCRUD(t *testing.T) { testSimpleCRUD(t, ns, noxuDefinition, noxuVersionClient) } -func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, noxuVersionClient *dynamic.Client) { +func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, noxuVersionClient dynamic.Interface) { noxuResourceClient := NewNamespacedCustomResourceClient(ns, noxuVersionClient, noxuDefinition) initialList, err := noxuResourceClient.List(metav1.ListOptions{}) if err != nil { @@ -501,7 +501,7 @@ func TestCrossNamespaceListWatch(t *testing.T) { checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2) } -func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient *dynamic.ResourceClient, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) *unstructured.Unstructured { +func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.ResourceInterface, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) *unstructured.Unstructured { createdInstance, err := instantiateCustomResource(t, testserver.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition) if err != nil { t.Fatalf("unable to create noxu Instance:%v", err) diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go index 4fe6471873a..a9bab6aec2a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go @@ -39,7 +39,7 @@ import ( "k8s.io/client-go/dynamic" ) -func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client *dynamic.ResourceClient, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { +func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { createdInstance, err := client.Create(instanceToCreate) if err != nil { t.Logf("%#v", createdInstance) @@ -66,7 +66,7 @@ func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unst return createdInstance, nil } -func NewNamespacedCustomResourceClient(ns string, client *dynamic.Client, definition *apiextensionsv1beta1.CustomResourceDefinition) *dynamic.ResourceClient { +func NewNamespacedCustomResourceClient(ns string, client dynamic.Interface, definition *apiextensionsv1beta1.CustomResourceDefinition) dynamic.ResourceInterface { return client.Resource(&metav1.APIResource{ Name: definition.Spec.Names.Plural, Namespaced: definition.Spec.Scope == apiextensionsv1beta1.NamespaceScoped, diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go index a68c7393c49..2a5583a155b 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go @@ -141,7 +141,7 @@ func NewCurletInstance(namespace, name string) *unstructured.Unstructured { } } -func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, clientPool dynamic.ClientPool) (*dynamic.Client, error) { +func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, clientPool dynamic.ClientPool) (dynamic.Interface, error) { _, err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Create(crd) if err != nil { return nil, err @@ -194,7 +194,7 @@ func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceD return dynamicClient, nil } -func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient *dynamic.Client) error { +func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) error { ns := "" if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { ns = "aval" diff --git a/staging/src/k8s.io/client-go/dynamic/client.go b/staging/src/k8s.io/client-go/dynamic/client.go index 74fc74c4279..a941189d7f9 100644 --- a/staging/src/k8s.io/client-go/dynamic/client.go +++ b/staging/src/k8s.io/client-go/dynamic/client.go @@ -40,8 +40,42 @@ import ( "k8s.io/client-go/util/flowcontrol" ) -// Client is a Kubernetes client that allows you to access metadata +// Interface is a Kubernetes client that allows you to access metadata // and manipulate metadata of a Kubernetes API group. +type Interface interface { + // GetRateLimiter returns the rate limiter for this client. + GetRateLimiter() flowcontrol.RateLimiter + // Resource returns an API interface to the specified resource for this client's + // group and version. If resource is not a namespaced resource, then namespace + // is ignored. The ResourceInterface inherits the paramater codec of this client. + Resource(resource *metav1.APIResource, namespace string) ResourceInterface + // ParameterCodec returns a client with the provided parameter codec. + ParameterCodec(parameterCodec runtime.ParameterCodec) Interface +} + +// ResourceInterface is an API interface to a specific resource under a +// dynamic client. +type ResourceInterface interface { + // List returns a list of objects for this resource. + List(opts metav1.ListOptions) (runtime.Object, error) + // Get gets the resource with the specified name. + Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) + // Delete deletes the resource with the specified name. + Delete(name string, opts *metav1.DeleteOptions) error + // DeleteCollection deletes a collection of objects. + DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error + // Create creates the provided resource. + Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) + // Update updates the provided resource. + Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) + // Watch returns a watch.Interface that watches the resource. + Watch(opts metav1.ListOptions) (watch.Interface, error) + // Patch patches the provided resource. + Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) +} + +// Client is a Kubernetes client that allows you to access metadata +// and manipulate metadata of a Kubernetes API group, and implements Interface. type Client struct { cl *restclient.RESTClient parameterCodec runtime.ParameterCodec @@ -84,8 +118,8 @@ func (c *Client) GetRateLimiter() flowcontrol.RateLimiter { // Resource returns an API interface to the specified resource for this client's // group and version. If resource is not a namespaced resource, then namespace -// is ignored. The ResourceClient inherits the parameter codec of c. -func (c *Client) Resource(resource *metav1.APIResource, namespace string) *ResourceClient { +// is ignored. The ResourceInterface inherits the parameter codec of c. +func (c *Client) Resource(resource *metav1.APIResource, namespace string) ResourceInterface { return &ResourceClient{ cl: c.cl, resource: resource, @@ -95,7 +129,7 @@ func (c *Client) Resource(resource *metav1.APIResource, namespace string) *Resou } // ParameterCodec returns a client with the provided parameter codec. -func (c *Client) ParameterCodec(parameterCodec runtime.ParameterCodec) *Client { +func (c *Client) ParameterCodec(parameterCodec runtime.ParameterCodec) Interface { return &Client{ cl: c.cl, parameterCodec: parameterCodec, @@ -103,7 +137,7 @@ func (c *Client) ParameterCodec(parameterCodec runtime.ParameterCodec) *Client { } // ResourceClient is an API interface to a specific resource under a -// dynamic client. +// dynamic client, and implements ResourceInterface. type ResourceClient struct { cl *restclient.RESTClient resource *metav1.APIResource diff --git a/staging/src/k8s.io/client-go/dynamic/client_pool.go b/staging/src/k8s.io/client-go/dynamic/client_pool.go index f4f2596a4e5..277dad49cc6 100644 --- a/staging/src/k8s.io/client-go/dynamic/client_pool.go +++ b/staging/src/k8s.io/client-go/dynamic/client_pool.go @@ -28,10 +28,10 @@ import ( type ClientPool interface { // ClientForGroupVersionKind returns a client configured for the specified groupVersionResource. // Resource may be empty. - ClientForGroupVersionResource(resource schema.GroupVersionResource) (*Client, error) + ClientForGroupVersionResource(resource schema.GroupVersionResource) (Interface, error) // ClientForGroupVersionKind returns a client configured for the specified groupVersionKind. // Kind may be empty. - ClientForGroupVersionKind(kind schema.GroupVersionKind) (*Client, error) + ClientForGroupVersionKind(kind schema.GroupVersionKind) (Interface, error) } // APIPathResolverFunc knows how to convert a groupVersion to its API path. The Kind field is @@ -79,7 +79,7 @@ func NewDynamicClientPool(cfg *restclient.Config) ClientPool { // ClientForGroupVersionResource uses the provided RESTMapper to identify the appropriate resource. Resource may // be empty. If no matching kind is found the underlying client for that group is still returned. -func (c *clientPoolImpl) ClientForGroupVersionResource(resource schema.GroupVersionResource) (*Client, error) { +func (c *clientPoolImpl) ClientForGroupVersionResource(resource schema.GroupVersionResource) (Interface, error) { kinds, err := c.mapper.KindsFor(resource) if err != nil { if meta.IsNoMatchError(err) { @@ -92,7 +92,7 @@ func (c *clientPoolImpl) ClientForGroupVersionResource(resource schema.GroupVers // ClientForGroupVersion returns a client for the specified groupVersion, creates one if none exists. Kind // in the GroupVersionKind may be empty. -func (c *clientPoolImpl) ClientForGroupVersionKind(kind schema.GroupVersionKind) (*Client, error) { +func (c *clientPoolImpl) ClientForGroupVersionKind(kind schema.GroupVersionKind) (Interface, error) { c.lock.Lock() defer c.lock.Unlock() diff --git a/staging/src/k8s.io/client-go/dynamic/client_test.go b/staging/src/k8s.io/client-go/dynamic/client_test.go index 67cdcef2efa..1459429c085 100644 --- a/staging/src/k8s.io/client-go/dynamic/client_test.go +++ b/staging/src/k8s.io/client-go/dynamic/client_test.go @@ -58,7 +58,7 @@ func getObject(version, kind, name string) *unstructured.Unstructured { } } -func getClientServer(gv *schema.GroupVersion, h func(http.ResponseWriter, *http.Request)) (*Client, *httptest.Server, error) { +func getClientServer(gv *schema.GroupVersion, h func(http.ResponseWriter, *http.Request)) (Interface, *httptest.Server, error) { srv := httptest.NewServer(http.HandlerFunc(h)) cl, err := NewClient(&restclient.Config{ Host: srv.URL, From 3e6bf24e08645512a7b40d91bd61f0f2ea175026 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Fri, 5 May 2017 14:47:38 -0400 Subject: [PATCH 2/2] [client-go] Add fake dynamic Client/ClientPool This introduces fake implementations of dynamic.Client and dynamic.ClientPool. They function similarly to the fake generated clientsets, since they're also based in testing.Fake. --- hack/.golint_failures | 1 + .../src/k8s.io/client-go/dynamic/fake/BUILD | 29 ++++ .../k8s.io/client-go/dynamic/fake/client.go | 163 ++++++++++++++++++ .../client-go/dynamic/fake/client_pool.go | 48 ++++++ 4 files changed, 241 insertions(+) create mode 100644 staging/src/k8s.io/client-go/dynamic/fake/BUILD create mode 100644 staging/src/k8s.io/client-go/dynamic/fake/client.go create mode 100644 staging/src/k8s.io/client-go/dynamic/fake/client_pool.go diff --git a/hack/.golint_failures b/hack/.golint_failures index fc6cad78dd6..17b2c535a7e 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -699,6 +699,7 @@ staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook staging/src/k8s.io/client-go/discovery/cached staging/src/k8s.io/client-go/discovery/fake staging/src/k8s.io/client-go/dynamic +staging/src/k8s.io/client-go/dynamic/fake staging/src/k8s.io/client-go/examples/workqueue staging/src/k8s.io/client-go/informers/internalinterfaces staging/src/k8s.io/client-go/kubernetes diff --git a/staging/src/k8s.io/client-go/dynamic/fake/BUILD b/staging/src/k8s.io/client-go/dynamic/fake/BUILD new file mode 100644 index 00000000000..f2e8d40cf8e --- /dev/null +++ b/staging/src/k8s.io/client-go/dynamic/fake/BUILD @@ -0,0 +1,29 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = [ + "client.go", + "client_pool.go", + ], + tags = ["automanaged"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/testing:go_default_library", + "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library", + ], +) diff --git a/staging/src/k8s.io/client-go/dynamic/fake/client.go b/staging/src/k8s.io/client-go/dynamic/fake/client.go new file mode 100644 index 00000000000..6c11d4227db --- /dev/null +++ b/staging/src/k8s.io/client-go/dynamic/fake/client.go @@ -0,0 +1,163 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package fake provides a fake client interface to arbitrary Kubernetes +// APIs that exposes common high level operations and exposes common +// metadata. +package fake + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/testing" + "k8s.io/client-go/util/flowcontrol" +) + +// FakeClient is a fake implementation of dynamic.Interface. +type FakeClient struct { + GroupVersion schema.GroupVersion + + *testing.Fake +} + +// GetRateLimiter returns the rate limiter for this client. +func (c *FakeClient) GetRateLimiter() flowcontrol.RateLimiter { + return nil +} + +// Resource returns an API interface to the specified resource for this client's +// group and version. If resource is not a namespaced resource, then namespace +// is ignored. The ResourceClient inherits the paramater codec of this client +func (c *FakeClient) Resource(resource *metav1.APIResource, namespace string) dynamic.ResourceInterface { + return &FakeResourceClient{ + Resource: c.GroupVersion.WithResource(resource.Name), + Kind: c.GroupVersion.WithKind(resource.Kind), + Namespace: namespace, + + Fake: c.Fake, + } +} + +// ParameterCodec returns a client with the provided parameter codec. +func (c *FakeClient) ParameterCodec(parameterCodec runtime.ParameterCodec) dynamic.Interface { + return &FakeClient{ + Fake: c.Fake, + } +} + +// FakeResourceClient is a fake implementation of dynamic.ResourceInterface +type FakeResourceClient struct { + Resource schema.GroupVersionResource + Kind schema.GroupVersionKind + Namespace string + + *testing.Fake +} + +// List returns a list of objects for this resource. +func (c *FakeResourceClient) List(opts metav1.ListOptions) (runtime.Object, error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(c.Resource, c.Kind, c.Namespace, opts), &unstructured.UnstructuredList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &unstructured.UnstructuredList{} + for _, item := range obj.(*unstructured.UnstructuredList).Items { + if label.Matches(labels.Set(item.GetLabels())) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Get gets the resource with the specified name. +func (c *FakeResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(c.Resource, c.Namespace, name), &unstructured.Unstructured{}) + + if obj == nil { + return nil, err + } + + return obj.(*unstructured.Unstructured), err +} + +// Delete deletes the resource with the specified name. +func (c *FakeResourceClient) Delete(name string, opts *metav1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(c.Resource, c.Namespace, name), &unstructured.Unstructured{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeResourceClient) DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteCollectionAction(c.Resource, c.Namespace, listOptions), &unstructured.Unstructured{}) + + return err +} + +// Create creates the provided resource. +func (c *FakeResourceClient) Create(inObj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(c.Resource, c.Namespace, inObj), &unstructured.Unstructured{}) + + if obj == nil { + return nil, err + } + return obj.(*unstructured.Unstructured), err +} + +// Update updates the provided resource. +func (c *FakeResourceClient) Update(inObj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(c.Resource, c.Namespace, inObj), &unstructured.Unstructured{}) + + if obj == nil { + return nil, err + } + return obj.(*unstructured.Unstructured), err +} + +// Watch returns a watch.Interface that watches the resource. +func (c *FakeResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(c.Resource, c.Namespace, opts)) +} + +// Patch patches the provided resource. +func (c *FakeResourceClient) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) { + obj, err := c.Fake. + Invokes(testing.NewPatchAction(c.Resource, c.Namespace, name, data), &unstructured.Unstructured{}) + + if obj == nil { + return nil, err + } + return obj.(*unstructured.Unstructured), err +} diff --git a/staging/src/k8s.io/client-go/dynamic/fake/client_pool.go b/staging/src/k8s.io/client-go/dynamic/fake/client_pool.go new file mode 100644 index 00000000000..7ec114892ea --- /dev/null +++ b/staging/src/k8s.io/client-go/dynamic/fake/client_pool.go @@ -0,0 +1,48 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package fake provides a fake client interface to arbitrary Kubernetes +// APIs that exposes common high level operations and exposes common +// metadata. +package fake + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/testing" +) + +// FakeClientPool provides a fake implementation of dynamic.ClientPool. +// It assumes resource GroupVersions are the same as their corresponding kind GroupVersions. +type FakeClientPool struct { + testing.Fake +} + +// ClientForGroupVersionKind returns a client configured for the specified groupVersionResource. +// Resource may be empty. +func (p *FakeClientPool) ClientForGroupVersionResource(resource schema.GroupVersionResource) (dynamic.Interface, error) { + return p.ClientForGroupVersionKind(resource.GroupVersion().WithKind("")) +} + +// ClientForGroupVersionKind returns a client configured for the specified groupVersionKind. +// Kind may be empty. +func (p *FakeClientPool) ClientForGroupVersionKind(kind schema.GroupVersionKind) (dynamic.Interface, error) { + // we can just create a new client every time for testing purposes + return &FakeClient{ + GroupVersion: kind.GroupVersion(), + Fake: &p.Fake, + }, nil +}