diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index f81458a9412..2e6138c4f62 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -20,7 +20,7 @@ import ( "fmt" "io" - "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/spf13/cobra" ) @@ -57,7 +57,7 @@ Examples: checkErr(err) } - err = kubectl.NewRESTHelper(client, mapping).Create(namespace, true, data) + err = resource.NewHelper(client, mapping).Create(namespace, true, data) checkErr(err) fmt.Fprintf(out, "%s\n", name) }, diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 9d220d6eeac..39e9ea781d7 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -23,7 +23,7 @@ import ( "github.com/golang/glog" "github.com/spf13/cobra" - "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" ) func (f *Factory) NewCmdDelete(out io.Writer) *cobra.Command { @@ -59,9 +59,9 @@ Examples: checkErr(err) selector := GetFlagString(cmd, "selector") found := 0 - ResourcesFromArgsOrFile(cmd, args, filename, selector, f.Typer, f.Mapper, f.Client, schema).Visit(func(r *ResourceInfo) error { + ResourcesFromArgsOrFile(cmd, args, filename, selector, f.Typer, f.Mapper, f.Client, schema).Visit(func(r *resource.Info) error { found++ - if err := kubectl.NewRESTHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil { + if err := resource.NewHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil { return err } fmt.Fprintf(out, "%s\n", r.Name) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index f458bd1a3c1..46079a9800d 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -21,7 +21,9 @@ import ( "io" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/spf13/cobra" ) @@ -70,8 +72,13 @@ Examples: printer, err := kubectl.GetPrinter(outputFormat, templateFile, outputVersion, mapping.ObjectConvertor, defaultPrinter) checkErr(err) - restHelper := kubectl.NewRESTHelper(client, mapping) - obj, err := restHelper.Get(namespace, name, labelSelector) + restHelper := resource.NewHelper(client, mapping) + var obj runtime.Object + if len(name) == 0 { + obj, err = restHelper.List(namespace, labelSelector) + } else { + obj, err = restHelper.Get(namespace, name) + } checkErr(err) isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only") diff --git a/pkg/kubectl/cmd/resource.go b/pkg/kubectl/cmd/resource.go index 9055a308d58..93da904cd80 100644 --- a/pkg/kubectl/cmd/resource.go +++ b/pkg/kubectl/cmd/resource.go @@ -18,122 +18,19 @@ package cmd import ( "fmt" - "log" "strings" - "github.com/golang/glog" "github.com/spf13/cobra" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) -// ResourceInfo contains temporary info to execute REST call -type ResourceInfo struct { - Client kubectl.RESTClient - Mapping *meta.RESTMapping - Namespace string - Name string - - // Optional, this is the most recent value returned by the server if available - runtime.Object -} - -// ResourceVisitor lets clients walk the list of resources -type ResourceVisitor interface { - Visit(func(*ResourceInfo) error) error -} - -type ResourceVisitorList []ResourceVisitor - -// Visit implements ResourceVisitor -func (l ResourceVisitorList) Visit(fn func(r *ResourceInfo) error) error { - for i := range l { - if err := l[i].Visit(fn); err != nil { - return err - } - } - return nil -} - -func NewResourceInfo(client kubectl.RESTClient, mapping *meta.RESTMapping, namespace, name string) *ResourceInfo { - return &ResourceInfo{ - Client: client, - Mapping: mapping, - Namespace: namespace, - Name: name, - } -} - -// Visit implements ResourceVisitor -func (r *ResourceInfo) Visit(fn func(r *ResourceInfo) error) error { - return fn(r) -} - -// ResourceSelector is a facade for all the resources fetched via label selector -type ResourceSelector struct { - Client kubectl.RESTClient - Mapping *meta.RESTMapping - Namespace string - Selector labels.Selector -} - -// NewResourceSelector creates a resource selector which hides details of getting items by their label selector. -func NewResourceSelector(client kubectl.RESTClient, mapping *meta.RESTMapping, namespace string, selector labels.Selector) *ResourceSelector { - return &ResourceSelector{ - Client: client, - Mapping: mapping, - Namespace: namespace, - Selector: selector, - } -} - -// Visit implements ResourceVisitor -func (r *ResourceSelector) Visit(fn func(r *ResourceInfo) error) error { - list, err := kubectl.NewRESTHelper(r.Client, r.Mapping).List(r.Namespace, r.Selector) - if err != nil { - if errors.IsBadRequest(err) || errors.IsNotFound(err) { - glog.V(2).Infof("Unable to perform a label selector query on %s with labels %s: %v", r.Mapping.Resource, r.Selector, err) - return nil - } - return err - } - items, err := runtime.ExtractList(list) - if err != nil { - return err - } - accessor := meta.NewAccessor() - for i := range items { - name, err := accessor.Name(items[i]) - if err != nil { - // items without names cannot be visited - glog.V(2).Infof("Found %s with labels %s, but can't access the item by name.", r.Mapping.Resource, r.Selector) - continue - } - item := &ResourceInfo{ - Client: r.Client, - Mapping: r.Mapping, - Namespace: r.Namespace, - Name: name, - Object: items[i], - } - if err := fn(item); err != nil { - if errors.IsNotFound(err) { - glog.V(2).Infof("Found %s named %q, but can't be accessed now: %v", r.Mapping.Resource, name, err) - return nil - } - log.Printf("got error for resource %s: %v", r.Mapping.Resource, err) - return err - } - } - return nil -} - // ResourcesFromArgsOrFile computes a list of Resources by extracting info from filename or args. It will // handle label selectors provided. func ResourcesFromArgsOrFile( @@ -144,7 +41,7 @@ func ResourcesFromArgsOrFile( mapper meta.RESTMapper, clientBuilder func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error), schema validation.Schema, -) ResourceVisitor { +) resource.Visitor { // handling filename & resource id if len(selector) == 0 { @@ -152,34 +49,34 @@ func ResourcesFromArgsOrFile( client, err := clientBuilder(cmd, mapping) checkErr(err) - return NewResourceInfo(client, mapping, namespace, name) + return resource.NewInfo(client, mapping, namespace, name) } labelSelector, err := labels.ParseSelector(selector) checkErr(err) namespace := GetKubeNamespace(cmd) - visitors := ResourceVisitorList{} + visitors := resource.VisitorList{} if len(args) != 1 { usageError(cmd, "Must specify the type of resource") } types := SplitResourceArgument(args[0]) for _, arg := range types { - resource := kubectl.ExpandResourceShortcut(arg) - if len(resource) == 0 { - usageError(cmd, "Unknown resource %s", resource) + resourceName := kubectl.ExpandResourceShortcut(arg) + if len(resourceName) == 0 { + usageError(cmd, "Unknown resource %s", resourceName) } - version, kind, err := mapper.VersionAndKindForResource(resource) + version, kind, err := mapper.VersionAndKindForResource(resourceName) checkErr(err) - mapping, err := mapper.RESTMapping(version, kind) + mapping, err := mapper.RESTMapping(kind, version) checkErr(err) client, err := clientBuilder(cmd, mapping) checkErr(err) - visitors = append(visitors, NewResourceSelector(client, mapping, namespace, labelSelector)) + visitors = append(visitors, resource.NewSelector(client, mapping, namespace, labelSelector)) } return visitors } diff --git a/pkg/kubectl/cmd/update.go b/pkg/kubectl/cmd/update.go index 8f812bc070c..32eac2ab534 100644 --- a/pkg/kubectl/cmd/update.go +++ b/pkg/kubectl/cmd/update.go @@ -20,7 +20,7 @@ import ( "fmt" "io" - "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/spf13/cobra" ) @@ -52,7 +52,7 @@ Examples: err = CompareNamespaceFromFile(cmd, namespace) checkErr(err) - err = kubectl.NewRESTHelper(client, mapping).Update(namespace, name, true, data) + err = resource.NewHelper(client, mapping).Update(namespace, name, true, data) checkErr(err) fmt.Fprintf(out, "%s\n", name) }, diff --git a/pkg/kubectl/resthelper.go b/pkg/kubectl/resthelper.go deleted file mode 100644 index defe294be0d..00000000000 --- a/pkg/kubectl/resthelper.go +++ /dev/null @@ -1,146 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -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 kubectl - -import ( - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" - "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" -) - -// RESTHelper provides methods for retrieving or mutating a RESTful -// resource. -type RESTHelper struct { - Resource string - // A RESTClient capable of mutating this resource - RESTClient RESTClient - // A codec for decoding and encoding objects of this resource type. - Codec runtime.Codec - // An interface for reading or writing the resource version of this - // type. - Versioner runtime.ResourceVersioner -} - -// NewRESTHelper creates a RESTHelper from a ResourceMapping -func NewRESTHelper(client RESTClient, mapping *meta.RESTMapping) *RESTHelper { - return &RESTHelper{ - RESTClient: client, - Resource: mapping.Resource, - Codec: mapping.Codec, - Versioner: mapping.MetadataAccessor, - } -} - -func (m *RESTHelper) Get(namespace, name string, selector labels.Selector) (runtime.Object, error) { - return m.RESTClient.Get().Resource(m.Resource).Namespace(namespace).Name(name).SelectorParam("labels", selector).Do().Get() -} - -func (m *RESTHelper) List(namespace string, selector labels.Selector) (runtime.Object, error) { - return m.RESTClient.Get().Resource(m.Resource).Namespace(namespace).SelectorParam("labels", selector).Do().Get() -} - -func (m *RESTHelper) Watch(namespace, resourceVersion string, labelSelector, fieldSelector labels.Selector) (watch.Interface, error) { - return m.RESTClient.Get(). - Prefix("watch"). - Namespace(namespace). - Resource(m.Resource). - Param("resourceVersion", resourceVersion). - SelectorParam("labels", labelSelector). - SelectorParam("fields", fieldSelector). - Watch() -} - -func (m *RESTHelper) Delete(namespace, name string) error { - return m.RESTClient.Delete().Namespace(namespace).Resource(m.Resource).Name(name).Do().Error() -} - -func (m *RESTHelper) Create(namespace string, modify bool, data []byte) error { - if modify { - obj, err := m.Codec.Decode(data) - if err != nil { - // We don't know how to check a version on this object, but create it anyway - return createResource(m.RESTClient, m.Resource, namespace, data) - } - - // Attempt to version the object based on client logic. - version, err := m.Versioner.ResourceVersion(obj) - if err != nil { - // We don't know how to clear the version on this object, so send it to the server as is - return createResource(m.RESTClient, m.Resource, namespace, data) - } - if version != "" { - if err := m.Versioner.SetResourceVersion(obj, ""); err != nil { - return err - } - newData, err := m.Codec.Encode(obj) - if err != nil { - return err - } - data = newData - } - } - - return createResource(m.RESTClient, m.Resource, namespace, data) -} - -func createResource(c RESTClient, resource, namespace string, data []byte) error { - return c.Post().Namespace(namespace).Resource(resource).Body(data).Do().Error() -} - -func (m *RESTHelper) Update(namespace, name string, overwrite bool, data []byte) error { - c := m.RESTClient - - obj, err := m.Codec.Decode(data) - if err != nil { - // We don't know how to handle this object, but update it anyway - return updateResource(c, m.Resource, namespace, name, data) - } - - // Attempt to version the object based on client logic. - version, err := m.Versioner.ResourceVersion(obj) - if err != nil { - // We don't know how to version this object, so send it to the server as is - return updateResource(c, m.Resource, namespace, name, data) - } - if version == "" && overwrite { - // Retrieve the current version of the object to overwrite the server object - serverObj, err := c.Get().Resource(m.Resource).Name(name).Do().Get() - if err != nil { - // The object does not exist, but we want it to be created - return updateResource(c, m.Resource, namespace, name, data) - } - serverVersion, err := m.Versioner.ResourceVersion(serverObj) - if err != nil { - return err - } - if err := m.Versioner.SetResourceVersion(obj, serverVersion); err != nil { - return err - } - newData, err := m.Codec.Encode(obj) - if err != nil { - return err - } - data = newData - } - - return updateResource(c, m.Resource, namespace, name, data) -} - -func updateResource(c RESTClient, resource, namespace, name string, data []byte) error { - return c.Put().Namespace(namespace).Resource(resource).Name(name).Body(data).Do().Error() -} diff --git a/pkg/kubectl/resthelper_test.go b/pkg/kubectl/resthelper_test.go deleted file mode 100644 index 94db10f48f3..00000000000 --- a/pkg/kubectl/resthelper_test.go +++ /dev/null @@ -1,393 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -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 kubectl - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "net/http" - "reflect" - "strings" - "testing" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" - "github.com/GoogleCloudPlatform/kubernetes/pkg/client" - "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" - "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" -) - -func objBody(obj runtime.Object) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Codec(), obj)))) -} - -// splitPath returns the segments for a URL path. -func splitPath(path string) []string { - path = strings.Trim(path, "/") - if path == "" { - return []string{} - } - return strings.Split(path, "/") -} - -func TestRESTHelperDelete(t *testing.T) { - tests := []struct { - Err bool - Req func(*http.Request) bool - Resp *http.Response - HttpErr error - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&api.Status{Status: api.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&api.Status{Status: api.StatusSuccess}), - }, - Req: func(req *http.Request) bool { - if req.Method != "DELETE" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req) - return false - } - if parts[2] != "foo" { - t.Errorf("url doesn't contain name: %#v", req) - return false - } - return true - }, - }, - } - for _, test := range tests { - client := &client.FakeRESTClient{ - Codec: testapi.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &RESTHelper{ - RESTClient: client, - } - err := modifier.Delete("bar", "foo") - if (err != nil) != test.Err { - t.Errorf("unexpected error: %t %v", test.Err, err) - } - if err != nil { - continue - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("unexpected request: %#v", client.Req) - } - } -} - -func TestRESTHelperCreate(t *testing.T) { - expectPost := func(req *http.Request) bool { - if req.Method != "POST" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req) - return false - } - return true - } - - tests := []struct { - Resp *http.Response - RespFunc client.HTTPClientFunc - HttpErr error - Modify bool - Object runtime.Object - - ExpectObject runtime.Object - Err bool - Req func(*http.Request) bool - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&api.Status{Status: api.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&api.Status{Status: api.StatusSuccess}), - }, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Req: expectPost, - }, - { - Modify: false, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})}, - Req: expectPost, - }, - { - Modify: true, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})}, - Req: expectPost, - }, - } - for i, test := range tests { - client := &client.FakeRESTClient{ - Codec: testapi.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - if test.RespFunc != nil { - client.Client = test.RespFunc - } - modifier := &RESTHelper{ - RESTClient: client, - Codec: testapi.Codec(), - Versioner: testapi.MetadataAccessor(), - } - data := []byte{} - if test.Object != nil { - data = []byte(runtime.EncodeOrDie(testapi.Codec(), test.Object)) - } - err := modifier.Create("bar", test.Modify, data) - if (err != nil) != test.Err { - t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) - } - if err != nil { - continue - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("%d: unexpected request: %#v", i, client.Req) - } - body, err := ioutil.ReadAll(client.Req.Body) - if err != nil { - t.Fatalf("%d: unexpected error: %#v", i, err) - } - t.Logf("got body: %s", string(body)) - expect := []byte{} - if test.ExpectObject != nil { - expect = []byte(runtime.EncodeOrDie(testapi.Codec(), test.ExpectObject)) - } - if !reflect.DeepEqual(expect, body) { - t.Errorf("%d: unexpected body: %s", i, string(body)) - } - - } -} - -func TestRESTHelperGet(t *testing.T) { - tests := []struct { - Err bool - Req func(*http.Request) bool - Resp *http.Response - HttpErr error - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&api.Status{Status: api.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}), - }, - Req: func(req *http.Request) bool { - if req.Method != "GET" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req) - return false - } - if parts[2] != "foo" { - t.Errorf("url doesn't contain name: %#v", req) - return false - } - return true - }, - }, - } - for _, test := range tests { - client := &client.FakeRESTClient{ - Codec: testapi.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &RESTHelper{ - RESTClient: client, - } - obj, err := modifier.Get("bar", "foo", labels.Everything()) - if (err != nil) != test.Err { - t.Errorf("unexpected error: %t %v", test.Err, err) - } - if err != nil { - continue - } - if obj.(*api.Pod).Name != "foo" { - t.Errorf("unexpected object: %#v", obj) - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("unexpected request: %#v", client.Req) - } - } -} - -func TestRESTHelperUpdate(t *testing.T) { - expectPut := func(req *http.Request) bool { - if req.Method != "PUT" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req.URL) - return false - } - if parts[2] != "foo" { - t.Errorf("url doesn't contain name: %#v", req) - return false - } - return true - } - - tests := []struct { - Resp *http.Response - RespFunc client.HTTPClientFunc - HttpErr error - Overwrite bool - Object runtime.Object - - ExpectObject runtime.Object - Err bool - Req func(*http.Request) bool - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&api.Status{Status: api.StatusFailure}), - }, - Err: true, - }, - { - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&api.Status{Status: api.StatusSuccess}), - }, - Req: expectPut, - }, - { - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - - Overwrite: true, - RespFunc: func(req *http.Request) (*http.Response, error) { - if req.Method == "PUT" { - return &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})}, nil - } - return &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}})}, nil - }, - Req: expectPut, - }, - { - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})}, - Req: expectPut, - }, - } - for i, test := range tests { - client := &client.FakeRESTClient{ - Codec: testapi.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - if test.RespFunc != nil { - client.Client = test.RespFunc - } - modifier := &RESTHelper{ - RESTClient: client, - Codec: testapi.Codec(), - Versioner: testapi.MetadataAccessor(), - } - data := []byte{} - if test.Object != nil { - data = []byte(runtime.EncodeOrDie(testapi.Codec(), test.Object)) - } - err := modifier.Update("bar", "foo", test.Overwrite, data) - if (err != nil) != test.Err { - t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) - } - if err != nil { - continue - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("%d: unexpected request: %#v", i, client.Req) - } - body, err := ioutil.ReadAll(client.Req.Body) - if err != nil { - t.Fatalf("%d: unexpected error: %#v", i, err) - } - t.Logf("got body: %s", string(body)) - expect := []byte{} - if test.ExpectObject != nil { - expect = []byte(runtime.EncodeOrDie(testapi.Codec(), test.ExpectObject)) - } - if !reflect.DeepEqual(expect, body) { - t.Errorf("%d: unexpected body: %s", i, string(body)) - } - } -}