From 8a833ca70160048773102892b0ce236b800def9a Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 7 Dec 2014 21:19:10 -0500 Subject: [PATCH] Add a List type that can contain arbitrary objects Supports objects that the core schema may not recognize and preserves them unmodified as runtime.Unknown --- pkg/api/latest/latest_test.go | 22 +++++++++++++++++++ pkg/api/register.go | 2 ++ pkg/api/serialization_test.go | 40 +++++++++++++++++++++++++++++++++-- pkg/api/types.go | 9 ++++++++ pkg/api/v1beta1/register.go | 2 ++ pkg/api/v1beta1/types.go | 7 ++++++ pkg/api/v1beta2/register.go | 2 ++ pkg/api/v1beta2/types.go | 7 ++++++ pkg/api/v1beta3/register.go | 2 ++ pkg/api/v1beta3/types.go | 18 ++++++++++++++-- pkg/runtime/scheme.go | 2 ++ 11 files changed, 109 insertions(+), 4 deletions(-) diff --git a/pkg/api/latest/latest_test.go b/pkg/api/latest/latest_test.go index f484fadcff..e446fdb3b2 100644 --- a/pkg/api/latest/latest_test.go +++ b/pkg/api/latest/latest_test.go @@ -25,7 +25,9 @@ import ( internal "github.com/GoogleCloudPlatform/kubernetes/pkg/api" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + docker "github.com/fsouza/go-dockerclient" fuzz "github.com/google/gofuzz" ) @@ -83,6 +85,26 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( // only replicas round trips j.Replicas = int(c.RandUint64()) }, + func(j *internal.List, c fuzz.Continue) { + c.Fuzz(&j.ListMeta) + c.Fuzz(&j.Items) + if j.Items == nil { + j.Items = []runtime.Object{} + } + }, + func(j *runtime.Object, c fuzz.Continue) { + if c.RandBool() { + *j = &runtime.Unknown{ + TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, + RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), + } + } else { + types := []runtime.Object{&internal.Pod{}, &internal.ReplicationController{}} + t := types[c.Rand.Intn(len(types))] + c.Fuzz(t) + *j = t + } + }, func(intstr *util.IntOrString, c fuzz.Continue) { // util.IntOrString will panic if its kind is set wrong. if c.RandBool() { diff --git a/pkg/api/register.go b/pkg/api/register.go index 14f7283c5c..12fb68d1d8 100644 --- a/pkg/api/register.go +++ b/pkg/api/register.go @@ -45,6 +45,7 @@ func init() { &ContainerManifestList{}, &BoundPod{}, &BoundPods{}, + &List{}, ) } @@ -68,3 +69,4 @@ func (*ContainerManifest) IsAnAPIObject() {} func (*ContainerManifestList) IsAnAPIObject() {} func (*BoundPod) IsAnAPIObject() {} func (*BoundPods) IsAnAPIObject() {} +func (*List) IsAnAPIObject() {} diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 932852ace0..e65bffe127 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -90,6 +90,26 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( // only replicas round trips j.Replicas = int(c.RandUint64()) }, + func(j *api.List, c fuzz.Continue) { + c.Fuzz(&j.ListMeta) + c.Fuzz(&j.Items) + if j.Items == nil { + j.Items = []runtime.Object{} + } + }, + func(j *runtime.Object, c fuzz.Continue) { + if c.RandBool() { + *j = &runtime.Unknown{ + TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, + RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), + } + } else { + types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} + t := types[c.Rand.Intn(len(types))] + c.Fuzz(t) + *j = t + } + }, func(intstr *util.IntOrString, c fuzz.Continue) { // util.IntOrString will panic if its kind is set wrong. if c.RandBool() { @@ -145,7 +165,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) { obj2, err := codec.Decode(data) if err != nil { - t.Errorf("%v: %v", name, err) + t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), source) return } if !reflect.DeepEqual(source, obj2) { @@ -179,7 +199,21 @@ func TestSpecificKind(t *testing.T) { api.Scheme.Log(nil) } +func TestList(t *testing.T) { + api.Scheme.Log(t) + kind := "List" + item, err := api.Scheme.New("", kind) + if err != nil { + t.Errorf("Couldn't make a %v? %v", kind, err) + return + } + runTest(t, v1beta1.Codec, item) + runTest(t, v1beta2.Codec, item) + api.Scheme.Log(nil) +} + var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest") +var nonInternalRoundTrippableTypes = util.NewStringSet("List") func TestRoundTripTypes(t *testing.T) { for kind := range api.Scheme.KnownTypes("") { @@ -197,7 +231,9 @@ func TestRoundTripTypes(t *testing.T) { } runTest(t, v1beta1.Codec, item) runTest(t, v1beta2.Codec, item) - runTest(t, api.Codec, item) + if !nonInternalRoundTrippableTypes.Has(kind) { + runTest(t, api.Codec, item) + } } } } diff --git a/pkg/api/types.go b/pkg/api/types.go index 8b87f8b7da..521483fb8e 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -19,6 +19,7 @@ package api import ( "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -999,3 +1000,11 @@ type BoundPods struct { // Items is the list of all pods bound to a given host. Items []BoundPod `json:"items"` } + +// List holds a list of objects, which may not be known by the server. +type List struct { + TypeMeta `json:",inline"` + ListMeta `json:"metadata,omitempty"` + + Items []runtime.Object `json:"items"` +} diff --git a/pkg/api/v1beta1/register.go b/pkg/api/v1beta1/register.go index b278d7769e..202ebee51d 100644 --- a/pkg/api/v1beta1/register.go +++ b/pkg/api/v1beta1/register.go @@ -46,6 +46,7 @@ func init() { &ContainerManifestList{}, &BoundPod{}, &BoundPods{}, + &List{}, ) } @@ -69,3 +70,4 @@ func (*ContainerManifest) IsAnAPIObject() {} func (*ContainerManifestList) IsAnAPIObject() {} func (*BoundPod) IsAnAPIObject() {} func (*BoundPods) IsAnAPIObject() {} +func (*List) IsAnAPIObject() {} diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index fad4907f12..9b0499a8e2 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -19,6 +19,7 @@ package v1beta1 import ( "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -780,3 +781,9 @@ type BoundPods struct { // Items is the list of all pods bound to a given host. Items []BoundPod `json:"items" description:"list of all pods bound to a given host"` } + +// List holds a list of objects, which may not be known by the server. +type List struct { + TypeMeta `json:",inline"` + Items []runtime.RawExtension `json:"items" description:"list of objects"` +} diff --git a/pkg/api/v1beta2/register.go b/pkg/api/v1beta2/register.go index e0319b5ee1..fcbb3cdc07 100644 --- a/pkg/api/v1beta2/register.go +++ b/pkg/api/v1beta2/register.go @@ -46,6 +46,7 @@ func init() { &ContainerManifestList{}, &BoundPod{}, &BoundPods{}, + &List{}, ) } @@ -69,3 +70,4 @@ func (*ContainerManifest) IsAnAPIObject() {} func (*ContainerManifestList) IsAnAPIObject() {} func (*BoundPod) IsAnAPIObject() {} func (*BoundPods) IsAnAPIObject() {} +func (*List) IsAnAPIObject() {} diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index b5b1f1adca..79ce7d307e 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -19,6 +19,7 @@ package v1beta2 import ( "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -781,3 +782,9 @@ type BoundPods struct { // Items is the list of all pods bound to a given host. Items []BoundPod `json:"items" description:"list of all pods bound to a given host"` } + +// List holds a list of objects, which may not be known by the server. +type List struct { + TypeMeta `json:",inline"` + Items []runtime.RawExtension `json:"items" description:"list of objects"` +} diff --git a/pkg/api/v1beta3/register.go b/pkg/api/v1beta3/register.go index db6c3957f0..5d73780b71 100644 --- a/pkg/api/v1beta3/register.go +++ b/pkg/api/v1beta3/register.go @@ -46,6 +46,7 @@ func init() { &OperationList{}, &Event{}, &EventList{}, + &List{}, ) } @@ -69,3 +70,4 @@ func (*Operation) IsAnAPIObject() {} func (*OperationList) IsAnAPIObject() {} func (*Event) IsAnAPIObject() {} func (*EventList) IsAnAPIObject() {} +func (*List) IsAnAPIObject() {} diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 65160c5dc6..346695bf62 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -19,6 +19,7 @@ package v1beta3 import ( "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -556,9 +557,14 @@ type ReplicationControllerSpec struct { // Selector is a label query over pods that should match the Replicas count. Selector map[string]string `json:"selector,omitempty"` - // Template is a reference to an object that describes the pod that will be created if + // TemplateRef is a reference to an object that describes the pod that will be created if // insufficient replicas are detected. - Template ObjectReference `json:"template,omitempty"` + TemplateRef *ObjectReference `json:"templateRef,omitempty"` + + // Template is the object that describes the pod that will be created if + // insufficient replicas are detected. This takes precedence over a + // TemplateRef. + Template *PodTemplateSpec `json:"template,omitempty"` } // ReplicationControllerStatus represents the current status of a replication @@ -944,3 +950,11 @@ type EventList struct { Items []Event `json:"items"` } + +// List holds a list of objects, which may not be known by the server. +type List struct { + TypeMeta `json:",inline"` + ListMeta `json:"metadata,omitempty"` + + Items []runtime.RawExtension `json:"items" description:"list of objects"` +} diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 5227607555..0a3f8d040a 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -366,6 +366,8 @@ func (s *Scheme) DecodeInto(data []byte, obj Object) error { // Copy does a deep copy of an API object. Useful mostly for tests. // TODO(dbsmith): implement directly instead of via Encode/Decode +// TODO(claytonc): Copy cannot be used for objects which do not encode type information, such +// as lists of runtime.Objects func (s *Scheme) Copy(obj Object) (Object, error) { data, err := s.EncodeToVersion(obj, "") if err != nil {