From b99fc2818deb31509d75cf591897d1cab90dc849 Mon Sep 17 00:00:00 2001 From: Kris Date: Thu, 5 May 2016 13:29:10 -0700 Subject: [PATCH] Adding patch and cleaning up namespace code --- pkg/client/typed/dynamic/client.go | 44 +++++++++------- pkg/client/typed/dynamic/client_test.go | 67 +++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/pkg/client/typed/dynamic/client.go b/pkg/client/typed/dynamic/client.go index 8df836f49c1..96700ac944a 100644 --- a/pkg/client/typed/dynamic/client.go +++ b/pkg/client/typed/dynamic/client.go @@ -98,20 +98,11 @@ type ResourceClient struct { ns string } -// namespace applies a namespace to the request if the configured -// resource is a namespaced resource. Otherwise, it just returns the -// passed in request. -func (rc *ResourceClient) namespace(req *restclient.Request) *restclient.Request { - if rc.resource.Namespaced { - return req.Namespace(rc.ns) - } - return req -} - // List returns a list of objects for this resource. func (rc *ResourceClient) List(opts runtime.Object) (*runtime.UnstructuredList, error) { result := new(runtime.UnstructuredList) - err := rc.namespace(rc.cl.Get()). + err := rc.cl.Get(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). VersionedParams(opts, parameterEncoder). Do(). @@ -122,7 +113,8 @@ func (rc *ResourceClient) List(opts runtime.Object) (*runtime.UnstructuredList, // Get gets the resource with the specified name. func (rc *ResourceClient) Get(name string) (*runtime.Unstructured, error) { result := new(runtime.Unstructured) - err := rc.namespace(rc.cl.Get()). + err := rc.cl.Get(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). Name(name). Do(). @@ -132,7 +124,8 @@ func (rc *ResourceClient) Get(name string) (*runtime.Unstructured, error) { // Delete deletes the resource with the specified name. func (rc *ResourceClient) Delete(name string, opts *v1.DeleteOptions) error { - return rc.namespace(rc.cl.Delete()). + return rc.cl.Delete(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). Name(name). Body(opts). @@ -142,7 +135,8 @@ func (rc *ResourceClient) Delete(name string, opts *v1.DeleteOptions) error { // DeleteCollection deletes a collection of objects. func (rc *ResourceClient) DeleteCollection(deleteOptions *v1.DeleteOptions, listOptions runtime.Object) error { - return rc.namespace(rc.cl.Delete()). + return rc.cl.Delete(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). VersionedParams(listOptions, parameterEncoder). Body(deleteOptions). @@ -153,7 +147,8 @@ func (rc *ResourceClient) DeleteCollection(deleteOptions *v1.DeleteOptions, list // Create creates the provided resource. func (rc *ResourceClient) Create(obj *runtime.Unstructured) (*runtime.Unstructured, error) { result := new(runtime.Unstructured) - err := rc.namespace(rc.cl.Post()). + err := rc.cl.Post(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). Body(obj). Do(). @@ -167,7 +162,8 @@ func (rc *ResourceClient) Update(obj *runtime.Unstructured) (*runtime.Unstructur if len(obj.GetName()) == 0 { return result, errors.New("object missing name") } - err := rc.namespace(rc.cl.Put()). + err := rc.cl.Put(). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). Name(obj.GetName()). Body(obj). @@ -178,12 +174,26 @@ func (rc *ResourceClient) Update(obj *runtime.Unstructured) (*runtime.Unstructur // Watch returns a watch.Interface that watches the resource. func (rc *ResourceClient) Watch(opts runtime.Object) (watch.Interface, error) { - return rc.namespace(rc.cl.Get().Prefix("watch")). + return rc.cl.Get(). + Prefix("watch"). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). VersionedParams(opts, parameterEncoder). Watch() } +func (rc *ResourceClient) Patch(name string, pt api.PatchType, data []byte) (*runtime.Unstructured, error) { + result := new(runtime.Unstructured) + err := rc.cl.Patch(pt). + NamespaceIfScoped(rc.ns, rc.resource.Namespaced). + Resource(rc.resource.Name). + Name(name). + Body(data). + Do(). + Into(result) + return result, err +} + // dynamicCodec is a codec that wraps the standard unstructured codec // with special handling for Status objects. type dynamicCodec struct{} diff --git a/pkg/client/typed/dynamic/client_test.go b/pkg/client/typed/dynamic/client_test.go index a5a028bd009..ff0ea554ca4 100644 --- a/pkg/client/typed/dynamic/client_test.go +++ b/pkg/client/typed/dynamic/client_test.go @@ -25,6 +25,7 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/client/restclient" @@ -475,3 +476,69 @@ func TestWatch(t *testing.T) { } } } + +func TestPatch(t *testing.T) { + tcs := []struct { + name string + namespace string + patch []byte + want *runtime.Unstructured + path string + }{ + { + name: "normal_patch", + path: "/api/gtest/vtest/rtest/normal_patch", + patch: getJSON("vTest", "rTest", "normal_patch"), + want: getObject("vTest", "rTest", "normal_patch"), + }, + { + name: "namespaced_patch", + namespace: "nstest", + path: "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_patch", + patch: getJSON("vTest", "rTest", "namespaced_patch"), + want: getObject("vTest", "rTest", "namespaced_patch"), + }, + } + for _, tc := range tcs { + gv := &unversioned.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &unversioned.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "PATCH" { + t.Errorf("Patch(%q) got HTTP method %s. wanted PATCH", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Patch(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + content := r.Header.Get("Content-Type") + if content != string(api.StrategicMergePatchType) { + t.Errorf("Patch(%q) got Content-Type %s. wanted %s", tc.name, content, api.StrategicMergePatchType) + } + + data, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Patch(%q) unexpected error reading body: %v", tc.name, err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Write(data) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + got, err := cl.Resource(resource, tc.namespace).Patch(tc.name, api.StrategicMergePatchType, tc.patch) + if err != nil { + t.Errorf("unexpected error when patching %q: %v", tc.name, err) + continue + } + + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Patch(%q) want: %v\ngot: %v", tc.name, tc.want, got) + } + } +}