mirror of https://github.com/k3s-io/k3s
Merge pull request #2786 from smarterclayton/load_opaque_objects
Allow runtime.Object to be encoded as runtime.RawExtensionpull/6/head
commit
f81ec248d0
|
@ -25,7 +25,9 @@ import (
|
||||||
internal "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
internal "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
fuzz "github.com/google/gofuzz"
|
fuzz "github.com/google/gofuzz"
|
||||||
)
|
)
|
||||||
|
@ -83,6 +85,26 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
|
||||||
// only replicas round trips
|
// only replicas round trips
|
||||||
j.Replicas = int(c.RandUint64())
|
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) {
|
func(intstr *util.IntOrString, c fuzz.Continue) {
|
||||||
// util.IntOrString will panic if its kind is set wrong.
|
// util.IntOrString will panic if its kind is set wrong.
|
||||||
if c.RandBool() {
|
if c.RandBool() {
|
||||||
|
|
|
@ -45,6 +45,7 @@ func init() {
|
||||||
&ContainerManifestList{},
|
&ContainerManifestList{},
|
||||||
&BoundPod{},
|
&BoundPod{},
|
||||||
&BoundPods{},
|
&BoundPods{},
|
||||||
|
&List{},
|
||||||
)
|
)
|
||||||
// Legacy names are supported
|
// Legacy names are supported
|
||||||
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
|
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
|
||||||
|
@ -71,3 +72,4 @@ func (*ContainerManifest) IsAnAPIObject() {}
|
||||||
func (*ContainerManifestList) IsAnAPIObject() {}
|
func (*ContainerManifestList) IsAnAPIObject() {}
|
||||||
func (*BoundPod) IsAnAPIObject() {}
|
func (*BoundPod) IsAnAPIObject() {}
|
||||||
func (*BoundPods) IsAnAPIObject() {}
|
func (*BoundPods) IsAnAPIObject() {}
|
||||||
|
func (*List) IsAnAPIObject() {}
|
||||||
|
|
|
@ -48,15 +48,6 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
|
||||||
// APIVersion and Kind must remain blank in memory.
|
// APIVersion and Kind must remain blank in memory.
|
||||||
j.APIVersion = ""
|
j.APIVersion = ""
|
||||||
j.Kind = ""
|
j.Kind = ""
|
||||||
|
|
||||||
j.Name = c.RandString()
|
|
||||||
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
|
|
||||||
j.SelfLink = c.RandString()
|
|
||||||
|
|
||||||
var sec, nsec int64
|
|
||||||
c.Fuzz(&sec)
|
|
||||||
c.Fuzz(&nsec)
|
|
||||||
j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
|
|
||||||
},
|
},
|
||||||
func(j *api.TypeMeta, c fuzz.Continue) {
|
func(j *api.TypeMeta, c fuzz.Continue) {
|
||||||
// We have to customize the randomization of TypeMetas because their
|
// We have to customize the randomization of TypeMetas because their
|
||||||
|
@ -99,6 +90,26 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
|
||||||
// only replicas round trips
|
// only replicas round trips
|
||||||
j.Replicas = int(c.RandUint64())
|
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) {
|
func(intstr *util.IntOrString, c fuzz.Continue) {
|
||||||
// util.IntOrString will panic if its kind is set wrong.
|
// util.IntOrString will panic if its kind is set wrong.
|
||||||
if c.RandBool() {
|
if c.RandBool() {
|
||||||
|
@ -154,7 +165,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
|
||||||
|
|
||||||
obj2, err := codec.Decode(data)
|
obj2, err := codec.Decode(data)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(source, obj2) {
|
if !reflect.DeepEqual(source, obj2) {
|
||||||
|
@ -188,7 +199,21 @@ func TestSpecificKind(t *testing.T) {
|
||||||
api.Scheme.Log(nil)
|
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 nonRoundTrippableTypes = util.NewStringSet("ContainerManifest")
|
||||||
|
var nonInternalRoundTrippableTypes = util.NewStringSet("List")
|
||||||
|
|
||||||
func TestRoundTripTypes(t *testing.T) {
|
func TestRoundTripTypes(t *testing.T) {
|
||||||
for kind := range api.Scheme.KnownTypes("") {
|
for kind := range api.Scheme.KnownTypes("") {
|
||||||
|
@ -206,7 +231,9 @@ func TestRoundTripTypes(t *testing.T) {
|
||||||
}
|
}
|
||||||
runTest(t, v1beta1.Codec, item)
|
runTest(t, v1beta1.Codec, item)
|
||||||
runTest(t, v1beta2.Codec, item)
|
runTest(t, v1beta2.Codec, item)
|
||||||
runTest(t, api.Codec, item)
|
if !nonInternalRoundTrippableTypes.Has(kind) {
|
||||||
|
runTest(t, api.Codec, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1000,3 +1001,11 @@ type BoundPods struct {
|
||||||
// Items is the list of all pods bound to a given host.
|
// Items is the list of all pods bound to a given host.
|
||||||
Items []BoundPod `json:"items"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ func init() {
|
||||||
&ContainerManifestList{},
|
&ContainerManifestList{},
|
||||||
&BoundPod{},
|
&BoundPod{},
|
||||||
&BoundPods{},
|
&BoundPods{},
|
||||||
|
&List{},
|
||||||
)
|
)
|
||||||
// Future names are supported
|
// Future names are supported
|
||||||
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
|
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
|
||||||
|
@ -76,3 +77,4 @@ func (*ContainerManifest) IsAnAPIObject() {}
|
||||||
func (*ContainerManifestList) IsAnAPIObject() {}
|
func (*ContainerManifestList) IsAnAPIObject() {}
|
||||||
func (*BoundPod) IsAnAPIObject() {}
|
func (*BoundPod) IsAnAPIObject() {}
|
||||||
func (*BoundPods) IsAnAPIObject() {}
|
func (*BoundPods) IsAnAPIObject() {}
|
||||||
|
func (*List) IsAnAPIObject() {}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package v1beta1
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -782,3 +783,9 @@ type BoundPods struct {
|
||||||
// Items is the list of all pods bound to a given host.
|
// 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"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ func init() {
|
||||||
&ContainerManifestList{},
|
&ContainerManifestList{},
|
||||||
&BoundPod{},
|
&BoundPod{},
|
||||||
&BoundPods{},
|
&BoundPods{},
|
||||||
|
&List{},
|
||||||
)
|
)
|
||||||
// Future names are supported
|
// Future names are supported
|
||||||
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
|
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
|
||||||
|
@ -76,3 +77,4 @@ func (*ContainerManifest) IsAnAPIObject() {}
|
||||||
func (*ContainerManifestList) IsAnAPIObject() {}
|
func (*ContainerManifestList) IsAnAPIObject() {}
|
||||||
func (*BoundPod) IsAnAPIObject() {}
|
func (*BoundPod) IsAnAPIObject() {}
|
||||||
func (*BoundPods) IsAnAPIObject() {}
|
func (*BoundPods) IsAnAPIObject() {}
|
||||||
|
func (*List) IsAnAPIObject() {}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package v1beta2
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -783,3 +784,9 @@ type BoundPods struct {
|
||||||
// Items is the list of all pods bound to a given host.
|
// 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"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ func init() {
|
||||||
&OperationList{},
|
&OperationList{},
|
||||||
&Event{},
|
&Event{},
|
||||||
&EventList{},
|
&EventList{},
|
||||||
|
&List{},
|
||||||
)
|
)
|
||||||
// Legacy names are supported
|
// Legacy names are supported
|
||||||
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
|
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
|
||||||
|
@ -72,3 +73,4 @@ func (*Operation) IsAnAPIObject() {}
|
||||||
func (*OperationList) IsAnAPIObject() {}
|
func (*OperationList) IsAnAPIObject() {}
|
||||||
func (*Event) IsAnAPIObject() {}
|
func (*Event) IsAnAPIObject() {}
|
||||||
func (*EventList) IsAnAPIObject() {}
|
func (*EventList) IsAnAPIObject() {}
|
||||||
|
func (*List) IsAnAPIObject() {}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package v1beta3
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"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 is a label query over pods that should match the Replicas count.
|
||||||
Selector map[string]string `json:"selector,omitempty"`
|
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.
|
// 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
|
// ReplicationControllerStatus represents the current status of a replication
|
||||||
|
@ -944,3 +950,11 @@ type EventList struct {
|
||||||
|
|
||||||
Items []Event `json:"items"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -49,15 +49,6 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
|
||||||
// APIVersion and Kind must remain blank in memory.
|
// APIVersion and Kind must remain blank in memory.
|
||||||
j.APIVersion = ""
|
j.APIVersion = ""
|
||||||
j.Kind = ""
|
j.Kind = ""
|
||||||
|
|
||||||
j.Name = c.RandString()
|
|
||||||
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
|
|
||||||
j.SelfLink = c.RandString()
|
|
||||||
|
|
||||||
var sec, nsec int64
|
|
||||||
c.Fuzz(&sec)
|
|
||||||
c.Fuzz(&nsec)
|
|
||||||
j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
|
|
||||||
},
|
},
|
||||||
func(j *api.TypeMeta, c fuzz.Continue) {
|
func(j *api.TypeMeta, c fuzz.Continue) {
|
||||||
// We have to customize the randomization of TypeMetas because their
|
// We have to customize the randomization of TypeMetas because their
|
||||||
|
|
|
@ -112,7 +112,7 @@ func (s *Scheme) DecodeInto(data []byte, obj interface{}) error {
|
||||||
} else {
|
} else {
|
||||||
external, err := s.NewObject(dataVersion, dataKind)
|
external, err := s.NewObject(dataVersion, dataKind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create new object of type ('%s', '%s')", dataVersion, dataKind)
|
return err
|
||||||
}
|
}
|
||||||
// yaml is a superset of json, so we use it to decode here. That way,
|
// yaml is a superset of json, so we use it to decode here. That way,
|
||||||
// we understand both.
|
// we understand both.
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
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 conversion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type notRegisteredErr struct {
|
||||||
|
kind string
|
||||||
|
version string
|
||||||
|
t reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *notRegisteredErr) Error() string {
|
||||||
|
if k.t != nil {
|
||||||
|
return fmt.Sprintf("no kind is registered for the type %v", k.t)
|
||||||
|
}
|
||||||
|
if len(k.kind) == 0 {
|
||||||
|
return fmt.Sprintf("no version %q has been registered", k.version)
|
||||||
|
}
|
||||||
|
if len(k.version) == 0 {
|
||||||
|
return fmt.Sprintf("no kind %q is registered for the default version", k.kind)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("no kind %q is registered for version %q", k.kind, k.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotRegisteredError returns true if the error indicates the provided
|
||||||
|
// object or input data is not registered.
|
||||||
|
func IsNotRegisteredError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := err.(*notRegisteredErr)
|
||||||
|
return ok
|
||||||
|
}
|
|
@ -145,14 +145,14 @@ func (s *Scheme) KnownTypes(version string) map[string]reflect.Type {
|
||||||
|
|
||||||
// NewObject returns a new object of the given version and name,
|
// NewObject returns a new object of the given version and name,
|
||||||
// or an error if it hasn't been registered.
|
// or an error if it hasn't been registered.
|
||||||
func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) {
|
func (s *Scheme) NewObject(versionName, kind string) (interface{}, error) {
|
||||||
if types, ok := s.versionMap[versionName]; ok {
|
if types, ok := s.versionMap[versionName]; ok {
|
||||||
if t, ok := types[typeName]; ok {
|
if t, ok := types[kind]; ok {
|
||||||
return reflect.New(t).Interface(), nil
|
return reflect.New(t).Interface(), nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no type '%v' for version '%v'", typeName, versionName)
|
return nil, ¬RegisteredErr{kind: kind, version: versionName}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no version '%v'", versionName)
|
return nil, ¬RegisteredErr{kind: kind, version: versionName}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddConversionFuncs adds functions to the list of conversion functions. The given
|
// AddConversionFuncs adds functions to the list of conversion functions. The given
|
||||||
|
@ -187,8 +187,7 @@ func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) {
|
||||||
// add conversion functions for things with changed/removed fields.
|
// add conversion functions for things with changed/removed fields.
|
||||||
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
||||||
for _, f := range conversionFuncs {
|
for _, f := range conversionFuncs {
|
||||||
err := s.converter.Register(f)
|
if err := s.converter.Register(f); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,7 +285,7 @@ func (s *Scheme) ObjectVersionAndKind(obj interface{}) (apiVersion, kind string,
|
||||||
version, vOK := s.typeToVersion[t]
|
version, vOK := s.typeToVersion[t]
|
||||||
kinds, kOK := s.typeToKind[t]
|
kinds, kOK := s.typeToKind[t]
|
||||||
if !vOK || !kOK {
|
if !vOK || !kOK {
|
||||||
return "", "", fmt.Errorf("unregistered type: %v", t)
|
return "", "", ¬RegisteredErr{t: t}
|
||||||
}
|
}
|
||||||
apiVersion = version
|
apiVersion = version
|
||||||
kind = kinds[0]
|
kind = kinds[0]
|
||||||
|
|
|
@ -22,16 +22,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var scheme = runtime.NewScheme()
|
|
||||||
var Codec = runtime.CodecFor(scheme, "v1test")
|
|
||||||
|
|
||||||
type EmbeddedTest struct {
|
type EmbeddedTest struct {
|
||||||
runtime.TypeMeta `json:",inline"`
|
runtime.TypeMeta
|
||||||
ID string `json:"id,omitempty"`
|
ID string
|
||||||
Object runtime.EmbeddedObject `json:"object,omitempty"`
|
Object runtime.EmbeddedObject
|
||||||
EmptyObject runtime.EmbeddedObject `json:"emptyObject,omitempty"`
|
EmptyObject runtime.EmbeddedObject
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmbeddedTestExternal struct {
|
type EmbeddedTestExternal struct {
|
||||||
|
@ -41,21 +39,91 @@ type EmbeddedTestExternal struct {
|
||||||
EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
|
EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ObjectTest struct {
|
||||||
|
runtime.TypeMeta
|
||||||
|
|
||||||
|
ID string
|
||||||
|
Items []runtime.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjectTestExternal struct {
|
||||||
|
runtime.TypeMeta `yaml:",inline" json:",inline"`
|
||||||
|
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Items []runtime.RawExtension `json:"items,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ObjectTest) IsAnAPIObject() {}
|
||||||
|
func (*ObjectTestExternal) IsAnAPIObject() {}
|
||||||
func (*EmbeddedTest) IsAnAPIObject() {}
|
func (*EmbeddedTest) IsAnAPIObject() {}
|
||||||
func (*EmbeddedTestExternal) IsAnAPIObject() {}
|
func (*EmbeddedTestExternal) IsAnAPIObject() {}
|
||||||
|
|
||||||
|
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
|
||||||
|
s := runtime.NewScheme()
|
||||||
|
s.AddKnownTypes("", &ObjectTest{})
|
||||||
|
s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
|
||||||
|
|
||||||
|
_, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("unexpected non-error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayOfRuntimeObject(t *testing.T) {
|
||||||
|
s := runtime.NewScheme()
|
||||||
|
s.AddKnownTypes("", &EmbeddedTest{})
|
||||||
|
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
|
||||||
|
s.AddKnownTypes("", &ObjectTest{})
|
||||||
|
s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
|
||||||
|
|
||||||
|
internal := &ObjectTest{
|
||||||
|
Items: []runtime.Object{
|
||||||
|
&EmbeddedTest{ID: "foo"},
|
||||||
|
&EmbeddedTest{ID: "bar"},
|
||||||
|
// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
|
||||||
|
&runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown","foo":"bar","kind":"OtherTest"}`)},
|
||||||
|
&ObjectTest{
|
||||||
|
Items: []runtime.Object{
|
||||||
|
&EmbeddedTest{ID: "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wire, err := s.EncodeToVersion(internal, "v1test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
t.Logf("Wire format is:\n%s\n", string(wire))
|
||||||
|
|
||||||
|
obj := &ObjectTestExternal{}
|
||||||
|
if err := json.Unmarshal(wire, obj); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
t.Logf("exact wire is: %#v", string(obj.Items[0].RawJSON))
|
||||||
|
|
||||||
|
decoded, err := s.Decode(wire)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal.Items[2].(*runtime.Unknown).Kind = "OtherTest"
|
||||||
|
internal.Items[2].(*runtime.Unknown).APIVersion = "unknown"
|
||||||
|
if e, a := internal, decoded; !reflect.DeepEqual(e, a) {
|
||||||
|
t.Log(string(decoded.(*ObjectTest).Items[2].(*runtime.Unknown).RawJSON))
|
||||||
|
t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEmbeddedObject(t *testing.T) {
|
func TestEmbeddedObject(t *testing.T) {
|
||||||
s := scheme
|
s := runtime.NewScheme()
|
||||||
s.AddKnownTypes("", &EmbeddedTest{})
|
s.AddKnownTypes("", &EmbeddedTest{})
|
||||||
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
|
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
|
||||||
|
|
||||||
outer := &EmbeddedTest{
|
outer := &EmbeddedTest{
|
||||||
TypeMeta: runtime.TypeMeta{Name: "outer"},
|
ID: "outer",
|
||||||
ID: "outer",
|
|
||||||
Object: runtime.EmbeddedObject{
|
Object: runtime.EmbeddedObject{
|
||||||
&EmbeddedTest{
|
&EmbeddedTest{
|
||||||
TypeMeta: runtime.TypeMeta{Name: "inner"},
|
ID: "inner",
|
||||||
ID: "inner",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -83,7 +151,7 @@ func TestEmbeddedObject(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected decode error %v", err)
|
t.Fatalf("Unexpected decode error %v", err)
|
||||||
}
|
}
|
||||||
if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.Name != "outer" {
|
if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" {
|
||||||
t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON)
|
t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(externalViaJSON.EmptyObject.RawJSON, []byte("null")) || len(externalViaJSON.Object.RawJSON) == 0 {
|
if !reflect.DeepEqual(externalViaJSON.EmptyObject.RawJSON, []byte("null")) || len(externalViaJSON.Object.RawJSON) == 0 {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsNotRegisteredError returns true if the error indicates the provided
|
||||||
|
// object or input data is not registered.
|
||||||
|
func IsNotRegisteredError(err error) bool {
|
||||||
|
return conversion.IsNotRegisteredError(err)
|
||||||
|
}
|
|
@ -26,6 +26,8 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (re *RawExtension) MarshalJSON() ([]byte, error) {
|
// Marshal may get called on pointers or values, so implement MarshalJSON on value.
|
||||||
|
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
|
||||||
|
func (re RawExtension) MarshalJSON() ([]byte, error) {
|
||||||
return re.RawJSON, nil
|
return re.RawJSON, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmbeddedRawExtensionMarshal(t *testing.T) {
|
||||||
|
type test struct {
|
||||||
|
Ext runtime.RawExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
extension := test{Ext: runtime.RawExtension{RawJSON: []byte(`{"foo":"bar"}`)}}
|
||||||
|
data, err := json.Marshal(extension)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if string(data) != `{"Ext":{"foo":"bar"}}` {
|
||||||
|
t.Errorf("unexpected data: %s", string(data))
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,15 +140,77 @@ func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *Embedded
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runtimeObjectToRawExtensionArray takes a list of objects and encodes them as RawExtension in the output version
|
||||||
|
// defined by the conversion.Scope. If objects must be encoded to different schema versions you should set them as
|
||||||
|
// runtime.Unknown in the internal version instead.
|
||||||
|
func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExtension, s conversion.Scope) error {
|
||||||
|
src := *in
|
||||||
|
dest := make([]RawExtension, len(src))
|
||||||
|
|
||||||
|
_, outVersion, scheme := self.fromScope(s)
|
||||||
|
|
||||||
|
for i := range src {
|
||||||
|
switch t := src[i].(type) {
|
||||||
|
case *Unknown:
|
||||||
|
dest[i].RawJSON = t.RawJSON
|
||||||
|
default:
|
||||||
|
data, err := scheme.EncodeToVersion(src[i], outVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest[i].RawJSON = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = dest
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawExtensionToRuntimeObjectArray attempts to decode objects from the array - if they are unrecognized objects,
|
||||||
|
// they are added as Unknown.
|
||||||
|
func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]Object, s conversion.Scope) error {
|
||||||
|
src := *in
|
||||||
|
dest := make([]Object, len(src))
|
||||||
|
|
||||||
|
_, _, scheme := self.fromScope(s)
|
||||||
|
|
||||||
|
for i := range src {
|
||||||
|
data := src[i].RawJSON
|
||||||
|
obj, err := scheme.Decode(data)
|
||||||
|
if err != nil {
|
||||||
|
if !IsNotRegisteredError(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
version, kind, err := scheme.raw.DataVersionAndKind(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
obj = &Unknown{
|
||||||
|
TypeMeta: TypeMeta{
|
||||||
|
APIVersion: version,
|
||||||
|
Kind: kind,
|
||||||
|
},
|
||||||
|
RawJSON: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest[i] = obj
|
||||||
|
}
|
||||||
|
*out = dest
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
||||||
func NewScheme() *Scheme {
|
func NewScheme() *Scheme {
|
||||||
s := &Scheme{conversion.NewScheme()}
|
s := &Scheme{conversion.NewScheme()}
|
||||||
s.raw.InternalVersion = ""
|
s.raw.InternalVersion = ""
|
||||||
s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"}
|
s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"}
|
||||||
s.raw.AddConversionFuncs(
|
if err := s.raw.AddConversionFuncs(
|
||||||
s.embeddedObjectToRawExtension,
|
s.embeddedObjectToRawExtension,
|
||||||
s.rawExtensionToEmbeddedObject,
|
s.rawExtensionToEmbeddedObject,
|
||||||
)
|
s.runtimeObjectToRawExtensionArray,
|
||||||
|
s.rawExtensionToRuntimeObjectArray,
|
||||||
|
); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,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.
|
// Copy does a deep copy of an API object. Useful mostly for tests.
|
||||||
// TODO(dbsmith): implement directly instead of via Encode/Decode
|
// 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) {
|
func (s *Scheme) Copy(obj Object) (Object, error) {
|
||||||
data, err := s.EncodeToVersion(obj, "")
|
data, err := s.EncodeToVersion(obj, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -16,9 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import ()
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Note that the types provided in this file are not versioned and are intended to be
|
// Note that the types provided in this file are not versioned and are intended to be
|
||||||
// safe to use from within all versions of every API object.
|
// safe to use from within all versions of every API object.
|
||||||
|
@ -35,15 +33,8 @@ import (
|
||||||
// your own with the same fields.
|
// your own with the same fields.
|
||||||
//
|
//
|
||||||
type TypeMeta struct {
|
type TypeMeta struct {
|
||||||
APIVersion string `json:"apiVersion,omitempty"`
|
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||||
Kind string `json:"kind,omitempty"`
|
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||||
|
|
||||||
Namespace string `json:"namespace,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
UID string `json:"uid,omitempty"`
|
|
||||||
CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
|
|
||||||
SelfLink string `json:"selfLink,omitempty"`
|
|
||||||
ResourceVersion string `json:"resourceVersion,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded
|
// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded
|
||||||
|
|
Loading…
Reference in New Issue