mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Simplify Codec and split responsibilities
Break Codec into two general purpose interfaces, Encoder and Decoder, and move parameter codec responsibilities to ParameterCodec. Make unversioned types explicit when registering - these types go through conversion without modification. Switch to use "__internal" instead of "" to represent the internal version. Future commits will also add group defaulting (so that "" is expanded internally into a known group version, and only cleared during set). For embedded types like runtime.Object -> runtime.RawExtension, put the responsibility on the caller of Decode/Encode to handle transformation into destination serialization. Future commits will expand RawExtension and Unknown to accept a content encoding as well as bytes. Make Unknown a bit more powerful and use it to carry unrecognized types.
This commit is contained in:
parent
6582b4c2ea
commit
63a7a41ddf
@ -104,7 +104,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
pkgname = gv.Group
|
pkgname = gv.Group
|
||||||
}
|
}
|
||||||
if len(gv.Version) != 0 {
|
if len(gv.Version) != 0 && gv.Version != kruntime.APIVersionInternal {
|
||||||
pkgname = gv.Version
|
pkgname = gv.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,99 +17,155 @@ limitations under the License.
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/util/yaml"
|
"k8s.io/kubernetes/pkg/conversion/queryparams"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// codec binds an encoder and decoder.
|
||||||
|
type codec struct {
|
||||||
|
Encoder
|
||||||
|
Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCodec creates a Codec from an Encoder and Decoder.
|
||||||
|
func NewCodec(e Encoder, d Decoder) Codec {
|
||||||
|
return codec{e, d}
|
||||||
|
}
|
||||||
|
|
||||||
// Encode is a convenience wrapper for encoding to a []byte from an Encoder
|
// Encode is a convenience wrapper for encoding to a []byte from an Encoder
|
||||||
// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered.
|
func Encode(e Encoder, obj Object, overrides ...unversioned.GroupVersion) ([]byte, error) {
|
||||||
func Encode(e Encoder, obj Object) ([]byte, error) {
|
// TODO: reuse buffer
|
||||||
return e.Encode(obj)
|
buf := &bytes.Buffer{}
|
||||||
|
if err := e.EncodeToStream(obj, buf, overrides...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode is a convenience wrapper for decoding data into an Object.
|
// Decode is a convenience wrapper for decoding data into an Object.
|
||||||
// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered.
|
|
||||||
func Decode(d Decoder, data []byte) (Object, error) {
|
func Decode(d Decoder, data []byte) (Object, error) {
|
||||||
return d.Decode(data)
|
obj, _, err := d.Decode(data, nil, nil)
|
||||||
|
return obj, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeInto performs a Decode into the provided object.
|
// DecodeInto performs a Decode into the provided object.
|
||||||
// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered.
|
|
||||||
func DecodeInto(d Decoder, data []byte, into Object) error {
|
func DecodeInto(d Decoder, data []byte, into Object) error {
|
||||||
return d.DecodeInto(data, into)
|
out, gvk, err := d.Decode(data, nil, into)
|
||||||
}
|
|
||||||
|
|
||||||
// CodecFor returns a Codec that invokes Encode with the provided version.
|
|
||||||
func CodecFor(codec ObjectCodec, version unversioned.GroupVersion) Codec {
|
|
||||||
return &codecWrapper{codec, version}
|
|
||||||
}
|
|
||||||
|
|
||||||
// yamlCodec converts YAML passed to the Decoder methods to JSON.
|
|
||||||
type yamlCodec struct {
|
|
||||||
// a Codec for JSON
|
|
||||||
Codec
|
|
||||||
}
|
|
||||||
|
|
||||||
// yamlCodec implements Codec
|
|
||||||
var _ Codec = yamlCodec{}
|
|
||||||
var _ Decoder = yamlCodec{}
|
|
||||||
|
|
||||||
// YAMLDecoder adds YAML decoding support to a codec that supports JSON.
|
|
||||||
func YAMLDecoder(codec Codec) Codec {
|
|
||||||
return &yamlCodec{codec}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c yamlCodec) Decode(data []byte) (Object, error) {
|
|
||||||
out, err := yaml.ToJSON(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data = out
|
|
||||||
return c.Codec.Decode(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c yamlCodec) DecodeInto(data []byte, obj Object) error {
|
|
||||||
out, err := yaml.ToJSON(data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data = out
|
if out != into {
|
||||||
return c.Codec.DecodeInto(data, obj)
|
return fmt.Errorf("unable to decode %s into %v", gvk, reflect.TypeOf(into))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
|
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
|
||||||
func EncodeOrDie(codec Codec, obj Object) string {
|
func EncodeOrDie(e Encoder, obj Object) string {
|
||||||
bytes, err := Encode(codec, obj)
|
bytes, err := Encode(e, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return string(bytes)
|
return string(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// codecWrapper implements encoding to an alternative
|
// UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or
|
||||||
// default version for a scheme.
|
// invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object.
|
||||||
type codecWrapper struct {
|
func UseOrCreateObject(t Typer, c ObjectCreater, gvk unversioned.GroupVersionKind, obj Object) (Object, error) {
|
||||||
ObjectCodec
|
if obj != nil {
|
||||||
version unversioned.GroupVersion
|
into, _, err := t.ObjectKind(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if gvk == *into {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.New(gvk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// codecWrapper implements Decoder
|
// NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
|
||||||
var _ Decoder = &codecWrapper{}
|
type NoopEncoder struct {
|
||||||
|
Decoder
|
||||||
// Encode implements Codec
|
|
||||||
func (c *codecWrapper) Encode(obj Object) ([]byte, error) {
|
|
||||||
return c.EncodeToVersion(obj, c.version.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codecWrapper) EncodeToStream(obj Object, stream io.Writer) error {
|
var _ Serializer = NoopEncoder{}
|
||||||
return c.EncodeToVersionStream(obj, c.version.String(), stream)
|
|
||||||
|
func (n NoopEncoder) EncodeToStream(obj Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
|
||||||
|
return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this behaviour default when we move everyone away from
|
// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
|
||||||
// the unversioned types.
|
type NoopDecoder struct {
|
||||||
//
|
Encoder
|
||||||
// func (c *codecWrapper) Decode(data []byte) (Object, error) {
|
}
|
||||||
// return c.DecodeToVersion(data, c.version)
|
|
||||||
// }
|
var _ Serializer = NoopDecoder{}
|
||||||
|
|
||||||
|
func (n NoopDecoder) Decode(data []byte, gvk *unversioned.GroupVersionKind, into Object) (Object, *unversioned.GroupVersionKind, error) {
|
||||||
|
return nil, nil, fmt.Errorf("decoding is not allowed for this codec: %v", reflect.TypeOf(n.Encoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back.
|
||||||
|
func NewParameterCodec(scheme *Scheme) ParameterCodec {
|
||||||
|
return ¶meterCodec{
|
||||||
|
typer: ObjectTyperToTyper(scheme),
|
||||||
|
convertor: scheme,
|
||||||
|
creator: scheme,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameterCodec implements conversion to and from query parameters and objects.
|
||||||
|
type parameterCodec struct {
|
||||||
|
typer Typer
|
||||||
|
convertor ObjectConvertor
|
||||||
|
creator ObjectCreater
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ParameterCodec = ¶meterCodec{}
|
||||||
|
|
||||||
|
// DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then
|
||||||
|
// converts that object to into (if necessary). Returns an error if the operation cannot be completed.
|
||||||
|
func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error {
|
||||||
|
if len(parameters) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
targetGVK, _, err := c.typer.ObjectKind(into)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if targetGVK.GroupVersion() == from {
|
||||||
|
return c.convertor.Convert(¶meters, into)
|
||||||
|
}
|
||||||
|
input, err := c.creator.New(from.WithKind(targetGVK.Kind))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.convertor.Convert(¶meters, input); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.convertor.Convert(input, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeParameters converts the provided object into the to version, then converts that object to url.Values.
|
||||||
|
// Returns an error if conversion is not possible.
|
||||||
|
func (c *parameterCodec) EncodeParameters(obj Object, to unversioned.GroupVersion) (url.Values, error) {
|
||||||
|
gvk, _, err := c.typer.ObjectKind(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if to != gvk.GroupVersion() {
|
||||||
|
out, err := c.convertor.ConvertToVersion(obj, to.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
obj = out
|
||||||
|
}
|
||||||
|
return queryparams.Convert(obj)
|
||||||
|
}
|
||||||
|
@ -102,10 +102,8 @@ func (g *conversionGenerator) AddImport(pkg string) string {
|
|||||||
func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVersion, reflection reflect.Type) error {
|
func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVersion, reflection reflect.Type) error {
|
||||||
kind := reflection.Name()
|
kind := reflection.Name()
|
||||||
// TODO this is equivalent to what it did before, but it needs to be fixed for the proper group
|
// TODO this is equivalent to what it did before, but it needs to be fixed for the proper group
|
||||||
internalVersion, exists := g.scheme.InternalVersions[gv.Group]
|
internalVersion := gv
|
||||||
if !exists {
|
internalVersion.Version = APIVersionInternal
|
||||||
return fmt.Errorf("no internal version for %v", gv)
|
|
||||||
}
|
|
||||||
|
|
||||||
internalObj, err := g.scheme.NewObject(internalVersion.WithKind(kind))
|
internalObj, err := g.scheme.NewObject(internalVersion.WithKind(kind))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -775,6 +773,10 @@ func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outTyp
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.scheme.Converter().IsConversionIgnored(inField.Type, outField.Type) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
existsConversion := g.scheme.Converter().HasConversionFunc(inField.Type, outField.Type)
|
existsConversion := g.scheme.Converter().HasConversionFunc(inField.Type, outField.Type)
|
||||||
_, hasPublicConversion := g.publicFuncs[typePair{inField.Type, outField.Type}]
|
_, hasPublicConversion := g.publicFuncs[typePair{inField.Type, outField.Type}]
|
||||||
// TODO: This allows a private conversion for a slice to take precedence over a public
|
// TODO: This allows a private conversion for a slice to take precedence over a public
|
||||||
@ -895,12 +897,7 @@ type typePair struct {
|
|||||||
outType reflect.Type
|
outType reflect.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultConversions []typePair = []typePair{
|
var defaultConversions []typePair = []typePair{}
|
||||||
{reflect.TypeOf([]RawExtension{}), reflect.TypeOf([]Object{})},
|
|
||||||
{reflect.TypeOf([]Object{}), reflect.TypeOf([]RawExtension{})},
|
|
||||||
{reflect.TypeOf(RawExtension{}), reflect.TypeOf(EmbeddedObject{})},
|
|
||||||
{reflect.TypeOf(EmbeddedObject{}), reflect.TypeOf(RawExtension{})},
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *conversionGenerator) OverwritePackage(pkg, overwrite string) {
|
func (g *conversionGenerator) OverwritePackage(pkg, overwrite string) {
|
||||||
g.pkgOverwrites[pkg] = overwrite
|
g.pkgOverwrites[pkg] = overwrite
|
||||||
|
@ -46,12 +46,11 @@ func (obj *InternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj
|
|||||||
func (obj *ExternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *ExternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
|
|
||||||
func TestStringMapConversion(t *testing.T) {
|
func TestStringMapConversion(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
scheme.Log(t)
|
scheme.Log(t)
|
||||||
scheme.AddInternalGroupVersion(internalGV)
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{})
|
scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{})
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{})
|
||||||
|
|
||||||
|
124
pkg/runtime/embedded.go
Normal file
124
pkg/runtime/embedded.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
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 runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encodable struct {
|
||||||
|
e Encoder `json:"-"`
|
||||||
|
obj Object
|
||||||
|
versions []unversioned.GroupVersion `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e encodable) GetObjectKind() unversioned.ObjectKind { return e.obj.GetObjectKind() }
|
||||||
|
|
||||||
|
// NewEncodable creates an object that will be encoded with the provided codec on demand.
|
||||||
|
// Provided as a convenience for test cases dealing with internal objects.
|
||||||
|
func NewEncodable(e Encoder, obj Object, versions ...unversioned.GroupVersion) Object {
|
||||||
|
if _, ok := obj.(*Unknown); ok {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
return encodable{e, obj, versions}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re encodable) UnmarshalJSON(in []byte) error {
|
||||||
|
return errors.New("runtime.encodable cannot be unmarshalled from JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 encodable) MarshalJSON() ([]byte, error) {
|
||||||
|
return Encode(re.e, re.obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncodableList creates an object that will be encoded with the provided codec on demand.
|
||||||
|
// Provided as a convenience for test cases dealing with internal objects.
|
||||||
|
func NewEncodableList(e Encoder, objects []Object, versions ...unversioned.GroupVersion) []Object {
|
||||||
|
out := make([]Object, len(objects))
|
||||||
|
for i := range objects {
|
||||||
|
if _, ok := objects[i].(*Unknown); ok {
|
||||||
|
out[i] = objects[i]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[i] = NewEncodable(e, objects[i], versions...)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *Unknown) UnmarshalJSON(in []byte) error {
|
||||||
|
if re == nil {
|
||||||
|
return errors.New("runtime.Unknown: UnmarshalJSON on nil pointer")
|
||||||
|
}
|
||||||
|
re.TypeMeta = TypeMeta{}
|
||||||
|
re.RawJSON = append(re.RawJSON[0:0], in...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Unknown) MarshalJSON() ([]byte, error) {
|
||||||
|
if re.RawJSON == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return re.RawJSON, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultEmbeddedConversions() []interface{} {
|
||||||
|
return []interface{}{
|
||||||
|
func(in *Object, out *RawExtension, s conversion.Scope) error {
|
||||||
|
if in == nil {
|
||||||
|
out.RawJSON = []byte("null")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
obj := *in
|
||||||
|
if unk, ok := obj.(*Unknown); ok {
|
||||||
|
if unk.RawJSON != nil {
|
||||||
|
out.RawJSON = unk.RawJSON
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
obj = out.Object
|
||||||
|
}
|
||||||
|
if obj == nil {
|
||||||
|
out.RawJSON = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out.Object = obj
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
func(in *RawExtension, out *Object, s conversion.Scope) error {
|
||||||
|
if in.Object != nil {
|
||||||
|
*out = in.Object
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data := in.RawJSON
|
||||||
|
if len(data) == 0 || (len(data) == 4 && string(data) == "null") {
|
||||||
|
*out = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*out = &Unknown{
|
||||||
|
RawJSON: data,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -19,20 +19,21 @@ package runtime_test
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime/serializer"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmbeddedTest struct {
|
type EmbeddedTest struct {
|
||||||
runtime.TypeMeta
|
runtime.TypeMeta
|
||||||
ID string
|
ID string
|
||||||
Object runtime.EmbeddedObject
|
Object runtime.Object
|
||||||
EmptyObject runtime.EmbeddedObject
|
EmptyObject runtime.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmbeddedTestExternal struct {
|
type EmbeddedTestExternal struct {
|
||||||
@ -62,16 +63,17 @@ func (obj *EmbeddedTest) GetObjectKind() unversioned.ObjectKind { return
|
|||||||
func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
|
|
||||||
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
|
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||||
externalGVK := externalGV.WithKind("ObjectTest")
|
externalGVK := externalGV.WithKind("ObjectTest")
|
||||||
|
|
||||||
s := runtime.NewScheme()
|
s := runtime.NewScheme()
|
||||||
s.AddInternalGroupVersion(internalGV)
|
|
||||||
s.AddKnownTypes(internalGV, &ObjectTest{})
|
s.AddKnownTypes(internalGV, &ObjectTest{})
|
||||||
s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{})
|
s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{})
|
||||||
|
|
||||||
obj, err := s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{}]}`))
|
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||||
|
|
||||||
|
obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -79,42 +81,51 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
|
|||||||
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" {
|
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" {
|
||||||
t.Fatalf("unexpected object: %#v", test.Items[0])
|
t.Fatalf("unexpected object: %#v", test.Items[0])
|
||||||
}
|
}
|
||||||
|
if *gvk != externalGVK {
|
||||||
|
t.Fatalf("unexpected kind: %#v", gvk)
|
||||||
|
}
|
||||||
|
|
||||||
obj, err = s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{"kind":"Other","apiVersion":"v1"}]}`))
|
obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
test = obj.(*ObjectTest)
|
test = obj.(*ObjectTest)
|
||||||
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "Other" || unk.APIVersion != "v1" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` {
|
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` {
|
||||||
t.Fatalf("unexpected object: %#v", test.Items[0])
|
t.Fatalf("unexpected object: %#v", test.Items[0])
|
||||||
}
|
}
|
||||||
|
if *gvk != externalGVK {
|
||||||
|
t.Fatalf("unexpected kind: %#v", gvk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayOfRuntimeObject(t *testing.T) {
|
func TestArrayOfRuntimeObject(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||||
|
|
||||||
s := runtime.NewScheme()
|
s := runtime.NewScheme()
|
||||||
s.AddInternalGroupVersion(internalGV)
|
|
||||||
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
||||||
s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
|
s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
|
||||||
s.AddKnownTypes(internalGV, &ObjectTest{})
|
s.AddKnownTypes(internalGV, &ObjectTest{})
|
||||||
s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})
|
s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})
|
||||||
|
|
||||||
internal := &ObjectTest{
|
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||||
Items: []runtime.Object{
|
|
||||||
&EmbeddedTest{ID: "foo"},
|
innerItems := []runtime.Object{
|
||||||
&EmbeddedTest{ID: "bar"},
|
&EmbeddedTest{ID: "baz"},
|
||||||
// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
|
}
|
||||||
&runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)},
|
items := []runtime.Object{
|
||||||
&ObjectTest{
|
&EmbeddedTest{ID: "foo"},
|
||||||
Items: []runtime.Object{
|
&EmbeddedTest{ID: "bar"},
|
||||||
&EmbeddedTest{ID: "baz"},
|
// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
|
||||||
},
|
&runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)},
|
||||||
},
|
&ObjectTest{
|
||||||
|
Items: runtime.NewEncodableList(codec, innerItems),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
wire, err := s.EncodeToVersion(internal, externalGV.String())
|
internal := &ObjectTest{
|
||||||
|
Items: runtime.NewEncodableList(codec, items),
|
||||||
|
}
|
||||||
|
wire, err := runtime.Encode(codec, internal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -126,7 +137,10 @@ func TestArrayOfRuntimeObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON))
|
t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON))
|
||||||
|
|
||||||
decoded, err := runtime.Decode(s, wire)
|
items[3] = &ObjectTest{Items: innerItems}
|
||||||
|
internal.Items = items
|
||||||
|
|
||||||
|
decoded, err := runtime.Decode(codec, wire)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -134,7 +148,7 @@ func TestArrayOfRuntimeObject(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if errs := runtime.DecodeList(list, s); len(errs) > 0 {
|
if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
|
||||||
t.Fatalf("unexpected error: %v", errs)
|
t.Fatalf("unexpected error: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,53 +156,65 @@ func TestArrayOfRuntimeObject(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if errs := runtime.DecodeList(list2, s); len(errs) > 0 {
|
if errs := runtime.DecodeList(list2, codec); len(errs) > 0 {
|
||||||
t.Fatalf("unexpected error: %v", errs)
|
t.Fatalf("unexpected error: %v", errs)
|
||||||
}
|
}
|
||||||
if err := meta.SetList(list[3], list2); err != nil {
|
if err := meta.SetList(list[3], list2); err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal.Items[2].(*runtime.Unknown).Kind = "OtherTest"
|
// we want DecodeList to set type meta if possible, even on runtime.Unknown objects
|
||||||
internal.Items[2].(*runtime.Unknown).APIVersion = "unknown.group/unknown"
|
internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"}
|
||||||
if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
|
if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a))
|
t.Errorf("mismatched decoded: %s", util.ObjectGoPrintSideBySide(e, a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmbeddedObject(t *testing.T) {
|
func TestNestedObject(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||||
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
|
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
|
||||||
|
|
||||||
s := runtime.NewScheme()
|
s := runtime.NewScheme()
|
||||||
s.AddInternalGroupVersion(internalGV)
|
|
||||||
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
||||||
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
|
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
|
||||||
|
|
||||||
|
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||||
|
|
||||||
|
inner := &EmbeddedTest{
|
||||||
|
ID: "inner",
|
||||||
|
}
|
||||||
outer := &EmbeddedTest{
|
outer := &EmbeddedTest{
|
||||||
ID: "outer",
|
ID: "outer",
|
||||||
Object: runtime.EmbeddedObject{
|
Object: runtime.NewEncodable(codec, inner),
|
||||||
Object: &EmbeddedTest{
|
|
||||||
ID: "inner",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wire, err := s.EncodeToVersion(outer, externalGV.String())
|
wire, err := runtime.Encode(codec, outer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected encode error '%v'", err)
|
t.Fatalf("Unexpected encode error '%v'", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Wire format is:\n%v\n", string(wire))
|
t.Logf("Wire format is:\n%v\n", string(wire))
|
||||||
|
|
||||||
decoded, err := runtime.Decode(s, wire)
|
decoded, err := runtime.Decode(codec, wire)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected decode error %v", err)
|
t.Fatalf("Unexpected decode error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for later tests
|
||||||
|
outer.Object = inner
|
||||||
|
|
||||||
|
if e, a := outer, decoded; reflect.DeepEqual(e, a) {
|
||||||
|
t.Errorf("Expected unequal %#v %#v", e, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).RawJSON)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
decoded.(*EmbeddedTest).Object = obj
|
||||||
if e, a := outer, decoded; !reflect.DeepEqual(e, a) {
|
if e, a := outer, decoded; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("Expected: %#v but got %#v", e, a)
|
t.Errorf("Expected equal %#v %#v", e, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test JSON decoding of the external object, which should preserve
|
// test JSON decoding of the external object, which should preserve
|
||||||
@ -211,46 +237,45 @@ func TestEmbeddedObject(t *testing.T) {
|
|||||||
// the external representation
|
// the external representation
|
||||||
var decodedViaJSON EmbeddedTest
|
var decodedViaJSON EmbeddedTest
|
||||||
err = json.Unmarshal(wire, &decodedViaJSON)
|
err = json.Unmarshal(wire, &decodedViaJSON)
|
||||||
if err != nil {
|
if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") {
|
||||||
t.Fatalf("Unexpected decode error %v", err)
|
t.Fatalf("Unexpected decode error %v", err)
|
||||||
}
|
}
|
||||||
if a := decodedViaJSON; a.Object.Object != nil || a.EmptyObject.Object != nil {
|
if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil {
|
||||||
t.Errorf("Expected embedded objects to be nil: %#v", a)
|
t.Errorf("Expected embedded objects to be nil: %#v", a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDeepCopyOfEmbeddedObject checks to make sure that EmbeddedObject's can be passed through DeepCopy with fidelity
|
// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity
|
||||||
func TestDeepCopyOfEmbeddedObject(t *testing.T) {
|
func TestDeepCopyOfRuntimeObject(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||||
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
|
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
|
||||||
|
|
||||||
s := runtime.NewScheme()
|
s := runtime.NewScheme()
|
||||||
s.AddInternalGroupVersion(internalGV)
|
|
||||||
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
||||||
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
|
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
|
||||||
|
|
||||||
original := &EmbeddedTest{
|
original := &EmbeddedTest{
|
||||||
ID: "outer",
|
ID: "outer",
|
||||||
Object: runtime.EmbeddedObject{
|
Object: &EmbeddedTest{
|
||||||
Object: &EmbeddedTest{
|
ID: "inner",
|
||||||
ID: "inner",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
originalData, err := s.EncodeToVersion(original, externalGV.String())
|
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||||
|
|
||||||
|
originalData, err := runtime.Encode(codec, original)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
t.Logf("originalRole = %v\n", string(originalData))
|
t.Logf("originalRole = %v\n", string(originalData))
|
||||||
|
|
||||||
copyOfOriginal, err := api.Scheme.DeepCopy(original)
|
copyOfOriginal, err := s.DeepCopy(original)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
copiedData, err := s.EncodeToVersion(copyOfOriginal.(runtime.Object), externalGV.String())
|
copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -37,3 +37,11 @@ func IsMissingKind(err error) bool {
|
|||||||
func IsMissingVersion(err error) bool {
|
func IsMissingVersion(err error) bool {
|
||||||
return conversion.IsMissingVersion(err)
|
return conversion.IsMissingVersion(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMissingKindErr(data string) error {
|
||||||
|
return conversion.NewMissingKindErr(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMissingVersionErr(data string) error {
|
||||||
|
return conversion.NewMissingVersionErr(data)
|
||||||
|
}
|
||||||
|
@ -16,7 +16,10 @@ limitations under the License.
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
func (re *RawExtension) UnmarshalJSON(in []byte) error {
|
func (re *RawExtension) UnmarshalJSON(in []byte) error {
|
||||||
if re == nil {
|
if re == nil {
|
||||||
@ -29,5 +32,16 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error {
|
|||||||
// Marshal may get called on pointers or values, so implement MarshalJSON on value.
|
// 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
|
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
|
||||||
func (re RawExtension) MarshalJSON() ([]byte, error) {
|
func (re RawExtension) MarshalJSON() ([]byte, error) {
|
||||||
|
if re.RawJSON == nil {
|
||||||
|
// TODO: this is to support legacy behavior of JSONPrinter and YAMLPrinter, which
|
||||||
|
// expect to call json.Marshal on arbitrary versioned objects (even those not in
|
||||||
|
// the scheme). pkg/kubectl/resource#AsVersionedObjects and its interaction with
|
||||||
|
// kubectl get on objects not in the scheme needs to be updated to ensure that the
|
||||||
|
// objects that are not part of the scheme are correctly put into the right form.
|
||||||
|
if re.Object != nil {
|
||||||
|
return json.Marshal(re.Object)
|
||||||
|
}
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
return re.RawJSON, nil
|
return re.RawJSON, nil
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,30 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
|
"k8s.io/kubernetes/pkg/util/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type objectTyperToTyper struct {
|
||||||
|
typer ObjectTyper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t objectTyperToTyper) ObjectKind(obj Object) (*unversioned.GroupVersionKind, bool, error) {
|
||||||
|
gvk, err := t.typer.ObjectKind(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
unversionedType, ok := t.typer.IsUnversioned(obj)
|
||||||
|
if !ok {
|
||||||
|
// ObjectTyper violates its contract
|
||||||
|
return nil, false, fmt.Errorf("typer returned a kind for %v, but then reported it was not in the scheme with IsUnversioned", reflect.TypeOf(obj))
|
||||||
|
}
|
||||||
|
return &gvk, unversionedType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjectTyperToTyper(typer ObjectTyper) Typer {
|
||||||
|
return objectTyperToTyper{typer: typer}
|
||||||
|
}
|
||||||
|
|
||||||
// fieldPtr puts the address of fieldName, which must be a member of v,
|
// 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
|
// into dest, which must be an address of a variable to which this field's
|
||||||
// address can be assigned.
|
// address can be assigned.
|
||||||
@ -48,32 +70,56 @@ func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
|
|||||||
return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type())
|
return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeList ensures that each object in an array is converted to a Unknown{} in serialized form.
|
||||||
|
// TODO: accept a content type.
|
||||||
|
func EncodeList(e Encoder, objects []Object, overrides ...unversioned.GroupVersion) error {
|
||||||
|
var errs []error
|
||||||
|
for i := range objects {
|
||||||
|
data, err := Encode(e, objects[i], overrides...)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
objects[i] = &Unknown{RawJSON: data}
|
||||||
|
}
|
||||||
|
return errors.NewAggregate(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeListItem(obj *Unknown, decoders []Decoder) (Object, error) {
|
||||||
|
for _, decoder := range decoders {
|
||||||
|
obj, err := Decode(decoder, obj.RawJSON)
|
||||||
|
if err != nil {
|
||||||
|
if IsNotRegisteredError(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
// could not decode, so leave the object as Unknown, but give the decoders the
|
||||||
|
// chance to set Unknown.TypeMeta if it is available.
|
||||||
|
for _, decoder := range decoders {
|
||||||
|
if err := DecodeInto(decoder, obj.RawJSON, obj); err == nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeList alters the list in place, attempting to decode any objects found in
|
// DecodeList alters the list in place, attempting to decode any objects found in
|
||||||
// the list that have the runtime.Unknown type. Any errors that occur are returned
|
// the list that have the Unknown type. Any errors that occur are returned
|
||||||
// after the entire list is processed. Decoders are tried in order.
|
// after the entire list is processed. Decoders are tried in order.
|
||||||
func DecodeList(objects []Object, decoders ...ObjectDecoder) []error {
|
func DecodeList(objects []Object, decoders ...Decoder) []error {
|
||||||
errs := []error(nil)
|
errs := []error(nil)
|
||||||
for i, obj := range objects {
|
for i, obj := range objects {
|
||||||
switch t := obj.(type) {
|
switch t := obj.(type) {
|
||||||
case *Unknown:
|
case *Unknown:
|
||||||
for _, decoder := range decoders {
|
decoded, err := decodeListItem(t, decoders)
|
||||||
gv, err := unversioned.ParseGroupVersion(t.APIVersion)
|
if err != nil {
|
||||||
if err != nil {
|
errs = append(errs, err)
|
||||||
errs = append(errs, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !decoder.Recognizes(gv.WithKind(t.Kind)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
obj, err := Decode(decoder, t.RawJSON)
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
objects[i] = obj
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
objects[i] = decoded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errs
|
return errs
|
||||||
@ -84,16 +130,6 @@ type MultiObjectTyper []ObjectTyper
|
|||||||
|
|
||||||
var _ ObjectTyper = MultiObjectTyper{}
|
var _ ObjectTyper = MultiObjectTyper{}
|
||||||
|
|
||||||
func (m MultiObjectTyper) DataKind(data []byte) (gvk unversioned.GroupVersionKind, err error) {
|
|
||||||
for _, t := range m {
|
|
||||||
gvk, err = t.DataKind(data)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) {
|
func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) {
|
||||||
for _, t := range m {
|
for _, t := range m {
|
||||||
gvk, err = t.ObjectKind(obj)
|
gvk, err = t.ObjectKind(obj)
|
||||||
@ -122,3 +158,12 @@ func (m MultiObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m MultiObjectTyper) IsUnversioned(obj Object) (bool, bool) {
|
||||||
|
for _, t := range m {
|
||||||
|
if unversioned, ok := t.IsUnversioned(obj); ok {
|
||||||
|
return unversioned, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
@ -32,7 +32,7 @@ func TestDecodeList(t *testing.T) {
|
|||||||
&runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}},
|
&runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if errs := runtime.DecodeList(pl.Items, api.Scheme); len(errs) != 0 {
|
if errs := runtime.DecodeList(pl.Items, testapi.Default.Codec()); len(errs) != 0 {
|
||||||
t.Fatalf("unexpected error %v", errs)
|
t.Fatalf("unexpected error %v", errs)
|
||||||
}
|
}
|
||||||
if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" {
|
if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" {
|
||||||
|
@ -23,70 +23,84 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec defines methods for serializing and deserializing API objects.
|
const (
|
||||||
type Codec interface {
|
APIVersionInternal = "__internal"
|
||||||
Decoder
|
APIVersionUnversioned = "__unversioned"
|
||||||
Encoder
|
)
|
||||||
|
|
||||||
|
// Typer retrieves information about an object's group, version, and kind.
|
||||||
|
type Typer interface {
|
||||||
|
// ObjectKind returns the version and kind of the provided object, or an
|
||||||
|
// error if the object is not recognized (IsNotRegisteredError will return true).
|
||||||
|
// It returns whether the object is considered unversioned at the same time.
|
||||||
|
// TODO: align the signature of ObjectTyper with this interface
|
||||||
|
ObjectKind(Object) (*unversioned.GroupVersionKind, bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decoder defines methods for deserializing API objects into a given type
|
|
||||||
type Decoder interface {
|
|
||||||
// TODO: change the signature of this method
|
|
||||||
Decode(data []byte) (Object, error)
|
|
||||||
// DEPRECATED: This method is being removed
|
|
||||||
DecodeToVersion(data []byte, groupVersion unversioned.GroupVersion) (Object, error)
|
|
||||||
// DEPRECATED: This method is being removed
|
|
||||||
DecodeInto(data []byte, obj Object) error
|
|
||||||
// DEPRECATED: This method is being removed
|
|
||||||
DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, groupVersionKind unversioned.GroupVersionKind) error
|
|
||||||
|
|
||||||
DecodeParametersInto(parameters url.Values, obj Object) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoder defines methods for serializing API objects into bytes
|
|
||||||
type Encoder interface {
|
type Encoder interface {
|
||||||
// DEPRECATED: This method is being removed
|
// EncodeToStream writes an object to a stream. Override versions may be provided for each group
|
||||||
Encode(obj Object) (data []byte, err error)
|
// that enforce a certain versioning. Implementations may return errors if the versions are incompatible,
|
||||||
EncodeToStream(obj Object, stream io.Writer) error
|
// or if no conversion is defined.
|
||||||
|
EncodeToStream(obj Object, stream io.Writer, overrides ...unversioned.GroupVersion) error
|
||||||
// TODO: Add method for processing url parameters.
|
|
||||||
// EncodeParameters(obj Object) (url.Values, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectCodec represents the common mechanisms for converting to and from a particular
|
type Decoder interface {
|
||||||
// binary representation of an object.
|
// Decode attempts to deserialize the provided data using either the innate typing of the scheme or the
|
||||||
// TODO: Remove this interface - it is used only in CodecFor() method.
|
// default kind, group, and version provided. It returns a decoded object as well as the kind, group, and
|
||||||
type ObjectCodec interface {
|
// version from the serialized data, or an error. If into is non-nil, it will be used as the target type
|
||||||
Decoder
|
// and implementations may choose to use it rather than reallocating an object. However, the object is not
|
||||||
|
// guaranteed to be populated. The returned object is not guaranteed to match into. If defaults are
|
||||||
// EncodeToVersion convert and serializes an object in the internal format
|
// provided, they are applied to the data by default. If no defaults or partial defaults are provided, the
|
||||||
// to a specified output version. An error is returned if the object
|
// type of the into may be used to guide conversion decisions.
|
||||||
// cannot be converted for any reason.
|
Decode(data []byte, defaults *unversioned.GroupVersionKind, into Object) (Object, *unversioned.GroupVersionKind, error)
|
||||||
EncodeToVersion(obj Object, outVersion string) ([]byte, error)
|
|
||||||
EncodeToVersionStream(obj Object, outVersion string, stream io.Writer) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectDecoder is a convenience interface for identifying serialized versions of objects
|
// Serializer is the core interface for transforming objects into a serialized format and back.
|
||||||
// and transforming them into Objects. It intentionally overlaps with ObjectTyper and
|
// Implementations may choose to perform conversion of the object, but no assumptions should be made.
|
||||||
// Decoder for use in decode only paths.
|
type Serializer interface {
|
||||||
// TODO: Consider removing this interface?
|
Encoder
|
||||||
type ObjectDecoder interface {
|
|
||||||
Decoder
|
Decoder
|
||||||
// DataVersionAndKind returns the group,version,kind of the provided data, or an error
|
}
|
||||||
// if another problem is detected. In many cases this method can be as expensive to
|
|
||||||
// invoke as the Decode method.
|
// Codec is a Serializer that deals with the details of versioning objects. It offers the same
|
||||||
DataKind([]byte) (unversioned.GroupVersionKind, error)
|
// interface as Serializer, so this is a marker to consumers that care about the version of the objects
|
||||||
// Recognizes returns true if the scheme is able to handle the provided group,version,kind
|
// they receive.
|
||||||
// of an object.
|
type Codec Serializer
|
||||||
Recognizes(unversioned.GroupVersionKind) bool
|
|
||||||
|
// ParameterCodec defines methods for serializing and deserializing API objects to url.Values and
|
||||||
|
// performing any necessary conversion. Unlike the normal Codec, query parameters are not self describing
|
||||||
|
// and the desired version must be specified.
|
||||||
|
type ParameterCodec interface {
|
||||||
|
// DecodeParameters takes the given url.Values in the specified group version and decodes them
|
||||||
|
// into the provided object, or returns an error.
|
||||||
|
DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error
|
||||||
|
// EncodeParameters encodes the provided object as query parameters or returns an error.
|
||||||
|
EncodeParameters(obj Object, to unversioned.GroupVersion) (url.Values, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NegotiatedSerializer is an interface used for obtaining encoders, decoders, and serializers
|
||||||
|
// for multiple supported media types.
|
||||||
|
type NegotiatedSerializer interface {
|
||||||
|
SupportedMediaTypes() []string
|
||||||
|
SerializerForMediaType(mediaType string, options map[string]string) (Serializer, bool)
|
||||||
|
EncoderForVersion(serializer Serializer, gv unversioned.GroupVersion) Encoder
|
||||||
|
DecoderToVersion(serializer Serializer, gv unversioned.GroupVersion) Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Non-codec interfaces
|
// Non-codec interfaces
|
||||||
|
|
||||||
|
type ObjectVersioner interface {
|
||||||
|
ConvertToVersion(in Object, outVersion string) (out Object, err error)
|
||||||
|
}
|
||||||
|
|
||||||
// ObjectConvertor converts an object to a different version.
|
// ObjectConvertor converts an object to a different version.
|
||||||
type ObjectConvertor interface {
|
type ObjectConvertor interface {
|
||||||
|
// Convert attempts to convert one object into another, or returns an error. This method does
|
||||||
|
// not guarantee the in object is not mutated.
|
||||||
Convert(in, out interface{}) error
|
Convert(in, out interface{}) error
|
||||||
|
// ConvertToVersion takes the provided object and converts it the provided version. This
|
||||||
|
// method does not guarantee that the in object is not mutated.
|
||||||
ConvertToVersion(in Object, outVersion string) (out Object, err error)
|
ConvertToVersion(in Object, outVersion string) (out Object, err error)
|
||||||
ConvertFieldLabel(version, kind, label, value string) (string, string, error)
|
ConvertFieldLabel(version, kind, label, value string) (string, string, error)
|
||||||
}
|
}
|
||||||
@ -94,10 +108,6 @@ type ObjectConvertor interface {
|
|||||||
// ObjectTyper contains methods for extracting the APIVersion and Kind
|
// ObjectTyper contains methods for extracting the APIVersion and Kind
|
||||||
// of objects.
|
// of objects.
|
||||||
type ObjectTyper interface {
|
type ObjectTyper interface {
|
||||||
// DataKind returns the group,version,kind of the provided data, or an error
|
|
||||||
// if another problem is detected. In many cases this method can be as expensive to
|
|
||||||
// invoke as the Decode method.
|
|
||||||
DataKind([]byte) (unversioned.GroupVersionKind, error)
|
|
||||||
// ObjectKind returns the default group,version,kind of the provided object, or an
|
// ObjectKind returns the default group,version,kind of the provided object, or an
|
||||||
// error if the object is not recognized (IsNotRegisteredError will return true).
|
// error if the object is not recognized (IsNotRegisteredError will return true).
|
||||||
ObjectKind(Object) (unversioned.GroupVersionKind, error)
|
ObjectKind(Object) (unversioned.GroupVersionKind, error)
|
||||||
@ -108,6 +118,10 @@ type ObjectTyper interface {
|
|||||||
// or more precisely that the provided version is a possible conversion or decoding
|
// or more precisely that the provided version is a possible conversion or decoding
|
||||||
// target.
|
// target.
|
||||||
Recognizes(gvk unversioned.GroupVersionKind) bool
|
Recognizes(gvk unversioned.GroupVersionKind) bool
|
||||||
|
// IsUnversioned returns true if the provided object is considered unversioned and thus
|
||||||
|
// should have Version and Group suppressed in the output. If the object is not recognized
|
||||||
|
// in the scheme, ok is false.
|
||||||
|
IsUnversioned(Object) (unversioned bool, ok bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectCreater contains methods for instantiating an object by kind and version.
|
// ObjectCreater contains methods for instantiating an object by kind and version.
|
||||||
|
@ -20,16 +20,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed PluginBase
|
|
||||||
func (obj *PluginBase) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
_, obj.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed PluginBase
|
|
||||||
func (obj *PluginBase) GroupVersionKind() *unversioned.GroupVersionKind {
|
|
||||||
return unversioned.FromAPIVersionAndKind("", obj.Kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
|
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
|
||||||
func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
||||||
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
@ -42,3 +32,33 @@ func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind {
|
|||||||
|
|
||||||
func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
|
|
||||||
|
// GetObjectKind implements Object for VersionedObjects, returning an empty ObjectKind
|
||||||
|
// interface if no objects are provided, or the ObjectKind interface of the object in the
|
||||||
|
// highest array position.
|
||||||
|
func (obj *VersionedObjects) GetObjectKind() unversioned.ObjectKind {
|
||||||
|
last := obj.Last()
|
||||||
|
if last == nil {
|
||||||
|
return unversioned.EmptyObjectKind
|
||||||
|
}
|
||||||
|
return last.GetObjectKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returns the leftmost object in the VersionedObjects array, which is usually the
|
||||||
|
// object as serialized on the wire.
|
||||||
|
func (obj *VersionedObjects) First() Object {
|
||||||
|
if len(obj.Objects) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return obj.Objects[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last is the rightmost object in the VersionedObjects array, which is the object after
|
||||||
|
// all transformations have been applied. This is the same object that would be returned
|
||||||
|
// by Decode in a normal invocation (without VersionedObjects in the into argument).
|
||||||
|
func (obj *VersionedObjects) Last() Object {
|
||||||
|
if len(obj.Objects) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return obj.Objects[len(obj.Objects)-1]
|
||||||
|
}
|
||||||
|
@ -17,9 +17,7 @@ limitations under the License.
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@ -36,9 +34,6 @@ type Scheme struct {
|
|||||||
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
|
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Decoder = &Scheme{}
|
|
||||||
var _ ObjectTyper = &Scheme{}
|
|
||||||
|
|
||||||
// Function to convert a field selector to internal representation.
|
// Function to convert a field selector to internal representation.
|
||||||
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
||||||
|
|
||||||
@ -55,205 +50,11 @@ func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string,
|
|||||||
return inVersion, outVersion, scheme
|
return inVersion, outVersion, scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
// emptyPlugin is used to copy the Kind field to and from plugin objects.
|
|
||||||
type emptyPlugin struct {
|
|
||||||
PluginBase `json:",inline"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// embeddedObjectToRawExtension does the conversion you would expect from the name, using the information
|
|
||||||
// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
|
|
||||||
// see the comment for RawExtension.
|
|
||||||
func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conversion.Scope) error {
|
|
||||||
if in.Object == nil {
|
|
||||||
out.RawJSON = []byte("null")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out the type and kind of the output object.
|
|
||||||
_, outGroupVersionString, scheme := self.fromScope(s)
|
|
||||||
objKind, err := scheme.raw.ObjectKind(in.Object)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manufacture an object of this type and kind.
|
|
||||||
outObj, err := scheme.New(outVersion.WithKind(objKind.Kind))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manually do the conversion.
|
|
||||||
err = s.Convert(in.Object, outObj, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the kind field into the output object.
|
|
||||||
err = s.Convert(
|
|
||||||
&emptyPlugin{PluginBase: PluginBase{Kind: objKind.Kind}},
|
|
||||||
outObj,
|
|
||||||
conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Because we provide the correct version, EncodeToVersion will not attempt a conversion.
|
|
||||||
raw, err := scheme.EncodeToVersion(outObj, outVersion.String())
|
|
||||||
if err != nil {
|
|
||||||
// TODO: if this fails, create an Unknown-- maybe some other
|
|
||||||
// component will understand it.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out.RawJSON = raw
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information
|
|
||||||
// given in conversion.Scope. It's placed in all schemes as a ConversionFunc to enable plugins;
|
|
||||||
// see the comment for RawExtension.
|
|
||||||
func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conversion.Scope) error {
|
|
||||||
if len(in.RawJSON) == 0 || (len(in.RawJSON) == 4 && string(in.RawJSON) == "null") {
|
|
||||||
out.Object = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Figure out the type and kind of the output object.
|
|
||||||
inGroupVersionString, outGroupVersionString, scheme := self.fromScope(s)
|
|
||||||
dataKind, err := scheme.raw.DataKind(in.RawJSON)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
inVersion, err := unversioned.ParseGroupVersion(inGroupVersionString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to make this object ourselves because we don't store the version field for
|
|
||||||
// plugin objects.
|
|
||||||
inObj, err := scheme.New(inVersion.WithKind(dataKind.Kind))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = DecodeInto(scheme, in.RawJSON, inObj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the desired internal version, and do the conversion.
|
|
||||||
outObj, err := scheme.New(outVersion.WithKind(dataKind.Kind))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = scheme.Convert(inObj, outObj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Last step, clear the Kind field; that should always be blank in memory.
|
|
||||||
err = s.Convert(
|
|
||||||
&emptyPlugin{PluginBase: PluginBase{Kind: ""}},
|
|
||||||
outObj,
|
|
||||||
conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
out.Object = outObj
|
|
||||||
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 than the default, you
|
|
||||||
// should encode them yourself with runtime.Unknown, or convert the object prior to invoking conversion. Objects
|
|
||||||
// outside of the current scheme must be added as runtime.Unknown.
|
|
||||||
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:
|
|
||||||
// TODO: this should be decoupled from the scheme (since it is JSON specific)
|
|
||||||
dest[i].RawJSON = t.RawJSON
|
|
||||||
case *Unstructured:
|
|
||||||
// TODO: this should be decoupled from the scheme (since it is JSON specific)
|
|
||||||
data, err := json.Marshal(t.Object)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dest[i].RawJSON = data
|
|
||||||
default:
|
|
||||||
version := outVersion
|
|
||||||
// if the object exists
|
|
||||||
// this code is try to set the outputVersion, but only if the object has a non-internal group version
|
|
||||||
if inGVK, err := scheme.ObjectKind(src[i]); err == nil && !inGVK.GroupVersion().IsEmpty() {
|
|
||||||
if self.raw.InternalVersions[inGVK.Group] != inGVK.GroupVersion() {
|
|
||||||
version = inGVK.GroupVersion().String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data, err := scheme.EncodeToVersion(src[i], version)
|
|
||||||
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
|
|
||||||
dataKind, err := scheme.raw.DataKind(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dest[i] = &Unknown{
|
|
||||||
TypeMeta: TypeMeta{
|
|
||||||
APIVersion: dataKind.GroupVersion().String(),
|
|
||||||
Kind: dataKind.Kind,
|
|
||||||
},
|
|
||||||
RawJSON: data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*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(internalGroupVersions ...unversioned.GroupVersion) *Scheme {
|
func NewScheme() *Scheme {
|
||||||
s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}}
|
s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}}
|
||||||
|
s.AddConversionFuncs(DefaultEmbeddedConversions()...)
|
||||||
|
|
||||||
for _, internalGV := range internalGroupVersions {
|
|
||||||
s.raw.InternalVersions[internalGV.Group] = internalGV
|
|
||||||
}
|
|
||||||
|
|
||||||
s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"}
|
|
||||||
if err := s.raw.AddConversionFuncs(
|
|
||||||
s.embeddedObjectToRawExtension,
|
|
||||||
s.rawExtensionToEmbeddedObject,
|
|
||||||
s.runtimeObjectToRawExtensionArray,
|
|
||||||
s.rawExtensionToRuntimeObjectArray,
|
|
||||||
); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Enable map[string][]string conversions by default
|
// Enable map[string][]string conversions by default
|
||||||
if err := s.raw.AddConversionFuncs(DefaultStringConversions...); err != nil {
|
if err := s.raw.AddConversionFuncs(DefaultStringConversions...); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -267,14 +68,22 @@ func NewScheme(internalGroupVersions ...unversioned.GroupVersion) *Scheme {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddInternalGroupVersion registers an internal GroupVersion with the scheme. This can later be
|
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
|
||||||
// used to lookup the internal GroupVersion for a given Group
|
// Whenever an object of this type is serialized, it is serialized with the provided group version and is not
|
||||||
func (s *Scheme) AddInternalGroupVersion(gv unversioned.GroupVersion) {
|
// converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an
|
||||||
s.raw.InternalVersions[gv.Group] = gv
|
// API group and version that would never be updated.
|
||||||
|
//
|
||||||
|
// TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into
|
||||||
|
// every version with particular schemas. Resolve tihs method at that point.
|
||||||
|
func (s *Scheme) AddUnversionedTypes(gv unversioned.GroupVersion, types ...Object) {
|
||||||
|
interfaces := make([]interface{}, len(types))
|
||||||
|
for i := range types {
|
||||||
|
interfaces[i] = types[i]
|
||||||
|
}
|
||||||
|
s.raw.AddUnversionedTypes(gv, interfaces...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddKnownTypes registers the types of the arguments to the marshaller of the package api.
|
// AddKnownTypes registers the types of the arguments to the marshaller of the package api.
|
||||||
// Encode() refuses the object unless its type is registered with AddKnownTypes.
|
|
||||||
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
|
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
|
||||||
interfaces := make([]interface{}, len(types))
|
interfaces := make([]interface{}, len(types))
|
||||||
for i := range types {
|
for i := range types {
|
||||||
@ -283,6 +92,12 @@ func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
|
|||||||
s.raw.AddKnownTypes(gv, interfaces...)
|
s.raw.AddKnownTypes(gv, interfaces...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddIgnoredConversionType declares a particular conversion that should be ignored - during conversion
|
||||||
|
// this method is not invoked.
|
||||||
|
func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
|
||||||
|
return s.raw.AddIgnoredConversionType(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
// AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
|
// AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
|
||||||
// be encoded as. Useful for testing when you don't want to make multiple packages to define
|
// be encoded as. Useful for testing when you don't want to make multiple packages to define
|
||||||
// your structs.
|
// your structs.
|
||||||
@ -296,12 +111,6 @@ func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type
|
|||||||
return s.raw.KnownTypes(gv)
|
return s.raw.KnownTypes(gv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataKind will return the group,version,kind of the given wire-format
|
|
||||||
// encoding of an API Object, or an error.
|
|
||||||
func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) {
|
|
||||||
return s.raw.DataKind(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectKind returns the default group,version,kind of the given Object.
|
// ObjectKind returns the default group,version,kind of the given Object.
|
||||||
func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) {
|
func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) {
|
||||||
return s.raw.ObjectKind(obj)
|
return s.raw.ObjectKind(obj)
|
||||||
@ -318,7 +127,12 @@ func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
|||||||
return s.raw.Recognizes(gvk)
|
return s.raw.Recognizes(gvk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new API object of the given kind, or an error if it hasn't been registered.
|
func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
|
||||||
|
return s.raw.IsUnversioned(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new API object of the given version ("" for internal
|
||||||
|
// representation) and name, or an error if it hasn't been registered.
|
||||||
func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
||||||
obj, err := s.raw.NewObject(kind)
|
obj, err := s.raw.NewObject(kind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -394,11 +208,31 @@ func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
|
|||||||
return s.raw.AddDefaultingFuncs(defaultingFuncs...)
|
return s.raw.AddDefaultingFuncs(defaultingFuncs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy does a deep copy of an API object.
|
||||||
|
func (s *Scheme) Copy(src Object) (Object, error) {
|
||||||
|
dst, err := s.raw.DeepCopy(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dst.(Object), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Performs a deep copy of the given object.
|
// Performs a deep copy of the given object.
|
||||||
func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) {
|
func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) {
|
||||||
return s.raw.DeepCopy(src)
|
return s.raw.DeepCopy(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithConversions returns an ObjectConvertor that has the additional conversion functions
|
||||||
|
// defined in fns. The current scheme is not altered.
|
||||||
|
func (s *Scheme) WithConversions(fns *conversion.ConversionFuncs) ObjectConvertor {
|
||||||
|
if fns == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
copied := *s
|
||||||
|
copied.raw = s.raw.WithConversions(*fns)
|
||||||
|
return &copied
|
||||||
|
}
|
||||||
|
|
||||||
// Convert will attempt to convert in into out. Both must be pointers.
|
// Convert will attempt to convert in into out. Both must be pointers.
|
||||||
// For easy testing of conversion functions. Returns an error if the conversion isn't
|
// For easy testing of conversion functions. Returns an error if the conversion isn't
|
||||||
// possible.
|
// possible.
|
||||||
@ -423,8 +257,19 @@ func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string,
|
|||||||
// version within this scheme. Will return an error if the provided version does not
|
// version within this scheme. Will return an error if the provided version does not
|
||||||
// contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also
|
// contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also
|
||||||
// return an error if the conversion does not result in a valid Object being
|
// return an error if the conversion does not result in a valid Object being
|
||||||
// returned.
|
// returned. The serializer handles loading/serializing nested objects.
|
||||||
func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) {
|
func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) {
|
||||||
|
gv, err := unversioned.ParseGroupVersion(outVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch in.(type) {
|
||||||
|
case *Unknown, *Unstructured:
|
||||||
|
old := in.GetObjectKind().GroupVersionKind()
|
||||||
|
defer in.GetObjectKind().SetGroupVersionKind(old)
|
||||||
|
setTargetVersion(in, s.raw, gv)
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
unknown, err := s.raw.ConvertToVersion(in, outVersion)
|
unknown, err := s.raw.ConvertToVersion(in, outVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -433,105 +278,16 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error)
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown)
|
return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown)
|
||||||
}
|
}
|
||||||
|
setTargetVersion(obj, s.raw, gv)
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeToVersion turns the given api object into an appropriate JSON string.
|
func setTargetVersion(obj Object, raw *conversion.Scheme, gv unversioned.GroupVersion) {
|
||||||
// Will return an error if the object doesn't have an embedded TypeMeta.
|
if gv.Version == APIVersionInternal {
|
||||||
// Obj may be a pointer to a struct, or a struct. If a struct, a copy
|
// internal is a special case
|
||||||
// must be made. If a pointer, the object may be modified before encoding,
|
obj.GetObjectKind().SetGroupVersionKind(nil)
|
||||||
// but will be put back into its original state before returning.
|
} else {
|
||||||
//
|
gvk, _ := raw.ObjectKind(obj)
|
||||||
// Memory/wire format differences:
|
obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind})
|
||||||
// * Having to keep track of the Kind and APIVersion fields makes tests
|
|
||||||
// very annoying, so the rule is that they are set only in wire format
|
|
||||||
// (json), not when in native (memory) format. This is possible because
|
|
||||||
// both pieces of information are implicit in the go typed object.
|
|
||||||
// * An exception: note that, if there are embedded API objects of known
|
|
||||||
// type, for example, PodList{... Items []Pod ...}, these embedded
|
|
||||||
// objects must be of the same version of the object they are embedded
|
|
||||||
// within, and their APIVersion and Kind must both be empty.
|
|
||||||
// * Note that the exception does not apply to the APIObject type, which
|
|
||||||
// recursively does Encode()/Decode(), and is capable of expressing any
|
|
||||||
// API object.
|
|
||||||
// * Only versioned objects should be encoded. This means that, if you pass
|
|
||||||
// a native object, Encode will convert it to a versioned object. For
|
|
||||||
// example, an api.Pod will get converted to a v1.Pod. However, if
|
|
||||||
// you pass in an object that's already versioned (v1.Pod), Encode
|
|
||||||
// will not modify it.
|
|
||||||
//
|
|
||||||
// The purpose of the above complex conversion behavior is to allow us to
|
|
||||||
// change the memory format yet not break compatibility with any stored
|
|
||||||
// objects, whether they be in our storage layer (e.g., etcd), or in user's
|
|
||||||
// config files.
|
|
||||||
func (s *Scheme) EncodeToVersion(obj Object, destVersion string) (data []byte, err error) {
|
|
||||||
return s.raw.EncodeToVersion(obj, destVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Scheme) EncodeToVersionStream(obj Object, destVersion string, stream io.Writer) error {
|
|
||||||
return s.raw.EncodeToVersionStream(obj, destVersion, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode converts a YAML or JSON string back into a pointer to an api object.
|
|
||||||
// Deduces the type based upon the APIVersion and Kind fields, which are set
|
|
||||||
// by Encode. Only versioned objects (APIVersion != "") are accepted. The object
|
|
||||||
// will be converted into the in-memory unversioned type before being returned.
|
|
||||||
func (s *Scheme) Decode(data []byte) (Object, error) {
|
|
||||||
obj, err := s.raw.Decode(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return obj.(Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeToVersion converts a YAML or JSON string back into a pointer to an api
|
|
||||||
// object. Deduces the type based upon the APIVersion and Kind fields, which
|
|
||||||
// are set by Encode. Only versioned objects (APIVersion != "") are
|
|
||||||
// accepted. The object will be converted into the in-memory versioned type
|
|
||||||
// requested before being returned.
|
|
||||||
func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) {
|
|
||||||
obj, err := s.raw.DecodeToVersion(data, gv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.(Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeInto parses a YAML or JSON string and stores it in obj. Returns an error
|
|
||||||
// if data.Kind is set and doesn't match the type of obj. Obj should be a
|
|
||||||
// pointer to an api type.
|
|
||||||
// If obj's APIVersion doesn't match that in data, an attempt will be made to convert
|
|
||||||
// data into obj's version.
|
|
||||||
// TODO: allow Decode/DecodeInto to take a default apiVersion and a default kind, to
|
|
||||||
// be applied if the provided object does not have either field (integrate external
|
|
||||||
// apis into the decoding scheme).
|
|
||||||
func (s *Scheme) DecodeInto(data []byte, obj Object) error {
|
|
||||||
return s.raw.DecodeInto(data, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeIntoWithSpecifiedVersionKind coerces the data into the obj, assuming that the data is
|
|
||||||
// of type GroupVersionKind
|
|
||||||
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error {
|
|
||||||
return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, gvk)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Scheme) DecodeParametersInto(parameters url.Values, obj Object) error {
|
|
||||||
return s.raw.DecodeParametersInto(parameters, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy does a deep copy of an API object. Useful mostly for tests.
|
|
||||||
func (s *Scheme) Copy(src Object) (Object, error) {
|
|
||||||
dst, err := s.raw.DeepCopy(src)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dst.(Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Scheme) CopyOrDie(obj Object) Object {
|
|
||||||
newObj, err := s.Copy(obj)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return newObj
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime/serializer"
|
||||||
|
"k8s.io/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TypeMeta struct {
|
type TypeMeta struct {
|
||||||
@ -51,23 +53,19 @@ type ExternalSimple struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *InternalSimple) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *ExternalSimple) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScheme(t *testing.T) {
|
func TestScheme(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
scheme.AddInternalGroupVersion(internalGV)
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||||
|
|
||||||
|
// If set, would clear TypeMeta during conversion.
|
||||||
|
//scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{})
|
||||||
|
|
||||||
// test that scheme is an ObjectTyper
|
// test that scheme is an ObjectTyper
|
||||||
var _ runtime.ObjectTyper = scheme
|
var _ runtime.ObjectTyper = scheme
|
||||||
|
|
||||||
@ -102,21 +100,27 @@ func TestScheme(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
|
codec := codecs.LegacyCodec(externalGV)
|
||||||
|
jsonserializer, _ := codecs.SerializerForFileExtension("json")
|
||||||
|
|
||||||
simple := &InternalSimple{
|
simple := &InternalSimple{
|
||||||
TestString: "foo",
|
TestString: "foo",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test Encode, Decode, DecodeInto, and DecodeToVersion
|
// Test Encode, Decode, DecodeInto, and DecodeToVersion
|
||||||
obj := runtime.Object(simple)
|
obj := runtime.Object(simple)
|
||||||
data, err := scheme.EncodeToVersion(obj, externalGV.String())
|
data, err := runtime.Encode(codec, obj)
|
||||||
obj2, err2 := runtime.Decode(scheme, data)
|
if err != nil {
|
||||||
obj3 := &InternalSimple{}
|
t.Fatal(err)
|
||||||
err3 := runtime.DecodeInto(scheme, data, obj3)
|
}
|
||||||
obj4, err4 := scheme.DecodeToVersion(data, externalGV)
|
|
||||||
if err != nil || err2 != nil || err3 != nil || err4 != nil {
|
obj2, err := runtime.Decode(codec, data)
|
||||||
t.Fatalf("Failure: '%v' '%v' '%v' '%v'", err, err2, err3, err4)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, ok := obj2.(*InternalSimple); !ok {
|
if _, ok := obj2.(*InternalSimple); !ok {
|
||||||
t.Fatalf("Got wrong type")
|
t.Fatalf("Got wrong type")
|
||||||
@ -124,9 +128,22 @@ func TestScheme(t *testing.T) {
|
|||||||
if e, a := simple, obj2; !reflect.DeepEqual(e, a) {
|
if e, a := simple, obj2; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj3 := &InternalSimple{}
|
||||||
|
if err := runtime.DecodeInto(codec, data, obj3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
|
||||||
|
// does not automatically clear TypeMeta anymore).
|
||||||
|
simple.TypeMeta = TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
|
||||||
if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
|
if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj4, err := runtime.Decode(jsonserializer, data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
if _, ok := obj4.(*ExternalSimple); !ok {
|
if _, ok := obj4.(*ExternalSimple); !ok {
|
||||||
t.Fatalf("Got wrong type")
|
t.Fatalf("Got wrong type")
|
||||||
}
|
}
|
||||||
@ -135,7 +152,7 @@ func TestScheme(t *testing.T) {
|
|||||||
external := &ExternalSimple{}
|
external := &ExternalSimple{}
|
||||||
err = scheme.Convert(simple, external)
|
err = scheme.Convert(simple, external)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if e, a := simple.TestString, external.TestString; e != a {
|
if e, a := simple.TestString, external.TestString; e != a {
|
||||||
t.Errorf("Expected %v, got %v", e, a)
|
t.Errorf("Expected %v, got %v", e, a)
|
||||||
@ -145,36 +162,23 @@ func TestScheme(t *testing.T) {
|
|||||||
if e, a := 2, internalToExternalCalls; e != a {
|
if e, a := 2, internalToExternalCalls; e != a {
|
||||||
t.Errorf("Expected %v, got %v", e, a)
|
t.Errorf("Expected %v, got %v", e, a)
|
||||||
}
|
}
|
||||||
// Decode and DecodeInto should each have caused an increment.
|
// DecodeInto and Decode should each have caused an increment because of a conversion
|
||||||
if e, a := 2, externalToInternalCalls; e != a {
|
if e, a := 2, externalToInternalCalls; e != a {
|
||||||
t.Errorf("Expected %v, got %v", e, a)
|
t.Errorf("Expected %v, got %v", e, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidObjectValueKind(t *testing.T) {
|
|
||||||
internalGV := unversioned.GroupVersion{Group: "", Version: ""}
|
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
|
||||||
|
|
||||||
embedded := &runtime.EmbeddedObject{}
|
|
||||||
switch obj := embedded.Object.(type) {
|
|
||||||
default:
|
|
||||||
_, err := scheme.ObjectKind(obj)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected error on invalid kind")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadJSONRejection(t *testing.T) {
|
func TestBadJSONRejection(t *testing.T) {
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
|
jsonserializer, _ := codecs.SerializerForFileExtension("json")
|
||||||
|
|
||||||
badJSONMissingKind := []byte(`{ }`)
|
badJSONMissingKind := []byte(`{ }`)
|
||||||
if _, err := runtime.Decode(scheme, badJSONMissingKind); err == nil {
|
if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
|
||||||
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
|
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
|
||||||
}
|
}
|
||||||
badJSONUnknownType := []byte(`{"kind": "bar"}`)
|
badJSONUnknownType := []byte(`{"kind": "bar"}`)
|
||||||
if _, err1 := runtime.Decode(scheme, badJSONUnknownType); err1 == nil {
|
if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil {
|
||||||
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
|
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
|
||||||
}
|
}
|
||||||
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
|
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
|
||||||
@ -184,13 +188,13 @@ func TestBadJSONRejection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ExtensionA struct {
|
type ExtensionA struct {
|
||||||
runtime.PluginBase `json:",inline"`
|
TypeMeta `json:",inline"`
|
||||||
TestString string `json:"testString"`
|
TestString string `json:"testString"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtensionB struct {
|
type ExtensionB struct {
|
||||||
runtime.PluginBase `json:",inline"`
|
TypeMeta `json:",inline"`
|
||||||
TestString string `json:"testString"`
|
TestString string `json:"testString"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalExtensionType struct {
|
type ExternalExtensionType struct {
|
||||||
@ -200,7 +204,7 @@ type ExternalExtensionType struct {
|
|||||||
|
|
||||||
type InternalExtensionType struct {
|
type InternalExtensionType struct {
|
||||||
TypeMeta `json:",inline"`
|
TypeMeta `json:",inline"`
|
||||||
Extension runtime.EmbeddedObject `json:"extension"`
|
Extension runtime.Object `json:"extension"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalOptionalExtensionType struct {
|
type ExternalOptionalExtensionType struct {
|
||||||
@ -210,143 +214,135 @@ type ExternalOptionalExtensionType struct {
|
|||||||
|
|
||||||
type InternalOptionalExtensionType struct {
|
type InternalOptionalExtensionType struct {
|
||||||
TypeMeta `json:",inline"`
|
TypeMeta `json:",inline"`
|
||||||
Extension runtime.EmbeddedObject `json:"extension,omitempty"`
|
Extension runtime.Object `json:"extension,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.PluginBase }
|
func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *ExtensionA) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
_, obj.PluginBase.Kind = gvk.ToAPIVersionAndKind()
|
func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
}
|
func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.PluginBase }
|
|
||||||
func (obj *ExtensionB) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
_, obj.PluginBase.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
|
||||||
func (obj *ExternalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
|
||||||
func (obj *InternalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *ExternalOptionalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
func (obj *InternalOptionalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
|
||||||
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExternalToInternalMapping(t *testing.T) {
|
func TestExternalToInternalMapping(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
scheme.AddInternalGroupVersion(internalGV)
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
|
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
|
||||||
|
|
||||||
|
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
obj runtime.Object
|
obj runtime.Object
|
||||||
encoded string
|
encoded string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
&InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}},
|
&InternalOptionalExtensionType{Extension: nil},
|
||||||
`{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
|
`{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range table {
|
for i, item := range table {
|
||||||
gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded))
|
gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
|
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
|
||||||
} else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
|
} else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
|
||||||
var eEx, aEx runtime.Object
|
t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a))
|
||||||
if obj, ok := e.(*InternalOptionalExtensionType); ok {
|
|
||||||
eEx = obj.Extension.Object
|
|
||||||
}
|
|
||||||
if obj, ok := a.(*InternalOptionalExtensionType); ok {
|
|
||||||
aEx = obj.Extension.Object
|
|
||||||
}
|
|
||||||
t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtensionMapping(t *testing.T) {
|
func TestExtensionMapping(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
scheme.AddInternalGroupVersion(internalGV)
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{})
|
scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{})
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
|
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{})
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{})
|
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{})
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
|
||||||
|
|
||||||
|
// register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the
|
||||||
|
// external version.
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{})
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{})
|
||||||
|
scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{})
|
||||||
|
scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{})
|
||||||
|
|
||||||
|
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
obj runtime.Object
|
obj runtime.Object
|
||||||
encoded string
|
expected runtime.Object
|
||||||
|
encoded string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
&InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionA{TestString: "foo"}}},
|
&InternalExtensionType{
|
||||||
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","testString":"foo"}}
|
Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}),
|
||||||
|
},
|
||||||
|
&InternalExtensionType{
|
||||||
|
Extension: &runtime.Unknown{
|
||||||
|
RawJSON: []byte(`{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// apiVersion is set in the serialized object for easier consumption by clients
|
||||||
|
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}}
|
||||||
`,
|
`,
|
||||||
}, {
|
}, {
|
||||||
&InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionB{TestString: "bar"}}},
|
&InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})},
|
||||||
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","testString":"bar"}}
|
&InternalExtensionType{
|
||||||
|
Extension: &runtime.Unknown{
|
||||||
|
RawJSON: []byte(`{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// apiVersion is set in the serialized object for easier consumption by clients
|
||||||
|
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}}
|
||||||
`,
|
`,
|
||||||
}, {
|
}, {
|
||||||
&InternalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}},
|
&InternalExtensionType{Extension: nil},
|
||||||
|
&InternalExtensionType{
|
||||||
|
Extension: nil,
|
||||||
|
},
|
||||||
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null}
|
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range table {
|
for i, item := range table {
|
||||||
gotEncoded, err := scheme.EncodeToVersion(item.obj, externalGV.String())
|
gotEncoded, err := runtime.Encode(codec, item.obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
|
t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
|
||||||
} else if e, a := item.encoded, string(gotEncoded); e != a {
|
} else if e, a := item.encoded, string(gotEncoded); e != a {
|
||||||
t.Errorf("expected\n%#v\ngot\n%#v\n", e, a)
|
t.Errorf("expected\n%#v\ngot\n%#v\n", e, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded))
|
gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
|
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
|
||||||
} else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
|
} else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) {
|
||||||
var eEx, aEx runtime.Object
|
t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a))
|
||||||
if obj, ok := e.(*InternalExtensionType); ok {
|
|
||||||
eEx = obj.Extension.Object
|
|
||||||
}
|
|
||||||
if obj, ok := a.(*InternalExtensionType); ok {
|
|
||||||
aEx = obj.Extension.Object
|
|
||||||
}
|
|
||||||
t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncode(t *testing.T) {
|
func TestEncode(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
scheme.AddInternalGroupVersion(internalGV)
|
|
||||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||||
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||||
codec := runtime.CodecFor(scheme, externalGV)
|
|
||||||
|
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||||
|
|
||||||
test := &InternalSimple{
|
test := &InternalSimple{
|
||||||
TestString: "I'm the same",
|
TestString: "I'm the same",
|
||||||
}
|
}
|
||||||
obj := runtime.Object(test)
|
obj := runtime.Object(test)
|
||||||
data, err := runtime.Encode(codec, obj)
|
data, err := runtime.Encode(codec, obj)
|
||||||
obj2, err2 := runtime.Decode(codec, data)
|
obj2, gvk, err2 := codec.Decode(data, nil, nil)
|
||||||
if err != nil || err2 != nil {
|
if err != nil || err2 != nil {
|
||||||
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
||||||
}
|
}
|
||||||
@ -354,6 +350,68 @@ func TestEncode(t *testing.T) {
|
|||||||
t.Fatalf("Got wrong type")
|
t.Fatalf("Got wrong type")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(obj2, test) {
|
if !reflect.DeepEqual(obj2, test) {
|
||||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &test, obj2)
|
t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) {
|
||||||
|
t.Errorf("unexpected gvk returned by decode: %#v", gvk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnversionedTypes(t *testing.T) {
|
||||||
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||||
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||||
|
otherGV := unversioned.GroupVersion{Group: "group", Version: "other"}
|
||||||
|
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
scheme.AddUnversionedTypes(externalGV, &InternalSimple{})
|
||||||
|
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||||
|
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||||
|
scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{})
|
||||||
|
|
||||||
|
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||||
|
|
||||||
|
if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok {
|
||||||
|
t.Fatal("type not unversioned and in scheme: %t %t", unv, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
kind, err := scheme.ObjectKind(&InternalSimple{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if kind != externalGV.WithKind("InternalSimple") {
|
||||||
|
t.Fatalf("unexpected: %#v", kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
test := &InternalSimple{
|
||||||
|
TestString: "I'm the same",
|
||||||
|
}
|
||||||
|
obj := runtime.Object(test)
|
||||||
|
data, err := runtime.Encode(codec, obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
obj2, gvk, err := codec.Decode(data, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, ok := obj2.(*InternalSimple); !ok {
|
||||||
|
t.Fatalf("Got wrong type")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(obj2, test) {
|
||||||
|
t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
|
||||||
|
}
|
||||||
|
// object is serialized as an unversioned object (in the group and version it was defined in)
|
||||||
|
if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) {
|
||||||
|
t.Errorf("unexpected gvk returned by decode: %#v", gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// when serialized to a different group, the object is kept in its preferred name
|
||||||
|
codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV)
|
||||||
|
data, err = runtime.Encode(codec, obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(data) != `{"kind":"InternalSimple","apiVersion":"test.group/testExternal","testString":"I'm the same"}`+"\n" {
|
||||||
|
t.Errorf("unexpected data: %s", data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,39 +36,18 @@ type TypeMeta struct {
|
|||||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded
|
// RawExtension is used to hold extensions in external versions.
|
||||||
// except while embedded in other objects.
|
|
||||||
type PluginBase struct {
|
|
||||||
Kind string `json:"kind,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's
|
|
||||||
// stored as a []byte, but in memory, the contained object is accessible as an Object
|
|
||||||
// via the Get() function. Only valid API objects may be stored via EmbeddedObject.
|
|
||||||
// The purpose of this is to allow an API object of type known only at runtime to be
|
|
||||||
// embedded within other API objects.
|
|
||||||
//
|
|
||||||
// Note that object assumes that you've registered all of your api types with the api package.
|
|
||||||
//
|
|
||||||
// EmbeddedObject and RawExtension can be used together to allow for API object extensions:
|
|
||||||
// see the comment for RawExtension.
|
|
||||||
type EmbeddedObject struct {
|
|
||||||
Object
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawExtension is used with EmbeddedObject to do a two-phase encoding of extension objects.
|
|
||||||
//
|
//
|
||||||
// To use this, make a field which has RawExtension as its type in your external, versioned
|
// To use this, make a field which has RawExtension as its type in your external, versioned
|
||||||
// struct, and EmbeddedObject in your internal struct. You also need to register your
|
// struct, and Object in your internal struct. You also need to register your
|
||||||
// various plugin types.
|
// various plugin types.
|
||||||
//
|
//
|
||||||
// // Internal package:
|
// // Internal package:
|
||||||
// type MyAPIObject struct {
|
// type MyAPIObject struct {
|
||||||
// runtime.TypeMeta `json:",inline"`
|
// runtime.TypeMeta `json:",inline"`
|
||||||
// MyPlugin runtime.EmbeddedObject `json:"myPlugin"`
|
// MyPlugin runtime.Object `json:"myPlugin"`
|
||||||
// }
|
// }
|
||||||
// type PluginA struct {
|
// type PluginA struct {
|
||||||
// runtime.PluginBase `json:",inline"`
|
|
||||||
// AOption string `json:"aOption"`
|
// AOption string `json:"aOption"`
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@ -78,7 +57,6 @@ type EmbeddedObject struct {
|
|||||||
// MyPlugin runtime.RawExtension `json:"myPlugin"`
|
// MyPlugin runtime.RawExtension `json:"myPlugin"`
|
||||||
// }
|
// }
|
||||||
// type PluginA struct {
|
// type PluginA struct {
|
||||||
// runtime.PluginBase `json:",inline"`
|
|
||||||
// AOption string `json:"aOption"`
|
// AOption string `json:"aOption"`
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@ -97,12 +75,16 @@ type EmbeddedObject struct {
|
|||||||
// The next step is to copy (using pkg/conversion) into the internal struct. The runtime
|
// The next step is to copy (using pkg/conversion) into the internal struct. The runtime
|
||||||
// package's DefaultScheme has conversion functions installed which will unpack the
|
// package's DefaultScheme has conversion functions installed which will unpack the
|
||||||
// JSON stored in RawExtension, turning it into the correct object type, and storing it
|
// JSON stored in RawExtension, turning it into the correct object type, and storing it
|
||||||
// in the EmbeddedObject. (TODO: In the case where the object is of an unknown type, a
|
// in the Object. (TODO: In the case where the object is of an unknown type, a
|
||||||
// runtime.Unknown object will be created and stored.)
|
// runtime.Unknown object will be created and stored.)
|
||||||
//
|
//
|
||||||
// +protobuf=true
|
// +protobuf=true
|
||||||
type RawExtension struct {
|
type RawExtension struct {
|
||||||
|
// RawJSON is the underlying serialization of this object.
|
||||||
RawJSON []byte
|
RawJSON []byte
|
||||||
|
// Object can hold a representation of this extension - useful for working with versioned
|
||||||
|
// structs.
|
||||||
|
Object Object `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown allows api objects with unknown types to be passed-through. This can be used
|
// Unknown allows api objects with unknown types to be passed-through. This can be used
|
||||||
@ -131,3 +113,13 @@ type Unstructured struct {
|
|||||||
// children.
|
// children.
|
||||||
Object map[string]interface{}
|
Object map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VersionedObjects is used by Decoders to give callers a way to access all versions
|
||||||
|
// of an object during the decoding process.
|
||||||
|
type VersionedObjects struct {
|
||||||
|
// Objects is the set of objects retrieved during decoding, in order of conversion.
|
||||||
|
// The 0 index is the object as serialized on the wire. If conversion has occured,
|
||||||
|
// other objects may be present. The right most object is the same as would be returned
|
||||||
|
// by a normal Decode call.
|
||||||
|
Objects []Object
|
||||||
|
}
|
||||||
|
@ -18,9 +18,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"io"
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
@ -28,36 +26,19 @@ import (
|
|||||||
|
|
||||||
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
|
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
|
||||||
// type, which can be used for generic access to objects without a predefined scheme.
|
// type, which can be used for generic access to objects without a predefined scheme.
|
||||||
var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{}
|
// TODO: move into serializer/json.
|
||||||
|
var UnstructuredJSONScheme Decoder = unstructuredJSONScheme{}
|
||||||
|
|
||||||
type unstructuredJSONScheme struct{}
|
type unstructuredJSONScheme struct{}
|
||||||
|
|
||||||
var _ Decoder = unstructuredJSONScheme{}
|
var _ Codec = unstructuredJSONScheme{}
|
||||||
var _ ObjectDecoder = unstructuredJSONScheme{}
|
|
||||||
|
|
||||||
// Recognizes returns true for any version or kind that is specified (internal
|
func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionKind, _ Object) (Object, *unversioned.GroupVersionKind, error) {
|
||||||
// versions are specifically excluded).
|
|
||||||
func (unstructuredJSONScheme) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
|
||||||
return !gvk.GroupVersion().IsEmpty() && len(gvk.Kind) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s unstructuredJSONScheme) Decode(data []byte) (Object, error) {
|
|
||||||
unstruct := &Unstructured{}
|
unstruct := &Unstructured{}
|
||||||
if err := DecodeInto(s, data, unstruct); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return unstruct, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error {
|
|
||||||
unstruct, ok := obj.(*Unstructured)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("the unstructured JSON scheme does not recognize %v", reflect.TypeOf(obj))
|
|
||||||
}
|
|
||||||
|
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
if err := json.Unmarshal(data, &m); err != nil {
|
if err := json.Unmarshal(data, &m); err != nil {
|
||||||
return err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if v, ok := m["kind"]; ok {
|
if v, ok := m["kind"]; ok {
|
||||||
if s, ok := v.(string); ok {
|
if s, ok := v.(string); ok {
|
||||||
@ -69,44 +50,30 @@ func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error {
|
|||||||
unstruct.APIVersion = s
|
unstruct.APIVersion = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(unstruct.APIVersion) == 0 {
|
if len(unstruct.APIVersion) == 0 {
|
||||||
return conversion.NewMissingVersionErr(string(data))
|
return nil, nil, conversion.NewMissingVersionErr(string(data))
|
||||||
}
|
}
|
||||||
|
gv, err := unversioned.ParseGroupVersion(unstruct.APIVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
gvk := gv.WithKind(unstruct.Kind)
|
||||||
if len(unstruct.Kind) == 0 {
|
if len(unstruct.Kind) == 0 {
|
||||||
return conversion.NewMissingKindErr(string(data))
|
return nil, &gvk, conversion.NewMissingKindErr(string(data))
|
||||||
}
|
}
|
||||||
unstruct.Object = m
|
unstruct.Object = m
|
||||||
return nil
|
return unstruct, &gvk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error {
|
func (s unstructuredJSONScheme) EncodeToStream(obj Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
|
||||||
return nil
|
switch t := obj.(type) {
|
||||||
}
|
case *Unstructured:
|
||||||
|
return json.NewEncoder(w).Encode(t.Object)
|
||||||
func (unstructuredJSONScheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) {
|
case *Unknown:
|
||||||
return nil, nil
|
_, err := w.Write(t.RawJSON)
|
||||||
}
|
return err
|
||||||
|
default:
|
||||||
func (unstructuredJSONScheme) DecodeParametersInto(paramaters url.Values, obj Object) error {
|
return json.NewEncoder(w).Encode(t)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (unstructuredJSONScheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) {
|
|
||||||
obj := TypeMeta{}
|
|
||||||
if err := json.Unmarshal(data, &obj); err != nil {
|
|
||||||
return unversioned.GroupVersionKind{}, err
|
|
||||||
}
|
}
|
||||||
if len(obj.APIVersion) == 0 {
|
|
||||||
return unversioned.GroupVersionKind{}, conversion.NewMissingVersionErr(string(data))
|
|
||||||
}
|
|
||||||
if len(obj.Kind) == 0 {
|
|
||||||
return unversioned.GroupVersionKind{}, conversion.NewMissingKindErr(string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
gv, err := unversioned.ParseGroupVersion(obj.APIVersion)
|
|
||||||
if err != nil {
|
|
||||||
return unversioned.GroupVersionKind{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return gv.WithKind(obj.Kind), nil
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func TestDecodeUnstructured(t *testing.T) {
|
|||||||
if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
|
if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
|
||||||
t.Errorf("object not converted: %#v", pl.Items[1])
|
t.Errorf("object not converted: %#v", pl.Items[1])
|
||||||
}
|
}
|
||||||
if _, ok := pl.Items[2].(*runtime.Unknown); !ok {
|
if pod, ok := pl.Items[2].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
|
||||||
t.Errorf("object should not have been converted: %#v", pl.Items[2])
|
t.Errorf("object not converted: %#v", pl.Items[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user