git mv Ingress ingress
This commit is contained in:
parent
34b949c134
commit
3da4e74e5a
2185 changed files with 754743 additions and 0 deletions
171
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/codec.go
generated
vendored
Normal file
171
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/codec.go
generated
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"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
|
||||
func Encode(e Encoder, obj Object, overrides ...unversioned.GroupVersion) ([]byte, error) {
|
||||
// TODO: reuse buffer
|
||||
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.
|
||||
func Decode(d Decoder, data []byte) (Object, error) {
|
||||
obj, _, err := d.Decode(data, nil, nil)
|
||||
return obj, err
|
||||
}
|
||||
|
||||
// DecodeInto performs a Decode into the provided object.
|
||||
func DecodeInto(d Decoder, data []byte, into Object) error {
|
||||
out, gvk, err := d.Decode(data, nil, into)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if out != into {
|
||||
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.
|
||||
func EncodeOrDie(e Encoder, obj Object) string {
|
||||
bytes, err := Encode(e, obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or
|
||||
// invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object.
|
||||
func UseOrCreateObject(t Typer, c ObjectCreater, gvk unversioned.GroupVersionKind, obj Object) (Object, error) {
|
||||
if obj != nil {
|
||||
into, _, err := t.ObjectKind(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gvk == *into {
|
||||
return obj, nil
|
||||
}
|
||||
}
|
||||
return c.New(gvk)
|
||||
}
|
||||
|
||||
// NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding.
|
||||
type NoopEncoder struct {
|
||||
Decoder
|
||||
}
|
||||
|
||||
var _ Serializer = NoopEncoder{}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
|
||||
type NoopDecoder struct {
|
||||
Encoder
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
98
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion.go
generated
vendored
Normal file
98
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Defines conversions between generic types and structs to map query strings
|
||||
// to struct objects.
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
)
|
||||
|
||||
// JSONKeyMapper uses the struct tags on a conversion to determine the key value for
|
||||
// the other side. Use when mapping from a map[string]* to a struct or vice versa.
|
||||
func JSONKeyMapper(key string, sourceTag, destTag reflect.StructTag) (string, string) {
|
||||
if s := destTag.Get("json"); len(s) > 0 {
|
||||
return strings.SplitN(s, ",", 2)[0], key
|
||||
}
|
||||
if s := sourceTag.Get("json"); len(s) > 0 {
|
||||
return key, strings.SplitN(s, ",", 2)[0]
|
||||
}
|
||||
return key, key
|
||||
}
|
||||
|
||||
// DefaultStringConversions are helpers for converting []string and string to real values.
|
||||
var DefaultStringConversions = []interface{}{
|
||||
convertStringSliceToString,
|
||||
convertStringSliceToInt,
|
||||
convertStringSliceToBool,
|
||||
convertStringSliceToInt64,
|
||||
}
|
||||
|
||||
func convertStringSliceToString(input *[]string, out *string, s conversion.Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = ""
|
||||
}
|
||||
*out = (*input)[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertStringSliceToInt(input *[]string, out *int, s conversion.Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = 0
|
||||
}
|
||||
str := (*input)[0]
|
||||
i, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*out = i
|
||||
return nil
|
||||
}
|
||||
|
||||
// converStringSliceToBool will convert a string parameter to boolean.
|
||||
// Only the absence of a value, a value of "false", or a value of "0" resolve to false.
|
||||
// Any other value (including empty string) resolves to true.
|
||||
func convertStringSliceToBool(input *[]string, out *bool, s conversion.Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = false
|
||||
return nil
|
||||
}
|
||||
switch strings.ToLower((*input)[0]) {
|
||||
case "false", "0":
|
||||
*out = false
|
||||
default:
|
||||
*out = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertStringSliceToInt64(input *[]string, out *int64, s conversion.Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = 0
|
||||
}
|
||||
str := (*input)[0]
|
||||
i, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*out = i
|
||||
return nil
|
||||
}
|
||||
913
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_generator.go
generated
vendored
Normal file
913
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_generator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"path"
|
||||
"reflect"
|
||||
goruntime "runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
type ConversionGenerator interface {
|
||||
GenerateConversionsForType(groupVersion unversioned.GroupVersion, reflection reflect.Type) error
|
||||
WriteConversionFunctions(w io.Writer) error
|
||||
RegisterConversionFunctions(w io.Writer, pkg string) error
|
||||
AddImport(pkg string) string
|
||||
RepackImports(exclude sets.String)
|
||||
WriteImports(w io.Writer) error
|
||||
OverwritePackage(pkg, overwrite string)
|
||||
AssumePrivateConversions()
|
||||
}
|
||||
|
||||
func NewConversionGenerator(scheme *Scheme, targetPkg string) ConversionGenerator {
|
||||
g := &conversionGenerator{
|
||||
scheme: scheme,
|
||||
|
||||
nameFormat: "Convert_%s_%s_To_%s_%s",
|
||||
generatedNamePrefix: "auto",
|
||||
targetPkg: targetPkg,
|
||||
|
||||
publicFuncs: make(map[typePair]functionName),
|
||||
convertibles: make(map[reflect.Type]reflect.Type),
|
||||
overridden: make(map[reflect.Type]bool),
|
||||
pkgOverwrites: make(map[string]string),
|
||||
imports: make(map[string]string),
|
||||
shortImports: make(map[string]string),
|
||||
}
|
||||
g.targetPackage(targetPkg)
|
||||
g.AddImport("reflect")
|
||||
g.AddImport("k8s.io/kubernetes/pkg/conversion")
|
||||
return g
|
||||
}
|
||||
|
||||
var complexTypes []reflect.Kind = []reflect.Kind{reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct}
|
||||
|
||||
type functionName struct {
|
||||
name string
|
||||
packageName string
|
||||
}
|
||||
|
||||
type conversionGenerator struct {
|
||||
scheme *Scheme
|
||||
|
||||
nameFormat string
|
||||
generatedNamePrefix string
|
||||
targetPkg string
|
||||
|
||||
publicFuncs map[typePair]functionName
|
||||
convertibles map[reflect.Type]reflect.Type
|
||||
overridden map[reflect.Type]bool
|
||||
// If pkgOverwrites is set for a given package name, that package name
|
||||
// will be replaced while writing conversion function. If empty, package
|
||||
// name will be omitted.
|
||||
pkgOverwrites map[string]string
|
||||
// map of package names to shortname
|
||||
imports map[string]string
|
||||
// map of short names to package names
|
||||
shortImports map[string]string
|
||||
|
||||
// A buffer that is used for storing lines that needs to be written.
|
||||
linesToPrint []string
|
||||
|
||||
// if true, we assume conversions on the scheme are not available to us in the current package
|
||||
assumePrivateConversions bool
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) AssumePrivateConversions() {
|
||||
g.assumePrivateConversions = true
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) AddImport(pkg string) string {
|
||||
return g.addImportByPath(pkg)
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVersion, reflection reflect.Type) error {
|
||||
kind := reflection.Name()
|
||||
// TODO this is equivalent to what it did before, but it needs to be fixed for the proper group
|
||||
internalVersion := gv
|
||||
internalVersion.Version = APIVersionInternal
|
||||
|
||||
internalObj, err := g.scheme.New(internalVersion.WithKind(kind))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create an object of type %v in internal version", kind)
|
||||
}
|
||||
internalObjType := reflect.TypeOf(internalObj)
|
||||
if internalObjType.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("created object should be of type Ptr: %v", internalObjType.Kind())
|
||||
}
|
||||
inErr := g.generateConversionsBetween(reflection, internalObjType.Elem())
|
||||
outErr := g.generateConversionsBetween(internalObjType.Elem(), reflection)
|
||||
if inErr != nil || outErr != nil {
|
||||
return fmt.Errorf("errors: %v, %v", inErr, outErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// primitiveConversion returns true if the two types can be converted via a cast.
|
||||
func primitiveConversion(inType, outType reflect.Type) (string, bool) {
|
||||
switch inType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
switch outType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
return outType.Name(), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) generateConversionsBetween(inType, outType reflect.Type) error {
|
||||
existingConversion := g.scheme.Converter().HasConversionFunc(inType, outType) && g.scheme.Converter().HasConversionFunc(outType, inType)
|
||||
|
||||
// Avoid processing the same type multiple times.
|
||||
if value, found := g.convertibles[inType]; found {
|
||||
if value != outType {
|
||||
return fmt.Errorf("multiple possible convertibles for %v", inType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if inType == outType {
|
||||
switch inType.Kind() {
|
||||
case reflect.Ptr:
|
||||
return g.generateConversionsBetween(inType.Elem(), inType.Elem())
|
||||
case reflect.Struct:
|
||||
// pointers to structs invoke new(inType)
|
||||
g.addImportByPath(inType.PkgPath())
|
||||
}
|
||||
g.rememberConversionFunction(inType, inType, false)
|
||||
// Don't generate conversion methods for the same type.
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, ok := primitiveConversion(inType, outType); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if inType.Kind() != outType.Kind() {
|
||||
if existingConversion {
|
||||
g.rememberConversionFunction(inType, outType, false)
|
||||
g.rememberConversionFunction(outType, inType, false)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("cannot convert types of different kinds: %v %v", inType, outType)
|
||||
}
|
||||
|
||||
g.addImportByPath(inType.PkgPath())
|
||||
g.addImportByPath(outType.PkgPath())
|
||||
|
||||
// We should be able to generate conversions both sides.
|
||||
switch inType.Kind() {
|
||||
case reflect.Map:
|
||||
inErr := g.generateConversionsForMap(inType, outType)
|
||||
outErr := g.generateConversionsForMap(outType, inType)
|
||||
if !existingConversion && (inErr != nil || outErr != nil) {
|
||||
return inErr
|
||||
}
|
||||
// We don't add it to g.convertibles - maps should be handled correctly
|
||||
// inside appropriate conversion functions.
|
||||
return nil
|
||||
case reflect.Ptr:
|
||||
inErr := g.generateConversionsBetween(inType.Elem(), outType.Elem())
|
||||
outErr := g.generateConversionsBetween(outType.Elem(), inType.Elem())
|
||||
if !existingConversion && (inErr != nil || outErr != nil) {
|
||||
return inErr
|
||||
}
|
||||
// We don't add it to g.convertibles - maps should be handled correctly
|
||||
// inside appropriate conversion functions.
|
||||
return nil
|
||||
case reflect.Slice:
|
||||
inErr := g.generateConversionsForSlice(inType, outType)
|
||||
outErr := g.generateConversionsForSlice(outType, inType)
|
||||
if !existingConversion && (inErr != nil || outErr != nil) {
|
||||
return inErr
|
||||
}
|
||||
// We don't add it to g.convertibles - slices should be handled correctly
|
||||
// inside appropriate conversion functions.
|
||||
return nil
|
||||
case reflect.Interface:
|
||||
// TODO(wojtek-t): Currently we don't support converting interfaces.
|
||||
return fmt.Errorf("interfaces are not supported")
|
||||
case reflect.Struct:
|
||||
inErr := g.generateConversionsForStruct(inType, outType)
|
||||
outErr := g.generateConversionsForStruct(outType, inType)
|
||||
if !existingConversion && (inErr != nil || outErr != nil) {
|
||||
return inErr
|
||||
}
|
||||
g.rememberConversionFunction(inType, outType, true)
|
||||
if existingConversion {
|
||||
g.overridden[inType] = true
|
||||
}
|
||||
g.convertibles[inType] = outType
|
||||
return nil
|
||||
default:
|
||||
// All simple types should be handled correctly with default conversion.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func isComplexType(reflection reflect.Type) bool {
|
||||
for _, complexType := range complexTypes {
|
||||
if complexType == reflection.Kind() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) rememberConversionFunction(inType, outType reflect.Type, willGenerate bool) {
|
||||
if _, ok := g.publicFuncs[typePair{inType, outType}]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
if v, ok := g.scheme.Converter().ConversionFuncValue(inType, outType); ok {
|
||||
if fn := goruntime.FuncForPC(v.Pointer()); fn != nil {
|
||||
name := fn.Name()
|
||||
var p, n string
|
||||
if last := strings.LastIndex(name, "."); last != -1 {
|
||||
p = name[:last]
|
||||
n = name[last+1:]
|
||||
} else {
|
||||
n = name
|
||||
}
|
||||
if isPublic(n) {
|
||||
g.AddImport(p)
|
||||
g.publicFuncs[typePair{inType, outType}] = functionName{name: n, packageName: p}
|
||||
} else {
|
||||
log.Printf("WARNING: Cannot generate conversion %v -> %v, method %q is private", inType, outType, fn.Name())
|
||||
}
|
||||
} else {
|
||||
log.Printf("WARNING: Cannot generate conversion %v -> %v, method is not accessible", inType, outType)
|
||||
}
|
||||
} else if willGenerate {
|
||||
g.publicFuncs[typePair{inType, outType}] = functionName{name: g.conversionFunctionName(inType, outType)}
|
||||
}
|
||||
}
|
||||
|
||||
func isPublic(name string) bool {
|
||||
return strings.ToUpper(name[:1]) == name[:1]
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) generateConversionsForMap(inType, outType reflect.Type) error {
|
||||
inKey := inType.Key()
|
||||
outKey := outType.Key()
|
||||
g.addImportByPath(inKey.PkgPath())
|
||||
g.addImportByPath(outKey.PkgPath())
|
||||
if err := g.generateConversionsBetween(inKey, outKey); err != nil {
|
||||
return err
|
||||
}
|
||||
inValue := inType.Elem()
|
||||
outValue := outType.Elem()
|
||||
g.addImportByPath(inValue.PkgPath())
|
||||
g.addImportByPath(outValue.PkgPath())
|
||||
if err := g.generateConversionsBetween(inValue, outValue); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) generateConversionsForSlice(inType, outType reflect.Type) error {
|
||||
inElem := inType.Elem()
|
||||
outElem := outType.Elem()
|
||||
// slice conversion requires the package for the destination type in order to instantiate the map
|
||||
g.addImportByPath(outElem.PkgPath())
|
||||
if err := g.generateConversionsBetween(inElem, outElem); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) generateConversionsForStruct(inType, outType reflect.Type) error {
|
||||
for i := 0; i < inType.NumField(); i++ {
|
||||
inField := inType.Field(i)
|
||||
outField, found := outType.FieldByName(inField.Name)
|
||||
if !found {
|
||||
return fmt.Errorf("couldn't find a corresponding field %v in %v", inField.Name, outType)
|
||||
}
|
||||
if isComplexType(inField.Type) {
|
||||
if err := g.generateConversionsBetween(inField.Type, outField.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A buffer of lines that will be written.
|
||||
type bufferedLine struct {
|
||||
line string
|
||||
indentation int
|
||||
}
|
||||
|
||||
type buffer struct {
|
||||
lines []bufferedLine
|
||||
}
|
||||
|
||||
func newBuffer() *buffer {
|
||||
return &buffer{
|
||||
lines: make([]bufferedLine, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buffer) addLine(line string, indent int) {
|
||||
b.lines = append(b.lines, bufferedLine{line, indent})
|
||||
}
|
||||
|
||||
func (b *buffer) flushLines(w io.Writer) error {
|
||||
for _, line := range b.lines {
|
||||
indentation := strings.Repeat("\t", line.indentation)
|
||||
fullLine := fmt.Sprintf("%s%s", indentation, line.line)
|
||||
if _, err := io.WriteString(w, fullLine); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type byName []reflect.Type
|
||||
|
||||
func (s byName) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s byName) Less(i, j int) bool {
|
||||
fullNameI := s[i].PkgPath() + "/" + s[i].Name()
|
||||
fullNameJ := s[j].PkgPath() + "/" + s[j].Name()
|
||||
return fullNameI < fullNameJ
|
||||
}
|
||||
|
||||
func (s byName) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) targetPackage(pkg string) {
|
||||
g.imports[pkg] = ""
|
||||
g.shortImports[""] = pkg
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) RepackImports(exclude sets.String) {
|
||||
var packages []string
|
||||
for key := range g.imports {
|
||||
packages = append(packages, key)
|
||||
}
|
||||
sort.Strings(packages)
|
||||
g.imports = make(map[string]string)
|
||||
g.shortImports = make(map[string]string)
|
||||
g.targetPackage(g.targetPkg)
|
||||
for _, pkg := range packages {
|
||||
if !exclude.Has(pkg) {
|
||||
g.addImportByPath(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) WriteImports(w io.Writer) error {
|
||||
var packages []string
|
||||
for key := range g.imports {
|
||||
packages = append(packages, key)
|
||||
}
|
||||
sort.Strings(packages)
|
||||
|
||||
buffer := newBuffer()
|
||||
indent := 0
|
||||
buffer.addLine("import (\n", indent)
|
||||
for _, importPkg := range packages {
|
||||
if len(importPkg) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(g.imports[importPkg]) == 0 {
|
||||
continue
|
||||
}
|
||||
buffer.addLine(fmt.Sprintf("%s \"%s\"\n", g.imports[importPkg], importPkg), indent+1)
|
||||
}
|
||||
buffer.addLine(")\n", indent)
|
||||
buffer.addLine("\n", indent)
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) WriteConversionFunctions(w io.Writer) error {
|
||||
// It's desired to print conversion functions always in the same order
|
||||
// (e.g. for better tracking of what has really been added).
|
||||
var keys []reflect.Type
|
||||
for key := range g.convertibles {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Sort(byName(keys))
|
||||
|
||||
buffer := newBuffer()
|
||||
indent := 0
|
||||
for _, inType := range keys {
|
||||
outType := g.convertibles[inType]
|
||||
// All types in g.convertibles are structs.
|
||||
if inType.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("non-struct conversions are not-supported")
|
||||
}
|
||||
if err := g.writeConversionForType(buffer, inType, outType, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeRegisterHeader(b *buffer, pkg string, indent int) {
|
||||
b.addLine("func init() {\n", indent)
|
||||
b.addLine(fmt.Sprintf("err := %s.AddGeneratedConversionFuncs(\n", pkg), indent+1)
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeRegisterFooter(b *buffer, indent int) {
|
||||
b.addLine(")\n", indent+1)
|
||||
b.addLine("if err != nil {\n", indent+1)
|
||||
b.addLine("// If one of the conversion functions is malformed, detect it immediately.\n", indent+2)
|
||||
b.addLine("panic(err)\n", indent+2)
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
b.addLine("\n", indent)
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) RegisterConversionFunctions(w io.Writer, pkg string) error {
|
||||
// Write conversion function names alphabetically ordered.
|
||||
var names []string
|
||||
for inType, outType := range g.convertibles {
|
||||
names = append(names, g.generatedFunctionName(inType, outType))
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
buffer := newBuffer()
|
||||
indent := 0
|
||||
g.writeRegisterHeader(buffer, pkg, indent)
|
||||
for _, name := range names {
|
||||
buffer.addLine(fmt.Sprintf("%s,\n", name), indent+2)
|
||||
}
|
||||
g.writeRegisterFooter(buffer, indent)
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) addImportByPath(pkg string) string {
|
||||
if name, ok := g.imports[pkg]; ok {
|
||||
return name
|
||||
}
|
||||
name := path.Base(pkg)
|
||||
if _, ok := g.shortImports[name]; !ok {
|
||||
g.imports[pkg] = name
|
||||
g.shortImports[name] = pkg
|
||||
return name
|
||||
}
|
||||
if dirname := path.Base(path.Dir(pkg)); len(dirname) > 0 {
|
||||
name = dirname + name
|
||||
if _, ok := g.shortImports[name]; !ok {
|
||||
g.imports[pkg] = name
|
||||
g.shortImports[name] = pkg
|
||||
return name
|
||||
}
|
||||
if subdirname := path.Base(path.Dir(path.Dir(pkg))); len(subdirname) > 0 {
|
||||
name = subdirname + name
|
||||
if _, ok := g.shortImports[name]; !ok {
|
||||
g.imports[pkg] = name
|
||||
g.shortImports[name] = pkg
|
||||
return name
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 2; i < 100; i++ {
|
||||
generatedName := fmt.Sprintf("%s%d", name, i)
|
||||
if _, ok := g.shortImports[generatedName]; !ok {
|
||||
g.imports[pkg] = generatedName
|
||||
g.shortImports[generatedName] = pkg
|
||||
return generatedName
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("unable to find a unique name for the package path %q: %v", pkg, g.shortImports))
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) typeName(inType reflect.Type) string {
|
||||
switch inType.Kind() {
|
||||
case reflect.Slice:
|
||||
return fmt.Sprintf("[]%s", g.typeName(inType.Elem()))
|
||||
case reflect.Ptr:
|
||||
return fmt.Sprintf("*%s", g.typeName(inType.Elem()))
|
||||
case reflect.Map:
|
||||
if len(inType.Name()) == 0 {
|
||||
return fmt.Sprintf("map[%s]%s", g.typeName(inType.Key()), g.typeName(inType.Elem()))
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
pkg, name := inType.PkgPath(), inType.Name()
|
||||
if len(name) == 0 && inType.Kind() == reflect.Struct {
|
||||
return "struct{}"
|
||||
}
|
||||
if len(pkg) == 0 {
|
||||
// Default package.
|
||||
return name
|
||||
}
|
||||
if val, found := g.pkgOverwrites[pkg]; found {
|
||||
pkg = val
|
||||
}
|
||||
if len(pkg) == 0 {
|
||||
return name
|
||||
}
|
||||
short := g.addImportByPath(pkg)
|
||||
if len(short) > 0 {
|
||||
return fmt.Sprintf("%s.%s", short, name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeDefaultingFunc(b *buffer, inType reflect.Type, indent int) error {
|
||||
getStmt := "if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {\n"
|
||||
b.addLine(getStmt, indent)
|
||||
callFormat := "defaulting.(func(*%s))(in)\n"
|
||||
callStmt := fmt.Sprintf(callFormat, g.typeName(inType))
|
||||
b.addLine(callStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func packageForName(inType reflect.Type) string {
|
||||
if inType.PkgPath() == "" {
|
||||
return ""
|
||||
}
|
||||
slices := strings.Split(inType.PkgPath(), "/")
|
||||
return slices[len(slices)-1]
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) conversionFunctionName(inType, outType reflect.Type) string {
|
||||
funcNameFormat := g.nameFormat
|
||||
inPkg := packageForName(inType)
|
||||
outPkg := packageForName(outType)
|
||||
funcName := fmt.Sprintf(funcNameFormat, inPkg, inType.Name(), outPkg, outType.Name())
|
||||
return funcName
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) conversionFunctionCall(inType, outType reflect.Type, scopeName string, args ...string) string {
|
||||
if named, ok := g.publicFuncs[typePair{inType, outType}]; ok {
|
||||
args[len(args)-1] = scopeName
|
||||
name := named.name
|
||||
localPackageName, ok := g.imports[named.packageName]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("have not defined an import for %s", named.packageName))
|
||||
}
|
||||
if len(named.packageName) > 0 && len(localPackageName) > 0 {
|
||||
name = localPackageName + "." + name
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", name, strings.Join(args, ", "))
|
||||
}
|
||||
log.Printf("WARNING: Using reflection to convert %v -> %v (no public conversion)", inType, outType)
|
||||
return fmt.Sprintf("%s.Convert(%s)", scopeName, strings.Join(args, ", "))
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) generatedFunctionName(inType, outType reflect.Type) string {
|
||||
return g.generatedNamePrefix + g.conversionFunctionName(inType, outType)
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeHeader(b *buffer, name, inType, outType string, indent int) {
|
||||
format := "func %s(in *%s, out *%s, s conversion.Scope) error {\n"
|
||||
stmt := fmt.Sprintf(format, name, inType, outType)
|
||||
b.addLine(stmt, indent)
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeFooter(b *buffer, indent int) {
|
||||
b.addLine("return nil\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeConversionForMap(b *buffer, inField, outField reflect.StructField, indent int) error {
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
makeFormat := "out.%s = make(%s)\n"
|
||||
makeStmt := fmt.Sprintf(makeFormat, outField.Name, g.typeName(outField.Type))
|
||||
b.addLine(makeStmt, indent+1)
|
||||
forFormat := "for key, val := range in.%s {\n"
|
||||
forStmt := fmt.Sprintf(forFormat, inField.Name)
|
||||
b.addLine(forStmt, indent+1)
|
||||
|
||||
// Whether we need to explicitly create a new value.
|
||||
newValue := false
|
||||
if isComplexType(inField.Type.Elem()) || !inField.Type.Elem().ConvertibleTo(outField.Type.Elem()) {
|
||||
newValue = true
|
||||
newFormat := "newVal := %s{}\n"
|
||||
newStmt := fmt.Sprintf(newFormat, g.typeName(outField.Type.Elem()))
|
||||
b.addLine(newStmt, indent+2)
|
||||
call := g.conversionFunctionCall(inField.Type.Elem(), outField.Type.Elem(), "s", "&val", "&newVal", "0")
|
||||
convertStmt := fmt.Sprintf("if err := %s; err != nil {\n", call)
|
||||
b.addLine(convertStmt, indent+2)
|
||||
b.addLine("return err\n", indent+3)
|
||||
b.addLine("}\n", indent+2)
|
||||
}
|
||||
if inField.Type.Key().ConvertibleTo(outField.Type.Key()) {
|
||||
value := "val"
|
||||
if newValue {
|
||||
value = "newVal"
|
||||
}
|
||||
assignStmt := ""
|
||||
if inField.Type.Key().AssignableTo(outField.Type.Key()) {
|
||||
assignStmt = fmt.Sprintf("out.%s[key] = %s\n", outField.Name, value)
|
||||
} else {
|
||||
assignStmt = fmt.Sprintf("out.%s[%s(key)] = %s\n", outField.Name, g.typeName(outField.Type.Key()), value)
|
||||
}
|
||||
b.addLine(assignStmt, indent+2)
|
||||
} else {
|
||||
// TODO(wojtek-t): Support maps with keys that are non-convertible to each other.
|
||||
return fmt.Errorf("conversions between unconvertible keys in map are not supported.")
|
||||
}
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
nilFormat := "out.%s = nil\n"
|
||||
nilStmt := fmt.Sprintf(nilFormat, outField.Name)
|
||||
b.addLine(nilStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeConversionForSlice(b *buffer, inField, outField reflect.StructField, indent int) error {
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
makeFormat := "out.%s = make(%s, len(in.%s))\n"
|
||||
makeStmt := fmt.Sprintf(makeFormat, outField.Name, g.typeName(outField.Type), inField.Name)
|
||||
b.addLine(makeStmt, indent+1)
|
||||
forFormat := "for i := range in.%s {\n"
|
||||
forStmt := fmt.Sprintf(forFormat, inField.Name)
|
||||
b.addLine(forStmt, indent+1)
|
||||
|
||||
assigned := false
|
||||
switch inField.Type.Elem().Kind() {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
// Don't copy these via assignment/conversion!
|
||||
default:
|
||||
// This should handle all simple types.
|
||||
if inField.Type.Elem().AssignableTo(outField.Type.Elem()) {
|
||||
assignFormat := "out.%s[i] = in.%s[i]\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, outField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
assigned = true
|
||||
} else if inField.Type.Elem().ConvertibleTo(outField.Type.Elem()) {
|
||||
assignFormat := "out.%s[i] = %s(in.%s[i])\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, outField.Name, g.typeName(outField.Type.Elem()), inField.Name)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
assigned = true
|
||||
}
|
||||
}
|
||||
if !assigned {
|
||||
call := g.conversionFunctionCall(inField.Type.Elem(), outField.Type.Elem(), "s", "&in."+inField.Name+"[i]", "&out."+outField.Name+"[i]", "0")
|
||||
assignStmt := fmt.Sprintf("if err := %s; err != nil {\n", call)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
b.addLine("return err\n", indent+3)
|
||||
b.addLine("}\n", indent+2)
|
||||
}
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
nilFormat := "out.%s = nil\n"
|
||||
nilStmt := fmt.Sprintf(nilFormat, outField.Name)
|
||||
b.addLine(nilStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeConversionForPtr(b *buffer, inField, outField reflect.StructField, indent int) error {
|
||||
switch inField.Type.Elem().Kind() {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
// Don't copy these via assignment/conversion!
|
||||
default:
|
||||
// This should handle pointers to all simple types.
|
||||
assignable := inField.Type.Elem().AssignableTo(outField.Type.Elem())
|
||||
convertible := inField.Type.Elem().ConvertibleTo(outField.Type.Elem())
|
||||
if assignable || convertible {
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
newFormat := "out.%s = new(%s)\n"
|
||||
newStmt := fmt.Sprintf(newFormat, outField.Name, g.typeName(outField.Type.Elem()))
|
||||
b.addLine(newStmt, indent+1)
|
||||
}
|
||||
if assignable {
|
||||
assignFormat := "*out.%s = *in.%s\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, outField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent+1)
|
||||
} else if convertible {
|
||||
assignFormat := "*out.%s = %s(*in.%s)\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, outField.Name, g.typeName(outField.Type.Elem()), inField.Name)
|
||||
b.addLine(assignStmt, indent+1)
|
||||
}
|
||||
if assignable || convertible {
|
||||
b.addLine("} else {\n", indent)
|
||||
nilFormat := "out.%s = nil\n"
|
||||
nilStmt := fmt.Sprintf(nilFormat, outField.Name)
|
||||
b.addLine(nilStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
b.addLine(fmt.Sprintf("// unable to generate simple pointer conversion for %v -> %v\n", inField.Type.Elem(), outField.Type.Elem()), indent)
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
assignStmt := ""
|
||||
if _, ok := g.publicFuncs[typePair{inField.Type.Elem(), outField.Type.Elem()}]; ok {
|
||||
newFormat := "out.%s = new(%s)\n"
|
||||
newStmt := fmt.Sprintf(newFormat, outField.Name, g.typeName(outField.Type.Elem()))
|
||||
b.addLine(newStmt, indent+1)
|
||||
call := g.conversionFunctionCall(inField.Type.Elem(), outField.Type.Elem(), "s", "in."+inField.Name, "out."+outField.Name, "0")
|
||||
assignStmt = fmt.Sprintf("if err := %s; err != nil {\n", call)
|
||||
} else {
|
||||
call := g.conversionFunctionCall(inField.Type.Elem(), outField.Type.Elem(), "s", "&in."+inField.Name, "&out."+outField.Name, "0")
|
||||
assignStmt = fmt.Sprintf("if err := %s; err != nil {\n", call)
|
||||
}
|
||||
b.addLine(assignStmt, indent+1)
|
||||
b.addLine("return err\n", indent+2)
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
nilFormat := "out.%s = nil\n"
|
||||
nilStmt := fmt.Sprintf(nilFormat, outField.Name)
|
||||
b.addLine(nilStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) canTryConversion(b *buffer, inType reflect.Type, inField, outField reflect.StructField, indent int) (bool, error) {
|
||||
if inField.Type.Kind() != outField.Type.Kind() {
|
||||
if !g.overridden[inType] {
|
||||
return false, fmt.Errorf("input %s.%s (%s) does not match output (%s) and conversion is not overridden", inType, inField.Name, inField.Type.Kind(), outField.Type.Kind())
|
||||
}
|
||||
b.addLine(fmt.Sprintf("// in.%s has no peer in out\n", inField.Name), indent)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outType reflect.Type, indent int) error {
|
||||
for i := 0; i < inType.NumField(); i++ {
|
||||
inField := inType.Field(i)
|
||||
outField, found := outType.FieldByName(inField.Name)
|
||||
if !found {
|
||||
if !g.overridden[inType] {
|
||||
return fmt.Errorf("input %s.%s has no peer in output %s and conversion is not overridden", inType, inField.Name, outType)
|
||||
}
|
||||
b.addLine(fmt.Sprintf("// in.%s has no peer in out\n", inField.Name), indent)
|
||||
continue
|
||||
}
|
||||
|
||||
if g.scheme.Converter().IsConversionIgnored(inField.Type, outField.Type) {
|
||||
continue
|
||||
}
|
||||
|
||||
existsConversion := g.scheme.Converter().HasConversionFunc(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
|
||||
// conversion for the field, even though that is technically slower. We should report when
|
||||
// we generate an inefficient conversion.
|
||||
if existsConversion || hasPublicConversion {
|
||||
// Use the conversion method that is already defined.
|
||||
call := g.conversionFunctionCall(inField.Type, outField.Type, "s", "&in."+inField.Name, "&out."+outField.Name, "0")
|
||||
assignStmt := fmt.Sprintf("if err := %s; err != nil {\n", call)
|
||||
b.addLine(assignStmt, indent)
|
||||
b.addLine("return err\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
continue
|
||||
}
|
||||
|
||||
switch inField.Type.Kind() {
|
||||
case reflect.Map:
|
||||
if try, err := g.canTryConversion(b, inType, inField, outField, indent); err != nil {
|
||||
return err
|
||||
} else if !try {
|
||||
continue
|
||||
}
|
||||
if err := g.writeConversionForMap(b, inField, outField, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
case reflect.Ptr:
|
||||
if try, err := g.canTryConversion(b, inType, inField, outField, indent); err != nil {
|
||||
return err
|
||||
} else if !try {
|
||||
continue
|
||||
}
|
||||
if err := g.writeConversionForPtr(b, inField, outField, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
case reflect.Slice:
|
||||
if try, err := g.canTryConversion(b, inType, inField, outField, indent); err != nil {
|
||||
return err
|
||||
} else if !try {
|
||||
continue
|
||||
}
|
||||
if err := g.writeConversionForSlice(b, inField, outField, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
case reflect.Interface, reflect.Struct:
|
||||
// Don't copy these via assignment/conversion!
|
||||
default:
|
||||
// This should handle all simple types.
|
||||
if inField.Type.AssignableTo(outField.Type) {
|
||||
assignFormat := "out.%s = in.%s\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, outField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent)
|
||||
continue
|
||||
}
|
||||
if inField.Type.ConvertibleTo(outField.Type) {
|
||||
assignFormat := "out.%s = %s(in.%s)\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, outField.Name, g.typeName(outField.Type), inField.Name)
|
||||
b.addLine(assignStmt, indent)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
call := g.conversionFunctionCall(inField.Type, outField.Type, "s", "&in."+inField.Name, "&out."+outField.Name, "0")
|
||||
assignStmt := fmt.Sprintf("if err := %s; err != nil {\n", call)
|
||||
b.addLine(assignStmt, indent)
|
||||
b.addLine("return err\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) writeConversionForType(b *buffer, inType, outType reflect.Type, indent int) error {
|
||||
// Always emit the auto-generated name.
|
||||
autoFuncName := g.generatedFunctionName(inType, outType)
|
||||
g.writeHeader(b, autoFuncName, g.typeName(inType), g.typeName(outType), indent)
|
||||
if err := g.writeDefaultingFunc(b, inType, indent+1); err != nil {
|
||||
return err
|
||||
}
|
||||
switch inType.Kind() {
|
||||
case reflect.Struct:
|
||||
if err := g.writeConversionForStruct(b, inType, outType, indent+1); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("type not supported: %v", inType)
|
||||
}
|
||||
g.writeFooter(b, indent)
|
||||
b.addLine("\n", 0)
|
||||
|
||||
if !g.overridden[inType] {
|
||||
// Also emit the "user-facing" name.
|
||||
userFuncName := g.conversionFunctionName(inType, outType)
|
||||
g.writeHeader(b, userFuncName, g.typeName(inType), g.typeName(outType), indent)
|
||||
b.addLine(fmt.Sprintf("return %s(in, out, s)\n", autoFuncName), indent+1)
|
||||
b.addLine("}\n\n", 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *conversionGenerator) existsConversionFunction(inType, outType reflect.Type) bool {
|
||||
if val, found := g.convertibles[inType]; found && val == outType {
|
||||
return true
|
||||
}
|
||||
if val, found := g.convertibles[outType]; found && val == inType {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(wojtek-t): We should somehow change the conversion methods registered under:
|
||||
// pkg/runtime/scheme.go to implement the naming convention for conversion functions
|
||||
// and get rid of this hack.
|
||||
type typePair struct {
|
||||
inType reflect.Type
|
||||
outType reflect.Type
|
||||
}
|
||||
|
||||
var defaultConversions []typePair = []typePair{}
|
||||
|
||||
func (g *conversionGenerator) OverwritePackage(pkg, overwrite string) {
|
||||
g.pkgOverwrites[pkg] = overwrite
|
||||
}
|
||||
135
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_test.go
generated
vendored
Normal file
135
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
type InternalComplex struct {
|
||||
runtime.TypeMeta
|
||||
String string
|
||||
Integer int
|
||||
Integer64 int64
|
||||
Int64 int64
|
||||
Bool bool
|
||||
}
|
||||
|
||||
type ExternalComplex struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
String string `json:"string" description:"testing"`
|
||||
Integer int `json:"int"`
|
||||
Integer64 int64 `json:",omitempty"`
|
||||
Int64 int64
|
||||
Bool bool `json:"bool"`
|
||||
}
|
||||
|
||||
func (obj *InternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *ExternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
func TestStringMapConversion(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.Log(t)
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{})
|
||||
scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{})
|
||||
|
||||
testCases := map[string]struct {
|
||||
input map[string][]string
|
||||
errFn func(error) bool
|
||||
expected runtime.Object
|
||||
}{
|
||||
"ignores omitempty": {
|
||||
input: map[string][]string{
|
||||
"String": {"not_used"},
|
||||
"string": {"value"},
|
||||
"int": {"1"},
|
||||
"Integer64": {"2"},
|
||||
},
|
||||
expected: &ExternalComplex{String: "value", Integer: 1},
|
||||
},
|
||||
"returns error on bad int": {
|
||||
input: map[string][]string{
|
||||
"int": {"a"},
|
||||
},
|
||||
errFn: func(err error) bool { return err != nil },
|
||||
expected: &ExternalComplex{},
|
||||
},
|
||||
"parses int64": {
|
||||
input: map[string][]string{
|
||||
"Int64": {"-1"},
|
||||
},
|
||||
expected: &ExternalComplex{Int64: -1},
|
||||
},
|
||||
"returns error on bad int64": {
|
||||
input: map[string][]string{
|
||||
"Int64": {"a"},
|
||||
},
|
||||
errFn: func(err error) bool { return err != nil },
|
||||
expected: &ExternalComplex{},
|
||||
},
|
||||
"parses boolean true": {
|
||||
input: map[string][]string{
|
||||
"bool": {"true"},
|
||||
},
|
||||
expected: &ExternalComplex{Bool: true},
|
||||
},
|
||||
"parses boolean any value": {
|
||||
input: map[string][]string{
|
||||
"bool": {"foo"},
|
||||
},
|
||||
expected: &ExternalComplex{Bool: true},
|
||||
},
|
||||
"parses boolean false": {
|
||||
input: map[string][]string{
|
||||
"bool": {"false"},
|
||||
},
|
||||
expected: &ExternalComplex{Bool: false},
|
||||
},
|
||||
"parses boolean empty value": {
|
||||
input: map[string][]string{
|
||||
"bool": {""},
|
||||
},
|
||||
expected: &ExternalComplex{Bool: true},
|
||||
},
|
||||
"parses boolean no value": {
|
||||
input: map[string][]string{
|
||||
"bool": {},
|
||||
},
|
||||
expected: &ExternalComplex{Bool: false},
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range testCases {
|
||||
out := &ExternalComplex{}
|
||||
if err := scheme.Convert(&tc.input, out); (tc.errFn == nil && err != nil) || (tc.errFn != nil && !tc.errFn(err)) {
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
continue
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(out, tc.expected) {
|
||||
t.Errorf("%s: unexpected output: %#v", k, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
609
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/deep_copy_generator.go
generated
vendored
Normal file
609
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/deep_copy_generator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
// TODO(wojtek-t): As suggested in #8320, we should consider the strategy
|
||||
// to first do the shallow copy and then recurse into things that need a
|
||||
// deep copy (maps, pointers, slices). That sort of copy function would
|
||||
// need one parameter - a pointer to the thing it's supposed to expand,
|
||||
// and it would involve a lot less memory copying.
|
||||
type DeepCopyGenerator interface {
|
||||
// Adds a type to a generator.
|
||||
// If the type is non-struct, it will return an error, otherwise deep-copy
|
||||
// functions for this type and all nested types will be generated.
|
||||
AddType(inType reflect.Type) error
|
||||
|
||||
// ReplaceType registers a type that should be used instead of the type
|
||||
// with the provided pkgPath and name.
|
||||
ReplaceType(pkgPath, name string, in interface{})
|
||||
|
||||
// AddImport registers a package name with the generator and returns its
|
||||
// short name.
|
||||
AddImport(pkgPath string) string
|
||||
|
||||
// RepackImports creates a stable ordering of import short names
|
||||
RepackImports()
|
||||
|
||||
// Writes all imports that are necessary for deep-copy function and
|
||||
// their registration.
|
||||
WriteImports(w io.Writer) error
|
||||
|
||||
// Writes deel-copy functions for all types added via AddType() method
|
||||
// and their nested types.
|
||||
WriteDeepCopyFunctions(w io.Writer) error
|
||||
|
||||
// Writes an init() function that registers all the generated deep-copy
|
||||
// functions.
|
||||
RegisterDeepCopyFunctions(w io.Writer, pkg string) error
|
||||
|
||||
// When generating code, all references to "pkg" package name will be
|
||||
// replaced with "overwrite". It is used mainly to replace references
|
||||
// to name of the package in which the code will be created with empty
|
||||
// string.
|
||||
OverwritePackage(pkg, overwrite string)
|
||||
}
|
||||
|
||||
func NewDeepCopyGenerator(scheme *Scheme, targetPkg string, include sets.String) DeepCopyGenerator {
|
||||
g := &deepCopyGenerator{
|
||||
scheme: scheme,
|
||||
targetPkg: targetPkg,
|
||||
copyables: make(map[reflect.Type]bool),
|
||||
imports: make(map[string]string),
|
||||
shortImports: make(map[string]string),
|
||||
pkgOverwrites: make(map[string]string),
|
||||
replace: make(map[pkgPathNamePair]reflect.Type),
|
||||
include: include,
|
||||
}
|
||||
g.targetPackage(targetPkg)
|
||||
g.AddImport("k8s.io/kubernetes/pkg/conversion")
|
||||
return g
|
||||
}
|
||||
|
||||
type pkgPathNamePair struct {
|
||||
PkgPath string
|
||||
Name string
|
||||
}
|
||||
|
||||
type deepCopyGenerator struct {
|
||||
scheme *Scheme
|
||||
targetPkg string
|
||||
copyables map[reflect.Type]bool
|
||||
// map of package names to shortname
|
||||
imports map[string]string
|
||||
// map of short names to package names
|
||||
shortImports map[string]string
|
||||
pkgOverwrites map[string]string
|
||||
replace map[pkgPathNamePair]reflect.Type
|
||||
include sets.String
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) addImportByPath(pkg string) string {
|
||||
if name, ok := g.imports[pkg]; ok {
|
||||
return name
|
||||
}
|
||||
name := path.Base(pkg)
|
||||
if _, ok := g.shortImports[name]; !ok {
|
||||
g.imports[pkg] = name
|
||||
g.shortImports[name] = pkg
|
||||
return name
|
||||
}
|
||||
if dirname := path.Base(path.Dir(pkg)); len(dirname) > 0 {
|
||||
name = dirname + name
|
||||
if _, ok := g.shortImports[name]; !ok {
|
||||
g.imports[pkg] = name
|
||||
g.shortImports[name] = pkg
|
||||
return name
|
||||
}
|
||||
if subdirname := path.Base(path.Dir(path.Dir(pkg))); len(subdirname) > 0 {
|
||||
name = subdirname + name
|
||||
if _, ok := g.shortImports[name]; !ok {
|
||||
g.imports[pkg] = name
|
||||
g.shortImports[name] = pkg
|
||||
return name
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 2; i < 100; i++ {
|
||||
generatedName := fmt.Sprintf("%s%d", name, i)
|
||||
if _, ok := g.shortImports[generatedName]; !ok {
|
||||
g.imports[pkg] = generatedName
|
||||
g.shortImports[generatedName] = pkg
|
||||
return generatedName
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("unable to find a unique name for the package path %q: %v", pkg, g.shortImports))
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) targetPackage(pkg string) {
|
||||
g.imports[pkg] = ""
|
||||
g.shortImports[""] = pkg
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) addAllRecursiveTypes(inType reflect.Type) error {
|
||||
if _, found := g.copyables[inType]; found {
|
||||
return nil
|
||||
}
|
||||
switch inType.Kind() {
|
||||
case reflect.Map:
|
||||
if err := g.addAllRecursiveTypes(inType.Key()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.addAllRecursiveTypes(inType.Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Slice, reflect.Ptr:
|
||||
if err := g.addAllRecursiveTypes(inType.Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Interface:
|
||||
g.addImportByPath(inType.PkgPath())
|
||||
return nil
|
||||
case reflect.Struct:
|
||||
g.addImportByPath(inType.PkgPath())
|
||||
found := false
|
||||
for s := range g.include {
|
||||
if strings.HasPrefix(inType.PkgPath(), s) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < inType.NumField(); i++ {
|
||||
inField := inType.Field(i)
|
||||
if err := g.addAllRecursiveTypes(inField.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
g.copyables[inType] = true
|
||||
default:
|
||||
// Simple types should be copied automatically.
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) AddImport(pkg string) string {
|
||||
return g.addImportByPath(pkg)
|
||||
}
|
||||
|
||||
// ReplaceType registers a replacement type to be used instead of the named type
|
||||
func (g *deepCopyGenerator) ReplaceType(pkgPath, name string, t interface{}) {
|
||||
g.replace[pkgPathNamePair{pkgPath, name}] = reflect.TypeOf(t)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) AddType(inType reflect.Type) error {
|
||||
if inType.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("non-struct copies are not supported")
|
||||
}
|
||||
return g.addAllRecursiveTypes(inType)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) RepackImports() {
|
||||
var packages []string
|
||||
for key := range g.imports {
|
||||
packages = append(packages, key)
|
||||
}
|
||||
sort.Strings(packages)
|
||||
g.imports = make(map[string]string)
|
||||
g.shortImports = make(map[string]string)
|
||||
|
||||
g.targetPackage(g.targetPkg)
|
||||
for _, pkg := range packages {
|
||||
g.addImportByPath(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) WriteImports(w io.Writer) error {
|
||||
var packages []string
|
||||
for key := range g.imports {
|
||||
packages = append(packages, key)
|
||||
}
|
||||
sort.Strings(packages)
|
||||
|
||||
buffer := newBuffer()
|
||||
indent := 0
|
||||
buffer.addLine("import (\n", indent)
|
||||
for _, importPkg := range packages {
|
||||
if len(importPkg) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(g.imports[importPkg]) == 0 {
|
||||
continue
|
||||
}
|
||||
buffer.addLine(fmt.Sprintf("%s \"%s\"\n", g.imports[importPkg], importPkg), indent+1)
|
||||
}
|
||||
buffer.addLine(")\n", indent)
|
||||
buffer.addLine("\n", indent)
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type byPkgAndName []reflect.Type
|
||||
|
||||
func (s byPkgAndName) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s byPkgAndName) Less(i, j int) bool {
|
||||
fullNameI := s[i].PkgPath() + "/" + s[i].Name()
|
||||
fullNameJ := s[j].PkgPath() + "/" + s[j].Name()
|
||||
return fullNameI < fullNameJ
|
||||
}
|
||||
|
||||
func (s byPkgAndName) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) nameForType(inType reflect.Type) string {
|
||||
switch inType.Kind() {
|
||||
case reflect.Slice:
|
||||
return fmt.Sprintf("[]%s", g.typeName(inType.Elem()))
|
||||
case reflect.Ptr:
|
||||
return fmt.Sprintf("*%s", g.typeName(inType.Elem()))
|
||||
case reflect.Map:
|
||||
if len(inType.Name()) == 0 {
|
||||
return fmt.Sprintf("map[%s]%s", g.typeName(inType.Key()), g.typeName(inType.Elem()))
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
pkg, name := inType.PkgPath(), inType.Name()
|
||||
if len(name) == 0 && inType.Kind() == reflect.Struct {
|
||||
return "struct{}"
|
||||
}
|
||||
if len(pkg) == 0 {
|
||||
// Default package.
|
||||
return name
|
||||
}
|
||||
if val, found := g.pkgOverwrites[pkg]; found {
|
||||
pkg = val
|
||||
}
|
||||
if len(pkg) == 0 {
|
||||
return name
|
||||
}
|
||||
short := g.addImportByPath(pkg)
|
||||
if len(short) > 0 {
|
||||
return fmt.Sprintf("%s.%s", short, name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) typeName(inType reflect.Type) string {
|
||||
if t, ok := g.replace[pkgPathNamePair{inType.PkgPath(), inType.Name()}]; ok {
|
||||
return g.nameForType(t)
|
||||
}
|
||||
return g.nameForType(inType)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) deepCopyFunctionName(inType reflect.Type) string {
|
||||
funcNameFormat := "deepCopy_%s_%s"
|
||||
inPkg := packageForName(inType)
|
||||
funcName := fmt.Sprintf(funcNameFormat, inPkg, inType.Name())
|
||||
return funcName
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeHeader(b *buffer, inType reflect.Type, indent int) {
|
||||
format := "func %s(in %s, out *%s, c *conversion.Cloner) error {\n"
|
||||
stmt := fmt.Sprintf(format, g.deepCopyFunctionName(inType), g.typeName(inType), g.typeName(inType))
|
||||
b.addLine(stmt, indent)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeFooter(b *buffer, indent int) {
|
||||
b.addLine("return nil\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) WriteDeepCopyFunctions(w io.Writer) error {
|
||||
var keys []reflect.Type
|
||||
for key := range g.copyables {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Sort(byPkgAndName(keys))
|
||||
|
||||
buffer := newBuffer()
|
||||
indent := 0
|
||||
for _, inType := range keys {
|
||||
if err := g.writeDeepCopyForType(buffer, inType, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
buffer.addLine("\n", 0)
|
||||
}
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeDeepCopyForMap(b *buffer, inField reflect.StructField, indent int) error {
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
newFormat := "out.%s = make(%s)\n"
|
||||
newStmt := fmt.Sprintf(newFormat, inField.Name, g.typeName(inField.Type))
|
||||
b.addLine(newStmt, indent+1)
|
||||
forFormat := "for key, val := range in.%s {\n"
|
||||
forStmt := fmt.Sprintf(forFormat, inField.Name)
|
||||
b.addLine(forStmt, indent+1)
|
||||
|
||||
switch inField.Type.Key().Kind() {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
return fmt.Errorf("not supported")
|
||||
default:
|
||||
switch inField.Type.Elem().Kind() {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
if _, found := g.copyables[inField.Type.Elem()]; found {
|
||||
newFormat := "newVal := new(%s)\n"
|
||||
newStmt := fmt.Sprintf(newFormat, g.typeName(inField.Type.Elem()))
|
||||
b.addLine(newStmt, indent+2)
|
||||
assignFormat := "if err := %s(val, newVal, c); err != nil {\n"
|
||||
funcName := g.deepCopyFunctionName(inField.Type.Elem())
|
||||
assignStmt := fmt.Sprintf(assignFormat, funcName)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
b.addLine("return err\n", indent+3)
|
||||
b.addLine("}\n", indent+2)
|
||||
setFormat := "out.%s[key] = *newVal\n"
|
||||
setStmt := fmt.Sprintf(setFormat, inField.Name)
|
||||
b.addLine(setStmt, indent+2)
|
||||
} else {
|
||||
ifStmt := "if newVal, err := c.DeepCopy(val); err != nil {\n"
|
||||
b.addLine(ifStmt, indent+2)
|
||||
b.addLine("return err\n", indent+3)
|
||||
b.addLine("} else {\n", indent+2)
|
||||
assignFormat := "out.%s[key] = newVal.(%s)\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, g.typeName(inField.Type.Elem()))
|
||||
b.addLine(assignStmt, indent+3)
|
||||
b.addLine("}\n", indent+2)
|
||||
}
|
||||
default:
|
||||
assignFormat := "out.%s[key] = val\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
}
|
||||
}
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
elseFormat := "out.%s = nil\n"
|
||||
elseStmt := fmt.Sprintf(elseFormat, inField.Name)
|
||||
b.addLine(elseStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeDeepCopyForPtr(b *buffer, inField reflect.StructField, indent int) error {
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
|
||||
kind := inField.Type.Elem().Kind()
|
||||
switch kind {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
if _, found := g.copyables[inField.Type.Elem()]; found {
|
||||
newFormat := "out.%s = new(%s)\n"
|
||||
newStmt := fmt.Sprintf(newFormat, inField.Name, g.typeName(inField.Type.Elem()))
|
||||
b.addLine(newStmt, indent+1)
|
||||
assignFormat := "if err := %s(*in.%s, out.%s, c); err != nil {\n"
|
||||
funcName := g.deepCopyFunctionName(inField.Type.Elem())
|
||||
assignStmt := fmt.Sprintf(assignFormat, funcName, inField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent+1)
|
||||
b.addLine("return err\n", indent+2)
|
||||
b.addLine("}\n", indent+1)
|
||||
} else {
|
||||
ifFormat := "if newVal, err := c.DeepCopy(in.%s); err != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent+1)
|
||||
b.addLine("return err\n", indent+2)
|
||||
if kind != reflect.Struct {
|
||||
b.addLine("} else if newVal == nil {\n", indent+1)
|
||||
b.addLine(fmt.Sprintf("out.%s = nil\n", inField.Name), indent+2)
|
||||
}
|
||||
b.addLine("} else {\n", indent+1)
|
||||
assignFormat := "out.%s = newVal.(%s)\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, g.typeName(inField.Type))
|
||||
b.addLine(assignStmt, indent+2)
|
||||
b.addLine("}\n", indent+1)
|
||||
}
|
||||
default:
|
||||
newFormat := "out.%s = new(%s)\n"
|
||||
newStmt := fmt.Sprintf(newFormat, inField.Name, g.typeName(inField.Type.Elem()))
|
||||
b.addLine(newStmt, indent+1)
|
||||
assignFormat := "*out.%s = *in.%s\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent+1)
|
||||
}
|
||||
b.addLine("} else {\n", indent)
|
||||
elseFormat := "out.%s = nil\n"
|
||||
elseStmt := fmt.Sprintf(elseFormat, inField.Name)
|
||||
b.addLine(elseStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeDeepCopyForSlice(b *buffer, inField reflect.StructField, indent int) error {
|
||||
ifFormat := "if in.%s != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
newFormat := "out.%s = make(%s, len(in.%s))\n"
|
||||
newStmt := fmt.Sprintf(newFormat, inField.Name, g.typeName(inField.Type), inField.Name)
|
||||
b.addLine(newStmt, indent+1)
|
||||
forFormat := "for i := range in.%s {\n"
|
||||
forStmt := fmt.Sprintf(forFormat, inField.Name)
|
||||
b.addLine(forStmt, indent+1)
|
||||
|
||||
kind := inField.Type.Elem().Kind()
|
||||
switch kind {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
if _, found := g.copyables[inField.Type.Elem()]; found {
|
||||
assignFormat := "if err := %s(in.%s[i], &out.%s[i], c); err != nil {\n"
|
||||
funcName := g.deepCopyFunctionName(inField.Type.Elem())
|
||||
assignStmt := fmt.Sprintf(assignFormat, funcName, inField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
b.addLine("return err\n", indent+3)
|
||||
b.addLine("}\n", indent+2)
|
||||
} else {
|
||||
ifFormat := "if newVal, err := c.DeepCopy(in.%s[i]); err != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent+2)
|
||||
b.addLine("return err\n", indent+3)
|
||||
if kind != reflect.Struct {
|
||||
b.addLine("} else if newVal == nil {\n", indent+2)
|
||||
b.addLine(fmt.Sprintf("out.%s[i] = nil\n", inField.Name), indent+3)
|
||||
}
|
||||
b.addLine("} else {\n", indent+2)
|
||||
assignFormat := "out.%s[i] = newVal.(%s)\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, g.typeName(inField.Type.Elem()))
|
||||
b.addLine(assignStmt, indent+3)
|
||||
b.addLine("}\n", indent+2)
|
||||
}
|
||||
default:
|
||||
assignFormat := "out.%s[i] = in.%s[i]\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent+2)
|
||||
}
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
elseFormat := "out.%s = nil\n"
|
||||
elseStmt := fmt.Sprintf(elseFormat, inField.Name)
|
||||
b.addLine(elseStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeDeepCopyForStruct(b *buffer, inType reflect.Type, indent int) error {
|
||||
for i := 0; i < inType.NumField(); i++ {
|
||||
inField := inType.Field(i)
|
||||
switch inField.Type.Kind() {
|
||||
case reflect.Map:
|
||||
if err := g.writeDeepCopyForMap(b, inField, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if err := g.writeDeepCopyForPtr(b, inField, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Slice:
|
||||
if err := g.writeDeepCopyForSlice(b, inField, indent); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Interface:
|
||||
ifFormat := "if newVal, err := c.DeepCopy(in.%s); err != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
b.addLine("return err\n", indent+1)
|
||||
b.addLine("} else if newVal == nil {\n", indent)
|
||||
b.addLine(fmt.Sprintf("out.%s = nil\n", inField.Name), indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
copyFormat := "out.%s = newVal.(%s)\n"
|
||||
copyStmt := fmt.Sprintf(copyFormat, inField.Name, g.typeName(inField.Type))
|
||||
b.addLine(copyStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
case reflect.Struct:
|
||||
if _, found := g.copyables[inField.Type]; found {
|
||||
ifFormat := "if err := %s(in.%s, &out.%s, c); err != nil {\n"
|
||||
funcName := g.deepCopyFunctionName(inField.Type)
|
||||
ifStmt := fmt.Sprintf(ifFormat, funcName, inField.Name, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
b.addLine("return err\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
} else {
|
||||
ifFormat := "if newVal, err := c.DeepCopy(in.%s); err != nil {\n"
|
||||
ifStmt := fmt.Sprintf(ifFormat, inField.Name)
|
||||
b.addLine(ifStmt, indent)
|
||||
b.addLine("return err\n", indent+1)
|
||||
b.addLine("} else {\n", indent)
|
||||
assignFormat := "out.%s = newVal.(%s)\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, g.typeName(inField.Type))
|
||||
b.addLine(assignStmt, indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
}
|
||||
default:
|
||||
// This should handle all simple types.
|
||||
assignFormat := "out.%s = in.%s\n"
|
||||
assignStmt := fmt.Sprintf(assignFormat, inField.Name, inField.Name)
|
||||
b.addLine(assignStmt, indent)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeDeepCopyForType(b *buffer, inType reflect.Type, indent int) error {
|
||||
g.writeHeader(b, inType, indent)
|
||||
switch inType.Kind() {
|
||||
case reflect.Struct:
|
||||
if err := g.writeDeepCopyForStruct(b, inType, indent+1); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("type not supported: %v", inType)
|
||||
}
|
||||
g.writeFooter(b, indent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeRegisterHeader(b *buffer, pkg string, indent int) {
|
||||
b.addLine("func init() {\n", indent)
|
||||
registerFormat := "err := %s.AddGeneratedDeepCopyFuncs(\n"
|
||||
b.addLine(fmt.Sprintf(registerFormat, pkg), indent+1)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) writeRegisterFooter(b *buffer, indent int) {
|
||||
b.addLine(")\n", indent+1)
|
||||
b.addLine("if err != nil {\n", indent+1)
|
||||
b.addLine("// if one of the deep copy functions is malformed, detect it immediately.\n", indent+2)
|
||||
b.addLine("panic(err)\n", indent+2)
|
||||
b.addLine("}\n", indent+1)
|
||||
b.addLine("}\n", indent)
|
||||
b.addLine("\n", indent)
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) RegisterDeepCopyFunctions(w io.Writer, pkg string) error {
|
||||
var keys []reflect.Type
|
||||
for key := range g.copyables {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Sort(byPkgAndName(keys))
|
||||
|
||||
buffer := newBuffer()
|
||||
indent := 0
|
||||
g.writeRegisterHeader(buffer, pkg, indent)
|
||||
for _, inType := range keys {
|
||||
funcStmt := fmt.Sprintf("%s,\n", g.deepCopyFunctionName(inType))
|
||||
buffer.addLine(funcStmt, indent+2)
|
||||
}
|
||||
g.writeRegisterFooter(buffer, indent)
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *deepCopyGenerator) OverwritePackage(pkg, overwrite string) {
|
||||
g.pkgOverwrites[pkg] = overwrite
|
||||
}
|
||||
44
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/doc.go
generated
vendored
Normal file
44
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
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 includes helper functions for working with API objects
|
||||
// that follow the kubernetes API object conventions, which are:
|
||||
//
|
||||
// 0. Your API objects have a common metadata struct member, TypeMeta.
|
||||
// 1. Your code refers to an internal set of API objects.
|
||||
// 2. In a separate package, you have an external set of API objects.
|
||||
// 3. The external set is considered to be versioned, and no breaking
|
||||
// changes are ever made to it (fields may be added but not changed
|
||||
// or removed).
|
||||
// 4. As your api evolves, you'll make an additional versioned package
|
||||
// with every major change.
|
||||
// 5. Versioned packages have conversion functions which convert to
|
||||
// and from the internal version.
|
||||
// 6. You'll continue to support older versions according to your
|
||||
// deprecation policy, and you can easily provide a program/library
|
||||
// to update old versions into new versions because of 5.
|
||||
// 7. All of your serializations and deserializations are handled in a
|
||||
// centralized place.
|
||||
//
|
||||
// Package runtime provides a conversion helper to make 5 easy, and the
|
||||
// Encode/Decode/DecodeInto trio to accomplish 7. You can also register
|
||||
// additional "codecs" which use a version of your choice. It's
|
||||
// recommended that you register your types with runtime in your
|
||||
// package's init function.
|
||||
//
|
||||
// As a bonus, a few common types useful from all api objects and versions
|
||||
// are provided in types.go.
|
||||
package runtime
|
||||
124
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded.go
generated
vendored
Normal file
124
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded.go
generated
vendored
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
|
||||
},
|
||||
}
|
||||
}
|
||||
287
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded_test.go
generated
vendored
Normal file
287
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type EmbeddedTest struct {
|
||||
runtime.TypeMeta
|
||||
ID string
|
||||
Object runtime.Object
|
||||
EmptyObject runtime.Object
|
||||
}
|
||||
|
||||
type EmbeddedTestExternal struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Object runtime.RawExtension `json:"object,omitempty"`
|
||||
EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
|
||||
}
|
||||
|
||||
type ObjectTest struct {
|
||||
runtime.TypeMeta
|
||||
|
||||
ID string
|
||||
Items []runtime.Object
|
||||
}
|
||||
|
||||
type ObjectTestExternal struct {
|
||||
runtime.TypeMeta `yaml:",inline" json:",inline"`
|
||||
|
||||
ID string `json:"id,omitempty"`
|
||||
Items []runtime.RawExtension `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
func (obj *ObjectTest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *ObjectTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *EmbeddedTest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||
externalGVK := externalGV.WithKind("ObjectTest")
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(internalGV, &ObjectTest{})
|
||||
s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{})
|
||||
|
||||
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||
|
||||
obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
test := obj.(*ObjectTest)
|
||||
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" {
|
||||
t.Fatalf("unexpected object: %#v", test.Items[0])
|
||||
}
|
||||
if *gvk != externalGVK {
|
||||
t.Fatalf("unexpected kind: %#v", gvk)
|
||||
}
|
||||
|
||||
obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
test = obj.(*ObjectTest)
|
||||
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])
|
||||
}
|
||||
if *gvk != externalGVK {
|
||||
t.Fatalf("unexpected kind: %#v", gvk)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArrayOfRuntimeObject(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
|
||||
s.AddKnownTypes(internalGV, &ObjectTest{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})
|
||||
|
||||
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||
|
||||
innerItems := []runtime.Object{
|
||||
&EmbeddedTest{ID: "baz"},
|
||||
}
|
||||
items := []runtime.Object{
|
||||
&EmbeddedTest{ID: "foo"},
|
||||
&EmbeddedTest{ID: "bar"},
|
||||
// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
|
||||
&runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)},
|
||||
&ObjectTest{
|
||||
Items: runtime.NewEncodableList(codec, innerItems),
|
||||
},
|
||||
}
|
||||
internal := &ObjectTest{
|
||||
Items: runtime.NewEncodableList(codec, items),
|
||||
}
|
||||
wire, err := runtime.Encode(codec, internal)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
t.Logf("Wire format is:\n%s\n", string(wire))
|
||||
|
||||
obj := &ObjectTestExternal{}
|
||||
if err := json.Unmarshal(wire, obj); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON))
|
||||
|
||||
items[3] = &ObjectTest{Items: innerItems}
|
||||
internal.Items = items
|
||||
|
||||
decoded, err := runtime.Decode(codec, wire)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
list, err := meta.ExtractList(decoded)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
|
||||
t.Fatalf("unexpected error: %v", errs)
|
||||
}
|
||||
|
||||
list2, err := meta.ExtractList(list[3])
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if errs := runtime.DecodeList(list2, codec); len(errs) > 0 {
|
||||
t.Fatalf("unexpected error: %v", errs)
|
||||
}
|
||||
if err := meta.SetList(list[3], list2); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// we want DecodeList to set type meta if possible, even on runtime.Unknown objects
|
||||
internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"}
|
||||
if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("mismatched decoded: %s", util.ObjectGoPrintSideBySide(e, a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedObject(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
||||
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
|
||||
|
||||
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||
|
||||
inner := &EmbeddedTest{
|
||||
ID: "inner",
|
||||
}
|
||||
outer := &EmbeddedTest{
|
||||
ID: "outer",
|
||||
Object: runtime.NewEncodable(codec, inner),
|
||||
}
|
||||
|
||||
wire, err := runtime.Encode(codec, outer)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected encode error '%v'", err)
|
||||
}
|
||||
|
||||
t.Logf("Wire format is:\n%v\n", string(wire))
|
||||
|
||||
decoded, err := runtime.Decode(codec, wire)
|
||||
if err != nil {
|
||||
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) {
|
||||
t.Errorf("Expected equal %#v %#v", e, a)
|
||||
}
|
||||
|
||||
// test JSON decoding of the external object, which should preserve
|
||||
// raw bytes
|
||||
var externalViaJSON EmbeddedTestExternal
|
||||
err = json.Unmarshal(wire, &externalViaJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected decode error %v", err)
|
||||
}
|
||||
if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" {
|
||||
t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON)
|
||||
}
|
||||
if !reflect.DeepEqual(externalViaJSON.EmptyObject.RawJSON, []byte("null")) || len(externalViaJSON.Object.RawJSON) == 0 {
|
||||
t.Errorf("Expected deserialization of nested objects into bytes, got %#v", externalViaJSON)
|
||||
}
|
||||
|
||||
// test JSON decoding, too, since Decode uses yaml unmarshalling.
|
||||
// Generic Unmarshalling of JSON cannot load the nested objects because there is
|
||||
// no default schema set. Consumers wishing to get direct JSON decoding must use
|
||||
// the external representation
|
||||
var decodedViaJSON EmbeddedTest
|
||||
err = json.Unmarshal(wire, &decodedViaJSON)
|
||||
if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") {
|
||||
t.Fatalf("Unexpected decode error %v", err)
|
||||
}
|
||||
if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil {
|
||||
t.Errorf("Expected embedded objects to be nil: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity
|
||||
func TestDeepCopyOfRuntimeObject(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
|
||||
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(internalGV, &EmbeddedTest{})
|
||||
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
|
||||
|
||||
original := &EmbeddedTest{
|
||||
ID: "outer",
|
||||
Object: &EmbeddedTest{
|
||||
ID: "inner",
|
||||
},
|
||||
}
|
||||
|
||||
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
|
||||
|
||||
originalData, err := runtime.Encode(codec, original)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
t.Logf("originalRole = %v\n", string(originalData))
|
||||
|
||||
copyOfOriginal, err := s.DeepCopy(original)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
t.Logf("copyOfRole = %v\n", string(copiedData))
|
||||
|
||||
if !reflect.DeepEqual(original, copyOfOriginal) {
|
||||
t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData))
|
||||
}
|
||||
}
|
||||
102
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/error.go
generated
vendored
Normal file
102
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/error.go
generated
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
type notRegisteredErr struct {
|
||||
gvk unversioned.GroupVersionKind
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
// NewNotRegisteredErr is exposed for testing.
|
||||
func NewNotRegisteredErr(gvk unversioned.GroupVersionKind, t reflect.Type) error {
|
||||
return ¬RegisteredErr{gvk: gvk, t: t}
|
||||
}
|
||||
|
||||
func (k *notRegisteredErr) Error() string {
|
||||
if k.t != nil {
|
||||
return fmt.Sprintf("no kind is registered for the type %v", k.t)
|
||||
}
|
||||
if len(k.gvk.Kind) == 0 {
|
||||
return fmt.Sprintf("no version %q has been registered", k.gvk.GroupVersion())
|
||||
}
|
||||
if k.gvk.Version == APIVersionInternal {
|
||||
return fmt.Sprintf("no kind %q is registered for the internal version of group %q", k.gvk.Kind, k.gvk.Group)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("no kind %q is registered for version %q", k.gvk.Kind, k.gvk.GroupVersion())
|
||||
}
|
||||
|
||||
// IsNotRegisteredError returns true if the error indicates the provided
|
||||
// object or input data is not registered.
|
||||
func IsNotRegisteredError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(*notRegisteredErr)
|
||||
return ok
|
||||
}
|
||||
|
||||
type missingKindErr struct {
|
||||
data string
|
||||
}
|
||||
|
||||
func NewMissingKindErr(data string) error {
|
||||
return &missingKindErr{data}
|
||||
}
|
||||
|
||||
func (k *missingKindErr) Error() string {
|
||||
return fmt.Sprintf("Object 'Kind' is missing in '%s'", k.data)
|
||||
}
|
||||
|
||||
// IsMissingKind returns true if the error indicates that the provided object
|
||||
// is missing a 'Kind' field.
|
||||
func IsMissingKind(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(*missingKindErr)
|
||||
return ok
|
||||
}
|
||||
|
||||
type missingVersionErr struct {
|
||||
data string
|
||||
}
|
||||
|
||||
// IsMissingVersion returns true if the error indicates that the provided object
|
||||
// is missing a 'Versioj' field.
|
||||
func NewMissingVersionErr(data string) error {
|
||||
return &missingVersionErr{data}
|
||||
}
|
||||
|
||||
func (k *missingVersionErr) Error() string {
|
||||
return fmt.Sprintf("Object 'apiVersion' is missing in '%s'", k.data)
|
||||
}
|
||||
|
||||
func IsMissingVersion(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(*missingVersionErr)
|
||||
return ok
|
||||
}
|
||||
47
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension.go
generated
vendored
Normal file
47
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension.go
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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 (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (re *RawExtension) UnmarshalJSON(in []byte) error {
|
||||
if re == nil {
|
||||
return errors.New("runtime.RawExtension: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
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 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
|
||||
}
|
||||
39
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension_test.go
generated
vendored
Normal file
39
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestEmbeddedRawExtensionMarshal(t *testing.T) {
|
||||
type test struct {
|
||||
Ext runtime.RawExtension
|
||||
}
|
||||
|
||||
extension := test{Ext: runtime.RawExtension{RawJSON: []byte(`{"foo":"bar"}`)}}
|
||||
data, err := json.Marshal(extension)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if string(data) != `{"Ext":{"foo":"bar"}}` {
|
||||
t.Errorf("unexpected data: %s", string(data))
|
||||
}
|
||||
}
|
||||
169
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper.go
generated
vendored
Normal file
169
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper.go
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"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,
|
||||
// into dest, which must be an address of a variable to which this field's
|
||||
// address can be assigned.
|
||||
func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
|
||||
field := v.FieldByName(fieldName)
|
||||
if !field.IsValid() {
|
||||
return fmt.Errorf("couldn't find %v field in %#v", fieldName, v.Interface())
|
||||
}
|
||||
v, err := conversion.EnforcePtr(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field = field.Addr()
|
||||
if field.Type().AssignableTo(v.Type()) {
|
||||
v.Set(field)
|
||||
return nil
|
||||
}
|
||||
if field.Type().ConvertibleTo(v.Type()) {
|
||||
v.Set(field.Convert(v.Type()))
|
||||
return nil
|
||||
}
|
||||
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
|
||||
// the list that have the Unknown type. Any errors that occur are returned
|
||||
// after the entire list is processed. Decoders are tried in order.
|
||||
func DecodeList(objects []Object, decoders ...Decoder) []error {
|
||||
errs := []error(nil)
|
||||
for i, obj := range objects {
|
||||
switch t := obj.(type) {
|
||||
case *Unknown:
|
||||
decoded, err := decodeListItem(t, decoders)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
break
|
||||
}
|
||||
objects[i] = decoded
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// MultiObjectTyper returns the types of objects across multiple schemes in order.
|
||||
type MultiObjectTyper []ObjectTyper
|
||||
|
||||
var _ ObjectTyper = MultiObjectTyper{}
|
||||
|
||||
func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) {
|
||||
for _, t := range m {
|
||||
gvk, err = t.ObjectKind(obj)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m MultiObjectTyper) ObjectKinds(obj Object) (gvks []unversioned.GroupVersionKind, err error) {
|
||||
for _, t := range m {
|
||||
gvks, err = t.ObjectKinds(obj)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m MultiObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
||||
for _, t := range m {
|
||||
if t.Recognizes(gvk) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
41
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper_test.go
generated
vendored
Normal file
41
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestDecodeList(t *testing.T) {
|
||||
pl := &api.List{
|
||||
Items: []runtime.Object{
|
||||
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
|
||||
&runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: testapi.Default.GroupVersion().String()}, RawJSON: []byte(`{"kind":"Pod","apiVersion":"` + testapi.Default.GroupVersion().String() + `","metadata":{"name":"test"}}`)},
|
||||
&runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}},
|
||||
},
|
||||
}
|
||||
if errs := runtime.DecodeList(pl.Items, testapi.Default.Codec()); len(errs) != 0 {
|
||||
t.Fatalf("unexpected error %v", errs)
|
||||
}
|
||||
if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" {
|
||||
t.Errorf("object not converted: %#v", pl.Items[1])
|
||||
}
|
||||
}
|
||||
165
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/interfaces.go
generated
vendored
Normal file
165
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/interfaces.go
generated
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
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 (
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
const (
|
||||
// APIVersionInternal may be used if you are registering a type that should not
|
||||
// be considered stable or serialized - it is a convention only and has no
|
||||
// special behavior in this package.
|
||||
APIVersionInternal = "__internal"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
type Encoder interface {
|
||||
// EncodeToStream writes an object to a stream. Override versions may be provided for each group
|
||||
// that enforce a certain versioning. Implementations may return errors if the versions are incompatible,
|
||||
// or if no conversion is defined.
|
||||
EncodeToStream(obj Object, stream io.Writer, overrides ...unversioned.GroupVersion) error
|
||||
}
|
||||
|
||||
type Decoder interface {
|
||||
// Decode attempts to deserialize the provided data using either the innate typing of the scheme or the
|
||||
// default kind, group, and version provided. It returns a decoded object as well as the kind, group, and
|
||||
// version from the serialized data, or an error. If into is non-nil, it will be used as the target type
|
||||
// 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
|
||||
// provided, they are applied to the data by default. If no defaults or partial defaults are provided, the
|
||||
// type of the into may be used to guide conversion decisions.
|
||||
Decode(data []byte, defaults *unversioned.GroupVersionKind, into Object) (Object, *unversioned.GroupVersionKind, error)
|
||||
}
|
||||
|
||||
// Serializer is the core interface for transforming objects into a serialized format and back.
|
||||
// Implementations may choose to perform conversion of the object, but no assumptions should be made.
|
||||
type Serializer interface {
|
||||
Encoder
|
||||
Decoder
|
||||
}
|
||||
|
||||
// Codec is a Serializer that deals with the details of versioning objects. It offers the same
|
||||
// interface as Serializer, so this is a marker to consumers that care about the version of the objects
|
||||
// they receive.
|
||||
type Codec Serializer
|
||||
|
||||
// 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
|
||||
|
||||
type ObjectVersioner interface {
|
||||
ConvertToVersion(in Object, outVersion string) (out Object, err error)
|
||||
}
|
||||
|
||||
// ObjectConvertor converts an object to a different version.
|
||||
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
|
||||
// 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)
|
||||
ConvertFieldLabel(version, kind, label, value string) (string, string, error)
|
||||
}
|
||||
|
||||
// ObjectTyper contains methods for extracting the APIVersion and Kind
|
||||
// of objects.
|
||||
type ObjectTyper interface {
|
||||
// ObjectKind returns the default group,version,kind of the provided object, or an
|
||||
// error if the object is not recognized (IsNotRegisteredError will return true).
|
||||
ObjectKind(Object) (unversioned.GroupVersionKind, error)
|
||||
// ObjectKinds returns the all possible group,version,kind of the provided object, or an
|
||||
// error if the object is not recognized (IsNotRegisteredError will return true).
|
||||
ObjectKinds(Object) ([]unversioned.GroupVersionKind, error)
|
||||
// Recognizes returns true if the scheme is able to handle the provided version and kind,
|
||||
// or more precisely that the provided version is a possible conversion or decoding
|
||||
// target.
|
||||
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.
|
||||
type ObjectCreater interface {
|
||||
New(kind unversioned.GroupVersionKind) (out Object, err error)
|
||||
}
|
||||
|
||||
// ObjectCopier duplicates an object.
|
||||
type ObjectCopier interface {
|
||||
// Copy returns an exact copy of the provided Object, or an error if the
|
||||
// copy could not be completed.
|
||||
Copy(Object) (Object, error)
|
||||
}
|
||||
|
||||
// ResourceVersioner provides methods for setting and retrieving
|
||||
// the resource version from an API object.
|
||||
type ResourceVersioner interface {
|
||||
SetResourceVersion(obj Object, version string) error
|
||||
ResourceVersion(obj Object) (string, error)
|
||||
}
|
||||
|
||||
// SelfLinker provides methods for setting and retrieving the SelfLink field of an API object.
|
||||
type SelfLinker interface {
|
||||
SetSelfLink(obj Object, selfLink string) error
|
||||
SelfLink(obj Object) (string, error)
|
||||
|
||||
// Knowing Name is sometimes necessary to use a SelfLinker.
|
||||
Name(obj Object) (string, error)
|
||||
// Knowing Namespace is sometimes necessary to use a SelfLinker
|
||||
Namespace(obj Object) (string, error)
|
||||
}
|
||||
|
||||
// All API types registered with Scheme must support the Object interface. Since objects in a scheme are
|
||||
// expected to be serialized to the wire, the interface an Object must provide to the Scheme allows
|
||||
// serializers to set the kind, version, and group the object is represented as. An Object may choose
|
||||
// to return a no-op ObjectKindAccessor in cases where it is not expected to be serialized.
|
||||
type Object interface {
|
||||
GetObjectKind() unversioned.ObjectKind
|
||||
}
|
||||
18
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/doc.go
generated
vendored
Normal file
18
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package protobuf implements ProtoBuf serialization and deserialization.
|
||||
package protobuf
|
||||
158
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/protobuf.go
generated
vendored
Normal file
158
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/protobuf.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// +build proto
|
||||
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// NewCodec
|
||||
func NewCodec(version string, creater runtime.ObjectCreater, typer runtime.ObjectTyper, convertor runtime.ObjectConvertor) runtime.Codec {
|
||||
return &codec{
|
||||
version: version,
|
||||
creater: creater,
|
||||
typer: typer,
|
||||
convertor: convertor,
|
||||
}
|
||||
}
|
||||
|
||||
// codec decodes protobuf objects
|
||||
type codec struct {
|
||||
version string
|
||||
outputVersion string
|
||||
creater runtime.ObjectCreater
|
||||
typer runtime.ObjectTyper
|
||||
convertor runtime.ObjectConvertor
|
||||
}
|
||||
|
||||
var _ runtime.Codec = codec{}
|
||||
|
||||
func (c codec) Decode(data []byte) (runtime.Object, error) {
|
||||
unknown := &runtime.Unknown{}
|
||||
if err := proto.Unmarshal(data, unknown); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, err := c.creater.New(unknown.APIVersion, unknown.Kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pobj, ok := obj.(proto.Message)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj))
|
||||
}
|
||||
if err := proto.Unmarshal(unknown.RawJSON, pobj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unknown.APIVersion != c.outputVersion {
|
||||
out, err := c.convertor.ConvertToVersion(obj, c.outputVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj = out
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (c codec) DecodeToVersion(data []byte, version unversioned.GroupVersion) (runtime.Object, error) {
|
||||
return nil, fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (c codec) DecodeInto(data []byte, obj runtime.Object) error {
|
||||
version, kind, err := c.typer.ObjectVersionAndKind(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unknown := &runtime.Unknown{}
|
||||
if err := proto.Unmarshal(data, unknown); err != nil {
|
||||
return err
|
||||
}
|
||||
if unknown.APIVersion == version && unknown.Kind == kind {
|
||||
pobj, ok := obj.(proto.Message)
|
||||
if !ok {
|
||||
return fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj))
|
||||
}
|
||||
|
||||
return proto.Unmarshal(unknown.RawJSON, pobj)
|
||||
}
|
||||
|
||||
versioned, err := c.creater.New(unknown.APIVersion, unknown.Kind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pobj, ok := versioned.(proto.Message)
|
||||
if !ok {
|
||||
return fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj))
|
||||
}
|
||||
|
||||
if err := proto.Unmarshal(unknown.RawJSON, pobj); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.convertor.Convert(versioned, obj)
|
||||
}
|
||||
|
||||
func (c codec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, kind unversioned.GroupVersionKind) error {
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (c codec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error {
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (c codec) Encode(obj runtime.Object) (data []byte, err error) {
|
||||
version, kind, err := c.typer.ObjectVersionAndKind(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(version) == 0 {
|
||||
version = c.version
|
||||
converted, err := c.convertor.ConvertToVersion(obj, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj = converted
|
||||
}
|
||||
m, ok := obj.(proto.Marshaler)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("object %v (kind: %s in version: %s) does not implement ProtoBuf marshalling", reflect.TypeOf(obj), kind, c.version)
|
||||
}
|
||||
b, err := m.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (&runtime.Unknown{
|
||||
TypeMeta: runtime.TypeMeta{
|
||||
Kind: kind,
|
||||
APIVersion: version,
|
||||
},
|
||||
RawJSON: b,
|
||||
}).Marshal()
|
||||
}
|
||||
|
||||
func (c codec) EncodeToStream(obj runtime.Object, stream io.Writer) error {
|
||||
return fmt.Errorf("unimplemented")
|
||||
}
|
||||
65
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/register.go
generated
vendored
Normal file
65
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/register.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
|
||||
func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
||||
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||
}
|
||||
|
||||
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
|
||||
func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind {
|
||||
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||
}
|
||||
|
||||
func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *UnstructuredList) 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]
|
||||
}
|
||||
541
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme.go
generated
vendored
Normal file
541
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme.go
generated
vendored
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
)
|
||||
|
||||
// Scheme defines methods for serializing and deserializing API objects, a type
|
||||
// registry for converting group, version, and kind information to and from Go
|
||||
// schemas, and mappings between Go schemas of different versions. A scheme is the
|
||||
// foundation for a versioned API and versioned configuration over time.
|
||||
//
|
||||
// In a Scheme, a Type is a particular Go struct, a Version is a point-in-time
|
||||
// identifier for a particular representation of that Type (typically backwards
|
||||
// compatible), a Kind is the unique name for that Type within the Version, and a
|
||||
// Group identifies a set of Versions, Kinds, and Types that evolve over time. An
|
||||
// Unversioned Type is one that is not yet formally bound to a type and is promised
|
||||
// to be backwards compatible (effectively a "v1" of a Type that does not expect
|
||||
// to break in the future).
|
||||
//
|
||||
// Schemes are not expected to change at runtime and are only threadsafe after
|
||||
// registration is complete.
|
||||
type Scheme struct {
|
||||
// versionMap allows one to figure out the go type of an object with
|
||||
// the given version and name.
|
||||
gvkToType map[unversioned.GroupVersionKind]reflect.Type
|
||||
|
||||
// typeToGroupVersion allows one to find metadata for a given go object.
|
||||
// The reflect.Type we index by should *not* be a pointer.
|
||||
typeToGVK map[reflect.Type][]unversioned.GroupVersionKind
|
||||
|
||||
// unversionedTypes are transformed without conversion in ConvertToVersion.
|
||||
unversionedTypes map[reflect.Type]unversioned.GroupVersionKind
|
||||
|
||||
// unversionedKinds are the names of kinds that can be created in the context of any group
|
||||
// or version
|
||||
// TODO: resolve the status of unversioned types.
|
||||
unversionedKinds map[string]reflect.Type
|
||||
|
||||
// Map from version and resource to the corresponding func to convert
|
||||
// resource field labels in that version to internal version.
|
||||
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
|
||||
|
||||
// converter stores all registered conversion functions. It also has
|
||||
// default coverting behavior.
|
||||
converter *conversion.Converter
|
||||
|
||||
// cloner stores all registered copy functions. It also has default
|
||||
// deep copy behavior.
|
||||
cloner *conversion.Cloner
|
||||
}
|
||||
|
||||
// Function to convert a field selector to internal representation.
|
||||
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
||||
|
||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
||||
func NewScheme() *Scheme {
|
||||
s := &Scheme{
|
||||
gvkToType: map[unversioned.GroupVersionKind]reflect.Type{},
|
||||
typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{},
|
||||
unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{},
|
||||
unversionedKinds: map[string]reflect.Type{},
|
||||
cloner: conversion.NewCloner(),
|
||||
fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{},
|
||||
}
|
||||
s.converter = conversion.NewConverter(s.nameFunc)
|
||||
|
||||
s.AddConversionFuncs(DefaultEmbeddedConversions()...)
|
||||
|
||||
// Enable map[string][]string conversions by default
|
||||
if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// nameFunc returns the name of the type that we wish to use to determine when two types attempt
|
||||
// a conversion. Defaults to the go name of the type if the type is not registered.
|
||||
func (s *Scheme) nameFunc(t reflect.Type) string {
|
||||
// find the preferred names for this type
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return t.Name()
|
||||
}
|
||||
|
||||
for _, gvk := range gvks {
|
||||
internalGV := gvk.GroupVersion()
|
||||
internalGV.Version = "__internal" // this is hacky and maybe should be passed in
|
||||
internalGVK := internalGV.WithKind(gvk.Kind)
|
||||
|
||||
if internalType, exists := s.gvkToType[internalGVK]; exists {
|
||||
return s.typeToGVK[internalType][0].Kind
|
||||
}
|
||||
}
|
||||
|
||||
return gvks[0].Kind
|
||||
}
|
||||
|
||||
// fromScope gets the input version, desired output version, and desired Scheme
|
||||
// from a conversion.Scope.
|
||||
func (s *Scheme) fromScope(scope conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
|
||||
scheme = s
|
||||
inVersion = scope.Meta().SrcVersion
|
||||
outVersion = scope.Meta().DestVersion
|
||||
return inVersion, outVersion, scheme
|
||||
}
|
||||
|
||||
// Converter allows access to the converter for the scheme
|
||||
func (s *Scheme) Converter() *conversion.Converter {
|
||||
return s.converter
|
||||
}
|
||||
|
||||
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
|
||||
// Whenever an object of this type is serialized, it is serialized with the provided group version and is not
|
||||
// converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an
|
||||
// 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 this method at that point.
|
||||
func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) {
|
||||
s.AddKnownTypes(version, types...)
|
||||
for _, obj := range types {
|
||||
t := reflect.TypeOf(obj).Elem()
|
||||
gvk := version.WithKind(t.Name())
|
||||
s.unversionedTypes[t] = gvk
|
||||
if _, ok := s.unversionedKinds[gvk.Kind]; ok {
|
||||
panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind))
|
||||
}
|
||||
s.unversionedKinds[gvk.Kind] = t
|
||||
}
|
||||
}
|
||||
|
||||
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
|
||||
// All objects passed to types should be pointers to structs. The name that go reports for
|
||||
// the struct becomes the "kind" field when encoding. Version may not be empty - use the
|
||||
// APIVersionInternal constant if you have a type that does not have a formal version.
|
||||
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
|
||||
if len(gv.Version) == 0 {
|
||||
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
|
||||
}
|
||||
for _, obj := range types {
|
||||
t := reflect.TypeOf(obj)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
|
||||
gvk := gv.WithKind(t.Name())
|
||||
s.gvkToType[gvk] = t
|
||||
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// your structs. Version may not be empty - use the APIVersionInternal constant if you have a
|
||||
// type that does not have a formal version.
|
||||
func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj Object) {
|
||||
t := reflect.TypeOf(obj)
|
||||
if len(gvk.Version) == 0 {
|
||||
panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t))
|
||||
}
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
|
||||
s.gvkToType[gvk] = t
|
||||
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
|
||||
}
|
||||
|
||||
// KnownTypes returns the types known for the given version.
|
||||
func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type {
|
||||
types := make(map[string]reflect.Type)
|
||||
for gvk, t := range s.gvkToType {
|
||||
if gv != gvk.GroupVersion() {
|
||||
continue
|
||||
}
|
||||
|
||||
types[gvk.Kind] = t
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
// ObjectKind returns the group,version,kind of the go object,
|
||||
// or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) {
|
||||
gvks, err := s.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return unversioned.GroupVersionKind{}, err
|
||||
}
|
||||
return gvks[0], nil
|
||||
}
|
||||
|
||||
// ObjectKinds returns all possible group,version,kind of the go object,
|
||||
// or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, error) {
|
||||
v, err := conversion.EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return nil, ¬RegisteredErr{t: t}
|
||||
}
|
||||
|
||||
return gvks, nil
|
||||
}
|
||||
|
||||
// Recognizes returns true if the scheme is able to handle the provided group,version,kind
|
||||
// of an object.
|
||||
func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
||||
_, exists := s.gvkToType[gvk]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
|
||||
v, err := conversion.EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
if _, ok := s.typeToGVK[t]; !ok {
|
||||
return false, false
|
||||
}
|
||||
_, ok := s.unversionedTypes[t]
|
||||
return ok, true
|
||||
}
|
||||
|
||||
// New returns a new API object of the given version and name, or an error if it hasn't
|
||||
// been registered. The version and kind fields must be specified.
|
||||
func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
||||
if t, exists := s.gvkToType[kind]; exists {
|
||||
return reflect.New(t).Interface().(Object), nil
|
||||
}
|
||||
|
||||
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
||||
return reflect.New(t).Interface().(Object), nil
|
||||
}
|
||||
return nil, ¬RegisteredErr{gvk: kind}
|
||||
}
|
||||
|
||||
// Log sets a logger on the scheme. For test purposes only
|
||||
func (s *Scheme) Log(l conversion.DebugLogger) {
|
||||
s.converter.Debug = l
|
||||
}
|
||||
|
||||
// AddIgnoredConversionType identifies a pair of types that should be skipped by
|
||||
// conversion (because the data inside them is explicitly dropped during
|
||||
// conversion).
|
||||
func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
|
||||
return s.converter.RegisterIgnoredConversion(from, to)
|
||||
}
|
||||
|
||||
// AddConversionFuncs adds functions to the list of conversion functions. The given
|
||||
// functions should know how to convert between two of your API objects, or their
|
||||
// sub-objects. We deduce how to call these functions from the types of their two
|
||||
// parameters; see the comment for Converter.Register.
|
||||
//
|
||||
// Note that, if you need to copy sub-objects that didn't change, you can use the
|
||||
// conversion.Scope object that will be passed to your conversion function.
|
||||
// Additionally, all conversions started by Scheme will set the SrcVersion and
|
||||
// DestVersion fields on the Meta object. Example:
|
||||
//
|
||||
// s.AddConversionFuncs(
|
||||
// func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
|
||||
// // You can depend on Meta() being non-nil, and this being set to
|
||||
// // the source version, e.g., ""
|
||||
// s.Meta().SrcVersion
|
||||
// // You can depend on this being set to the destination version,
|
||||
// // e.g., "v1".
|
||||
// s.Meta().DestVersion
|
||||
// // Call scope.Convert to copy sub-fields.
|
||||
// s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
|
||||
// return nil
|
||||
// },
|
||||
// )
|
||||
//
|
||||
// (For more detail about conversion functions, see Converter.Register's comment.)
|
||||
//
|
||||
// Also note that the default behavior, if you don't add a conversion function, is to
|
||||
// sanely copy fields that have the same names and same type names. It's OK if the
|
||||
// destination type has extra fields, but it must not remove any. So you only need to
|
||||
// add conversion functions for things with changed/removed fields.
|
||||
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.RegisterConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddConversionFuncs, but registers conversion functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.RegisterGeneratedConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDeepCopyFuncs adds a function to the list of deep-copy functions.
|
||||
// For the expected format of deep-copy function, see the comment for
|
||||
// Copier.RegisterDeepCopyFunction.
|
||||
func (s *Scheme) AddDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
||||
for _, f := range deepCopyFuncs {
|
||||
if err := s.cloner.RegisterDeepCopyFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddDeepCopyFuncs, but registers deep-copy functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
||||
for _, f := range deepCopyFuncs {
|
||||
if err := s.cloner.RegisterGeneratedDeepCopyFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFieldLabelConversionFunc adds a conversion function to convert field selectors
|
||||
// of the given kind from the given version to internal version representation.
|
||||
func (s *Scheme) AddFieldLabelConversionFunc(version, kind string, conversionFunc FieldLabelConversionFunc) error {
|
||||
if s.fieldLabelConversionFuncs[version] == nil {
|
||||
s.fieldLabelConversionFuncs[version] = map[string]FieldLabelConversionFunc{}
|
||||
}
|
||||
|
||||
s.fieldLabelConversionFuncs[version][kind] = conversionFunc
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddStructFieldConversion allows you to specify a mechanical copy for a moved
|
||||
// or renamed struct field without writing an entire conversion function. See
|
||||
// the comment in conversion.Converter.SetStructFieldCopy for parameter details.
|
||||
// Call as many times as needed, even on the same fields.
|
||||
func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
|
||||
return s.converter.SetStructFieldCopy(srcFieldType, srcFieldName, destFieldType, destFieldName)
|
||||
}
|
||||
|
||||
// RegisterInputDefaults sets the provided field mapping function and field matching
|
||||
// as the defaults for the provided input type. The fn may be nil, in which case no
|
||||
// mapping will happen by default. Use this method to register a mechanism for handling
|
||||
// a specific input type in conversion, such as a map[string]string to structs.
|
||||
func (s *Scheme) RegisterInputDefaults(in interface{}, fn conversion.FieldMappingFunc, defaultFlags conversion.FieldMatchingFlags) error {
|
||||
return s.converter.RegisterInputDefaults(in, fn, defaultFlags)
|
||||
}
|
||||
|
||||
// AddDefaultingFuncs adds functions to the list of default-value functions.
|
||||
// Each of the given functions is responsible for applying default values
|
||||
// when converting an instance of a versioned API object into an internal
|
||||
// API object. These functions do not need to handle sub-objects. We deduce
|
||||
// how to call these functions from the types of their two parameters.
|
||||
//
|
||||
// s.AddDefaultingFuncs(
|
||||
// func(obj *v1.Pod) {
|
||||
// if obj.OptionalField == "" {
|
||||
// obj.OptionalField = "DefaultValue"
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
|
||||
for _, f := range defaultingFuncs {
|
||||
err := s.converter.RegisterDefaultingFunc(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy does a deep copy of an API object.
|
||||
func (s *Scheme) Copy(src Object) (Object, error) {
|
||||
dst, err := s.DeepCopy(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dst.(Object), nil
|
||||
}
|
||||
|
||||
// Performs a deep copy of the given object.
|
||||
func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) {
|
||||
return s.cloner.DeepCopy(src)
|
||||
}
|
||||
|
||||
// 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
|
||||
// possible. You can call this with types that haven't been registered (for example,
|
||||
// a to test conversion of types that are nested within registered types), but in
|
||||
// that case, the conversion.Scope object passed to your conversion functions won't
|
||||
// have SrcVersion or DestVersion fields set correctly in Meta().
|
||||
func (s *Scheme) Convert(in, out interface{}) error {
|
||||
inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
if inObj, ok := in.(Object); ok {
|
||||
if gvk, err := s.ObjectKind(inObj); err == nil {
|
||||
inVersion = gvk.GroupVersion()
|
||||
}
|
||||
}
|
||||
if outObj, ok := out.(Object); ok {
|
||||
if gvk, err := s.ObjectKind(outObj); err == nil {
|
||||
outVersion = gvk.GroupVersion()
|
||||
}
|
||||
}
|
||||
flags, meta := s.generateConvertMeta(inVersion, outVersion, in)
|
||||
if flags == 0 {
|
||||
flags = conversion.AllowDifferentFieldTypeNames
|
||||
}
|
||||
return s.converter.Convert(in, out, flags, meta)
|
||||
}
|
||||
|
||||
// Converts the given field label and value for an kind field selector from
|
||||
// versioned representation to an unversioned one.
|
||||
func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
|
||||
if s.fieldLabelConversionFuncs[version] == nil {
|
||||
return "", "", fmt.Errorf("No field label conversion function found for version: %s", version)
|
||||
}
|
||||
conversionFunc, ok := s.fieldLabelConversionFuncs[version][kind]
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("No field label conversion function found for version %s and kind %s", version, kind)
|
||||
}
|
||||
return conversionFunc(label, value)
|
||||
}
|
||||
|
||||
// ConvertToVersion attempts to convert an input object to its matching Kind in another
|
||||
// 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
|
||||
// return an error if the conversion does not result in a valid Object being
|
||||
// returned. The serializer handles loading/serializing nested objects.
|
||||
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, *UnstructuredList:
|
||||
old := in.GetObjectKind().GroupVersionKind()
|
||||
defer in.GetObjectKind().SetGroupVersionKind(old)
|
||||
setTargetVersion(in, s, gv)
|
||||
return in, nil
|
||||
}
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("only pointer types may be converted: %v", t)
|
||||
}
|
||||
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
|
||||
}
|
||||
|
||||
var kind unversioned.GroupVersionKind
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind = unversionedKind
|
||||
} else {
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion)
|
||||
}
|
||||
kind = kinds[0]
|
||||
}
|
||||
|
||||
outKind := gv.WithKind(kind.Kind)
|
||||
|
||||
inKind, err := s.ObjectKind(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := s.New(outKind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flags, meta := s.generateConvertMeta(inKind.GroupVersion(), gv, in)
|
||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setTargetVersion(out, s, gv)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
flags, meta := s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||
meta.SrcVersion = srcGroupVersion.String()
|
||||
meta.DestVersion = destGroupVersion.String()
|
||||
return flags, meta
|
||||
}
|
||||
|
||||
func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
||||
if gv.Version == APIVersionInternal {
|
||||
// internal is a special case
|
||||
obj.GetObjectKind().SetGroupVersionKind(nil)
|
||||
} else {
|
||||
gvk, _ := raw.ObjectKind(obj)
|
||||
obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind})
|
||||
}
|
||||
}
|
||||
673
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme_test.go
generated
vendored
Normal file
673
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
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_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
|
||||
|
||||
type InternalSimple struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
type ExternalSimple struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
func TestScheme(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||
|
||||
// If set, would clear TypeMeta during conversion.
|
||||
//scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{})
|
||||
|
||||
// test that scheme is an ObjectTyper
|
||||
var _ runtime.ObjectTyper = scheme
|
||||
|
||||
internalToExternalCalls := 0
|
||||
externalToInternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := scheme.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
|
||||
if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := externalGV.String(), scope.Meta().DestVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
|
||||
if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := internalGV.String(), scope.Meta().DestVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
externalToInternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
codec := codecs.LegacyCodec(externalGV)
|
||||
jsonserializer, _ := codecs.SerializerForFileExtension("json")
|
||||
|
||||
simple := &InternalSimple{
|
||||
TestString: "foo",
|
||||
}
|
||||
|
||||
// Test Encode, Decode, DecodeInto, and DecodeToVersion
|
||||
obj := runtime.Object(simple)
|
||||
data, err := runtime.Encode(codec, obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
obj2, err := runtime.Decode(codec, data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := obj2.(*InternalSimple); !ok {
|
||||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
if e, a := simple, obj2; !reflect.DeepEqual(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 = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
|
||||
if e, a := simple, obj3; !reflect.DeepEqual(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 {
|
||||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
|
||||
// Test Convert
|
||||
external := &ExternalSimple{}
|
||||
err = scheme.Convert(simple, external)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if e, a := simple.TestString, external.TestString; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// Encode and Convert should each have caused an increment.
|
||||
if e, a := 2, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
// DecodeInto and Decode should each have caused an increment because of a conversion
|
||||
if e, a := 2, externalToInternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadJSONRejection(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
jsonserializer, _ := codecs.SerializerForFileExtension("json")
|
||||
|
||||
badJSONMissingKind := []byte(`{ }`)
|
||||
if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
|
||||
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
|
||||
}
|
||||
badJSONUnknownType := []byte(`{"kind": "bar"}`)
|
||||
if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil {
|
||||
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
|
||||
}
|
||||
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
|
||||
if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil {
|
||||
t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
|
||||
}*/
|
||||
}
|
||||
|
||||
type ExtensionA struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
type ExtensionB struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
type ExternalExtensionType struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.RawExtension `json:"extension"`
|
||||
}
|
||||
|
||||
type InternalExtensionType struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.Object `json:"extension"`
|
||||
}
|
||||
|
||||
type ExternalOptionalExtensionType struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.RawExtension `json:"extension,omitempty"`
|
||||
}
|
||||
|
||||
type InternalOptionalExtensionType struct {
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.Object `json:"extension,omitempty"`
|
||||
}
|
||||
|
||||
func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
|
||||
func TestExternalToInternalMapping(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
|
||||
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
|
||||
|
||||
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||
|
||||
table := []struct {
|
||||
obj runtime.Object
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
&InternalOptionalExtensionType{Extension: nil},
|
||||
`{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range table {
|
||||
gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
|
||||
} else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtensionMapping(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{})
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
|
||||
scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{})
|
||||
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("B"), &ExtensionB{})
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{})
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{})
|
||||
|
||||
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||
|
||||
table := []struct {
|
||||
obj runtime.Object
|
||||
expected runtime.Object
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
&InternalExtensionType{
|
||||
Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}),
|
||||
},
|
||||
&InternalExtensionType{
|
||||
Extension: &runtime.Unknown{
|
||||
RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`),
|
||||
},
|
||||
},
|
||||
// apiVersion is set in the serialized object for easier consumption by clients
|
||||
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}}
|
||||
`,
|
||||
}, {
|
||||
&InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})},
|
||||
&InternalExtensionType{
|
||||
Extension: &runtime.Unknown{
|
||||
RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`),
|
||||
},
|
||||
},
|
||||
// apiVersion is set in the serialized object for easier consumption by clients
|
||||
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}}
|
||||
`,
|
||||
}, {
|
||||
&InternalExtensionType{Extension: nil},
|
||||
&InternalExtensionType{
|
||||
Extension: nil,
|
||||
},
|
||||
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range table {
|
||||
gotEncoded, err := runtime.Encode(codec, item.obj)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
|
||||
} else if e, a := item.encoded, string(gotEncoded); e != a {
|
||||
t.Errorf("expected\n%#v\ngot\n%#v\n", e, a)
|
||||
}
|
||||
|
||||
gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
|
||||
} else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||
|
||||
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
|
||||
|
||||
test := &InternalSimple{
|
||||
TestString: "I'm the same",
|
||||
}
|
||||
obj := runtime.Object(test)
|
||||
data, err := runtime.Encode(codec, obj)
|
||||
obj2, gvk, err2 := codec.Decode(data, nil, nil)
|
||||
if err != nil || err2 != nil {
|
||||
t.Fatalf("Failure: '%v' '%v'", err, err2)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" {
|
||||
t.Errorf("unexpected data: %s", data)
|
||||
}
|
||||
}
|
||||
|
||||
// Test a weird version/kind embedding format.
|
||||
type MyWeirdCustomEmbeddedVersionKindField struct {
|
||||
ID string `json:"ID,omitempty"`
|
||||
APIVersion string `json:"myVersionKey,omitempty"`
|
||||
ObjectKind string `json:"myKindKey,omitempty"`
|
||||
Z string `json:"Z,omitempty"`
|
||||
Y uint64 `json:"Y,omitempty"`
|
||||
}
|
||||
|
||||
type TestType1 struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
C int8 `json:"C,omitempty"`
|
||||
D int16 `json:"D,omitempty"`
|
||||
E int32 `json:"E,omitempty"`
|
||||
F int64 `json:"F,omitempty"`
|
||||
G uint `json:"G,omitempty"`
|
||||
H uint8 `json:"H,omitempty"`
|
||||
I uint16 `json:"I,omitempty"`
|
||||
J uint32 `json:"J,omitempty"`
|
||||
K uint64 `json:"K,omitempty"`
|
||||
L bool `json:"L,omitempty"`
|
||||
M map[string]int `json:"M,omitempty"`
|
||||
N map[string]TestType2 `json:"N,omitempty"`
|
||||
O *TestType2 `json:"O,omitempty"`
|
||||
P []TestType2 `json:"Q,omitempty"`
|
||||
}
|
||||
|
||||
type TestType2 struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
}
|
||||
|
||||
type ExternalTestType2 struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
}
|
||||
type ExternalTestType1 struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
C int8 `json:"C,omitempty"`
|
||||
D int16 `json:"D,omitempty"`
|
||||
E int32 `json:"E,omitempty"`
|
||||
F int64 `json:"F,omitempty"`
|
||||
G uint `json:"G,omitempty"`
|
||||
H uint8 `json:"H,omitempty"`
|
||||
I uint16 `json:"I,omitempty"`
|
||||
J uint32 `json:"J,omitempty"`
|
||||
K uint64 `json:"K,omitempty"`
|
||||
L bool `json:"L,omitempty"`
|
||||
M map[string]int `json:"M,omitempty"`
|
||||
N map[string]ExternalTestType2 `json:"N,omitempty"`
|
||||
O *ExternalTestType2 `json:"O,omitempty"`
|
||||
P []ExternalTestType2 `json:"Q,omitempty"`
|
||||
}
|
||||
|
||||
type ExternalInternalSame struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A TestType2 `json:"A,omitempty"`
|
||||
}
|
||||
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
||||
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
|
||||
}
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind {
|
||||
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
|
||||
}
|
||||
|
||||
func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *TestType1) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
|
||||
func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind {
|
||||
return unversioned.EmptyObjectKind
|
||||
}
|
||||
|
||||
// TestObjectFuzzer can randomly populate all the above objects.
|
||||
var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
|
||||
func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
|
||||
// We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their
|
||||
// APIVersion and Kind must remain blank in memory.
|
||||
j.APIVersion = ""
|
||||
j.ObjectKind = ""
|
||||
j.ID = c.RandString()
|
||||
},
|
||||
)
|
||||
|
||||
// Returns a new Scheme set up with the test objects.
|
||||
func GetTestScheme() *runtime.Scheme {
|
||||
internalGV := unversioned.GroupVersion{Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Version: "v1"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
// Ordinarily, we wouldn't add TestType2, but because this is a test and
|
||||
// both types are from the same package, we need to get it into the system
|
||||
// so that converter will match it with ExternalType2.
|
||||
s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{})
|
||||
s.AddKnownTypes(externalGV, &ExternalInternalSame{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
|
||||
return s
|
||||
}
|
||||
|
||||
func TestKnownTypes(t *testing.T) {
|
||||
s := GetTestScheme()
|
||||
if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 {
|
||||
t.Errorf("should have no known types for v2")
|
||||
}
|
||||
|
||||
types := s.KnownTypes(unversioned.GroupVersion{Version: "v1"})
|
||||
for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} {
|
||||
if _, ok := types[s]; !ok {
|
||||
t.Errorf("missing type %q", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToVersion(t *testing.T) {
|
||||
s := GetTestScheme()
|
||||
tt := &TestType1{A: "I'm not a pointer object"}
|
||||
other, err := s.ConvertToVersion(tt, "v1")
|
||||
if err != nil {
|
||||
t.Fatalf("Failure: %v", err)
|
||||
}
|
||||
converted, ok := other.(*ExternalTestType1)
|
||||
if !ok {
|
||||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
if tt.A != converted.A {
|
||||
t.Fatalf("Failed to convert object correctly: %#v", converted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaValues(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||
|
||||
internalToExternalCalls := 0
|
||||
externalToInternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := s.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
|
||||
t.Logf("internal -> external")
|
||||
if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := externalGV.String(), scope.Meta().DestVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
|
||||
t.Logf("external -> internal")
|
||||
if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := internalGV.String(), scope.Meta().DestVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
externalToInternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
simple := &InternalSimple{
|
||||
TestString: "foo",
|
||||
}
|
||||
|
||||
s.Log(t)
|
||||
|
||||
out, err := s.ConvertToVersion(simple, externalGV.String())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
internal, err := s.ConvertToVersion(out, internalGV.String())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if e, a := simple, internal; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
||||
}
|
||||
|
||||
if e, a := 1, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 1, externalToInternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaValuesUnregisteredConvert(t *testing.T) {
|
||||
type InternalSimple struct {
|
||||
Version string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
type ExternalSimple struct {
|
||||
Version string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
s := runtime.NewScheme()
|
||||
// We deliberately don't register the types.
|
||||
|
||||
internalToExternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := s.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
|
||||
if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
simple := &InternalSimple{TestString: "foo"}
|
||||
external := &ExternalSimple{}
|
||||
err = s.Convert(simple, external)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if e, a := simple.TestString, external.TestString; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// Verify that our conversion handler got called.
|
||||
if e, a := 1, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
176
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_factory.go
generated
vendored
Normal file
176
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_factory.go
generated
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
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 serializer
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
||||
)
|
||||
|
||||
type serializerType struct {
|
||||
AcceptContentTypes []string
|
||||
ContentType string
|
||||
FileExtensions []string
|
||||
Serializer runtime.Serializer
|
||||
PrettySerializer runtime.Serializer
|
||||
}
|
||||
|
||||
// NewCodecFactory provides methods for retrieving serializers for the supported wire formats
|
||||
// and conversion wrappers to define preferred internal and external versions. In the future,
|
||||
// as the internal version is used less, callers may instead use a defaulting serializer and
|
||||
// only convert objects which are shared internally (Status, common API machinery).
|
||||
// TODO: allow other codecs to be compiled in?
|
||||
// TODO: accept a scheme interface
|
||||
func NewCodecFactory(scheme *runtime.Scheme) CodecFactory {
|
||||
return newCodecFactory(scheme, json.DefaultMetaFactory)
|
||||
}
|
||||
|
||||
// newCodecFactory is a helper for testing that allows a different metafactory to be specified.
|
||||
func newCodecFactory(scheme *runtime.Scheme, mf json.MetaFactory) CodecFactory {
|
||||
jsonSerializer := json.NewSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme), false)
|
||||
jsonPrettySerializer := json.NewSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme), true)
|
||||
yamlSerializer := json.NewYAMLSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme))
|
||||
serializers := []serializerType{
|
||||
{
|
||||
AcceptContentTypes: []string{"application/json"},
|
||||
ContentType: "application/json",
|
||||
FileExtensions: []string{"json"},
|
||||
Serializer: jsonSerializer,
|
||||
PrettySerializer: jsonPrettySerializer,
|
||||
},
|
||||
{
|
||||
AcceptContentTypes: []string{"application/yaml"},
|
||||
ContentType: "application/yaml",
|
||||
FileExtensions: []string{"yaml"},
|
||||
Serializer: yamlSerializer,
|
||||
},
|
||||
}
|
||||
decoders := make([]runtime.Decoder, 0, len(serializers))
|
||||
accepts := []string{}
|
||||
alreadyAccepted := make(map[string]struct{})
|
||||
for _, d := range serializers {
|
||||
decoders = append(decoders, d.Serializer)
|
||||
for _, mediaType := range d.AcceptContentTypes {
|
||||
if _, ok := alreadyAccepted[mediaType]; ok {
|
||||
continue
|
||||
}
|
||||
alreadyAccepted[mediaType] = struct{}{}
|
||||
accepts = append(accepts, mediaType)
|
||||
}
|
||||
}
|
||||
return CodecFactory{
|
||||
scheme: scheme,
|
||||
serializers: serializers,
|
||||
universal: recognizer.NewDecoder(decoders...),
|
||||
accepts: accepts,
|
||||
|
||||
legacySerializer: jsonSerializer,
|
||||
}
|
||||
}
|
||||
|
||||
// CodecFactory provides methods for retrieving codecs and serializers for specific
|
||||
// versions and content types.
|
||||
type CodecFactory struct {
|
||||
scheme *runtime.Scheme
|
||||
serializers []serializerType
|
||||
universal runtime.Decoder
|
||||
accepts []string
|
||||
|
||||
legacySerializer runtime.Serializer
|
||||
}
|
||||
|
||||
var _ runtime.NegotiatedSerializer = &CodecFactory{}
|
||||
|
||||
// SupportedMediaTypes returns the RFC2046 media types that this factory has serializers for.
|
||||
func (f CodecFactory) SupportedMediaTypes() []string {
|
||||
return f.accepts
|
||||
}
|
||||
|
||||
// LegacyCodec encodes output to a given API version, and decodes output into the internal form from
|
||||
// any recognized source. The returned codec will always encode output to JSON.
|
||||
//
|
||||
// This method is deprecated - clients and servers should negotiate a serializer by mime-type and
|
||||
// invoke CodecForVersions. Callers that need only to read data should use UniversalDecoder().
|
||||
func (f CodecFactory) LegacyCodec(version ...unversioned.GroupVersion) runtime.Codec {
|
||||
return f.CodecForVersions(runtime.NewCodec(f.legacySerializer, f.universal), version, nil)
|
||||
}
|
||||
|
||||
// UniversalDeserializer can convert any stored data recognized by this factory into a Go object that satisfies
|
||||
// runtime.Object. It does not perform conversion. It does not perform defaulting.
|
||||
func (f CodecFactory) UniversalDeserializer() runtime.Decoder {
|
||||
return f.universal
|
||||
}
|
||||
|
||||
// UniversalDecoder returns a runtime.Decoder capable of decoding all known API objects in all known formats. Used
|
||||
// by clients that do not need to encode objects but want to deserialize API objects stored on disk. Only decodes
|
||||
// objects in groups registered with the scheme. The GroupVersions passed may be used to select alternate
|
||||
// versions of objects to return - by default, runtime.APIVersionInternal is used. If any versions are specified,
|
||||
// unrecognized groups will be returned in the version they are encoded as (no conversion). This decoder performs
|
||||
// defaulting.
|
||||
//
|
||||
// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form
|
||||
func (f CodecFactory) UniversalDecoder(versions ...unversioned.GroupVersion) runtime.Decoder {
|
||||
return f.CodecForVersions(runtime.NoopEncoder{f.universal}, nil, versions)
|
||||
}
|
||||
|
||||
// CodecFor creates a codec with the provided serializer. If an object is decoded and its group is not in the list,
|
||||
// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not
|
||||
// converted. If encode or decode are nil, no conversion is performed.
|
||||
func (f CodecFactory) CodecForVersions(serializer runtime.Serializer, encode []unversioned.GroupVersion, decode []unversioned.GroupVersion) runtime.Codec {
|
||||
return versioning.NewCodecForScheme(f.scheme, serializer, encode, decode)
|
||||
}
|
||||
|
||||
// DecoderToVersion returns a decoder that targets the provided group version.
|
||||
func (f CodecFactory) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder {
|
||||
return f.CodecForVersions(serializer, nil, []unversioned.GroupVersion{gv})
|
||||
}
|
||||
|
||||
// EncoderForVersion returns an encoder that targets the provided group version.
|
||||
func (f CodecFactory) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder {
|
||||
return f.CodecForVersions(serializer, []unversioned.GroupVersion{gv}, nil)
|
||||
}
|
||||
|
||||
// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
||||
// serializer exists
|
||||
func (f CodecFactory) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) {
|
||||
for _, s := range f.serializers {
|
||||
for _, accepted := range s.AcceptContentTypes {
|
||||
if accepted == mediaType {
|
||||
if v, ok := options["pretty"]; ok && v == "1" && s.PrettySerializer != nil {
|
||||
return s.PrettySerializer, true
|
||||
}
|
||||
return s.Serializer, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// SerializerForFileExtension returns a serializer for the provided extension, or false if no serializer matches.
|
||||
func (f CodecFactory) SerializerForFileExtension(extension string) (runtime.Serializer, bool) {
|
||||
for _, s := range f.serializers {
|
||||
for _, ext := range s.FileExtensions {
|
||||
if extension == ext {
|
||||
return s.Serializer, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
400
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_test.go
generated
vendored
Normal file
400
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
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 serializer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/google/gofuzz"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
|
||||
|
||||
type testMetaFactory struct{}
|
||||
|
||||
func (testMetaFactory) Interpret(data []byte) (*unversioned.GroupVersionKind, error) {
|
||||
findKind := struct {
|
||||
APIVersion string `json:"myVersionKey,omitempty"`
|
||||
ObjectKind string `json:"myKindKey,omitempty"`
|
||||
}{}
|
||||
// yaml is a superset of json, so we use it to decode here. That way,
|
||||
// we understand both.
|
||||
if err := yaml.Unmarshal(data, &findKind); err != nil {
|
||||
return nil, fmt.Errorf("couldn't get version/kind: %v", err)
|
||||
}
|
||||
gv, err := unversioned.ParseGroupVersion(findKind.APIVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.ObjectKind}, nil
|
||||
}
|
||||
|
||||
// Test a weird version/kind embedding format.
|
||||
type MyWeirdCustomEmbeddedVersionKindField struct {
|
||||
ID string `json:"ID,omitempty"`
|
||||
APIVersion string `json:"myVersionKey,omitempty"`
|
||||
ObjectKind string `json:"myKindKey,omitempty"`
|
||||
Z string `json:"Z,omitempty"`
|
||||
Y uint64 `json:"Y,omitempty"`
|
||||
}
|
||||
|
||||
type TestType1 struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
C int8 `json:"C,omitempty"`
|
||||
D int16 `json:"D,omitempty"`
|
||||
E int32 `json:"E,omitempty"`
|
||||
F int64 `json:"F,omitempty"`
|
||||
G uint `json:"G,omitempty"`
|
||||
H uint8 `json:"H,omitempty"`
|
||||
I uint16 `json:"I,omitempty"`
|
||||
J uint32 `json:"J,omitempty"`
|
||||
K uint64 `json:"K,omitempty"`
|
||||
L bool `json:"L,omitempty"`
|
||||
M map[string]int `json:"M,omitempty"`
|
||||
N map[string]TestType2 `json:"N,omitempty"`
|
||||
O *TestType2 `json:"O,omitempty"`
|
||||
P []TestType2 `json:"Q,omitempty"`
|
||||
}
|
||||
|
||||
type TestType2 struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
}
|
||||
|
||||
type ExternalTestType2 struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
}
|
||||
type ExternalTestType1 struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
C int8 `json:"C,omitempty"`
|
||||
D int16 `json:"D,omitempty"`
|
||||
E int32 `json:"E,omitempty"`
|
||||
F int64 `json:"F,omitempty"`
|
||||
G uint `json:"G,omitempty"`
|
||||
H uint8 `json:"H,omitempty"`
|
||||
I uint16 `json:"I,omitempty"`
|
||||
J uint32 `json:"J,omitempty"`
|
||||
K uint64 `json:"K,omitempty"`
|
||||
L bool `json:"L,omitempty"`
|
||||
M map[string]int `json:"M,omitempty"`
|
||||
N map[string]ExternalTestType2 `json:"N,omitempty"`
|
||||
O *ExternalTestType2 `json:"O,omitempty"`
|
||||
P []ExternalTestType2 `json:"Q,omitempty"`
|
||||
}
|
||||
|
||||
type ExternalInternalSame struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A TestType2 `json:"A,omitempty"`
|
||||
}
|
||||
|
||||
// TestObjectFuzzer can randomly populate all the above objects.
|
||||
var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
|
||||
func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(j)
|
||||
j.APIVersion = ""
|
||||
j.ObjectKind = ""
|
||||
},
|
||||
)
|
||||
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
||||
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
|
||||
}
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind {
|
||||
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
|
||||
}
|
||||
|
||||
func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *TestType1) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
|
||||
func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind {
|
||||
return unversioned.EmptyObjectKind
|
||||
}
|
||||
|
||||
// Returns a new Scheme set up with the test objects.
|
||||
func GetTestScheme() (*runtime.Scheme, runtime.Codec) {
|
||||
internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Version: "v1"}
|
||||
externalGV2 := unversioned.GroupVersion{Version: "v2"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
// Ordinarily, we wouldn't add TestType2, but because this is a test and
|
||||
// both types are from the same package, we need to get it into the system
|
||||
// so that converter will match it with ExternalType2.
|
||||
s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{})
|
||||
s.AddKnownTypes(externalGV, &ExternalInternalSame{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
|
||||
s.AddKnownTypeWithName(externalGV2.WithKind("TestType1"), &ExternalTestType1{})
|
||||
|
||||
s.AddUnversionedTypes(externalGV, &unversioned.Status{})
|
||||
|
||||
cf := newCodecFactory(s, testMetaFactory{})
|
||||
codec := cf.LegacyCodec(unversioned.GroupVersion{Version: "v1"})
|
||||
return s, codec
|
||||
}
|
||||
|
||||
func objDiff(a, b interface{}) string {
|
||||
ab, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
panic("a")
|
||||
}
|
||||
bb, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
panic("b")
|
||||
}
|
||||
return util.StringDiff(string(ab), string(bb))
|
||||
|
||||
// An alternate diff attempt, in case json isn't showing you
|
||||
// the difference. (reflect.DeepEqual makes a distinction between
|
||||
// nil and empty slices, for example.)
|
||||
//return util.StringDiff(
|
||||
// fmt.Sprintf("%#v", a),
|
||||
// fmt.Sprintf("%#v", b),
|
||||
//)
|
||||
}
|
||||
|
||||
var semantic = conversion.EqualitiesOrDie(
|
||||
func(a, b MyWeirdCustomEmbeddedVersionKindField) bool {
|
||||
a.APIVersion, a.ObjectKind = "", ""
|
||||
b.APIVersion, b.ObjectKind = "", ""
|
||||
return a == b
|
||||
},
|
||||
)
|
||||
|
||||
func runTest(t *testing.T, source interface{}) {
|
||||
name := reflect.TypeOf(source).Elem().Name()
|
||||
TestObjectFuzzer.Fuzz(source)
|
||||
|
||||
_, codec := GetTestScheme()
|
||||
data, err := runtime.Encode(codec, source.(runtime.Object))
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v (%#v)", name, err, source)
|
||||
return
|
||||
}
|
||||
obj2, err := runtime.Decode(codec, data)
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v (%v)", name, err, string(data))
|
||||
return
|
||||
}
|
||||
if !semantic.DeepEqual(source, obj2) {
|
||||
t.Errorf("1: %v: diff: %v", name, util.ObjectGoPrintSideBySide(source, obj2))
|
||||
return
|
||||
}
|
||||
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
|
||||
if err := runtime.DecodeInto(codec, data, obj3.(runtime.Object)); err != nil {
|
||||
t.Errorf("2: %v: %v", name, err)
|
||||
return
|
||||
}
|
||||
if !semantic.DeepEqual(source, obj3) {
|
||||
t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
table := []interface{}{
|
||||
&TestType1{},
|
||||
&ExternalInternalSame{},
|
||||
}
|
||||
for _, item := range table {
|
||||
// Try a few times, since runTest uses random values.
|
||||
for i := 0; i < *fuzzIters; i++ {
|
||||
runTest(t, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionedEncoding(t *testing.T) {
|
||||
s, codec := GetTestScheme()
|
||||
out, err := runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v2"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(out) != `{"myVersionKey":"v2","myKindKey":"TestType1"}`+"\n" {
|
||||
t.Fatal(string(out))
|
||||
}
|
||||
_, err = runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v3"})
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cf := newCodecFactory(s, testMetaFactory{})
|
||||
encoder, _ := cf.SerializerForFileExtension("json")
|
||||
|
||||
// codec that is unversioned uses the target version
|
||||
unversionedCodec := cf.CodecForVersions(encoder, nil, nil)
|
||||
_, err = runtime.Encode(unversionedCodec, &TestType1{}, unversioned.GroupVersion{Version: "v3"})
|
||||
if err == nil || !runtime.IsNotRegisteredError(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// unversioned encode with no versions is written directly to wire
|
||||
out, err = runtime.Encode(unversionedCodec, &TestType1{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(out) != `{"myVersionKey":"__internal","myKindKey":"TestType1"}`+"\n" {
|
||||
t.Fatal(string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleNames(t *testing.T) {
|
||||
_, codec := GetTestScheme()
|
||||
|
||||
obj, _, err := codec.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
internal := obj.(*TestType1)
|
||||
if internal.A != "value" {
|
||||
t.Fatalf("unexpected decoded object: %#v", internal)
|
||||
}
|
||||
|
||||
out, err := runtime.Encode(codec, internal)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(out), `"myKindKey":"TestType1"`) {
|
||||
t.Errorf("unexpected encoded output: %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal}
|
||||
externalGV := unversioned.GroupVersion{Version: "v1"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
// create two names internally, with TestType1 being preferred
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{})
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{})
|
||||
// create two names externally, with TestType1 being preferred
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{})
|
||||
|
||||
ext := &ExternalTestType1{}
|
||||
ext.APIVersion = "v1"
|
||||
ext.ObjectKind = "OtherType1"
|
||||
ext.A = "test"
|
||||
data, err := json.Marshal(ext)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expect := &TestType1{A: "test"}
|
||||
|
||||
codec := newCodecFactory(s, testMetaFactory{}).LegacyCodec(unversioned.GroupVersion{Version: "v1"})
|
||||
|
||||
obj, err := runtime.Decode(codec, data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !semantic.DeepEqual(expect, obj) {
|
||||
t.Errorf("unexpected object: %#v", obj)
|
||||
}
|
||||
|
||||
into := &TestType1{}
|
||||
if err := runtime.DecodeInto(codec, data, into); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !semantic.DeepEqual(expect, into) {
|
||||
t.Errorf("unexpected object: %#v", obj)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode_Ptr(t *testing.T) {
|
||||
_, codec := GetTestScheme()
|
||||
tt := &TestType1{A: "I am a pointer object"}
|
||||
data, err := runtime.Encode(codec, tt)
|
||||
obj2, err2 := runtime.Decode(codec, data)
|
||||
if err != nil || err2 != nil {
|
||||
t.Fatalf("Failure: '%v' '%v'\n%s", err, err2, data)
|
||||
}
|
||||
if _, ok := obj2.(*TestType1); !ok {
|
||||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
if !semantic.DeepEqual(obj2, tt) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", tt, obj2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadJSONRejection(t *testing.T) {
|
||||
log.SetOutput(os.Stderr)
|
||||
_, codec := GetTestScheme()
|
||||
badJSONs := [][]byte{
|
||||
[]byte(`{"myVersionKey":"v1"}`), // Missing kind
|
||||
[]byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind
|
||||
[]byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version
|
||||
[]byte(`{"myKindKey":"TestType1"}`), // Missing version
|
||||
}
|
||||
for _, b := range badJSONs {
|
||||
if _, err := runtime.Decode(codec, b); err == nil {
|
||||
t.Errorf("Did not reject bad json: %s", string(b))
|
||||
}
|
||||
}
|
||||
badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`)
|
||||
if err := runtime.DecodeInto(codec, badJSONKindMismatch, &TestType1{}); err == nil {
|
||||
t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
|
||||
}
|
||||
if err := runtime.DecodeInto(codec, []byte(``), &TestType1{}); err != nil {
|
||||
t.Errorf("Should allow empty decode: %v", err)
|
||||
}
|
||||
if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err == nil {
|
||||
t.Errorf("Did not give error for empty data with only kind default")
|
||||
}
|
||||
if _, _, err := codec.Decode([]byte(`{"myVersionKey":"v1"}`), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err != nil {
|
||||
t.Errorf("Gave error for version and kind default")
|
||||
}
|
||||
if _, _, err := codec.Decode([]byte(`{"myKindKey":"ExternalInternalSame"}`), &unversioned.GroupVersionKind{Version: "v1"}, nil); err != nil {
|
||||
t.Errorf("Gave error for version and kind default")
|
||||
}
|
||||
if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame", Version: "v1"}, nil); err != nil {
|
||||
t.Errorf("Gave error for version and kind defaulted: %v", err)
|
||||
}
|
||||
if _, err := runtime.Decode(codec, []byte(``)); err == nil {
|
||||
t.Errorf("Did not give error for empty data")
|
||||
}
|
||||
}
|
||||
191
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json.go
generated
vendored
Normal file
191
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json.go
generated
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
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 json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/ugorji/go/codec"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilyaml "k8s.io/kubernetes/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer
|
||||
// is not nil, the object has the group, version, and kind fields set.
|
||||
func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.Typer, pretty bool) runtime.Serializer {
|
||||
return &Serializer{
|
||||
meta: meta,
|
||||
creater: creater,
|
||||
typer: typer,
|
||||
yaml: false,
|
||||
pretty: pretty,
|
||||
}
|
||||
}
|
||||
|
||||
// NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer
|
||||
// is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that
|
||||
// matches JSON, and will error if constructs are used that do not serialize to JSON.
|
||||
func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.Typer) runtime.Serializer {
|
||||
return &Serializer{
|
||||
meta: meta,
|
||||
creater: creater,
|
||||
typer: typer,
|
||||
yaml: true,
|
||||
}
|
||||
}
|
||||
|
||||
type Serializer struct {
|
||||
meta MetaFactory
|
||||
creater runtime.ObjectCreater
|
||||
typer runtime.Typer
|
||||
yaml bool
|
||||
pretty bool
|
||||
}
|
||||
|
||||
// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then
|
||||
// load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, the raw data will be
|
||||
// extracted and no decoding will be performed. If into is not registered with the typer, then the object will be straight decoded using
|
||||
// normal JSON/YAML unmarshalling. If into is provided and the original data is not fully qualified with kind/version/group, the type of
|
||||
// the into will be used to alter the returned gvk. On success or most errors, the method will return the calculated schema kind.
|
||||
func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
if versioned, ok := into.(*runtime.VersionedObjects); ok {
|
||||
into = versioned.Last()
|
||||
obj, actual, err := s.Decode(originalData, gvk, into)
|
||||
if err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
versioned.Objects = []runtime.Object{obj}
|
||||
return versioned, actual, nil
|
||||
}
|
||||
|
||||
data := originalData
|
||||
if s.yaml {
|
||||
altered, err := yaml.YAMLToJSON(data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
data = altered
|
||||
}
|
||||
|
||||
actual, err := s.meta.Interpret(data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if gvk != nil {
|
||||
// apply kind and version defaulting from provided default
|
||||
if len(actual.Kind) == 0 {
|
||||
actual.Kind = gvk.Kind
|
||||
}
|
||||
if len(actual.Version) == 0 && len(actual.Group) == 0 {
|
||||
actual.Group = gvk.Group
|
||||
actual.Version = gvk.Version
|
||||
}
|
||||
if len(actual.Version) == 0 && actual.Group == gvk.Group {
|
||||
actual.Version = gvk.Version
|
||||
}
|
||||
}
|
||||
|
||||
if unk, ok := into.(*runtime.Unknown); ok && unk != nil {
|
||||
unk.RawJSON = originalData
|
||||
// TODO: set content type here
|
||||
unk.GetObjectKind().SetGroupVersionKind(actual)
|
||||
return unk, actual, nil
|
||||
}
|
||||
|
||||
if into != nil {
|
||||
typed, _, err := s.typer.ObjectKind(into)
|
||||
switch {
|
||||
case runtime.IsNotRegisteredError(err):
|
||||
if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(into); err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
return into, actual, nil
|
||||
case err != nil:
|
||||
return nil, actual, err
|
||||
default:
|
||||
if len(actual.Kind) == 0 {
|
||||
actual.Kind = typed.Kind
|
||||
}
|
||||
if len(actual.Version) == 0 && len(actual.Group) == 0 {
|
||||
actual.Group = typed.Group
|
||||
actual.Version = typed.Version
|
||||
}
|
||||
if len(actual.Version) == 0 && actual.Group == typed.Group {
|
||||
actual.Version = typed.Version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(actual.Kind) == 0 {
|
||||
return nil, actual, runtime.NewMissingKindErr(string(originalData))
|
||||
}
|
||||
if len(actual.Version) == 0 {
|
||||
return nil, actual, runtime.NewMissingVersionErr(string(originalData))
|
||||
}
|
||||
|
||||
// use the target if necessary
|
||||
obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
|
||||
if err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
|
||||
if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
return obj, actual, nil
|
||||
}
|
||||
|
||||
// EncodeToStream serializes the provided object to the given writer. Overrides is ignored.
|
||||
func (s *Serializer) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
|
||||
if s.yaml {
|
||||
json, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := yaml.JSONToYAML(json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
if s.pretty {
|
||||
data, err := json.MarshalIndent(obj, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
encoder := json.NewEncoder(w)
|
||||
return encoder.Encode(obj)
|
||||
}
|
||||
|
||||
// RecognizesData implements the RecognizingDecoder interface.
|
||||
func (s *Serializer) RecognizesData(peek io.Reader) (bool, error) {
|
||||
_, ok := utilyaml.GuessJSONStream(peek, 2048)
|
||||
if s.yaml {
|
||||
return !ok, nil
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
268
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json_test.go
generated
vendored
Normal file
268
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package json_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type testDecodable struct {
|
||||
Other string
|
||||
Value int `json:"value"`
|
||||
gvk *unversioned.GroupVersionKind
|
||||
}
|
||||
|
||||
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
|
||||
func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk }
|
||||
func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk }
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
testCases := []struct {
|
||||
creater runtime.ObjectCreater
|
||||
typer runtime.Typer
|
||||
yaml bool
|
||||
pretty bool
|
||||
|
||||
data []byte
|
||||
defaultGVK *unversioned.GroupVersionKind
|
||||
into runtime.Object
|
||||
|
||||
errFn func(error) bool
|
||||
expectedObject runtime.Object
|
||||
expectedGVK *unversioned.GroupVersionKind
|
||||
}{
|
||||
{
|
||||
data: []byte("{}"),
|
||||
|
||||
expectedGVK: &unversioned.GroupVersionKind{},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
|
||||
},
|
||||
{
|
||||
data: []byte("{}"),
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
creater: &mockCreater{err: fmt.Errorf("fake error")},
|
||||
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
errFn: func(err error) bool { return err.Error() == "fake error" },
|
||||
},
|
||||
{
|
||||
data: []byte("{}"),
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
creater: &mockCreater{err: fmt.Errorf("fake error")},
|
||||
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
errFn: func(err error) bool { return err.Error() == "fake error" },
|
||||
},
|
||||
{
|
||||
data: []byte("{}"),
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
creater: &mockCreater{obj: &testDecodable{}},
|
||||
expectedObject: &testDecodable{
|
||||
gvk: nil, // json serializer does NOT set GVK
|
||||
},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
},
|
||||
|
||||
// version without group is not defaulted
|
||||
{
|
||||
data: []byte(`{"apiVersion":"blah"}`),
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
creater: &mockCreater{obj: &testDecodable{}},
|
||||
expectedObject: &testDecodable{
|
||||
gvk: nil, // json serializer does NOT set GVK
|
||||
},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"},
|
||||
},
|
||||
// group without version is defaulted
|
||||
{
|
||||
data: []byte(`{"apiVersion":"other/"}`),
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
creater: &mockCreater{obj: &testDecodable{}},
|
||||
expectedObject: &testDecodable{
|
||||
gvk: nil, // json serializer does NOT set GVK
|
||||
},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
},
|
||||
|
||||
// accept runtime.Unknown as into and bypass creator
|
||||
{
|
||||
data: []byte(`{}`),
|
||||
into: &runtime.Unknown{},
|
||||
|
||||
expectedGVK: &unversioned.GroupVersionKind{},
|
||||
expectedObject: &runtime.Unknown{
|
||||
RawJSON: []byte(`{}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
data: []byte(`{"test":"object"}`),
|
||||
into: &runtime.Unknown{},
|
||||
|
||||
expectedGVK: &unversioned.GroupVersionKind{},
|
||||
expectedObject: &runtime.Unknown{
|
||||
RawJSON: []byte(`{"test":"object"}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
data: []byte(`{"test":"object"}`),
|
||||
into: &runtime.Unknown{},
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedObject: &runtime.Unknown{
|
||||
TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
|
||||
RawJSON: []byte(`{"test":"object"}`),
|
||||
},
|
||||
},
|
||||
|
||||
// unregistered objects can be decoded into directly
|
||||
{
|
||||
data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
|
||||
into: &testDecodable{},
|
||||
typer: &mockTyper{err: runtime.NewNotRegisteredErr(unversioned.GroupVersionKind{}, nil)},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedObject: &testDecodable{
|
||||
Other: "test",
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
// registered types get defaulted by the into object kind
|
||||
{
|
||||
data: []byte(`{"value":1,"Other":"test"}`),
|
||||
into: &testDecodable{},
|
||||
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedObject: &testDecodable{
|
||||
Other: "test",
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
// registered types get defaulted by the into object kind even without version, but return an error
|
||||
{
|
||||
data: []byte(`{"value":1,"Other":"test"}`),
|
||||
into: &testDecodable{},
|
||||
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
|
||||
expectedObject: &testDecodable{
|
||||
Other: "test",
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
|
||||
// runtime.VersionedObjects are decoded
|
||||
{
|
||||
data: []byte(`{"value":1,"Other":"test"}`),
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||
creater: &mockCreater{obj: &testDecodable{}},
|
||||
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
|
||||
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedObject: &runtime.VersionedObjects{
|
||||
Objects: []runtime.Object{
|
||||
&testDecodable{
|
||||
Other: "test",
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// runtime.VersionedObjects with an object are decoded into
|
||||
{
|
||||
data: []byte(`{"Other":"test"}`),
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{&testDecodable{Value: 2}}},
|
||||
typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
|
||||
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||
expectedObject: &runtime.VersionedObjects{
|
||||
Objects: []runtime.Object{
|
||||
&testDecodable{
|
||||
Other: "test",
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
var s runtime.Serializer
|
||||
if test.yaml {
|
||||
s = json.NewYAMLSerializer(json.DefaultMetaFactory, test.creater, test.typer)
|
||||
} else {
|
||||
s = json.NewSerializer(json.DefaultMetaFactory, test.creater, test.typer, test.pretty)
|
||||
}
|
||||
obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into)
|
||||
|
||||
if !reflect.DeepEqual(test.expectedGVK, gvk) {
|
||||
t.Errorf("%d: unexpected GVK: %v", i, gvk)
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == nil && test.errFn != nil:
|
||||
t.Errorf("%d: failed: %v", i, err)
|
||||
continue
|
||||
case err != nil && test.errFn == nil:
|
||||
t.Errorf("%d: failed: %v", i, err)
|
||||
continue
|
||||
case err != nil:
|
||||
if !test.errFn(err) {
|
||||
t.Errorf("%d: failed: %v", i, err)
|
||||
}
|
||||
if obj != nil {
|
||||
t.Errorf("%d: should have returned nil object", i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if test.into != nil && test.into != obj {
|
||||
t.Errorf("%d: expected into to be returned: %v", i, obj)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedObject, obj) {
|
||||
t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type mockCreater struct {
|
||||
apiVersion string
|
||||
kind string
|
||||
err error
|
||||
obj runtime.Object
|
||||
}
|
||||
|
||||
func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) {
|
||||
c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind
|
||||
return c.obj, c.err
|
||||
}
|
||||
|
||||
type mockTyper struct {
|
||||
gvk *unversioned.GroupVersionKind
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) {
|
||||
return t.gvk, false, t.err
|
||||
}
|
||||
61
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta.go
generated
vendored
Normal file
61
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
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 json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
// MetaFactory is used to store and retrieve the version and kind
|
||||
// information for JSON objects in a serializer.
|
||||
type MetaFactory interface {
|
||||
// Interpret should return the version and kind of the wire-format of
|
||||
// the object.
|
||||
Interpret(data []byte) (*unversioned.GroupVersionKind, error)
|
||||
}
|
||||
|
||||
// DefaultMetaFactory is a default factory for versioning objects in JSON. The object
|
||||
// in memory and in the default JSON serialization will use the "kind" and "apiVersion"
|
||||
// fields.
|
||||
var DefaultMetaFactory = SimpleMetaFactory{}
|
||||
|
||||
// SimpleMetaFactory provides default methods for retrieving the type and version of objects
|
||||
// that are identified with an "apiVersion" and "kind" fields in their JSON
|
||||
// serialization. It may be parameterized with the names of the fields in memory, or an
|
||||
// optional list of base structs to search for those fields in memory.
|
||||
type SimpleMetaFactory struct {
|
||||
}
|
||||
|
||||
// Interpret will return the APIVersion and Kind of the JSON wire-format
|
||||
// encoding of an object, or an error.
|
||||
func (SimpleMetaFactory) Interpret(data []byte) (*unversioned.GroupVersionKind, error) {
|
||||
findKind := struct {
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
}{}
|
||||
if err := json.Unmarshal(data, &findKind); err != nil {
|
||||
return nil, fmt.Errorf("couldn't get version/kind; json parse error: %v", err)
|
||||
}
|
||||
gv, err := unversioned.ParseGroupVersion(findKind.APIVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.Kind}, nil
|
||||
}
|
||||
45
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta_test.go
generated
vendored
Normal file
45
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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 json
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSimpleMetaFactoryInterpret(t *testing.T) {
|
||||
factory := SimpleMetaFactory{}
|
||||
gvk, err := factory.Interpret([]byte(`{"apiVersion":"1","kind":"object"}`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if gvk.Version != "1" || gvk.Kind != "object" {
|
||||
t.Errorf("unexpected interpret: %#v", gvk)
|
||||
}
|
||||
|
||||
// no kind or version
|
||||
gvk, err = factory.Interpret([]byte(`{}`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if gvk.Version != "" || gvk.Kind != "" {
|
||||
t.Errorf("unexpected interpret: %#v", gvk)
|
||||
}
|
||||
|
||||
// unparsable
|
||||
gvk, err = factory.Interpret([]byte(`{`))
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
79
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer.go
generated
vendored
Normal file
79
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
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 recognizer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
type RecognizingDecoder interface {
|
||||
runtime.Decoder
|
||||
RecognizesData(peek io.Reader) (bool, error)
|
||||
}
|
||||
|
||||
func NewDecoder(decoders ...runtime.Decoder) runtime.Decoder {
|
||||
recognizing, blind := []RecognizingDecoder{}, []runtime.Decoder{}
|
||||
for _, d := range decoders {
|
||||
if r, ok := d.(RecognizingDecoder); ok {
|
||||
recognizing = append(recognizing, r)
|
||||
} else {
|
||||
blind = append(blind, d)
|
||||
}
|
||||
}
|
||||
return &decoder{
|
||||
recognizing: recognizing,
|
||||
blind: blind,
|
||||
}
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
recognizing []RecognizingDecoder
|
||||
blind []runtime.Decoder
|
||||
}
|
||||
|
||||
func (d *decoder) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
var lastErr error
|
||||
for _, r := range d.recognizing {
|
||||
buf := bytes.NewBuffer(data)
|
||||
ok, err := r.RecognizesData(buf)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
return r.Decode(data, gvk, into)
|
||||
}
|
||||
for _, d := range d.blind {
|
||||
out, actual, err := d.Decode(data, gvk, into)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
return out, actual, nil
|
||||
}
|
||||
if lastErr == nil {
|
||||
lastErr = fmt.Errorf("no serialization format matched the provided data")
|
||||
}
|
||||
return nil, nil, lastErr
|
||||
}
|
||||
57
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer_test.go
generated
vendored
Normal file
57
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
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 recognizer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
||||
)
|
||||
|
||||
type A struct{}
|
||||
|
||||
func (A) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
|
||||
|
||||
func TestRecognizer(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypes(unversioned.GroupVersion{Version: "v1"}, &A{})
|
||||
d := NewDecoder(
|
||||
json.NewSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s), false),
|
||||
json.NewYAMLSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s)),
|
||||
)
|
||||
out, _, err := d.Decode([]byte(`
|
||||
kind: A
|
||||
apiVersion: v1
|
||||
`), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", out)
|
||||
|
||||
out, _, err = d.Decode([]byte(`
|
||||
{
|
||||
"kind":"A",
|
||||
"apiVersion":"v1"
|
||||
}
|
||||
`), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%#v", out)
|
||||
}
|
||||
287
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning.go
generated
vendored
Normal file
287
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning.go
generated
vendored
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
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 versioning
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// EnableCrossGroupDecoding modifies the given decoder in place, if it is a codec
|
||||
// from this package. It allows objects from one group to be auto-decoded into
|
||||
// another group. 'destGroup' must already exist in the codec.
|
||||
func EnableCrossGroupDecoding(d runtime.Decoder, sourceGroup, destGroup string) error {
|
||||
internal, ok := d.(*codec)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported decoder type")
|
||||
}
|
||||
|
||||
dest, ok := internal.decodeVersion[destGroup]
|
||||
if !ok {
|
||||
return fmt.Errorf("group %q is not a possible destination group in the given codec", destGroup)
|
||||
}
|
||||
internal.decodeVersion[sourceGroup] = dest
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableCrossGroupEncoding modifies the given encoder in place, if it is a codec
|
||||
// from this package. It allows objects from one group to be auto-decoded into
|
||||
// another group. 'destGroup' must already exist in the codec.
|
||||
func EnableCrossGroupEncoding(e runtime.Encoder, sourceGroup, destGroup string) error {
|
||||
internal, ok := e.(*codec)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported encoder type")
|
||||
}
|
||||
|
||||
dest, ok := internal.encodeVersion[destGroup]
|
||||
if !ok {
|
||||
return fmt.Errorf("group %q is not a possible destination group in the given codec", destGroup)
|
||||
}
|
||||
internal.encodeVersion[sourceGroup] = dest
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCodecForScheme is a convenience method for callers that are using a scheme.
|
||||
func NewCodecForScheme(
|
||||
// TODO: I should be a scheme interface?
|
||||
scheme *runtime.Scheme,
|
||||
serializer runtime.Serializer,
|
||||
encodeVersion []unversioned.GroupVersion,
|
||||
decodeVersion []unversioned.GroupVersion,
|
||||
) runtime.Codec {
|
||||
return NewCodec(serializer, scheme, scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion)
|
||||
}
|
||||
|
||||
// NewCodec takes objects in their internal versions and converts them to external versions before
|
||||
// serializing them. It assumes the serializer provided to it only deals with external versions.
|
||||
// This class is also a serializer, but is generally used with a specific version.
|
||||
func NewCodec(
|
||||
serializer runtime.Serializer,
|
||||
convertor runtime.ObjectConvertor,
|
||||
creater runtime.ObjectCreater,
|
||||
copier runtime.ObjectCopier,
|
||||
typer runtime.Typer,
|
||||
encodeVersion []unversioned.GroupVersion,
|
||||
decodeVersion []unversioned.GroupVersion,
|
||||
) runtime.Codec {
|
||||
internal := &codec{
|
||||
serializer: serializer,
|
||||
convertor: convertor,
|
||||
creater: creater,
|
||||
copier: copier,
|
||||
typer: typer,
|
||||
}
|
||||
if encodeVersion != nil {
|
||||
internal.encodeVersion = make(map[string]unversioned.GroupVersion)
|
||||
for _, v := range encodeVersion {
|
||||
// first one for a group wins. This is consistent with best to worst order throughout the codebase
|
||||
if _, ok := internal.encodeVersion[v.Group]; ok {
|
||||
continue
|
||||
}
|
||||
internal.encodeVersion[v.Group] = v
|
||||
}
|
||||
}
|
||||
if decodeVersion != nil {
|
||||
internal.decodeVersion = make(map[string]unversioned.GroupVersion)
|
||||
for _, v := range decodeVersion {
|
||||
// first one for a group wins. This is consistent with best to worst order throughout the codebase
|
||||
if _, ok := internal.decodeVersion[v.Group]; ok {
|
||||
continue
|
||||
}
|
||||
internal.decodeVersion[v.Group] = v
|
||||
}
|
||||
}
|
||||
|
||||
return internal
|
||||
}
|
||||
|
||||
type codec struct {
|
||||
serializer runtime.Serializer
|
||||
convertor runtime.ObjectConvertor
|
||||
creater runtime.ObjectCreater
|
||||
copier runtime.ObjectCopier
|
||||
typer runtime.Typer
|
||||
|
||||
encodeVersion map[string]unversioned.GroupVersion
|
||||
decodeVersion map[string]unversioned.GroupVersion
|
||||
}
|
||||
|
||||
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
|
||||
// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
|
||||
// into that matches the serialized version.
|
||||
func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
versioned, isVersioned := into.(*runtime.VersionedObjects)
|
||||
if isVersioned {
|
||||
into = versioned.Last()
|
||||
}
|
||||
|
||||
obj, gvk, err := c.serializer.Decode(data, defaultGVK, into)
|
||||
if err != nil {
|
||||
return nil, gvk, err
|
||||
}
|
||||
|
||||
// if we specify a target, use generic conversion.
|
||||
if into != nil {
|
||||
if into == obj {
|
||||
if isVersioned {
|
||||
return versioned, gvk, nil
|
||||
}
|
||||
return into, gvk, nil
|
||||
}
|
||||
if err := c.convertor.Convert(obj, into); err != nil {
|
||||
return nil, gvk, err
|
||||
}
|
||||
if isVersioned {
|
||||
versioned.Objects = []runtime.Object{obj, into}
|
||||
return versioned, gvk, nil
|
||||
}
|
||||
return into, gvk, nil
|
||||
}
|
||||
|
||||
// invoke a version conversion
|
||||
group := gvk.Group
|
||||
if defaultGVK != nil {
|
||||
group = defaultGVK.Group
|
||||
}
|
||||
var targetGV unversioned.GroupVersion
|
||||
if c.decodeVersion == nil {
|
||||
// convert to internal by default
|
||||
targetGV.Group = group
|
||||
targetGV.Version = runtime.APIVersionInternal
|
||||
} else {
|
||||
gv, ok := c.decodeVersion[group]
|
||||
if !ok {
|
||||
// unknown objects are left in their original version
|
||||
if isVersioned {
|
||||
versioned.Objects = []runtime.Object{obj}
|
||||
return versioned, gvk, nil
|
||||
}
|
||||
return obj, gvk, nil
|
||||
}
|
||||
targetGV = gv
|
||||
}
|
||||
|
||||
if gvk.GroupVersion() == targetGV {
|
||||
if isVersioned {
|
||||
versioned.Objects = []runtime.Object{obj}
|
||||
return versioned, gvk, nil
|
||||
}
|
||||
return obj, gvk, nil
|
||||
}
|
||||
|
||||
if isVersioned {
|
||||
// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
|
||||
copied, err := c.copier.Copy(obj)
|
||||
if err != nil {
|
||||
copied = obj
|
||||
}
|
||||
versioned.Objects = []runtime.Object{copied}
|
||||
}
|
||||
|
||||
// Convert if needed.
|
||||
out, err := c.convertor.ConvertToVersion(obj, targetGV.String())
|
||||
if err != nil {
|
||||
return nil, gvk, err
|
||||
}
|
||||
if isVersioned {
|
||||
versioned.Objects = append(versioned.Objects, out)
|
||||
return versioned, gvk, nil
|
||||
}
|
||||
return out, gvk, nil
|
||||
}
|
||||
|
||||
// EncodeToStream ensures the provided object is output in the right scheme. If overrides are specified, when
|
||||
// encoding the object the first override that matches the object's group is used. Other overrides are ignored.
|
||||
func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
|
||||
if _, ok := obj.(*runtime.Unknown); ok {
|
||||
return c.serializer.EncodeToStream(obj, w, overrides...)
|
||||
}
|
||||
gvk, isUnversioned, err := c.typer.ObjectKind(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (c.encodeVersion == nil && len(overrides) == 0) || isUnversioned {
|
||||
old := obj.GetObjectKind().GroupVersionKind()
|
||||
obj.GetObjectKind().SetGroupVersionKind(gvk)
|
||||
defer obj.GetObjectKind().SetGroupVersionKind(old)
|
||||
return c.serializer.EncodeToStream(obj, w, overrides...)
|
||||
}
|
||||
|
||||
targetGV, ok := c.encodeVersion[gvk.Group]
|
||||
// use override if provided
|
||||
for i, override := range overrides {
|
||||
if override.Group == gvk.Group {
|
||||
ok = true
|
||||
targetGV = override
|
||||
// swap the position of the override
|
||||
overrides[0], overrides[i] = targetGV, overrides[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// attempt a conversion to the sole encode version
|
||||
if !ok && len(c.encodeVersion) == 1 {
|
||||
ok = true
|
||||
for _, v := range c.encodeVersion {
|
||||
targetGV = v
|
||||
}
|
||||
// ensure the target override is first
|
||||
overrides = promoteOrPrependGroupVersion(targetGV, overrides)
|
||||
}
|
||||
|
||||
// if no fallback is available, error
|
||||
if !ok {
|
||||
return fmt.Errorf("the codec does not recognize group %q for kind %q and cannot encode it", gvk.Group, gvk.Kind)
|
||||
}
|
||||
|
||||
// Perform a conversion if necessary
|
||||
if gvk.GroupVersion() != targetGV {
|
||||
out, err := c.convertor.ConvertToVersion(obj, targetGV.String())
|
||||
if err != nil {
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
obj = out
|
||||
}
|
||||
} else {
|
||||
old := obj.GetObjectKind().GroupVersionKind()
|
||||
defer obj.GetObjectKind().SetGroupVersionKind(old)
|
||||
obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: targetGV.Group, Version: targetGV.Version, Kind: gvk.Kind})
|
||||
}
|
||||
|
||||
return c.serializer.EncodeToStream(obj, w, overrides...)
|
||||
}
|
||||
|
||||
// promoteOrPrependGroupVersion finds the group version in the provided group versions that has the same group as target.
|
||||
// If the group is found the returned array will have that group version in the first position - if the group is not found
|
||||
// the returned array will have target in the first position.
|
||||
func promoteOrPrependGroupVersion(target unversioned.GroupVersion, gvs []unversioned.GroupVersion) []unversioned.GroupVersion {
|
||||
for i, gv := range gvs {
|
||||
if gv.Group == target.Group {
|
||||
gvs[0], gvs[i] = gvs[i], gvs[0]
|
||||
return gvs
|
||||
}
|
||||
}
|
||||
return append([]unversioned.GroupVersion{target}, gvs...)
|
||||
}
|
||||
300
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning_test.go
generated
vendored
Normal file
300
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioning
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type testDecodable struct {
|
||||
Other string
|
||||
Value int `json:"value"`
|
||||
gvk *unversioned.GroupVersionKind
|
||||
}
|
||||
|
||||
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
|
||||
func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk }
|
||||
func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk }
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
|
||||
decodable1 := &testDecodable{}
|
||||
decodable2 := &testDecodable{}
|
||||
decodable3 := &testDecodable{}
|
||||
versionedDecodable1 := &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}}
|
||||
|
||||
testCases := []struct {
|
||||
serializer runtime.Serializer
|
||||
convertor runtime.ObjectConvertor
|
||||
creater runtime.ObjectCreater
|
||||
copier runtime.ObjectCopier
|
||||
typer runtime.Typer
|
||||
yaml bool
|
||||
pretty bool
|
||||
|
||||
encodes, decodes []unversioned.GroupVersion
|
||||
|
||||
defaultGVK *unversioned.GroupVersionKind
|
||||
into runtime.Object
|
||||
|
||||
errFn func(error) bool
|
||||
expectedObject runtime.Object
|
||||
sameObject runtime.Object
|
||||
expectedGVK *unversioned.GroupVersionKind
|
||||
}{
|
||||
{
|
||||
serializer: &mockSerializer{actual: gvk1},
|
||||
convertor: &checkConvertor{groupVersion: "other/__internal"},
|
||||
expectedGVK: gvk1,
|
||||
},
|
||||
{
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: decodable2,
|
||||
},
|
||||
// defaultGVK.Group is allowed to force a conversion to the destination group
|
||||
{
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
defaultGVK: &unversioned.GroupVersionKind{Group: "force"},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "force/__internal"},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: decodable2,
|
||||
},
|
||||
// uses direct conversion for into when objects differ
|
||||
{
|
||||
into: decodable3,
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable3, directConvert: true},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: decodable3,
|
||||
},
|
||||
{
|
||||
into: versionedDecodable1,
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable3},
|
||||
convertor: &checkConvertor{in: decodable3, obj: decodable1, directConvert: true},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: versionedDecodable1,
|
||||
},
|
||||
// returns directly when serializer returns into
|
||||
{
|
||||
into: decodable3,
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable3},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: decodable3,
|
||||
},
|
||||
// returns directly when serializer returns into
|
||||
{
|
||||
into: versionedDecodable1,
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: versionedDecodable1,
|
||||
},
|
||||
|
||||
// runtime.VersionedObjects are decoded
|
||||
{
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
copier: &checkCopy{in: decodable1, obj: decodable1},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
||||
},
|
||||
{
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
copier: &checkCopy{in: decodable1, obj: nil, err: fmt.Errorf("error on copy")},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
||||
},
|
||||
|
||||
// decode into the same version as the serialized object
|
||||
{
|
||||
decodes: []unversioned.GroupVersion{gvk1.GroupVersion()},
|
||||
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: decodable1,
|
||||
},
|
||||
{
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||
decodes: []unversioned.GroupVersion{gvk1.GroupVersion()},
|
||||
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
||||
},
|
||||
|
||||
// codec with non matching version skips conversion altogether
|
||||
{
|
||||
decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}},
|
||||
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: decodable1,
|
||||
},
|
||||
{
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||
decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}},
|
||||
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
t.Logf("%d", i)
|
||||
s := NewCodec(test.serializer, test.convertor, test.creater, test.copier, test.typer, test.encodes, test.decodes)
|
||||
obj, gvk, err := s.Decode([]byte(`{}`), test.defaultGVK, test.into)
|
||||
|
||||
if !reflect.DeepEqual(test.expectedGVK, gvk) {
|
||||
t.Errorf("%d: unexpected GVK: %v", i, gvk)
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == nil && test.errFn != nil:
|
||||
t.Errorf("%d: failed: %v", i, err)
|
||||
continue
|
||||
case err != nil && test.errFn == nil:
|
||||
t.Errorf("%d: failed: %v", i, err)
|
||||
continue
|
||||
case err != nil:
|
||||
if !test.errFn(err) {
|
||||
t.Errorf("%d: failed: %v", i, err)
|
||||
}
|
||||
if obj != nil {
|
||||
t.Errorf("%d: should have returned nil object", i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if test.into != nil && test.into != obj {
|
||||
t.Errorf("%d: expected into to be returned: %v", i, obj)
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case test.expectedObject != nil:
|
||||
if !reflect.DeepEqual(test.expectedObject, obj) {
|
||||
t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj))
|
||||
}
|
||||
case test.sameObject != nil:
|
||||
if test.sameObject != obj {
|
||||
t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.sameObject, obj))
|
||||
}
|
||||
case obj != nil:
|
||||
t.Errorf("%d: unexpected object: %#v", i, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type checkCopy struct {
|
||||
in, obj runtime.Object
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) {
|
||||
if c.in != nil && c.in != obj {
|
||||
return nil, fmt.Errorf("unexpected input to copy: %#v", obj)
|
||||
}
|
||||
return c.obj, c.err
|
||||
}
|
||||
|
||||
type checkConvertor struct {
|
||||
err error
|
||||
in, obj runtime.Object
|
||||
groupVersion string
|
||||
directConvert bool
|
||||
}
|
||||
|
||||
func (c *checkConvertor) Convert(in, out interface{}) error {
|
||||
if !c.directConvert {
|
||||
return fmt.Errorf("unexpected call to Convert")
|
||||
}
|
||||
if c.in != nil && c.in != in {
|
||||
return fmt.Errorf("unexpected in: %s", in)
|
||||
}
|
||||
if c.obj != nil && c.obj != out {
|
||||
return fmt.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
return c.err
|
||||
}
|
||||
func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion string) (out runtime.Object, err error) {
|
||||
if c.directConvert {
|
||||
return nil, fmt.Errorf("unexpected call to ConvertToVersion")
|
||||
}
|
||||
if c.in != nil && c.in != in {
|
||||
return nil, fmt.Errorf("unexpected in: %s", in)
|
||||
}
|
||||
if c.groupVersion != outVersion {
|
||||
return nil, fmt.Errorf("unexpected outversion: %s", outVersion)
|
||||
}
|
||||
return c.obj, c.err
|
||||
}
|
||||
func (c *checkConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
|
||||
return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel")
|
||||
}
|
||||
|
||||
type mockSerializer struct {
|
||||
err error
|
||||
obj runtime.Object
|
||||
versions []unversioned.GroupVersion
|
||||
|
||||
defaults, actual *unversioned.GroupVersionKind
|
||||
into runtime.Object
|
||||
}
|
||||
|
||||
func (s *mockSerializer) Decode(data []byte, defaults *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
s.defaults = defaults
|
||||
s.into = into
|
||||
return s.obj, s.actual, s.err
|
||||
}
|
||||
|
||||
func (s *mockSerializer) EncodeToStream(obj runtime.Object, w io.Writer, versions ...unversioned.GroupVersion) error {
|
||||
s.obj = obj
|
||||
s.versions = versions
|
||||
return s.err
|
||||
}
|
||||
|
||||
type mockCreater struct {
|
||||
err error
|
||||
obj runtime.Object
|
||||
}
|
||||
|
||||
func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) {
|
||||
return c.obj, c.err
|
||||
}
|
||||
|
||||
type mockTyper struct {
|
||||
gvk *unversioned.GroupVersionKind
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) {
|
||||
return t.gvk, false, t.err
|
||||
}
|
||||
46
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/yaml/yaml.go
generated
vendored
Normal file
46
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/yaml/yaml.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 yaml
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// yamlSerializer converts YAML passed to the Decoder methods to JSON.
|
||||
type yamlSerializer struct {
|
||||
// the nested serializer
|
||||
runtime.Serializer
|
||||
}
|
||||
|
||||
// yamlSerializer implements Serializer
|
||||
var _ runtime.Serializer = yamlSerializer{}
|
||||
|
||||
// NewDecodingSerializer adds YAML decoding support to a serializer that supports JSON.
|
||||
func NewDecodingSerializer(jsonSerializer runtime.Serializer) runtime.Serializer {
|
||||
return &yamlSerializer{jsonSerializer}
|
||||
}
|
||||
|
||||
func (c yamlSerializer) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) {
|
||||
out, err := yaml.ToJSON(data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
data = out
|
||||
return c.Serializer.Decode(data, gvk, into)
|
||||
}
|
||||
231
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator.go
generated
vendored
Normal file
231
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/doc"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Pair of strings. We keed the name of fields and the doc
|
||||
type Pair struct {
|
||||
Name, Doc string
|
||||
}
|
||||
|
||||
// KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself
|
||||
type KubeTypes []Pair
|
||||
|
||||
func astFrom(filePath string) *doc.Package {
|
||||
fset := token.NewFileSet()
|
||||
m := make(map[string]*ast.File)
|
||||
|
||||
f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
m[filePath] = f
|
||||
apkg, _ := ast.NewPackage(fset, m, nil, nil)
|
||||
|
||||
return doc.New(apkg, "", 0)
|
||||
}
|
||||
|
||||
func fmtRawDoc(rawDoc string) string {
|
||||
var buffer bytes.Buffer
|
||||
delPrevChar := func() {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore all lines after ---
|
||||
rawDoc = strings.Split(rawDoc, "---")[0]
|
||||
|
||||
for _, line := range strings.Split(rawDoc, "\n") {
|
||||
line = strings.TrimRight(line, " ")
|
||||
leading := strings.TrimLeft(line, " ")
|
||||
switch {
|
||||
case len(line) == 0: // Keep paragraphs
|
||||
delPrevChar()
|
||||
buffer.WriteString("\n\n")
|
||||
case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
|
||||
case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
|
||||
default:
|
||||
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
|
||||
delPrevChar()
|
||||
line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
|
||||
} else {
|
||||
line += " "
|
||||
}
|
||||
buffer.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
postDoc := strings.TrimRight(buffer.String(), "\n")
|
||||
postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
|
||||
postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
|
||||
postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
|
||||
postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
|
||||
|
||||
return postDoc
|
||||
}
|
||||
|
||||
// fieldName returns the name of the field as it should appear in JSON format
|
||||
// "-" indicates that this field is not part of the JSON representation
|
||||
func fieldName(field *ast.Field) string {
|
||||
jsonTag := ""
|
||||
if field.Tag != nil {
|
||||
jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
|
||||
if strings.Contains(jsonTag, "inline") {
|
||||
return "-"
|
||||
}
|
||||
}
|
||||
|
||||
jsonTag = strings.Split(jsonTag, ",")[0] // This can return "-"
|
||||
if jsonTag == "" {
|
||||
if field.Names != nil {
|
||||
return field.Names[0].Name
|
||||
}
|
||||
return field.Type.(*ast.Ident).Name
|
||||
}
|
||||
return jsonTag
|
||||
}
|
||||
|
||||
func writeFuncHeader(b *buffer, structName string, indent int) {
|
||||
s := fmt.Sprintf("var map_%s = map[string]string {\n", structName)
|
||||
b.addLine(s, indent)
|
||||
}
|
||||
|
||||
func writeFuncFooter(b *buffer, structName string, indent int) {
|
||||
b.addLine("}\n", indent) // Closes the map definition
|
||||
|
||||
s := fmt.Sprintf("func (%s) SwaggerDoc() map[string]string {\n", structName)
|
||||
b.addLine(s, indent)
|
||||
s = fmt.Sprintf("return map_%s\n", structName)
|
||||
b.addLine(s, indent+1)
|
||||
b.addLine("}\n", indent) // Closes the function definition
|
||||
}
|
||||
|
||||
func writeMapBody(b *buffer, kubeType []Pair, indent int) {
|
||||
format := "\"%s\": \"%s\",\n"
|
||||
for _, pair := range kubeType {
|
||||
s := fmt.Sprintf(format, pair.Name, pair.Doc)
|
||||
b.addLine(s, indent+2)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseDocumentationFrom gets all types' documentation and returns them as an
|
||||
// array. Each type is again represented as an array (we have to use arrays as we
|
||||
// need to be sure for the order of the fields). This function returns fields and
|
||||
// struct definitions that have no documentation as {name, ""}.
|
||||
func ParseDocumentationFrom(src string) []KubeTypes {
|
||||
var docForTypes []KubeTypes
|
||||
|
||||
pkg := astFrom(src)
|
||||
|
||||
for _, kubType := range pkg.Types {
|
||||
if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok {
|
||||
var ks KubeTypes
|
||||
ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc)})
|
||||
|
||||
for _, field := range structType.Fields.List {
|
||||
if n := fieldName(field); n != "-" {
|
||||
fieldDoc := fmtRawDoc(field.Doc.Text())
|
||||
ks = append(ks, Pair{n, fieldDoc})
|
||||
}
|
||||
}
|
||||
docForTypes = append(docForTypes, ks)
|
||||
}
|
||||
}
|
||||
|
||||
return docForTypes
|
||||
}
|
||||
|
||||
// WriteSwaggerDocFunc writes a declaration of a function as a string. This function is used in
|
||||
// Swagger as a documentation source for structs and theirs fields
|
||||
func WriteSwaggerDocFunc(kubeTypes []KubeTypes, w io.Writer) error {
|
||||
for _, kubeType := range kubeTypes {
|
||||
structName := kubeType[0].Name
|
||||
kubeType[0].Name = ""
|
||||
|
||||
// Ignore empty documentation
|
||||
docfulTypes := make(KubeTypes, 0, len(kubeType))
|
||||
for _, pair := range kubeType {
|
||||
if pair.Doc != "" {
|
||||
docfulTypes = append(docfulTypes, pair)
|
||||
}
|
||||
}
|
||||
|
||||
if len(docfulTypes) == 0 {
|
||||
continue // If no fields and the struct have documentation, skip the function definition
|
||||
}
|
||||
|
||||
indent := 0
|
||||
buffer := newBuffer()
|
||||
|
||||
writeFuncHeader(buffer, structName, indent)
|
||||
writeMapBody(buffer, docfulTypes, indent)
|
||||
writeFuncFooter(buffer, structName, indent)
|
||||
buffer.addLine("\n", 0)
|
||||
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySwaggerDocsExist writes in a io.Writer a list of structs and fields that
|
||||
// are missing of documentation.
|
||||
func VerifySwaggerDocsExist(kubeTypes []KubeTypes, w io.Writer) (int, error) {
|
||||
missingDocs := 0
|
||||
buffer := newBuffer()
|
||||
|
||||
for _, kubeType := range kubeTypes {
|
||||
structName := kubeType[0].Name
|
||||
if kubeType[0].Doc == "" {
|
||||
format := "Missing documentation for the struct itself: %s\n"
|
||||
s := fmt.Sprintf(format, structName)
|
||||
buffer.addLine(s, 0)
|
||||
missingDocs++
|
||||
}
|
||||
kubeType = kubeType[1:] // Skip struct definition
|
||||
|
||||
for _, pair := range kubeType { // Iterate only the fields
|
||||
if pair.Doc == "" {
|
||||
format := "In struct: %s, field documentation is missing: %s\n"
|
||||
s := fmt.Sprintf(format, structName, pair.Name)
|
||||
buffer.addLine(s, 0)
|
||||
missingDocs++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := buffer.flushLines(w); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return missingDocs, nil
|
||||
}
|
||||
43
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator_test.go
generated
vendored
Normal file
43
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFmtRawDoc(t *testing.T) {
|
||||
tests := []struct {
|
||||
t, expected string
|
||||
}{
|
||||
{"aaa\n --- asd\n TODO: tooooodo\n toooodoooooo\n", "aaa"},
|
||||
{"aaa\nasd\n TODO: tooooodo\nbbbb\n --- toooodoooooo\n", "aaa asd bbbb"},
|
||||
{" TODO: tooooodo\n", ""},
|
||||
{"Par1\n\nPar2\n\n", "Par1\\n\\nPar2"},
|
||||
{"", ""},
|
||||
{" ", ""},
|
||||
{" \n", ""},
|
||||
{" \n\n ", ""},
|
||||
{"Example:\n\tl1\n\t\tl2\n", "Example:\\n\\tl1\\n\\t\\tl2"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if o := fmtRawDoc(test.t); o != test.expected {
|
||||
t.Fatalf("Expected: %q, got %q", test.expected, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
139
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/types.go
generated
vendored
Normal file
139
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
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
|
||||
|
||||
// Note that the types provided in this file are not versioned and are intended to be
|
||||
// safe to use from within all versions of every API object.
|
||||
|
||||
// TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type,
|
||||
// like this:
|
||||
// type MyAwesomeAPIObject struct {
|
||||
// runtime.TypeMeta `json:",inline"`
|
||||
// ... // other fields
|
||||
// }
|
||||
// func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { unversioned.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind
|
||||
//
|
||||
// TypeMeta is provided here for convenience. You may use it directly from this package or define
|
||||
// your own with the same fields.
|
||||
//
|
||||
// +protobuf=true
|
||||
type TypeMeta struct {
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
}
|
||||
|
||||
// RawExtension is used to hold extensions in external versions.
|
||||
//
|
||||
// To use this, make a field which has RawExtension as its type in your external, versioned
|
||||
// struct, and Object in your internal struct. You also need to register your
|
||||
// various plugin types.
|
||||
//
|
||||
// // Internal package:
|
||||
// type MyAPIObject struct {
|
||||
// runtime.TypeMeta `json:",inline"`
|
||||
// MyPlugin runtime.Object `json:"myPlugin"`
|
||||
// }
|
||||
// type PluginA struct {
|
||||
// AOption string `json:"aOption"`
|
||||
// }
|
||||
//
|
||||
// // External package:
|
||||
// type MyAPIObject struct {
|
||||
// runtime.TypeMeta `json:",inline"`
|
||||
// MyPlugin runtime.RawExtension `json:"myPlugin"`
|
||||
// }
|
||||
// type PluginA struct {
|
||||
// AOption string `json:"aOption"`
|
||||
// }
|
||||
//
|
||||
// // On the wire, the JSON will look something like this:
|
||||
// {
|
||||
// "kind":"MyAPIObject",
|
||||
// "apiVersion":"v1",
|
||||
// "myPlugin": {
|
||||
// "kind":"PluginA",
|
||||
// "aOption":"foo",
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// So what happens? Decode first uses json or yaml to unmarshal the serialized data into
|
||||
// your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.
|
||||
// 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
|
||||
// JSON stored in RawExtension, turning it into the correct object type, and storing it
|
||||
// in the Object. (TODO: In the case where the object is of an unknown type, a
|
||||
// runtime.Unknown object will be created and stored.)
|
||||
//
|
||||
// +protobuf=true
|
||||
type RawExtension struct {
|
||||
// RawJSON is the underlying serialization of this object.
|
||||
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
|
||||
// to deal with the API objects from a plug-in. Unknown objects still have functioning
|
||||
// TypeMeta features-- kind, version, etc.
|
||||
// TODO: Make this object have easy access to field based accessors and settors for
|
||||
// metadata and field mutatation.
|
||||
//
|
||||
// +protobuf=true
|
||||
type Unknown struct {
|
||||
TypeMeta `json:",inline"`
|
||||
// RawJSON will hold the complete JSON of the object which couldn't be matched
|
||||
// with a registered type. Most likely, nothing should be done with this
|
||||
// except for passing it through the system.
|
||||
RawJSON []byte
|
||||
}
|
||||
|
||||
// Unstructured allows objects that do not have Golang structs registered to be manipulated
|
||||
// generically. This can be used to deal with the API objects from a plug-in. Unstructured
|
||||
// objects still have functioning TypeMeta features-- kind, version, etc.
|
||||
// TODO: Make this object have easy access to field based accessors and settors for
|
||||
// metadata and field mutatation.
|
||||
type Unstructured struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
// Name is populated from metadata (if present) upon deserialization
|
||||
Name string
|
||||
|
||||
// Object is a JSON compatible map with string, float, int, []interface{}, or map[string]interface{}
|
||||
// children.
|
||||
Object map[string]interface{}
|
||||
}
|
||||
|
||||
// UnstructuredList allows lists that do not have Golang structs
|
||||
// registered to be manipulated generically. This can be used to deal
|
||||
// with the API lists from a plug-in.
|
||||
type UnstructuredList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
|
||||
// Items is a list of unstructured objects.
|
||||
Items []*Unstructured `json:"items"`
|
||||
}
|
||||
|
||||
// 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 occurred,
|
||||
// other objects may be present. The right most object is the same as would be returned
|
||||
// by a normal Decode call.
|
||||
Objects []Object
|
||||
}
|
||||
159
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured.go
generated
vendored
Normal file
159
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
|
||||
// type, which can be used for generic access to objects without a predefined scheme.
|
||||
// TODO: move into serializer/json.
|
||||
var UnstructuredJSONScheme Codec = unstructuredJSONScheme{}
|
||||
|
||||
type unstructuredJSONScheme struct{}
|
||||
|
||||
func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionKind, obj Object) (Object, *unversioned.GroupVersionKind, error) {
|
||||
var err error
|
||||
if obj != nil {
|
||||
err = s.decodeInto(data, obj)
|
||||
} else {
|
||||
obj, err = s.decode(data)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
if len(gvk.Kind) == 0 {
|
||||
return nil, gvk, NewMissingKindErr(string(data))
|
||||
}
|
||||
|
||||
return obj, gvk, nil
|
||||
}
|
||||
|
||||
func (unstructuredJSONScheme) EncodeToStream(obj Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
|
||||
switch t := obj.(type) {
|
||||
case *Unstructured:
|
||||
return json.NewEncoder(w).Encode(t.Object)
|
||||
case *UnstructuredList:
|
||||
type encodeList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
Items []map[string]interface{} `json:"items"`
|
||||
}
|
||||
eList := encodeList{
|
||||
TypeMeta: t.TypeMeta,
|
||||
}
|
||||
for _, i := range t.Items {
|
||||
eList.Items = append(eList.Items, i.Object)
|
||||
}
|
||||
return json.NewEncoder(w).Encode(eList)
|
||||
case *Unknown:
|
||||
_, err := w.Write(t.RawJSON)
|
||||
return err
|
||||
default:
|
||||
return json.NewEncoder(w).Encode(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (s unstructuredJSONScheme) decode(data []byte) (Object, error) {
|
||||
type detector struct {
|
||||
Items json.RawMessage
|
||||
}
|
||||
var det detector
|
||||
if err := json.Unmarshal(data, &det); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if det.Items != nil {
|
||||
list := &UnstructuredList{}
|
||||
err := s.decodeToList(data, list)
|
||||
return list, err
|
||||
}
|
||||
|
||||
// No Items field, so it wasn't a list.
|
||||
unstruct := &Unstructured{}
|
||||
err := s.decodeToUnstructured(data, unstruct)
|
||||
return unstruct, err
|
||||
}
|
||||
func (s unstructuredJSONScheme) decodeInto(data []byte, obj Object) error {
|
||||
switch x := obj.(type) {
|
||||
case *Unstructured:
|
||||
return s.decodeToUnstructured(data, x)
|
||||
case *UnstructuredList:
|
||||
return s.decodeToList(data, x)
|
||||
default:
|
||||
return json.Unmarshal(data, x)
|
||||
}
|
||||
}
|
||||
|
||||
func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, ok := m["kind"]; ok {
|
||||
if s, ok := v.(string); ok {
|
||||
unstruct.Kind = s
|
||||
}
|
||||
}
|
||||
if v, ok := m["apiVersion"]; ok {
|
||||
if s, ok := v.(string); ok {
|
||||
unstruct.APIVersion = s
|
||||
}
|
||||
}
|
||||
if metadata, ok := m["metadata"]; ok {
|
||||
if metadata, ok := metadata.(map[string]interface{}); ok {
|
||||
if name, ok := metadata["name"]; ok {
|
||||
if name, ok := name.(string); ok {
|
||||
unstruct.Name = name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unstruct.Object = m
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error {
|
||||
type decodeList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
Items []json.RawMessage
|
||||
}
|
||||
|
||||
var dList decodeList
|
||||
if err := json.Unmarshal(data, &dList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list.TypeMeta = dList.TypeMeta
|
||||
list.Items = nil
|
||||
for _, i := range dList.Items {
|
||||
unstruct := &Unstructured{}
|
||||
if err := s.decodeToUnstructured([]byte(i), unstruct); err != nil {
|
||||
return err
|
||||
}
|
||||
list.Items = append(list.Items, unstruct)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
123
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured_test.go
generated
vendored
Normal file
123
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestDecodeUnstructured(t *testing.T) {
|
||||
groupVersionString := testapi.Default.GroupVersion().String()
|
||||
rawJson := fmt.Sprintf(`{"kind":"Pod","apiVersion":"%s","metadata":{"name":"test"}}`, groupVersionString)
|
||||
pl := &api.List{
|
||||
Items: []runtime.Object{
|
||||
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
|
||||
&runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: groupVersionString}, RawJSON: []byte(rawJson)},
|
||||
&runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "", APIVersion: groupVersionString}, RawJSON: []byte(rawJson)},
|
||||
&runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}},
|
||||
},
|
||||
}
|
||||
if errs := runtime.DecodeList(pl.Items, runtime.UnstructuredJSONScheme); len(errs) == 1 {
|
||||
t.Fatalf("unexpected error %v", errs)
|
||||
}
|
||||
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])
|
||||
}
|
||||
if pod, ok := pl.Items[2].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" {
|
||||
t.Errorf("object not converted: %#v", pl.Items[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
tcs := []struct {
|
||||
json []byte
|
||||
want runtime.Object
|
||||
}{
|
||||
{
|
||||
json: []byte(`{"apiVersion": "test", "kind": "test_kind"}`),
|
||||
want: &runtime.Unstructured{
|
||||
TypeMeta: runtime.TypeMeta{
|
||||
APIVersion: "test",
|
||||
Kind: "test_kind",
|
||||
},
|
||||
Object: map[string]interface{}{"apiVersion": "test", "kind": "test_kind"},
|
||||
},
|
||||
},
|
||||
{
|
||||
json: []byte(`{"apiVersion": "test", "kind": "test_list", "items": []}`),
|
||||
want: &runtime.UnstructuredList{
|
||||
TypeMeta: runtime.TypeMeta{
|
||||
APIVersion: "test",
|
||||
Kind: "test_list",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
json: []byte(`{"items": [{"metadata": {"name": "object1"}, "apiVersion": "test", "kind": "test_kind"}, {"metadata": {"name": "object2"}, "apiVersion": "test", "kind": "test_kind"}], "apiVersion": "test", "kind": "test_list"}`),
|
||||
want: &runtime.UnstructuredList{
|
||||
TypeMeta: runtime.TypeMeta{
|
||||
APIVersion: "test",
|
||||
Kind: "test_list",
|
||||
},
|
||||
Items: []*runtime.Unstructured{
|
||||
{
|
||||
TypeMeta: runtime.TypeMeta{
|
||||
APIVersion: "test",
|
||||
Kind: "test_kind",
|
||||
},
|
||||
Name: "object1",
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"name": "object1"},
|
||||
"apiVersion": "test",
|
||||
"kind": "test_kind",
|
||||
},
|
||||
},
|
||||
{
|
||||
TypeMeta: runtime.TypeMeta{
|
||||
APIVersion: "test",
|
||||
Kind: "test_kind",
|
||||
},
|
||||
Name: "object2",
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"name": "object2"},
|
||||
"apiVersion": "test",
|
||||
"kind": "test_kind",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
got, _, err := runtime.UnstructuredJSONScheme.Decode(tc.json, nil, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for %q: %v", string(tc.json), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("Decode(%q) want: %v\ngot: %v", string(tc.json), tc.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unversioned_test.go
generated
vendored
Normal file
92
Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unversioned_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
// TODO: Ideally we should create the necessary package structure in e.g.,
|
||||
// pkg/conversion/test/... instead of importing pkg/api here.
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
var status = &unversioned.Status{
|
||||
Status: unversioned.StatusFailure,
|
||||
Code: 200,
|
||||
Reason: unversioned.StatusReasonUnknown,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
func TestV1EncodeDecodeStatus(t *testing.T) {
|
||||
|
||||
v1Codec := testapi.Default.Codec()
|
||||
|
||||
encoded, err := runtime.Encode(v1Codec, status)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
typeMeta := unversioned.TypeMeta{}
|
||||
if err := json.Unmarshal(encoded, &typeMeta); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if typeMeta.Kind != "Status" {
|
||||
t.Errorf("Kind is not set to \"Status\". Got %v", string(encoded))
|
||||
}
|
||||
if typeMeta.APIVersion != "v1" {
|
||||
t.Errorf("APIVersion is not set to \"v1\". Got %v", string(encoded))
|
||||
}
|
||||
decoded, err := runtime.Decode(v1Codec, encoded)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(status, decoded) {
|
||||
t.Errorf("expected: %v, got: %v", status, decoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExperimentalEncodeDecodeStatus(t *testing.T) {
|
||||
// TODO: caesarxuchao: use the testapi.Extensions.Codec() once the PR that
|
||||
// moves experimental from v1 to v1beta1 got merged.
|
||||
expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion)
|
||||
encoded, err := runtime.Encode(expCodec, status)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
typeMeta := unversioned.TypeMeta{}
|
||||
if err := json.Unmarshal(encoded, &typeMeta); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if typeMeta.Kind != "Status" {
|
||||
t.Errorf("Kind is not set to \"Status\". Got %s", encoded)
|
||||
}
|
||||
if typeMeta.APIVersion != "v1" {
|
||||
t.Errorf("APIVersion is not set to \"\". Got %s", encoded)
|
||||
}
|
||||
decoded, err := runtime.Decode(expCodec, encoded)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(status, decoded) {
|
||||
t.Errorf("expected: %v, got: %v", status, decoded)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue