| /* |
| Copyright 2016 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 deprecated_dynamic |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io/ioutil" |
| "net/http" |
| "net/http/httptest" |
| "reflect" |
| "testing" |
| |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/runtime/schema" |
| "k8s.io/apimachinery/pkg/runtime/serializer/streaming" |
| "k8s.io/apimachinery/pkg/types" |
| "k8s.io/apimachinery/pkg/watch" |
| restclient "k8s.io/client-go/rest" |
| restclientwatch "k8s.io/client-go/rest/watch" |
| ) |
| |
| func getJSON(version, kind, name string) []byte { |
| return []byte(fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "metadata": {"name": %q}}`, version, kind, name)) |
| } |
| |
| func getListJSON(version, kind string, items ...[]byte) []byte { |
| json := fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "items": [%s]}`, |
| version, kind, bytes.Join(items, []byte(","))) |
| return []byte(json) |
| } |
| |
| func getObject(version, kind, name string) *unstructured.Unstructured { |
| return &unstructured.Unstructured{ |
| Object: map[string]interface{}{ |
| "apiVersion": version, |
| "kind": kind, |
| "metadata": map[string]interface{}{ |
| "name": name, |
| }, |
| }, |
| } |
| } |
| |
| 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, |
| ContentConfig: restclient.ContentConfig{GroupVersion: gv}, |
| }, *gv) |
| if err != nil { |
| srv.Close() |
| return nil, nil, err |
| } |
| return cl, srv, nil |
| } |
| |
| func TestList(t *testing.T) { |
| tcs := []struct { |
| name string |
| namespace string |
| path string |
| resp []byte |
| want *unstructured.UnstructuredList |
| }{ |
| { |
| name: "normal_list", |
| path: "/apis/gtest/vtest/rtest", |
| resp: getListJSON("vTest", "rTestList", |
| getJSON("vTest", "rTest", "item1"), |
| getJSON("vTest", "rTest", "item2")), |
| want: &unstructured.UnstructuredList{ |
| Object: map[string]interface{}{ |
| "apiVersion": "vTest", |
| "kind": "rTestList", |
| }, |
| Items: []unstructured.Unstructured{ |
| *getObject("vTest", "rTest", "item1"), |
| *getObject("vTest", "rTest", "item2"), |
| }, |
| }, |
| }, |
| { |
| name: "namespaced_list", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest", |
| resp: getListJSON("vTest", "rTestList", |
| getJSON("vTest", "rTest", "item1"), |
| getJSON("vTest", "rTest", "item2")), |
| want: &unstructured.UnstructuredList{ |
| Object: map[string]interface{}{ |
| "apiVersion": "vTest", |
| "kind": "rTestList", |
| }, |
| Items: []unstructured.Unstructured{ |
| *getObject("vTest", "rTest", "item1"), |
| *getObject("vTest", "rTest", "item2"), |
| }, |
| }, |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "GET" { |
| t.Errorf("List(%q) got HTTP method %s. wanted GET", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("List(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| |
| w.Header().Set("Content-Type", runtime.ContentTypeJSON) |
| w.Write(tc.resp) |
| }) |
| if err != nil { |
| t.Errorf("unexpected error when creating client: %v", err) |
| continue |
| } |
| defer srv.Close() |
| |
| got, err := cl.Resource(resource, tc.namespace).List(metav1.ListOptions{}) |
| if err != nil { |
| t.Errorf("unexpected error when listing %q: %v", tc.name, err) |
| continue |
| } |
| |
| if !reflect.DeepEqual(got, tc.want) { |
| t.Errorf("List(%q) want: %v\ngot: %v", tc.name, tc.want, got) |
| } |
| } |
| } |
| |
| func TestGet(t *testing.T) { |
| tcs := []struct { |
| resource string |
| namespace string |
| name string |
| path string |
| resp []byte |
| want *unstructured.Unstructured |
| }{ |
| { |
| resource: "rtest", |
| name: "normal_get", |
| path: "/apis/gtest/vtest/rtest/normal_get", |
| resp: getJSON("vTest", "rTest", "normal_get"), |
| want: getObject("vTest", "rTest", "normal_get"), |
| }, |
| { |
| resource: "rtest", |
| namespace: "nstest", |
| name: "namespaced_get", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_get", |
| resp: getJSON("vTest", "rTest", "namespaced_get"), |
| want: getObject("vTest", "rTest", "namespaced_get"), |
| }, |
| { |
| resource: "rtest/srtest", |
| name: "normal_subresource_get", |
| path: "/apis/gtest/vtest/rtest/normal_subresource_get/srtest", |
| resp: getJSON("vTest", "srTest", "normal_subresource_get"), |
| want: getObject("vTest", "srTest", "normal_subresource_get"), |
| }, |
| { |
| resource: "rtest/srtest", |
| namespace: "nstest", |
| name: "namespaced_subresource_get", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest", |
| resp: getJSON("vTest", "srTest", "namespaced_subresource_get"), |
| want: getObject("vTest", "srTest", "namespaced_subresource_get"), |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "GET" { |
| t.Errorf("Get(%q) got HTTP method %s. wanted GET", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("Get(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| |
| w.Header().Set("Content-Type", runtime.ContentTypeJSON) |
| w.Write(tc.resp) |
| }) |
| if err != nil { |
| t.Errorf("unexpected error when creating client: %v", err) |
| continue |
| } |
| defer srv.Close() |
| |
| got, err := cl.Resource(resource, tc.namespace).Get(tc.name, metav1.GetOptions{}) |
| if err != nil { |
| t.Errorf("unexpected error when getting %q: %v", tc.name, err) |
| continue |
| } |
| |
| if !reflect.DeepEqual(got, tc.want) { |
| t.Errorf("Get(%q) want: %v\ngot: %v", tc.name, tc.want, got) |
| } |
| } |
| } |
| |
| func TestDelete(t *testing.T) { |
| background := metav1.DeletePropagationBackground |
| uid := types.UID("uid") |
| |
| statusOK := &metav1.Status{ |
| TypeMeta: metav1.TypeMeta{Kind: "Status"}, |
| Status: metav1.StatusSuccess, |
| } |
| tcs := []struct { |
| namespace string |
| name string |
| path string |
| deleteOptions *metav1.DeleteOptions |
| }{ |
| { |
| name: "normal_delete", |
| path: "/apis/gtest/vtest/rtest/normal_delete", |
| }, |
| { |
| namespace: "nstest", |
| name: "namespaced_delete", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete", |
| }, |
| { |
| namespace: "nstest", |
| name: "namespaced_delete_with_options", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete_with_options", |
| deleteOptions: &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &uid}, PropagationPolicy: &background}, |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "DELETE" { |
| t.Errorf("Delete(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("Delete(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| |
| w.Header().Set("Content-Type", runtime.ContentTypeJSON) |
| unstructured.UnstructuredJSONScheme.Encode(statusOK, w) |
| }) |
| if err != nil { |
| t.Errorf("unexpected error when creating client: %v", err) |
| continue |
| } |
| defer srv.Close() |
| |
| err = cl.Resource(resource, tc.namespace).Delete(tc.name, tc.deleteOptions) |
| if err != nil { |
| t.Errorf("unexpected error when deleting %q: %v", tc.name, err) |
| continue |
| } |
| } |
| } |
| |
| func TestDeleteCollection(t *testing.T) { |
| statusOK := &metav1.Status{ |
| TypeMeta: metav1.TypeMeta{Kind: "Status"}, |
| Status: metav1.StatusSuccess, |
| } |
| tcs := []struct { |
| namespace string |
| name string |
| path string |
| }{ |
| { |
| name: "normal_delete_collection", |
| path: "/apis/gtest/vtest/rtest", |
| }, |
| { |
| namespace: "nstest", |
| name: "namespaced_delete_collection", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest", |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "DELETE" { |
| t.Errorf("DeleteCollection(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("DeleteCollection(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| |
| w.Header().Set("Content-Type", runtime.ContentTypeJSON) |
| unstructured.UnstructuredJSONScheme.Encode(statusOK, w) |
| }) |
| if err != nil { |
| t.Errorf("unexpected error when creating client: %v", err) |
| continue |
| } |
| defer srv.Close() |
| |
| err = cl.Resource(resource, tc.namespace).DeleteCollection(nil, metav1.ListOptions{}) |
| if err != nil { |
| t.Errorf("unexpected error when deleting collection %q: %v", tc.name, err) |
| continue |
| } |
| } |
| } |
| |
| func TestCreate(t *testing.T) { |
| tcs := []struct { |
| resource string |
| name string |
| namespace string |
| obj *unstructured.Unstructured |
| path string |
| }{ |
| { |
| resource: "rtest", |
| name: "normal_create", |
| path: "/apis/gtest/vtest/rtest", |
| obj: getObject("gtest/vTest", "rTest", "normal_create"), |
| }, |
| { |
| resource: "rtest", |
| name: "namespaced_create", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest", |
| obj: getObject("gtest/vTest", "rTest", "namespaced_create"), |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "POST" { |
| t.Errorf("Create(%q) got HTTP method %s. wanted POST", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("Create(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| |
| w.Header().Set("Content-Type", runtime.ContentTypeJSON) |
| data, err := ioutil.ReadAll(r.Body) |
| if err != nil { |
| t.Errorf("Create(%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).Create(tc.obj) |
| if err != nil { |
| t.Errorf("unexpected error when creating %q: %v", tc.name, err) |
| continue |
| } |
| |
| if !reflect.DeepEqual(got, tc.obj) { |
| t.Errorf("Create(%q) want: %v\ngot: %v", tc.name, tc.obj, got) |
| } |
| } |
| } |
| |
| func TestUpdate(t *testing.T) { |
| tcs := []struct { |
| resource string |
| name string |
| namespace string |
| obj *unstructured.Unstructured |
| path string |
| }{ |
| { |
| resource: "rtest", |
| name: "normal_update", |
| path: "/apis/gtest/vtest/rtest/normal_update", |
| obj: getObject("gtest/vTest", "rTest", "normal_update"), |
| }, |
| { |
| resource: "rtest", |
| name: "namespaced_update", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update", |
| obj: getObject("gtest/vTest", "rTest", "namespaced_update"), |
| }, |
| { |
| resource: "rtest/srtest", |
| name: "normal_subresource_update", |
| path: "/apis/gtest/vtest/rtest/normal_update/srtest", |
| obj: getObject("gtest/vTest", "srTest", "normal_update"), |
| }, |
| { |
| resource: "rtest/srtest", |
| name: "namespaced_subresource_update", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest", |
| obj: getObject("gtest/vTest", "srTest", "namespaced_update"), |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "PUT" { |
| t.Errorf("Update(%q) got HTTP method %s. wanted PUT", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("Update(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| |
| w.Header().Set("Content-Type", runtime.ContentTypeJSON) |
| data, err := ioutil.ReadAll(r.Body) |
| if err != nil { |
| t.Errorf("Update(%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).Update(tc.obj) |
| if err != nil { |
| t.Errorf("unexpected error when updating %q: %v", tc.name, err) |
| continue |
| } |
| |
| if !reflect.DeepEqual(got, tc.obj) { |
| t.Errorf("Update(%q) want: %v\ngot: %v", tc.name, tc.obj, got) |
| } |
| } |
| } |
| |
| func TestWatch(t *testing.T) { |
| tcs := []struct { |
| name string |
| namespace string |
| events []watch.Event |
| path string |
| query string |
| }{ |
| { |
| name: "normal_watch", |
| path: "/apis/gtest/vtest/rtest", |
| query: "watch=true", |
| events: []watch.Event{ |
| {Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "normal_watch")}, |
| {Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "normal_watch")}, |
| {Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "normal_watch")}, |
| }, |
| }, |
| { |
| name: "namespaced_watch", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest", |
| query: "watch=true", |
| events: []watch.Event{ |
| {Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")}, |
| {Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")}, |
| {Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")}, |
| }, |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} |
| cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "GET" { |
| t.Errorf("Watch(%q) got HTTP method %s. wanted GET", tc.name, r.Method) |
| } |
| |
| if r.URL.Path != tc.path { |
| t.Errorf("Watch(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) |
| } |
| if r.URL.RawQuery != tc.query { |
| t.Errorf("Watch(%q) got query %s. wanted %s", tc.name, r.URL.RawQuery, tc.query) |
| } |
| |
| enc := restclientwatch.NewEncoder(streaming.NewEncoder(w, dynamicCodec{}), dynamicCodec{}) |
| for _, e := range tc.events { |
| enc.Encode(&e) |
| } |
| }) |
| if err != nil { |
| t.Errorf("unexpected error when creating client: %v", err) |
| continue |
| } |
| defer srv.Close() |
| |
| watcher, err := cl.Resource(resource, tc.namespace).Watch(metav1.ListOptions{}) |
| if err != nil { |
| t.Errorf("unexpected error when watching %q: %v", tc.name, err) |
| continue |
| } |
| |
| for _, want := range tc.events { |
| got := <-watcher.ResultChan() |
| if !reflect.DeepEqual(got, want) { |
| t.Errorf("Watch(%q) want: %v\ngot: %v", tc.name, want, got) |
| } |
| } |
| } |
| } |
| |
| func TestPatch(t *testing.T) { |
| tcs := []struct { |
| resource string |
| name string |
| namespace string |
| patch []byte |
| want *unstructured.Unstructured |
| path string |
| }{ |
| { |
| resource: "rtest", |
| name: "normal_patch", |
| path: "/apis/gtest/vtest/rtest/normal_patch", |
| patch: getJSON("gtest/vTest", "rTest", "normal_patch"), |
| want: getObject("gtest/vTest", "rTest", "normal_patch"), |
| }, |
| { |
| resource: "rtest", |
| name: "namespaced_patch", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_patch", |
| patch: getJSON("gtest/vTest", "rTest", "namespaced_patch"), |
| want: getObject("gtest/vTest", "rTest", "namespaced_patch"), |
| }, |
| { |
| resource: "rtest/srtest", |
| name: "normal_subresource_patch", |
| path: "/apis/gtest/vtest/rtest/normal_subresource_patch/srtest", |
| patch: getJSON("gtest/vTest", "srTest", "normal_subresource_patch"), |
| want: getObject("gtest/vTest", "srTest", "normal_subresource_patch"), |
| }, |
| { |
| resource: "rtest/srtest", |
| name: "namespaced_subresource_patch", |
| namespace: "nstest", |
| path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest", |
| patch: getJSON("gtest/vTest", "srTest", "namespaced_subresource_patch"), |
| want: getObject("gtest/vTest", "srTest", "namespaced_subresource_patch"), |
| }, |
| } |
| for _, tc := range tcs { |
| gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} |
| resource := &metav1.APIResource{Name: tc.resource, 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(types.StrategicMergePatchType) { |
| t.Errorf("Patch(%q) got Content-Type %s. wanted %s", tc.name, content, types.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.Header().Set("Content-Type", "application/json") |
| 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, types.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) |
| } |
| } |
| } |