Merge pull request #25221 from krousey/dynamic_client

Automatic merge from submit-queue

Adding patch and cleaning up namespace code

cc @kubernetes/sig-api-machinery
This commit is contained in:
k8s-merge-robot 2016-05-16 01:45:45 -07:00
commit c5d85c7822
2 changed files with 94 additions and 17 deletions

View File

@ -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{}

View File

@ -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"
@ -481,3 +482,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)
}
}
}