From 957b16053a6248801dded165a800731a1b324f15 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 23 Sep 2014 16:33:02 -0700 Subject: [PATCH] restore test that somehow vanished + add SetList --- pkg/runtime/helper.go | 91 +++++++++++++++++++++++++++++++++++++ pkg/runtime/helper_test.go | 93 ++++++++++++++++++++++++++++++++++++++ pkg/runtime/scheme.go | 25 ---------- 3 files changed, 184 insertions(+), 25 deletions(-) create mode 100644 pkg/runtime/helper.go create mode 100644 pkg/runtime/helper_test.go diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go new file mode 100644 index 0000000000..dcede13cfa --- /dev/null +++ b/pkg/runtime/helper.go @@ -0,0 +1,91 @@ +/* +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 runtime + +import ( + "fmt" + "reflect" +) + +// 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 := reflect.ValueOf(list) + if !v.IsValid() { + return nil, fmt.Errorf("nil list object") + } + items := v.Elem().FieldByName("Items") + if !items.IsValid() { + return nil, fmt.Errorf("no Items field in %#v", list) + } + if items.Kind() != reflect.Slice { + return nil, fmt.Errorf("Items field is not a slice") + } + return items.Addr().Interface(), nil +} + +// 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 Object) ([]Object, error) { + itemsPtr, err := GetItemsPtr(obj) + if err != nil { + return nil, err + } + items := reflect.ValueOf(itemsPtr).Elem() + list := make([]Object, items.Len()) + for i := range list { + raw := items.Index(i) + item, ok := raw.Addr().Interface().(Object) + if !ok { + return nil, fmt.Errorf("item in index %v isn't an object: %#v", i, raw.Interface()) + } + list[i] = item + } + return list, nil +} + +// 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 Object, objects []Object) error { + itemsPtr, err := GetItemsPtr(list) + if err != nil { + return err + } + items := reflect.ValueOf(itemsPtr).Elem() + slice := reflect.MakeSlice(items.Type(), len(objects), len(objects)) + for i := range objects { + dest := slice.Index(i) + src := reflect.ValueOf(objects[i]) + if !src.IsValid() || src.IsNil() { + return fmt.Errorf("an object was nil") + } + src = src.Elem() // Object is a pointer, but the items in slice are not. + 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("wrong type: need %v, got %v", dest.Type(), src.Type()) + } + } + items.Set(slice) + return nil +} diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go new file mode 100644 index 0000000000..f0d43522e0 --- /dev/null +++ b/pkg/runtime/helper_test.go @@ -0,0 +1,93 @@ +/* +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 runtime_test + +import ( + "reflect" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" + + "github.com/google/gofuzz" +) + +func TestExtractList(t *testing.T) { + pl := &api.PodList{ + Items: []api.Pod{ + {JSONBase: api.JSONBase{ID: "1"}}, + {JSONBase: api.JSONBase{ID: "2"}}, + {JSONBase: api.JSONBase{ID: "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).ID, pl.Items[i].ID; e != a { + t.Fatalf("Expected %v, got %v", e, a) + } + } +} + +func TestSetList(t *testing.T) { + pl := &api.PodList{} + list := []runtime.Object{ + &api.Pod{JSONBase: api.JSONBase{ID: "1"}}, + &api.Pod{JSONBase: api.JSONBase{ID: "2"}}, + &api.Pod{JSONBase: api.JSONBase{ID: "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).ID, pl.Items[i].ID; e != a { + t.Fatalf("Expected %v, got %v", 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/runtime/scheme.go b/pkg/runtime/scheme.go index 84cbc67ceb..4620e5a704 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -400,28 +400,3 @@ func (metaInsertion) Interpret(in interface{}) (version, kind string) { m := in.(*metaInsertion) return m.JSONBase.APIVersion, m.JSONBase.Kind } - -// Extract list 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 Object) ([]Object, error) { - v := reflect.ValueOf(obj) - if !v.IsValid() { - return nil, fmt.Errorf("nil object") - } - items := v.Elem().FieldByName("Items") - if !items.IsValid() { - return nil, fmt.Errorf("no Items field") - } - if items.Kind() != reflect.Slice { - return nil, fmt.Errorf("Items field is not a slice") - } - list := make([]Object, items.Len()) - for i := range list { - item, ok := items.Index(i).Addr().Interface().(Object) - if !ok { - return nil, fmt.Errorf("item in index %v isn't an object", i) - } - list[i] = item - } - return list, nil -}