Use resource package, delete older code

pull/6/head
Clayton Coleman 2014-12-27 23:49:51 -05:00
parent d75a3d5021
commit a1ee782df5
7 changed files with 26 additions and 661 deletions

View File

@ -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)
},

View File

@ -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)

View File

@ -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")

View File

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

View File

@ -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)
},

View File

@ -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()
}

View File

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