From ad79fa6e8451c8631f76d137ac7b6eb81928970e Mon Sep 17 00:00:00 2001 From: feihujiang Date: Thu, 12 Nov 2015 18:45:42 +0800 Subject: [PATCH] Move list functions from runtime to meta package --- pkg/api/meta/help.go | 128 ++++++++++ pkg/api/meta/help_test.go | 222 ++++++++++++++++++ pkg/apiserver/resthandler.go | 7 +- pkg/client/cache/reflector.go | 6 +- pkg/client/unversioned/testclient/fixture.go | 6 +- .../framework/fake_controller_source.go | 3 +- pkg/kubectl/cmd/get_test.go | 7 +- pkg/kubectl/custom_column_printer.go | 5 +- pkg/kubectl/resource/result.go | 2 +- pkg/kubectl/resource/visitor.go | 4 +- pkg/kubectl/sorting_printer.go | 7 +- pkg/runtime/embedded_test.go | 7 +- pkg/runtime/helper.go | 105 --------- pkg/runtime/helper_test.go | 197 ---------------- pkg/storage/cacher.go | 3 +- pkg/storage/etcd/etcd_helper.go | 5 +- pkg/storage/testing/utils.go | 4 +- 17 files changed, 387 insertions(+), 331 deletions(-) create mode 100644 pkg/api/meta/help.go create mode 100644 pkg/api/meta/help_test.go diff --git a/pkg/api/meta/help.go b/pkg/api/meta/help.go new file mode 100644 index 00000000000..3ddcfef87e6 --- /dev/null +++ b/pkg/api/meta/help.go @@ -0,0 +1,128 @@ +/* +Copyright 2015 The Kubernetes Authors 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 meta + +import ( + "fmt" + "reflect" + + "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/runtime" +) + +// IsListType returns true if the provided Object has a slice called Items +func IsListType(obj runtime.Object) bool { + _, err := GetItemsPtr(obj) + return err == nil +} + +// GetItemsPtr returns a pointer to the list object's Items member. +// If 'list' doesn't have an Items member, it's not really a list type +// and an error will be returned. +// This function will either return a pointer to a slice, or an error, but not both. +func GetItemsPtr(list runtime.Object) (interface{}, error) { + v, err := conversion.EnforcePtr(list) + if err != nil { + return nil, err + } + items := v.FieldByName("Items") + if !items.IsValid() { + return nil, fmt.Errorf("no Items field in %#v", list) + } + switch items.Kind() { + case reflect.Interface, reflect.Ptr: + target := reflect.TypeOf(items.Interface()).Elem() + if target.Kind() != reflect.Slice { + return nil, fmt.Errorf("items: Expected slice, got %s", target.Kind()) + } + return items.Interface(), nil + case reflect.Slice: + return items.Addr().Interface(), nil + default: + return nil, fmt.Errorf("items: Expected slice, got %s", items.Kind()) + } +} + +// ExtractList returns obj's Items element as an array of runtime.Objects. +// Returns an error if obj is not a List type (does not have an Items member). +func ExtractList(obj runtime.Object) ([]runtime.Object, error) { + itemsPtr, err := GetItemsPtr(obj) + if err != nil { + return nil, err + } + items, err := conversion.EnforcePtr(itemsPtr) + if err != nil { + return nil, err + } + list := make([]runtime.Object, items.Len()) + for i := range list { + raw := items.Index(i) + switch item := raw.Interface().(type) { + case runtime.Object: + list[i] = item + case runtime.RawExtension: + list[i] = &runtime.Unknown{ + RawJSON: item.RawJSON, + } + default: + var found bool + if list[i], found = raw.Addr().Interface().(runtime.Object); !found { + return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind()) + } + } + } + return list, nil +} + +// objectSliceType is the type of a slice of Objects +var objectSliceType = reflect.TypeOf([]runtime.Object{}) + +// SetList sets the given list object's Items member have the elements given in +// objects. +// Returns an error if list is not a List type (does not have an Items member), +// or if any of the objects are not of the right type. +func SetList(list runtime.Object, objects []runtime.Object) error { + itemsPtr, err := GetItemsPtr(list) + if err != nil { + return err + } + items, err := conversion.EnforcePtr(itemsPtr) + if err != nil { + return err + } + if items.Type() == objectSliceType { + items.Set(reflect.ValueOf(objects)) + return nil + } + slice := reflect.MakeSlice(items.Type(), len(objects), len(objects)) + for i := range objects { + dest := slice.Index(i) + src, err := conversion.EnforcePtr(objects[i]) + if err != nil { + return err + } + if src.Type().AssignableTo(dest.Type()) { + dest.Set(src) + } else if src.Type().ConvertibleTo(dest.Type()) { + dest.Set(src.Convert(dest.Type())) + } else { + return fmt.Errorf("item[%d]: Type mismatch: Expected %v, got %v", i, dest.Type(), src.Type()) + } + } + items.Set(slice) + return nil +} diff --git a/pkg/api/meta/help_test.go b/pkg/api/meta/help_test.go new file mode 100644 index 00000000000..f0c0bb50d4a --- /dev/null +++ b/pkg/api/meta/help_test.go @@ -0,0 +1,222 @@ +/* +Copyright 2014 The Kubernetes Authors 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 meta_test + +import ( + "reflect" + "testing" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util" + + "github.com/google/gofuzz" +) + +func TestIsList(t *testing.T) { + tests := []struct { + obj runtime.Object + isList bool + }{ + {&api.PodList{}, true}, + {&api.Pod{}, false}, + } + for _, item := range tests { + if e, a := item.isList, meta.IsListType(item.obj); e != a { + t.Errorf("%v: Expected %v, got %v", reflect.TypeOf(item.obj), e, a) + } + } +} + +func TestExtractList(t *testing.T) { + pl := &api.PodList{ + Items: []api.Pod{ + {ObjectMeta: api.ObjectMeta{Name: "1"}}, + {ObjectMeta: api.ObjectMeta{Name: "2"}}, + {ObjectMeta: api.ObjectMeta{Name: "3"}}, + }, + } + list, err := meta.ExtractList(pl) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := len(list), len(pl.Items); e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + for i := range list { + if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + } +} + +func TestExtractListGeneric(t *testing.T) { + pl := &api.List{ + Items: []runtime.Object{ + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, + &api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}}, + }, + } + list, err := meta.ExtractList(pl) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := len(list), len(pl.Items); e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + if obj, ok := list[0].(*api.Pod); !ok { + t.Fatalf("Expected list[0] to be *api.Pod, it is %#v", obj) + } + if obj, ok := list[1].(*api.Service); !ok { + t.Fatalf("Expected list[1] to be *api.Service, it is %#v", obj) + } +} + +func TestExtractListGenericV1(t *testing.T) { + pl := &v1.List{ + Items: []runtime.RawExtension{ + {RawJSON: []byte("foo")}, + {RawJSON: []byte("bar")}, + }, + } + list, err := meta.ExtractList(pl) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := len(list), len(pl.Items); e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + if obj, ok := list[0].(*runtime.Unknown); !ok { + t.Fatalf("Expected list[0] to be *runtime.Unknown, it is %#v", obj) + } + if obj, ok := list[1].(*runtime.Unknown); !ok { + t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj) + } +} + +type fakePtrInterfaceList struct { + Items *[]runtime.Object +} + +func (f fakePtrInterfaceList) IsAnAPIObject() {} + +func TestExtractListOfInterfacePtrs(t *testing.T) { + pl := &fakePtrInterfaceList{ + Items: &[]runtime.Object{}, + } + list, err := meta.ExtractList(pl) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if len(list) > 0 { + t.Fatalf("Expected empty list, got %#v", list) + } +} + +type fakePtrValueList struct { + Items []*api.Pod +} + +func (f fakePtrValueList) IsAnAPIObject() {} + +func TestExtractListOfValuePtrs(t *testing.T) { + pl := &fakePtrValueList{ + Items: []*api.Pod{ + {ObjectMeta: api.ObjectMeta{Name: "1"}}, + {ObjectMeta: api.ObjectMeta{Name: "2"}}, + }, + } + list, err := meta.ExtractList(pl) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := len(list), len(pl.Items); e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + for i := range list { + if obj, ok := list[i].(*api.Pod); !ok { + t.Fatalf("Expected list[%d] to be *api.Pod, it is %#v", i, obj) + } + } +} + +func TestSetList(t *testing.T) { + pl := &api.PodList{} + list := []runtime.Object{ + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, + } + err := meta.SetList(pl, list) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := len(list), len(pl.Items); e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + for i := range list { + if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + } +} + +func TestSetListToRuntimeObjectArray(t *testing.T) { + pl := &api.List{} + list := []runtime.Object{ + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, + &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, + } + err := meta.SetList(pl, list) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if e, a := len(list), len(pl.Items); e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + for i := range list { + if e, a := list[i], pl.Items[i]; e != a { + t.Fatalf("%d: unmatched: %s", i, util.ObjectDiff(e, a)) + } + } +} + +func TestSetExtractListRoundTrip(t *testing.T) { + fuzzer := fuzz.New().NilChance(0).NumElements(1, 5) + for i := 0; i < 5; i++ { + start := &api.PodList{} + fuzzer.Fuzz(&start.Items) + + list, err := meta.ExtractList(start) + if err != nil { + t.Errorf("Unexpected error %v", err) + continue + } + got := &api.PodList{} + err = meta.SetList(got, list) + if err != nil { + t.Errorf("Unexpected error %v", err) + continue + } + if e, a := start, got; !reflect.DeepEqual(e, a) { + t.Fatalf("Expected %#v, got %#v", e, a) + } + } +} diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index d9eaa916fd5..5a285dc44de 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/fields" @@ -793,7 +794,7 @@ func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) err // setListSelfLink sets the self link of a list to the base URL, then sets the self links // on all child objects returned. func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error { - if !runtime.IsListType(obj) { + if !meta.IsListType(obj) { return nil } @@ -812,7 +813,7 @@ func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) } // Set self-link of objects in the list. - items, err := runtime.ExtractList(obj) + items, err := meta.ExtractList(obj) if err != nil { return err } @@ -821,7 +822,7 @@ func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) return err } } - return runtime.SetList(obj, items) + return meta.SetList(obj, items) } diff --git a/pkg/client/cache/reflector.go b/pkg/client/cache/reflector.go index d7a02806e12..5b6b663403a 100644 --- a/pkg/client/cache/reflector.go +++ b/pkg/client/cache/reflector.go @@ -231,12 +231,12 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error { if err != nil { return fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err) } - meta, err := meta.Accessor(list) + metaInterface, err := meta.Accessor(list) if err != nil { return fmt.Errorf("%s: Unable to understand list result %#v", r.name, list) } - resourceVersion = meta.ResourceVersion() - items, err := runtime.ExtractList(list) + resourceVersion = metaInterface.ResourceVersion() + items, err := meta.ExtractList(list) if err != nil { return fmt.Errorf("%s: Unable to understand list result %#v (%v)", r.name, list, err) } diff --git a/pkg/client/unversioned/testclient/fixture.go b/pkg/client/unversioned/testclient/fixture.go index 64f82101f23..271a0177e51 100644 --- a/pkg/client/unversioned/testclient/fixture.go +++ b/pkg/client/unversioned/testclient/fixture.go @@ -167,7 +167,7 @@ func (o objects) Kind(kind, name string) (runtime.Object, error) { if err != nil { return nilValue, err } - if err := runtime.SetList(out, arr); err != nil { + if err := meta.SetList(out, arr); err != nil { return nilValue, err } if out, err = o.scheme.Copy(out); err != nil { @@ -210,12 +210,12 @@ func (o objects) Add(obj runtime.Object) error { } switch { - case runtime.IsListType(obj): + case meta.IsListType(obj): if kind != "List" { o.types[kind] = append(o.types[kind], obj) } - list, err := runtime.ExtractList(obj) + list, err := meta.ExtractList(obj) if err != nil { return err } diff --git a/pkg/controller/framework/fake_controller_source.go b/pkg/controller/framework/fake_controller_source.go index 654f022da20..3bcccc83fff 100644 --- a/pkg/controller/framework/fake_controller_source.go +++ b/pkg/controller/framework/fake_controller_source.go @@ -23,6 +23,7 @@ import ( "sync" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/watch" @@ -136,7 +137,7 @@ func (f *FakeControllerSource) List() (runtime.Object, error) { list = append(list, objCopy.(runtime.Object)) } listObj := &api.List{} - if err := runtime.SetList(listObj, list); err != nil { + if err := meta.SetList(listObj, list); err != nil { return nil, err } objMeta, err := api.ListMetaFor(listObj) diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index d2c659b3897..e40790035bf 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -28,6 +28,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/testapi" apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" @@ -331,7 +332,7 @@ func TestGetListObjects(t *testing.T) { func extractResourceList(objs []runtime.Object) ([]runtime.Object, error) { finalObjs := []runtime.Object{} for _, obj := range objs { - items, err := runtime.ExtractList(obj) + items, err := meta.ExtractList(obj) if err != nil { return nil, err } @@ -477,14 +478,14 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - list, err := runtime.ExtractList(out) + list, err := meta.ExtractList(out) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } - if err := runtime.SetList(out, list); err != nil { + if err := meta.SetList(out, list); err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/kubectl/custom_column_printer.go b/pkg/kubectl/custom_column_printer.go index c73feba8f16..7d91691f734 100644 --- a/pkg/kubectl/custom_column_printer.go +++ b/pkg/kubectl/custom_column_printer.go @@ -27,6 +27,7 @@ import ( "text/tabwriter" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/jsonpath" ) @@ -168,8 +169,8 @@ func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error } } - if runtime.IsListType(obj) { - objs, err := runtime.ExtractList(obj) + if meta.IsListType(obj) { + objs, err := meta.ExtractList(obj) if err != nil { return err } diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index 07ee99b6a29..c91a58d4e42 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -138,7 +138,7 @@ func (r *Result) Object() (runtime.Object, error) { return objects[0], nil } // if the item is a list already, don't create another list - if runtime.IsListType(objects[0]) { + if meta.IsListType(objects[0]) { return objects[0], nil } } diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index 79dbe26d7f3..29e17114998 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -335,7 +335,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error { if info.Object == nil { return fn(info, nil) } - items, err := runtime.ExtractList(info.Object) + items, err := meta.ExtractList(info.Object) if err != nil { return fn(info, nil) } @@ -564,7 +564,7 @@ func RetrieveLatest(info *Info, err error) error { if err != nil { return err } - if runtime.IsListType(info.Object) { + if meta.IsListType(info.Object) { return fmt.Errorf("watch is only supported on individual resources and resource collections, but a list of resources is found") } if len(info.Name) == 0 { diff --git a/pkg/kubectl/sorting_printer.go b/pkg/kubectl/sorting_printer.go index 08be4b024ef..c1f3c54ae45 100644 --- a/pkg/kubectl/sorting_printer.go +++ b/pkg/kubectl/sorting_printer.go @@ -23,6 +23,7 @@ import ( "sort" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/jsonpath" @@ -38,7 +39,7 @@ type SortingPrinter struct { } func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error { - if !runtime.IsListType(obj) { + if !meta.IsListType(obj) { return s.Delegate.PrintObj(obj, out) } @@ -54,7 +55,7 @@ func (p *SortingPrinter) HandledResources() []string { } func (s *SortingPrinter) sortObj(obj runtime.Object) error { - objs, err := runtime.ExtractList(obj) + objs, err := meta.ExtractList(obj) if err != nil { return err } @@ -76,7 +77,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error { list.Items = outputList return nil } - return runtime.SetList(obj, objs) + return meta.SetList(obj, objs) } func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error) { diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index 42995dc5337..0b396a18c35 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -22,6 +22,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" ) @@ -119,7 +120,7 @@ func TestArrayOfRuntimeObject(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - list, err := runtime.ExtractList(decoded) + list, err := meta.ExtractList(decoded) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -127,14 +128,14 @@ func TestArrayOfRuntimeObject(t *testing.T) { t.Fatalf("unexpected error: %v", errs) } - list2, err := runtime.ExtractList(list[3]) + list2, err := meta.ExtractList(list[3]) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list2, s); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } - if err := runtime.SetList(list[3], list2); err != nil { + if err := meta.SetList(list[3], list2); err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index 4fd370ca5ef..0dab10f974f 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -23,111 +23,6 @@ import ( "k8s.io/kubernetes/pkg/conversion" ) -// IsListType returns true if the provided Object has a slice called Items -func IsListType(obj Object) bool { - _, err := GetItemsPtr(obj) - return err == nil -} - -// GetItemsPtr returns a pointer to the list object's Items member. -// If 'list' doesn't have an Items member, it's not really a list type -// and an error will be returned. -// This function will either return a pointer to a slice, or an error, but not both. -func GetItemsPtr(list Object) (interface{}, error) { - v, err := conversion.EnforcePtr(list) - if err != nil { - return nil, err - } - items := v.FieldByName("Items") - if !items.IsValid() { - return nil, fmt.Errorf("no Items field in %#v", list) - } - switch items.Kind() { - case reflect.Interface, reflect.Ptr: - target := reflect.TypeOf(items.Interface()).Elem() - if target.Kind() != reflect.Slice { - return nil, fmt.Errorf("items: Expected slice, got %s", target.Kind()) - } - return items.Interface(), nil - case reflect.Slice: - return items.Addr().Interface(), nil - default: - return nil, fmt.Errorf("items: Expected slice, got %s", items.Kind()) - } -} - -// ExtractList returns obj's Items element as an array of runtime.Objects. -// Returns an error if obj is not a List type (does not have an Items member). -// TODO: move me to pkg/api/meta -func ExtractList(obj Object) ([]Object, error) { - itemsPtr, err := GetItemsPtr(obj) - if err != nil { - return nil, err - } - items, err := conversion.EnforcePtr(itemsPtr) - if err != nil { - return nil, err - } - list := make([]Object, items.Len()) - for i := range list { - raw := items.Index(i) - switch item := raw.Interface().(type) { - case Object: - list[i] = item - case RawExtension: - list[i] = &Unknown{ - RawJSON: item.RawJSON, - } - default: - var found bool - if list[i], found = raw.Addr().Interface().(Object); !found { - return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind()) - } - } - } - return list, nil -} - -// objectSliceType is the type of a slice of Objects -var objectSliceType = reflect.TypeOf([]Object{}) - -// SetList sets the given list object's Items member have the elements given in -// objects. -// Returns an error if list is not a List type (does not have an Items member), -// or if any of the objects are not of the right type. -// TODO: move me to pkg/api/meta -func SetList(list Object, objects []Object) error { - itemsPtr, err := GetItemsPtr(list) - if err != nil { - return err - } - items, err := conversion.EnforcePtr(itemsPtr) - if err != nil { - return err - } - if items.Type() == objectSliceType { - items.Set(reflect.ValueOf(objects)) - return nil - } - slice := reflect.MakeSlice(items.Type(), len(objects), len(objects)) - for i := range objects { - dest := slice.Index(i) - src, err := conversion.EnforcePtr(objects[i]) - if err != nil { - return err - } - if src.Type().AssignableTo(dest.Type()) { - dest.Set(src) - } else if src.Type().ConvertibleTo(dest.Type()) { - dest.Set(src.Convert(dest.Type())) - } else { - return fmt.Errorf("item[%d]: Type mismatch: Expected %v, got %v", i, dest.Type(), src.Type()) - } - } - items.Set(slice) - return nil -} - // fieldPtr puts the address of fieldName, which must be a member of v, // into dest, which must be an address of a variable to which this field's // address can be assigned. diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index fc5810b9bd7..8afa0c6f90d 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -17,145 +17,13 @@ limitations under the License. package runtime_test import ( - "reflect" "testing" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - - "github.com/google/gofuzz" ) -func TestIsList(t *testing.T) { - tests := []struct { - obj runtime.Object - isList bool - }{ - {&api.PodList{}, true}, - {&api.Pod{}, false}, - } - for _, item := range tests { - if e, a := item.isList, runtime.IsListType(item.obj); e != a { - t.Errorf("%v: Expected %v, got %v", reflect.TypeOf(item.obj), e, a) - } - } -} - -func TestExtractList(t *testing.T) { - pl := &api.PodList{ - Items: []api.Pod{ - {ObjectMeta: api.ObjectMeta{Name: "1"}}, - {ObjectMeta: api.ObjectMeta{Name: "2"}}, - {ObjectMeta: api.ObjectMeta{Name: "3"}}, - }, - } - list, err := runtime.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - } -} - -func TestExtractListGeneric(t *testing.T) { - pl := &api.List{ - Items: []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}}, - }, - } - list, err := runtime.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - if obj, ok := list[0].(*api.Pod); !ok { - t.Fatalf("Expected list[0] to be *api.Pod, it is %#v", obj) - } - if obj, ok := list[1].(*api.Service); !ok { - t.Fatalf("Expected list[1] to be *api.Service, it is %#v", obj) - } -} - -func TestExtractListGenericV1(t *testing.T) { - pl := &v1.List{ - Items: []runtime.RawExtension{ - {RawJSON: []byte("foo")}, - {RawJSON: []byte("bar")}, - }, - } - list, err := runtime.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - if obj, ok := list[0].(*runtime.Unknown); !ok { - t.Fatalf("Expected list[0] to be *runtime.Unknown, it is %#v", obj) - } - if obj, ok := list[1].(*runtime.Unknown); !ok { - t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj) - } -} - -type fakePtrInterfaceList struct { - Items *[]runtime.Object -} - -func (f fakePtrInterfaceList) IsAnAPIObject() {} - -func TestExtractListOfInterfacePtrs(t *testing.T) { - pl := &fakePtrInterfaceList{ - Items: &[]runtime.Object{}, - } - list, err := runtime.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if len(list) > 0 { - t.Fatalf("Expected empty list, got %#v", list) - } -} - -type fakePtrValueList struct { - Items []*api.Pod -} - -func (f fakePtrValueList) IsAnAPIObject() {} - -func TestExtractListOfValuePtrs(t *testing.T) { - pl := &fakePtrValueList{ - Items: []*api.Pod{ - {ObjectMeta: api.ObjectMeta{Name: "1"}}, - {ObjectMeta: api.ObjectMeta{Name: "2"}}, - }, - } - list, err := runtime.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if obj, ok := list[i].(*api.Pod); !ok { - t.Fatalf("Expected list[%d] to be *api.Pod, it is %#v", i, obj) - } - } -} - func TestDecodeList(t *testing.T) { pl := &api.List{ Items: []runtime.Object{ @@ -171,68 +39,3 @@ func TestDecodeList(t *testing.T) { t.Errorf("object not converted: %#v", pl.Items[1]) } } - -func TestSetList(t *testing.T) { - pl := &api.PodList{} - list := []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, - } - err := runtime.SetList(pl, list) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - } -} - -func TestSetListToRuntimeObjectArray(t *testing.T) { - pl := &api.List{} - list := []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, - } - err := runtime.SetList(pl, list) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i], pl.Items[i]; e != a { - t.Fatalf("%d: unmatched: %s", i, util.ObjectDiff(e, a)) - } - } -} - -func TestSetExtractListRoundTrip(t *testing.T) { - fuzzer := fuzz.New().NilChance(0).NumElements(1, 5) - for i := 0; i < 5; i++ { - start := &api.PodList{} - fuzzer.Fuzz(&start.Items) - - list, err := runtime.ExtractList(start) - if err != nil { - t.Errorf("Unexpected error %v", err) - continue - } - got := &api.PodList{} - err = runtime.SetList(got, list) - if err != nil { - t.Errorf("Unexpected error %v", err) - continue - } - if e, a := start, got; !reflect.DeepEqual(e, a) { - t.Fatalf("Expected %#v, got %#v", e, a) - } - } -} diff --git a/pkg/storage/cacher.go b/pkg/storage/cacher.go index 53d734c8983..bd53bcae3c5 100644 --- a/pkg/storage/cacher.go +++ b/pkg/storage/cacher.go @@ -25,6 +25,7 @@ import ( "time" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" @@ -276,7 +277,7 @@ func (c *Cacher) List(ctx context.Context, key string, resourceVersion uint64, f c.usable.RUnlock() // List elements from cache, with at least 'resourceVersion'. - listPtr, err := runtime.GetItemsPtr(listObj) + listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } diff --git a/pkg/storage/etcd/etcd_helper.go b/pkg/storage/etcd/etcd_helper.go index ff3d1294c0d..de9fef80f04 100644 --- a/pkg/storage/etcd/etcd_helper.go +++ b/pkg/storage/etcd/etcd_helper.go @@ -26,6 +26,7 @@ import ( "github.com/coreos/go-etcd/etcd" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" @@ -273,7 +274,7 @@ func (h *etcdHelper) GetToList(ctx context.Context, key string, filter storage.F glog.Errorf("Context is nil") } trace := util.NewTrace("GetToList " + getTypeName(listObj)) - listPtr, err := runtime.GetItemsPtr(listObj) + listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } @@ -356,7 +357,7 @@ func (h *etcdHelper) List(ctx context.Context, key string, resourceVersion uint6 } trace := util.NewTrace("List " + getTypeName(listObj)) defer trace.LogIfLong(time.Second) - listPtr, err := runtime.GetItemsPtr(listObj) + listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } diff --git a/pkg/storage/testing/utils.go b/pkg/storage/testing/utils.go index bd2d78b9015..74ca0aa4740 100644 --- a/pkg/storage/testing/utils.go +++ b/pkg/storage/testing/utils.go @@ -49,7 +49,7 @@ func CreateObjList(prefix string, helper storage.Interface, items []runtime.Obje // CreateList will properly create a list using the storage interface func CreateList(prefix string, helper storage.Interface, list runtime.Object) error { - items, err := runtime.ExtractList(list) + items, err := meta.ExtractList(list) if err != nil { return err } @@ -57,5 +57,5 @@ func CreateList(prefix string, helper storage.Interface, list runtime.Object) er if err != nil { return err } - return runtime.SetList(list, items) + return meta.SetList(list, items) }