From 9d5df20efe0e34bcae9425398d45727e145f3b2b Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 20 Dec 2015 01:37:50 -0500 Subject: [PATCH 01/17] jsonpath: Handle interface{}(nil) as empty array --- pkg/util/jsonpath/jsonpath.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/util/jsonpath/jsonpath.go b/pkg/util/jsonpath/jsonpath.go index 005e21d013c3..35fcdd92d7af 100644 --- a/pkg/util/jsonpath/jsonpath.go +++ b/pkg/util/jsonpath/jsonpath.go @@ -205,7 +205,7 @@ func (j *JSONPath) evalIdentifier(input []reflect.Value, node *IdentifierNode) ( return results, fmt.Errorf("not in range, nothing to end") } default: - return input, fmt.Errorf("unrecongnized identifier %v", node.Name) + return input, fmt.Errorf("unrecognized identifier %v", node.Name) } return results, nil } @@ -216,7 +216,10 @@ func (j *JSONPath) evalArray(input []reflect.Value, node *ArrayNode) ([]reflect. for _, value := range input { value, isNil := template.Indirect(value) - if isNil || (value.Kind() != reflect.Array && value.Kind() != reflect.Slice) { + if isNil { + continue + } + if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { return input, fmt.Errorf("%v is not array or slice", value.Type()) } params := node.Params @@ -404,7 +407,7 @@ func (j *JSONPath) evalFilter(input []reflect.Value, node *FilterNode) ([]reflec value, _ = template.Indirect(value) if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { - return input, fmt.Errorf("%v is not array or slice", value) + return input, fmt.Errorf("%v is not array or slice and cannot be filtered", value) } for i := 0; i < value.Len(); i++ { temp := []reflect.Value{value.Index(i)} From 6582b4c2eab22257bab264c37883a04668c85aa5 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:07:15 -0500 Subject: [PATCH 02/17] Remove codec awareness from conversion Allow convertors to be specialized, some cleanup to how conversion functions are stored internally to allow better reuse. --- pkg/conversion/converter.go | 122 +++++++-- pkg/conversion/converter_test.go | 211 +++++++++++++++ pkg/conversion/decode.go | 194 -------------- pkg/conversion/doc.go | 9 +- pkg/conversion/encode.go | 155 ----------- pkg/conversion/error.go | 5 + pkg/conversion/helper.go | 39 +++ pkg/conversion/helper_test.go | 38 +++ pkg/conversion/meta.go | 153 ----------- pkg/conversion/meta_test.go | 289 --------------------- pkg/conversion/queryparams/convert.go | 10 +- pkg/conversion/queryparams/convert_test.go | 3 +- pkg/conversion/scheme.go | 142 ++++++---- pkg/conversion/scheme_test.go | 208 +-------------- pkg/conversion/unversioned_test.go | 10 +- pkg/runtime/serializer/protobuf/doc.go | 18 ++ 16 files changed, 506 insertions(+), 1100 deletions(-) delete mode 100644 pkg/conversion/decode.go delete mode 100644 pkg/conversion/encode.go create mode 100644 pkg/conversion/helper.go create mode 100644 pkg/conversion/helper_test.go delete mode 100644 pkg/conversion/meta.go delete mode 100644 pkg/conversion/meta_test.go create mode 100644 pkg/runtime/serializer/protobuf/doc.go diff --git a/pkg/conversion/converter.go b/pkg/conversion/converter.go index 8c106d526119..00c9bcb492c3 100644 --- a/pkg/conversion/converter.go +++ b/pkg/conversion/converter.go @@ -40,8 +40,11 @@ type DebugLogger interface { type Converter struct { // Map from the conversion pair to a function which can // do the conversion. - conversionFuncs map[typePair]reflect.Value - generatedConversionFuncs map[typePair]reflect.Value + conversionFuncs ConversionFuncs + generatedConversionFuncs ConversionFuncs + + // Set of conversions that should be treated as a no-op + ignoredConversions map[typePair]struct{} // This is a map from a source field type and name, to a list of destination // field type and name. @@ -76,21 +79,30 @@ type Converter struct { // NewConverter creates a new Converter object. func NewConverter() *Converter { c := &Converter{ - conversionFuncs: map[typePair]reflect.Value{}, - generatedConversionFuncs: map[typePair]reflect.Value{}, - defaultingFuncs: map[reflect.Type]reflect.Value{}, - defaultingInterfaces: map[reflect.Type]interface{}{}, + conversionFuncs: NewConversionFuncs(), + generatedConversionFuncs: NewConversionFuncs(), + ignoredConversions: make(map[typePair]struct{}), + defaultingFuncs: make(map[reflect.Type]reflect.Value), + defaultingInterfaces: make(map[reflect.Type]interface{}), nameFunc: func(t reflect.Type) string { return t.Name() }, - structFieldDests: map[typeNamePair][]typeNamePair{}, - structFieldSources: map[typeNamePair][]typeNamePair{}, + structFieldDests: make(map[typeNamePair][]typeNamePair), + structFieldSources: make(map[typeNamePair][]typeNamePair), - inputFieldMappingFuncs: map[reflect.Type]FieldMappingFunc{}, - inputDefaultFlags: map[reflect.Type]FieldMatchingFlags{}, + inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc), + inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags), } c.RegisterConversionFunc(ByteSliceCopy) return c } +// WithConversions returns a Converter that is a copy of c but with the additional +// fns merged on top. +func (c *Converter) WithConversions(fns ConversionFuncs) *Converter { + copied := *c + copied.conversionFuncs = c.conversionFuncs.Merge(fns) + return &copied +} + // ByteSliceCopy prevents recursing into every byte func ByteSliceCopy(in *[]byte, out *[]byte, s Scope) error { *out = make([]byte, len(*in)) @@ -130,6 +142,42 @@ type Scope interface { // the value of the source or destination struct tags. type FieldMappingFunc func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string) +func NewConversionFuncs() ConversionFuncs { + return ConversionFuncs{fns: make(map[typePair]reflect.Value)} +} + +type ConversionFuncs struct { + fns map[typePair]reflect.Value +} + +// Add adds the provided conversion functions to the lookup table - they must have the signature +// `func(type1, type2, Scope) error`. Functions are added in the order passed and will override +// previously registered pairs. +func (c ConversionFuncs) Add(fns ...interface{}) error { + for _, fn := range fns { + fv := reflect.ValueOf(fn) + ft := fv.Type() + if err := verifyConversionFunctionSignature(ft); err != nil { + return err + } + c.fns[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv + } + return nil +} + +// Merge returns a new ConversionFuncs that contains all conversions from +// both other and c, with other conversions taking precedence. +func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs { + merged := NewConversionFuncs() + for k, v := range c.fns { + merged.fns[k] = v + } + for k, v := range other.fns { + merged.fns[k] = v + } + return merged +} + // Meta is supplied by Scheme, when it calls Convert. type Meta struct { SrcVersion string @@ -296,34 +344,44 @@ func verifyConversionFunctionSignature(ft reflect.Type) error { // return nil // }) func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error { - fv := reflect.ValueOf(conversionFunc) - ft := fv.Type() - if err := verifyConversionFunctionSignature(ft); err != nil { - return err - } - c.conversionFuncs[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv - return nil + return c.conversionFuncs.Add(conversionFunc) } // Similar to RegisterConversionFunc, but registers conversion function that were // automatically generated. func (c *Converter) RegisterGeneratedConversionFunc(conversionFunc interface{}) error { - fv := reflect.ValueOf(conversionFunc) - ft := fv.Type() - if err := verifyConversionFunctionSignature(ft); err != nil { - return err + return c.generatedConversionFuncs.Add(conversionFunc) +} + +// RegisterIgnoredConversion registers a "no-op" for conversion, where any requested +// conversion between from and to is ignored. +func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error { + typeFrom := reflect.TypeOf(from) + typeTo := reflect.TypeOf(to) + if reflect.TypeOf(from).Kind() != reflect.Ptr { + return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom) } - c.generatedConversionFuncs[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv + if typeTo.Kind() != reflect.Ptr { + return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo) + } + c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{} return nil } +// IsConversionIgnored returns true if the specified objects should be dropped during +// conversion. +func (c *Converter) IsConversionIgnored(inType, outType reflect.Type) bool { + _, found := c.ignoredConversions[typePair{inType, outType}] + return found +} + func (c *Converter) HasConversionFunc(inType, outType reflect.Type) bool { - _, found := c.conversionFuncs[typePair{inType, outType}] + _, found := c.conversionFuncs.fns[typePair{inType, outType}] return found } func (c *Converter) ConversionFuncValue(inType, outType reflect.Type) (reflect.Value, bool) { - value, found := c.conversionFuncs[typePair{inType, outType}] + value, found := c.conversionFuncs.fns[typePair{inType, outType}] return value, found } @@ -509,16 +567,26 @@ func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error { fv.Call(args) } + pair := typePair{st, dt} + + // ignore conversions of this type + if _, ok := c.ignoredConversions[pair]; ok { + if c.Debug != nil { + c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt) + } + return nil + } + // Convert sv to dv. - if fv, ok := c.conversionFuncs[typePair{st, dt}]; ok { + if fv, ok := c.conversionFuncs.fns[pair]; ok { if c.Debug != nil { c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt) } return c.callCustom(sv, dv, fv, scope) } - if fv, ok := c.generatedConversionFuncs[typePair{st, dt}]; ok { + if fv, ok := c.generatedConversionFuncs.fns[pair]; ok { if c.Debug != nil { - c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt) + c.Debug.Logf("Calling generated conversion of '%v' to '%v'", st, dt) } return c.callCustom(sv, dv, fv, scope) } diff --git a/pkg/conversion/converter_test.go b/pkg/conversion/converter_test.go index 6ef60347abc5..8787ec37454c 100644 --- a/pkg/conversion/converter_test.go +++ b/pkg/conversion/converter_test.go @@ -24,6 +24,8 @@ import ( "testing" "github.com/google/gofuzz" + + "k8s.io/kubernetes/pkg/api/unversioned" ) func testLogger(t *testing.T) DebugLogger { @@ -221,6 +223,55 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) { } } +func TestConverter_IgnoredConversion(t *testing.T) { + type A struct{} + type B struct{} + + count := 0 + c := NewConverter() + if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { + count++ + return nil + }); err != nil { + t.Fatalf("unexpected error %v", err) + } + if err := c.RegisterIgnoredConversion(&A{}, &B{}); err != nil { + t.Fatal(err) + } + a := A{} + b := B{} + if err := c.Convert(&a, &b, 0, nil); err != nil { + t.Errorf("%v", err) + } + if count != 0 { + t.Errorf("unexpected number of conversion invocations") + } +} + +func TestConverter_IgnoredConversionNested(t *testing.T) { + type C string + type A struct { + C C + } + type B struct { + C C + } + + c := NewConverter() + typed := C("") + if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil { + t.Fatal(err) + } + a := A{C: C("test")} + b := B{C: C("other")} + if err := c.Convert(&a, &b, AllowDifferentFieldTypeNames, nil); err != nil { + t.Errorf("%v", err) + } + if b.C != C("other") { + t.Errorf("expected no conversion of field C: %#v", b) + } +} + func TestConverter_GeneratedConversionOverriden(t *testing.T) { type A struct{} type B struct{} @@ -243,6 +294,37 @@ func TestConverter_GeneratedConversionOverriden(t *testing.T) { } } +func TestConverter_WithConversionOverriden(t *testing.T) { + type A struct{} + type B struct{} + c := NewConverter() + if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { + return fmt.Errorf("conversion function should be overriden") + }); err != nil { + t.Fatalf("unexpected error %v", err) + } + if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error { + return fmt.Errorf("generated function should be overriden") + }); err != nil { + t.Fatalf("unexpected error %v", err) + } + + ext := NewConversionFuncs() + ext.Add(func(in *A, out *B, s Scope) error { + return nil + }) + newc := c.WithConversions(ext) + + a := A{} + b := B{} + if err := c.Convert(&a, &b, 0, nil); err == nil || err.Error() != "conversion function should be overriden" { + t.Errorf("unexpected error: %v", err) + } + if err := newc.Convert(&a, &b, 0, nil); err != nil { + t.Errorf("%v", err) + } +} + func TestConverter_MapsStringArrays(t *testing.T) { type A struct { Foo string @@ -681,3 +763,132 @@ func TestConverter_FieldRename(t *testing.T) { } } } + +func TestMetaValues(t *testing.T) { + type InternalSimple struct { + APIVersion string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + TestString string `json:"testString"` + } + type ExternalSimple struct { + APIVersion string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + TestString string `json:"testString"` + } + internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} + + s := 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 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 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 := 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 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) + } +} diff --git a/pkg/conversion/decode.go b/pkg/conversion/decode.go deleted file mode 100644 index 0e5c7a762856..000000000000 --- a/pkg/conversion/decode.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -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 conversion - -import ( - "errors" - "fmt" - "net/url" - - "github.com/ugorji/go/codec" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -func (s *Scheme) DecodeToVersionedObject(data []byte) (interface{}, unversioned.GroupVersionKind, error) { - kind, err := s.DataKind(data) - if err != nil { - return nil, unversioned.GroupVersionKind{}, err - } - - internalGV, exists := s.InternalVersions[kind.Group] - if !exists { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("no internalVersion specified for %v", kind) - } - - if len(kind.Group) == 0 && len(internalGV.Group) != 0 { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("group not set in '%s'", string(data)) - } - if len(kind.Version) == 0 && len(internalGV.Version) != 0 { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("version not set in '%s'", string(data)) - } - if kind.Kind == "" { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("kind not set in '%s'", string(data)) - } - - obj, err := s.NewObject(kind) - if err != nil { - return nil, unversioned.GroupVersionKind{}, err - } - - if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil { - return nil, unversioned.GroupVersionKind{}, err - } - return obj, kind, nil -} - -// Decode converts a JSON string back into a pointer to an api object. -// Deduces the type based upon the fields added by the MetaInsertionFactory -// technique. The object will be converted, if necessary, into the -// s.InternalVersion type before being returned. Decode will not decode -// objects without version set unless InternalVersion is also "". -func (s *Scheme) Decode(data []byte) (interface{}, error) { - return s.DecodeToVersion(data, unversioned.GroupVersion{}) -} - -// DecodeToVersion converts a JSON string back into a pointer to an api object. -// Deduces the type based upon the fields added by the MetaInsertionFactory -// technique. The object will be converted, if necessary, into the versioned -// type before being returned. Decode will not decode objects without version -// set unless version is also "". -// a GroupVersion with .IsEmpty() == true is means "use the internal version for -// the object's group" -func (s *Scheme) DecodeToVersion(data []byte, targetVersion unversioned.GroupVersion) (interface{}, error) { - obj, sourceKind, err := s.DecodeToVersionedObject(data) - if err != nil { - return nil, err - } - // Version and Kind should be blank in memory. - if err := s.SetVersionAndKind("", "", obj); err != nil { - return nil, err - } - - // if the targetVersion is empty, then we want the internal version, but the internal version varies by - // group. We can lookup the group now because we have knowledge of the group - if targetVersion.IsEmpty() { - exists := false - targetVersion, exists = s.InternalVersions[sourceKind.Group] - if !exists { - return nil, fmt.Errorf("no internalVersion specified for %v", targetVersion) - } - } - - // Convert if needed. - if targetVersion != sourceKind.GroupVersion() { - objOut, err := s.NewObject(targetVersion.WithKind(sourceKind.Kind)) - if err != nil { - return nil, err - } - flags, meta := s.generateConvertMeta(sourceKind.GroupVersion(), targetVersion, obj) - if err := s.converter.Convert(obj, objOut, flags, meta); err != nil { - return nil, err - } - obj = objOut - } - return obj, nil -} - -// DecodeInto parses a JSON string and stores it in obj. Returns an error -// if data.Kind is set and doesn't match the type of obj. Obj should be a -// pointer to an api type. -// If obj's version doesn't match that in data, an attempt will be made to convert -// data into obj's version. -func (s *Scheme) DecodeInto(data []byte, obj interface{}) error { - return s.DecodeIntoWithSpecifiedVersionKind(data, obj, unversioned.GroupVersionKind{}) -} - -// DecodeIntoWithSpecifiedVersionKind compares the passed in requestGroupVersionKind -// with data.Version and data.Kind, defaulting data.Version and -// data.Kind to the specified value if they are empty, or generating an error if -// data.Version and data.Kind are not empty and differ from the specified value. -// The function then implements the functionality of DecodeInto. -// If specifiedVersion and specifiedKind are empty, the function degenerates to -// DecodeInto. -func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, requestedGVK unversioned.GroupVersionKind) error { - if len(data) == 0 { - return errors.New("empty input") - } - dataKind, err := s.DataKind(data) - if err != nil { - return err - } - if len(dataKind.Group) == 0 { - dataKind.Group = requestedGVK.Group - } - if len(dataKind.Version) == 0 { - dataKind.Version = requestedGVK.Version - } - if len(dataKind.Kind) == 0 { - dataKind.Kind = requestedGVK.Kind - } - - if len(requestedGVK.Group) > 0 && requestedGVK.Group != dataKind.Group { - return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataKind, requestedGVK)) - } - if len(requestedGVK.Version) > 0 && requestedGVK.Version != dataKind.Version { - return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataKind, requestedGVK)) - } - if len(requestedGVK.Kind) > 0 && requestedGVK.Kind != dataKind.Kind { - return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataKind, requestedGVK)) - } - - objGVK, err := s.ObjectKind(obj) - if err != nil { - return err - } - // Assume objects with unset fields are being unmarshalled into the - // correct type. - if len(dataKind.Group) == 0 { - dataKind.Group = objGVK.Group - } - if len(dataKind.Version) == 0 { - dataKind.Version = objGVK.Version - } - if len(dataKind.Kind) == 0 { - dataKind.Kind = objGVK.Kind - } - - external, err := s.NewObject(dataKind) - if err != nil { - return err - } - if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(external); err != nil { - return err - } - flags, meta := s.generateConvertMeta(dataKind.GroupVersion(), objGVK.GroupVersion(), external) - if err := s.converter.Convert(external, obj, flags, meta); err != nil { - return err - } - - // Version and Kind should be blank in memory. - return s.SetVersionAndKind("", "", obj) -} - -func (s *Scheme) DecodeParametersInto(parameters url.Values, obj interface{}) error { - if err := s.Convert(¶meters, obj); err != nil { - return err - } - // TODO: Should we do any convertion here? - return nil -} diff --git a/pkg/conversion/doc.go b/pkg/conversion/doc.go index f0626a569c21..3ef2eaba4576 100644 --- a/pkg/conversion/doc.go +++ b/pkg/conversion/doc.go @@ -14,18 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package conversion provides go object versioning and encoding/decoding -// mechanisms. +// Package conversion provides go object versioning. // // Specifically, conversion provides a way for you to define multiple versions // of the same object. You may write functions which implement conversion logic, // but for the fields which did not change, copying is automated. This makes it // easy to modify the structures you use in memory without affecting the format // you store on disk or respond to in your external API calls. -// -// The second offering of this package is automated encoding/decoding. The version -// and type of the object is recorded in the output, so it can be recreated upon -// reading. Currently, conversion writes JSON output, and interprets both JSON -// and YAML input. -// package conversion diff --git a/pkg/conversion/encode.go b/pkg/conversion/encode.go deleted file mode 100644 index 3120583d454d..000000000000 --- a/pkg/conversion/encode.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -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 conversion - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "path" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -// EncodeToVersion turns the given api object into an appropriate JSON string. -// Obj may be a pointer to a struct, or a struct. If a struct, a copy -// will be made, therefore it's recommended to pass a pointer to a -// struct. The type must have been registered. -// -// Memory/wire format differences: -// * Having to keep track of the Kind and Version fields makes tests -// very annoying, so the rule is that they are set only in wire format -// (json), not when in native (memory) format. This is possible because -// both pieces of information are implicit in the go typed object. -// * An exception: note that, if there are embedded API objects of known -// type, for example, PodList{... Items []Pod ...}, these embedded -// objects must be of the same version of the object they are embedded -// within, and their Version and Kind must both be empty. -// * Note that the exception does not apply to a generic APIObject type -// which recursively does Encode()/Decode(), and is capable of -// expressing any API object. -// * Only versioned objects should be encoded. This means that, if you pass -// a native object, Encode will convert it to a versioned object. For -// example, an api.Pod will get converted to a v1.Pod. However, if -// you pass in an object that's already versioned (v1.Pod), Encode -// will not modify it. -// -// The purpose of the above complex conversion behavior is to allow us to -// change the memory format yet not break compatibility with any stored -// objects, whether they be in our storage layer (e.g., etcd), or in user's -// config files. -// -func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) { - buff := &bytes.Buffer{} - if err := s.EncodeToVersionStream(obj, destVersion, buff); err != nil { - return nil, err - } - return buff.Bytes(), nil -} - -func (s *Scheme) EncodeToVersionStream(obj interface{}, destGroupVersionString string, stream io.Writer) error { - obj = maybeCopy(obj) - v, _ := EnforcePtr(obj) // maybeCopy guarantees a pointer - - // Don't encode an object defined in the unversioned package, unless if the - // destGroupVersionString is v1, encode it to v1 for backward compatibility. - pkg := path.Base(v.Type().PkgPath()) - if pkg == "unversioned" && destGroupVersionString != "v1" { - // TODO: convert this to streaming too - data, err := s.encodeUnversionedObject(obj) - if err != nil { - return err - } - _, err = stream.Write(data) - return err - } - - if _, registered := s.typeToGVK[v.Type()]; !registered { - return fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destGroupVersionString) - } - - objKind, err := s.ObjectKind(obj) - if err != nil { - return err - } - - destVersion, err := unversioned.ParseGroupVersion(destGroupVersionString) - if err != nil { - return err - } - - // Perform a conversion if necessary. - if objKind.GroupVersion() != destVersion { - objOut, err := s.NewObject(destVersion.WithKind(objKind.Kind)) - if err != nil { - return err - } - flags, meta := s.generateConvertMeta(objKind.GroupVersion(), destVersion, obj) - err = s.converter.Convert(obj, objOut, flags, meta) - if err != nil { - return err - } - obj = objOut - - // ensure the output object name comes from the destination type - newGroupVersionKind, err := s.ObjectKind(obj) - if err != nil { - return err - } - objKind.Kind = newGroupVersionKind.Kind - } - - // Version and Kind should be set on the wire. - err = s.SetVersionAndKind(destVersion.String(), objKind.Kind, obj) - if err != nil { - return err - } - - // To add metadata, do some simple surgery on the JSON. - encoder := json.NewEncoder(stream) - if err := encoder.Encode(obj); err != nil { - return err - } - - // Version and Kind should be blank in memory. Reset them, since it's - // possible that we modified a user object and not a copy above. - err = s.SetVersionAndKind("", "", obj) - if err != nil { - return err - } - - return nil -} - -func (s *Scheme) encodeUnversionedObject(obj interface{}) (data []byte, err error) { - objGVK, err := s.ObjectKind(obj) - if err != nil { - return nil, err - } - if err = s.SetVersionAndKind("", objGVK.Kind, obj); err != nil { - return nil, err - } - data, err = json.Marshal(obj) - if err != nil { - return nil, err - } - // Version and Kind should be blank in memory. Reset them, since it's - // possible that we modified a user object and not a copy above. - err = s.SetVersionAndKind("", "", obj) - return data, nil -} diff --git a/pkg/conversion/error.go b/pkg/conversion/error.go index 6ddbf9b7a783..94075e8adfbf 100644 --- a/pkg/conversion/error.go +++ b/pkg/conversion/error.go @@ -28,6 +28,11 @@ type notRegisteredErr struct { 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) diff --git a/pkg/conversion/helper.go b/pkg/conversion/helper.go new file mode 100644 index 000000000000..39f78265959b --- /dev/null +++ b/pkg/conversion/helper.go @@ -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 conversion + +import ( + "fmt" + "reflect" +) + +// EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value +// of the dereferenced pointer, ensuring that it is settable/addressable. +// Returns an error if this is not possible. +func EnforcePtr(obj interface{}) (reflect.Value, error) { + v := reflect.ValueOf(obj) + if v.Kind() != reflect.Ptr { + if v.Kind() == reflect.Invalid { + return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind") + } + return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type()) + } + if v.IsNil() { + return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") + } + return v.Elem(), nil +} diff --git a/pkg/conversion/helper_test.go b/pkg/conversion/helper_test.go new file mode 100644 index 000000000000..69fef3334b16 --- /dev/null +++ b/pkg/conversion/helper_test.go @@ -0,0 +1,38 @@ +/* +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 conversion + +import "testing" + +func TestInvalidPtrValueKind(t *testing.T) { + var simple interface{} + switch obj := simple.(type) { + default: + _, err := EnforcePtr(obj) + if err == nil { + t.Errorf("Expected error on invalid kind") + } + } +} + +func TestEnforceNilPtr(t *testing.T) { + var nilPtr *struct{} + _, err := EnforcePtr(nilPtr) + if err == nil { + t.Errorf("Expected error on nil pointer") + } +} diff --git a/pkg/conversion/meta.go b/pkg/conversion/meta.go deleted file mode 100644 index bb33d12cb56e..000000000000 --- a/pkg/conversion/meta.go +++ /dev/null @@ -1,153 +0,0 @@ -/* -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 conversion - -import ( - "encoding/json" - "fmt" - "path" - "reflect" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -// MetaFactory is used to store and retrieve the version and kind -// information for all objects in a scheme. -type MetaFactory interface { - // Update sets the given version and kind onto the object. - Update(version, kind string, obj interface{}) error - // Interpret should return the group,version,kind of the wire-format of - // the object. - Interpret(data []byte) (gvk unversioned.GroupVersionKind, err 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{KindField: "Kind", VersionField: "APIVersion"} - -// 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 { - // The name of the API version field in memory of the struct - VersionField string - // The name of the kind field in memory of the struct. - KindField string - // Optional, if set will look in the named inline structs to find the fields to set. - BaseFields []string -} - -// Interpret will return the group,version,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"` - }{} - err := json.Unmarshal(data, &findKind) - if err != nil { - return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't get version/kind; json parse error: %v", err) - } - gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) - if err != nil { - return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't parse apiVersion: %v", err) - } - - return gv.WithKind(findKind.Kind), nil -} - -func (f SimpleMetaFactory) Update(version, kind string, obj interface{}) error { - return UpdateVersionAndKind(f.BaseFields, f.VersionField, version, f.KindField, kind, obj) -} - -// UpdateVersionAndKind uses reflection to find and set the versionField and kindField fields -// on a pointer to a struct to version and kind. Provided as a convenience for others -// implementing MetaFactory. Pass an array to baseFields to check one or more nested structs -// for the named fields. The version field is treated as optional if it is not present in the struct. -// TODO: this method is on its way out -func UpdateVersionAndKind(baseFields []string, versionField, version, kindField, kind string, obj interface{}) error { - if typed, ok := obj.(unversioned.ObjectKind); ok { - if len(version) == 0 && len(kind) == 0 { - typed.SetGroupVersionKind(nil) - } else { - gv, err := unversioned.ParseGroupVersion(version) - if err != nil { - return err - } - typed.SetGroupVersionKind(&unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}) - } - return nil - } - v, err := EnforcePtr(obj) - if err != nil { - return err - } - pkg := path.Base(v.Type().PkgPath()) - t := v.Type() - name := t.Name() - if v.Kind() != reflect.Struct { - return fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), name, v.Interface()) - } - - for i := range baseFields { - base := v.FieldByName(baseFields[i]) - if !base.IsValid() { - continue - } - v = base - } - - field := v.FieldByName(kindField) - if !field.IsValid() { - // Types defined in the unversioned package are allowed to not have a - // kindField. Clients will have to know what they are based on the - // context. - // TODO: add some type trait here, or some way of indicating whether - // this feature is allowed on a per-type basis. Using package name is - // overly broad and a bit hacky. - if pkg == "unversioned" { - return nil - } - return fmt.Errorf("couldn't find %v field in %#v", kindField, v.Interface()) - } - field.SetString(kind) - - if field := v.FieldByName(versionField); field.IsValid() { - field.SetString(version) - } - - return nil -} - -// EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value -// of the dereferenced pointer, ensuring that it is settable/addressable. -// Returns an error if this is not possible. -func EnforcePtr(obj interface{}) (reflect.Value, error) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - if v.Kind() == reflect.Invalid { - return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind") - } - return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type()) - } - if v.IsNil() { - return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") - } - return v.Elem(), nil -} diff --git a/pkg/conversion/meta_test.go b/pkg/conversion/meta_test.go deleted file mode 100644 index 4cf92fb06889..000000000000 --- a/pkg/conversion/meta_test.go +++ /dev/null @@ -1,289 +0,0 @@ -/* -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 conversion - -import ( - "fmt" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -func TestSimpleMetaFactoryInterpret(t *testing.T) { - factory := SimpleMetaFactory{} - fqKind, err := factory.Interpret([]byte(`{"apiVersion":"g/1","kind":"object"}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expectedFQKind := unversioned.GroupVersionKind{Group: "g", Version: "1", Kind: "object"} - if expectedFQKind != fqKind { - t.Errorf("unexpected interpret: %s %s", expectedFQKind, fqKind) - } - - // no kind or version - fqKind, err = factory.Interpret([]byte(`{}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !fqKind.IsEmpty() { - t.Errorf("unexpected interpret: %s %s", fqKind) - } - - // unparsable - fqKind, err = factory.Interpret([]byte(`{`)) - if err == nil { - t.Errorf("unexpected non-error") - } -} - -func TestSimpleMetaFactoryUpdate(t *testing.T) { - factory := SimpleMetaFactory{VersionField: "V", KindField: "K"} - - obj := struct { - V string - K string - }{"1", "2"} - - // must pass a pointer - if err := factory.Update("test", "other", obj); err == nil { - t.Errorf("unexpected non-error") - } - if obj.V != "1" || obj.K != "2" { - t.Errorf("unexpected update: %v", obj) - } - - // updates - if err := factory.Update("test", "other", &obj); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if obj.V != "test" || obj.K != "other" { - t.Errorf("unexpected update: %v", obj) - } -} - -// Test Updating objects that don't have a Kind field. -func TestSimpleMetaFactoryUpdateNoKindField(t *testing.T) { - factory := SimpleMetaFactory{VersionField: "APIVersion", KindField: "Kind"} - // obj does not have a Kind field and is not defined in the unversioned package. - obj := struct { - SomeField string - }{"1"} - expectedError := fmt.Errorf("couldn't find %v field in %#v", factory.KindField, obj) - if err := factory.Update("test", "other", &obj); err == nil || expectedError.Error() != err.Error() { - t.Fatalf("expected error: %v, got: %v", expectedError, err) - } - - // ListMeta does not have a Kind field, but is defined in the unversioned package. - listMeta := unversioned.ListMeta{} - if err := factory.Update("test", "other", &listMeta); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestSimpleMetaFactoryUpdateStruct(t *testing.T) { - factory := SimpleMetaFactory{BaseFields: []string{"Test"}, VersionField: "V", KindField: "K"} - - type Inner struct { - V string - K string - } - obj := struct { - Test Inner - }{Test: Inner{"1", "2"}} - - // updates - if err := factory.Update("test", "other", &obj); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if obj.Test.V != "test" || obj.Test.K != "other" { - t.Errorf("unexpected update: %v", obj) - } -} - -func TestMetaValues(t *testing.T) { - type InternalSimple struct { - APIVersion string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - type ExternalSimple struct { - APIVersion string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} - - s := NewScheme() - s.InternalVersions[internalGV.Group] = internalGV - 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 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 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) - - // Test Encode, Decode, and DecodeInto - data, err := s.EncodeToVersion(simple, externalGV.String()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - t.Logf(string(data)) - obj2, err := s.Decode(data) - if err != nil { - t.Fatalf("unexpected error: %v", 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 := s.DecodeInto(data, obj3); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := simple, obj3; !reflect.DeepEqual(e, a) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) - } - - // Test Convert - 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) - } - - // Encode and Convert should each have caused an increment. - if e, a := 2, internalToExternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - // Decode and DecodeInto should each have caused an increment. - if e, a := 2, 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 := 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 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) - } -} - -func TestInvalidPtrValueKind(t *testing.T) { - var simple interface{} - switch obj := simple.(type) { - default: - _, err := EnforcePtr(obj) - if err == nil { - t.Errorf("Expected error on invalid kind") - } - } -} - -func TestEnforceNilPtr(t *testing.T) { - var nilPtr *struct{} - _, err := EnforcePtr(nilPtr) - if err == nil { - t.Errorf("Expected error on nil pointer") - } -} diff --git a/pkg/conversion/queryparams/convert.go b/pkg/conversion/queryparams/convert.go index 450a43001a21..0f04e7bb4cbf 100644 --- a/pkg/conversion/queryparams/convert.go +++ b/pkg/conversion/queryparams/convert.go @@ -21,8 +21,6 @@ import ( "net/url" "reflect" "strings" - - "k8s.io/kubernetes/pkg/runtime" ) func jsonTag(field reflect.StructField) (string, bool) { @@ -93,10 +91,10 @@ func addListOfParams(values url.Values, tag string, omitempty bool, list reflect } } -// Convert takes a versioned runtime.Object and serializes it to a url.Values object -// using JSON tags as parameter names. Only top-level simple values, arrays, and slices -// are serialized. Embedded structs, maps, etc. will not be serialized. -func Convert(obj runtime.Object) (url.Values, error) { +// Convert takes an object and converts it to a url.Values object using JSON tags as +// parameter names. Only top-level simple values, arrays, and slices are serialized. +// Embedded structs, maps, etc. will not be serialized. +func Convert(obj interface{}) (url.Values, error) { result := url.Values{} if obj == nil { return result, nil diff --git a/pkg/conversion/queryparams/convert_test.go b/pkg/conversion/queryparams/convert_test.go index 8b513f076dbc..405357557fad 100644 --- a/pkg/conversion/queryparams/convert_test.go +++ b/pkg/conversion/queryparams/convert_test.go @@ -23,7 +23,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion/queryparams" - "k8s.io/kubernetes/pkg/runtime" ) type namedString string @@ -85,7 +84,7 @@ func validateResult(t *testing.T, input interface{}, actual, expected url.Values func TestConvert(t *testing.T) { tests := []struct { - input runtime.Object + input interface{} expected url.Values }{ { diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index e5ea10b50c17..236ac39f9718 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -33,6 +33,13 @@ type Scheme struct { // 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 + // converter stores all registered conversion functions. It also has // default coverting behavior. converter *Converter @@ -44,34 +51,17 @@ type Scheme struct { // Indent will cause the JSON output from Encode to be indented, // if and only if it is true. Indent bool - - // InternalVersion is the default internal version. It is recommended that - // you use "" for the internal version. - // TODO logically the InternalVersion is different for every Group, so this structure - // must be map - InternalVersions map[string]unversioned.GroupVersion - - // MetaInsertionFactory is used to create an object to store and retrieve - // the version and kind information for all objects. The default uses the - // keys "apiVersion" and "kind" respectively. - MetaFactory MetaFactory } // NewScheme manufactures a new scheme. func NewScheme() *Scheme { s := &Scheme{ - gvkToType: map[unversioned.GroupVersionKind]reflect.Type{}, - typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{}, - converter: NewConverter(), - cloner: NewCloner(), - // TODO remove this hard coded list. As step one, hardcode it here so this pull doesn't become even bigger - InternalVersions: map[string]unversioned.GroupVersion{ - "": {}, - "componentconfig": {Group: "componentconfig"}, - "extensions": {Group: "extensions"}, - "metrics": {Group: "metrics"}, - }, - MetaFactory: DefaultMetaFactory, + gvkToType: map[unversioned.GroupVersionKind]reflect.Type{}, + typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{}, + unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{}, + unversionedKinds: map[string]reflect.Type{}, + converter: NewConverter(), + cloner: NewCloner(), } s.converter.nameFunc = s.nameFunc return s @@ -92,11 +82,8 @@ func (s *Scheme) nameFunc(t reflect.Type) string { } for _, gvk := range gvks { - internalGV, exists := s.InternalVersions[gvk.Group] - if !exists { - internalGV := gvk.GroupVersion() - internalGV.Version = "" - } + 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 { @@ -107,11 +94,30 @@ func (s *Scheme) nameFunc(t reflect.Type) string { return gvks[0].Kind } +// AddUnversionedTypes registers all types passed in 'types' as being members of version 'version', +// and marks them as being convertible to all API versions. +// All objects passed to types should be pointers to structs. The name that go reports for +// the struct becomes the "kind" field when encoding. +func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...interface{}) { + 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'. -// Encode() will refuse objects unless their type has been registered with AddKnownTypes. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...interface{}) { + 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 { @@ -133,6 +139,9 @@ func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...interface{} // your structs. func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj interface{}) { 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.") } @@ -168,6 +177,9 @@ func (s *Scheme) NewObject(kind unversioned.GroupVersionKind) (interface{}, erro return reflect.New(t).Interface(), nil } + if t, exists := s.unversionedKinds[kind.Kind]; exists { + return reflect.New(t).Interface(), nil + } return nil, ¬RegisteredErr{gvk: kind} } @@ -221,6 +233,13 @@ func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) err return nil } +// AddIgnoredConversionType identifies a pair of types that should be skipped by +// dynamic 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) +} + // AddDeepCopyFuncs adds functions to the list of deep copy functions. // Note that to copy sub-objects, you can use the conversion.Cloner object that // will be passed to your deep-copy function. @@ -282,6 +301,22 @@ func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { return exists } +// IsUnversioned returns true if the Go object is registered as an unversioned type, or sets +// ok to false if the provided object is not registered in the scheme. +func (s *Scheme) IsUnversioned(obj interface{}) (unversioned bool, registered bool) { + v, err := 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 +} + // 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 @@ -330,15 +365,23 @@ func (s *Scheme) ConvertToVersion(in interface{}, outGroupVersionString string) return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) } - gvks, ok := s.typeToGVK[t] - if !ok { - return nil, fmt.Errorf("%v cannot be converted into version %q", t, outGroupVersionString) - } outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString) if err != nil { return nil, err } - outKind := outVersion.WithKind(gvks[0].Kind) + + 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, outGroupVersionString) + } + kind = kinds[0] + } + + outKind := outVersion.WithKind(kind.Kind) inKind, err := s.ObjectKind(in) if err != nil { @@ -355,10 +398,6 @@ func (s *Scheme) ConvertToVersion(in interface{}, outGroupVersionString string) return nil, err } - if err := s.SetVersionAndKind(outVersion.String(), outKind.Kind, out); err != nil { - return nil, err - } - return out, nil } @@ -367,6 +406,14 @@ func (s *Scheme) Converter() *Converter { return s.converter } +// WithConversions returns a scheme with additional conversion functions +func (s *Scheme) WithConversions(fns ConversionFuncs) *Scheme { + c := s.converter.WithConversions(fns) + copied := *s + copied.converter = c + return &copied +} + // generateConvertMeta constructs the meta value we pass to Convert. func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (FieldMatchingFlags, *Meta) { t := reflect.TypeOf(in) @@ -377,12 +424,6 @@ func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversion } } -// DataKind will return the group,version,kind of the given wire-format -// encoding of an API Object, or an error. -func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { - return s.MetaFactory.Interpret(data) -} - // 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 interface{}) (unversioned.GroupVersionKind, error) { @@ -390,7 +431,6 @@ func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, erro if err != nil { return unversioned.GroupVersionKind{}, err } - return gvks[0], nil } @@ -399,22 +439,16 @@ func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, erro func (s *Scheme) ObjectKinds(obj interface{}) ([]unversioned.GroupVersionKind, error) { v, err := EnforcePtr(obj) if err != nil { - return []unversioned.GroupVersionKind{}, err + return nil, err } t := v.Type() gvks, ok := s.typeToGVK[t] if !ok { - return []unversioned.GroupVersionKind{}, ¬RegisteredErr{t: t} + return nil, ¬RegisteredErr{t: t} } - return gvks, nil -} -// SetVersionAndKind sets the version and kind fields (with help from -// MetaInsertionFactory). Returns an error if this isn't possible. obj -// must be a pointer. -func (s *Scheme) SetVersionAndKind(version, kind string, obj interface{}) error { - return s.MetaFactory.Update(version, kind, obj) + return gvks, nil } // maybeCopy copies obj if it is not a pointer, to get a settable/addressable diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index 9c62f407d283..766aa8b479bc 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -18,15 +18,11 @@ package conversion import ( "encoding/json" - "fmt" - "reflect" - "strings" "testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/util" - "github.com/ghodss/yaml" "github.com/google/gofuzz" flag "github.com/spf13/pflag" ) @@ -109,7 +105,7 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( // Returns a new Scheme set up with the test objects. func GetTestScheme() *Scheme { - internalGV := unversioned.GroupVersion{} + internalGV := unversioned.GroupVersion{Version: "__internal"} externalGV := unversioned.GroupVersion{Version: "v1"} s := NewScheme() @@ -122,34 +118,9 @@ func GetTestScheme() *Scheme { s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) - s.MetaFactory = testMetaFactory{} return s } -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. - err := yaml.Unmarshal(data, &findKind) - if err != nil { - return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't get version/kind: %v", err) - } - gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) - if err != nil { - return unversioned.GroupVersionKind{}, err - } - return gv.WithKind(findKind.ObjectKind), nil -} - -func (testMetaFactory) Update(version, kind string, obj interface{}) error { - return UpdateVersionAndKind(nil, "APIVersion", version, "ObjectKind", kind, obj) -} - func objDiff(a, b interface{}) string { ab, err := json.Marshal(a) if err != nil { @@ -170,111 +141,6 @@ func objDiff(a, b interface{}) string { //) } -func runTest(t *testing.T, source interface{}) { - name := reflect.TypeOf(source).Elem().Name() - TestObjectFuzzer.Fuzz(source) - - s := GetTestScheme() - data, err := s.EncodeToVersion(source, "v1") - if err != nil { - t.Errorf("%v: %v (%#v)", name, err, source) - return - } - obj2, err := s.Decode(data) - if err != nil { - t.Errorf("%v: %v (%v)", name, err, string(data)) - return - } - if !reflect.DeepEqual(source, obj2) { - t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2)) - return - } - obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface() - err = s.DecodeInto(data, obj3) - if err != nil { - t.Errorf("2: %v: %v", name, err) - return - } - if !reflect.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 TestMultipleNames(t *testing.T) { - s := GetTestScheme() - - obj, err := s.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`)) - 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 := s.EncodeToVersion(internal, "v1") - 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{} - externalGV := unversioned.GroupVersion{Version: "v1"} - - s := 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{}) - s.MetaFactory = testMetaFactory{} - - 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"} - - obj, err := s.Decode(data) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(expect, obj) { - t.Errorf("unexpected object: %#v", obj) - } - - into := &TestType1{} - if err := s.DecodeInto(data, into); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(expect, obj) { - t.Errorf("unexpected object: %#v", obj) - } -} - func TestKnownTypes(t *testing.T) { s := GetTestScheme() if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 { @@ -313,75 +179,3 @@ func TestConvertToVersionErr(t *testing.T) { t.Fatalf("unexpected non-error") } } - -func TestEncode_NonPtr(t *testing.T) { - s := GetTestScheme() - tt := TestType1{A: "I'm not a pointer object"} - obj := interface{}(tt) - data, err := s.EncodeToVersion(obj, "v1") - obj2, err2 := s.Decode(data) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'", err, err2) - } - if _, ok := obj2.(*TestType1); !ok { - t.Fatalf("Got wrong type") - } - if !reflect.DeepEqual(obj2, &tt) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", &tt, obj2) - } -} - -func TestEncode_Ptr(t *testing.T) { - s := GetTestScheme() - tt := &TestType1{A: "I am a pointer object"} - obj := interface{}(tt) - data, err := s.EncodeToVersion(obj, "v1") - obj2, err2 := s.Decode(data) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'", err, err2) - } - if _, ok := obj2.(*TestType1); !ok { - t.Fatalf("Got wrong type") - } - if !reflect.DeepEqual(obj2, tt) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", &tt, obj2) - } -} - -func TestBadJSONRejection(t *testing.T) { - s := GetTestScheme() - badJSONs := [][]byte{ - []byte(`{"myVersionKey":"v1"}`), // Missing kind - []byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind - []byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version - } - for _, b := range badJSONs { - if _, err := s.Decode(b); err == nil { - t.Errorf("Did not reject bad json: %s", string(b)) - } - } - badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) - if err := s.DecodeInto(badJSONKindMismatch, &TestType1{}); err == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - } - if err := s.DecodeInto([]byte(``), &TestType1{}); err == nil { - t.Errorf("Did not give error for empty data") - } -} - -func TestBadJSONRejectionForSetInternalVersion(t *testing.T) { - s := GetTestScheme() - s.InternalVersions[""] = unversioned.GroupVersion{Version: "v1"} - badJSONs := [][]byte{ - []byte(`{"myKindKey":"TestType1"}`), // Missing version - } - for _, b := range badJSONs { - if _, err := s.Decode(b); err == nil { - t.Errorf("Did not reject bad json: %s", string(b)) - } - } - badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) - if err := s.DecodeInto(badJSONKindMismatch, &TestType1{}); err == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - } -} diff --git a/pkg/conversion/unversioned_test.go b/pkg/conversion/unversioned_test.go index e4b0c1ea73ce..eee3e10c5071 100644 --- a/pkg/conversion/unversioned_test.go +++ b/pkg/conversion/unversioned_test.go @@ -23,10 +23,10 @@ import ( // 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/latest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" ) @@ -67,8 +67,8 @@ func TestV1EncodeDecodeStatus(t *testing.T) { 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 := runtime.CodecFor(api.Scheme, v1beta1.SchemeGroupVersion) - encoded, err := expCodec.Encode(status) + expCodec := latest.Codecs.LegacyCodec(extensions.SchemeGroupVersion) + encoded, err := runtime.Encode(expCodec, status) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -79,7 +79,7 @@ func TestExperimentalEncodeDecodeStatus(t *testing.T) { if typeMeta.Kind != "Status" { t.Errorf("Kind is not set to \"Status\". Got %s", encoded) } - if typeMeta.APIVersion != "" { + if typeMeta.APIVersion != "v1" { t.Errorf("APIVersion is not set to \"\". Got %s", encoded) } decoded, err := runtime.Decode(expCodec, encoded) diff --git a/pkg/runtime/serializer/protobuf/doc.go b/pkg/runtime/serializer/protobuf/doc.go new file mode 100644 index 000000000000..3fec7197aff6 --- /dev/null +++ b/pkg/runtime/serializer/protobuf/doc.go @@ -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 handles serializing API objects to and from wire formats. +package protobuf From 63a7a41ddff96e236d1f7b8707256c827adb40d4 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:08:33 -0500 Subject: [PATCH 03/17] Simplify Codec and split responsibilities Break Codec into two general purpose interfaces, Encoder and Decoder, and move parameter codec responsibilities to ParameterCodec. Make unversioned types explicit when registering - these types go through conversion without modification. Switch to use "__internal" instead of "" to represent the internal version. Future commits will also add group defaulting (so that "" is expanded internally into a known group version, and only cleared during set). For embedded types like runtime.Object -> runtime.RawExtension, put the responsibility on the caller of Decode/Encode to handle transformation into destination serialization. Future commits will expand RawExtension and Unknown to accept a content encoding as well as bytes. Make Unknown a bit more powerful and use it to carry unrecognized types. --- cmd/gendeepcopy/deep_copy.go | 2 +- pkg/runtime/codec.go | 176 ++++++++----- pkg/runtime/conversion_generator.go | 17 +- pkg/runtime/conversion_test.go | 3 +- pkg/runtime/embedded.go | 124 +++++++++ pkg/runtime/embedded_test.go | 131 ++++++---- pkg/runtime/error.go | 8 + pkg/runtime/extension.go | 16 +- pkg/runtime/helper.go | 101 +++++--- pkg/runtime/helper_test.go | 2 +- pkg/runtime/interfaces.go | 112 +++++---- pkg/runtime/register.go | 40 ++- pkg/runtime/scheme.go | 378 +++++----------------------- pkg/runtime/scheme_test.go | 268 ++++++++++++-------- pkg/runtime/types.go | 44 ++-- pkg/runtime/unstructured.go | 81 ++---- pkg/runtime/unstructured_test.go | 4 +- 17 files changed, 791 insertions(+), 716 deletions(-) create mode 100644 pkg/runtime/embedded.go diff --git a/cmd/gendeepcopy/deep_copy.go b/cmd/gendeepcopy/deep_copy.go index a10bfe3a011d..56fa74b03dc2 100644 --- a/cmd/gendeepcopy/deep_copy.go +++ b/cmd/gendeepcopy/deep_copy.go @@ -104,7 +104,7 @@ func main() { } else { pkgname = gv.Group } - if len(gv.Version) != 0 { + if len(gv.Version) != 0 && gv.Version != kruntime.APIVersionInternal { pkgname = gv.Version } diff --git a/pkg/runtime/codec.go b/pkg/runtime/codec.go index 3539388a4c3b..bde0ae9755d5 100644 --- a/pkg/runtime/codec.go +++ b/pkg/runtime/codec.go @@ -17,99 +17,155 @@ limitations under the License. package runtime import ( + "bytes" + "fmt" "io" + "net/url" + "reflect" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/util/yaml" + "k8s.io/kubernetes/pkg/conversion/queryparams" ) +// codec binds an encoder and decoder. +type codec struct { + Encoder + Decoder +} + +// NewCodec creates a Codec from an Encoder and Decoder. +func NewCodec(e Encoder, d Decoder) Codec { + return codec{e, d} +} + // Encode is a convenience wrapper for encoding to a []byte from an Encoder -// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered. -func Encode(e Encoder, obj Object) ([]byte, error) { - return e.Encode(obj) +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. -// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered. func Decode(d Decoder, data []byte) (Object, error) { - return d.Decode(data) + obj, _, err := d.Decode(data, nil, nil) + return obj, err } // DecodeInto performs a Decode into the provided object. -// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered. func DecodeInto(d Decoder, data []byte, into Object) error { - return d.DecodeInto(data, into) + 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) } -// CodecFor returns a Codec that invokes Encode with the provided version. -func CodecFor(codec ObjectCodec, version unversioned.GroupVersion) Codec { - return &codecWrapper{codec, version} +// 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) } -// yamlCodec converts YAML passed to the Decoder methods to JSON. -type yamlCodec struct { - // a Codec for JSON - Codec +// NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding. +type NoopEncoder struct { + Decoder } -// yamlCodec implements Codec -var _ Codec = yamlCodec{} -var _ Decoder = yamlCodec{} +var _ Serializer = NoopEncoder{} -// YAMLDecoder adds YAML decoding support to a codec that supports JSON. -func YAMLDecoder(codec Codec) Codec { - return &yamlCodec{codec} +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)) } -func (c yamlCodec) Decode(data []byte) (Object, error) { - out, err := yaml.ToJSON(data) - if err != nil { - return nil, err - } - data = out - return c.Codec.Decode(data) +// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding. +type NoopDecoder struct { + Encoder } -func (c yamlCodec) DecodeInto(data []byte, obj Object) error { - out, err := yaml.ToJSON(data) - if err != nil { - return err - } - data = out - return c.Codec.DecodeInto(data, obj) +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)) } -// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests. -func EncodeOrDie(codec Codec, obj Object) string { - bytes, err := Encode(codec, obj) - if err != nil { - panic(err) +// 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, } - return string(bytes) } -// codecWrapper implements encoding to an alternative -// default version for a scheme. -type codecWrapper struct { - ObjectCodec - version unversioned.GroupVersion +// parameterCodec implements conversion to and from query parameters and objects. +type parameterCodec struct { + typer Typer + convertor ObjectConvertor + creator ObjectCreater } -// codecWrapper implements Decoder -var _ Decoder = &codecWrapper{} +var _ ParameterCodec = ¶meterCodec{} -// Encode implements Codec -func (c *codecWrapper) Encode(obj Object) ([]byte, error) { - return c.EncodeToVersion(obj, c.version.String()) +// 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) } -func (c *codecWrapper) EncodeToStream(obj Object, stream io.Writer) error { - return c.EncodeToVersionStream(obj, c.version.String(), stream) +// 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) } - -// TODO: Make this behaviour default when we move everyone away from -// the unversioned types. -// -// func (c *codecWrapper) Decode(data []byte) (Object, error) { -// return c.DecodeToVersion(data, c.version) -// } diff --git a/pkg/runtime/conversion_generator.go b/pkg/runtime/conversion_generator.go index 54e2f0a9cc83..6d70224e9be2 100644 --- a/pkg/runtime/conversion_generator.go +++ b/pkg/runtime/conversion_generator.go @@ -102,10 +102,8 @@ func (g *conversionGenerator) AddImport(pkg string) string { 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, exists := g.scheme.InternalVersions[gv.Group] - if !exists { - return fmt.Errorf("no internal version for %v", gv) - } + internalVersion := gv + internalVersion.Version = APIVersionInternal internalObj, err := g.scheme.NewObject(internalVersion.WithKind(kind)) if err != nil { @@ -775,6 +773,10 @@ func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outTyp 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 @@ -895,12 +897,7 @@ type typePair struct { outType reflect.Type } -var defaultConversions []typePair = []typePair{ - {reflect.TypeOf([]RawExtension{}), reflect.TypeOf([]Object{})}, - {reflect.TypeOf([]Object{}), reflect.TypeOf([]RawExtension{})}, - {reflect.TypeOf(RawExtension{}), reflect.TypeOf(EmbeddedObject{})}, - {reflect.TypeOf(EmbeddedObject{}), reflect.TypeOf(RawExtension{})}, -} +var defaultConversions []typePair = []typePair{} func (g *conversionGenerator) OverwritePackage(pkg, overwrite string) { g.pkgOverwrites[pkg] = overwrite diff --git a/pkg/runtime/conversion_test.go b/pkg/runtime/conversion_test.go index cf31550839a2..6105e5aad5af 100644 --- a/pkg/runtime/conversion_test.go +++ b/pkg/runtime/conversion_test.go @@ -46,12 +46,11 @@ func (obj *InternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj func (obj *ExternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func TestStringMapConversion(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"} scheme := runtime.NewScheme() scheme.Log(t) - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{}) diff --git a/pkg/runtime/embedded.go b/pkg/runtime/embedded.go new file mode 100644 index 000000000000..0934d6837c2b --- /dev/null +++ b/pkg/runtime/embedded.go @@ -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 + }, + } +} diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index c0b9c867f99c..64d6d74fb942 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -19,20 +19,21 @@ package runtime_test import ( "encoding/json" "reflect" + "strings" "testing" - "k8s.io/kubernetes/pkg/api" "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.EmbeddedObject - EmptyObject runtime.EmbeddedObject + Object runtime.Object + EmptyObject runtime.Object } type EmbeddedTestExternal struct { @@ -62,16 +63,17 @@ func (obj *EmbeddedTest) GetObjectKind() unversioned.ObjectKind { return func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} externalGVK := externalGV.WithKind("ObjectTest") s := runtime.NewScheme() - s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) - obj, err := s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{}]}`)) + codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) + + obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -79,42 +81,51 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" { t.Fatalf("unexpected object: %#v", test.Items[0]) } + if *gvk != externalGVK { + t.Fatalf("unexpected kind: %#v", gvk) + } - obj, err = s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{"kind":"Other","apiVersion":"v1"}]}`)) + obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } test = obj.(*ObjectTest) - if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "Other" || unk.APIVersion != "v1" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` { + if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` { t.Fatalf("unexpected object: %#v", test.Items[0]) } + if *gvk != externalGVK { + t.Fatalf("unexpected kind: %#v", gvk) + } } func TestArrayOfRuntimeObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} s := runtime.NewScheme() - s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{}) s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{}) - internal := &ObjectTest{ - Items: []runtime.Object{ - &EmbeddedTest{ID: "foo"}, - &EmbeddedTest{ID: "bar"}, - // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization - &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)}, - &ObjectTest{ - Items: []runtime.Object{ - &EmbeddedTest{ID: "baz"}, - }, - }, + 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), }, } - wire, err := s.EncodeToVersion(internal, externalGV.String()) + internal := &ObjectTest{ + Items: runtime.NewEncodableList(codec, items), + } + wire, err := runtime.Encode(codec, internal) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -126,7 +137,10 @@ func TestArrayOfRuntimeObject(t *testing.T) { } t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON)) - decoded, err := runtime.Decode(s, wire) + items[3] = &ObjectTest{Items: innerItems} + internal.Items = items + + decoded, err := runtime.Decode(codec, wire) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -134,7 +148,7 @@ func TestArrayOfRuntimeObject(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if errs := runtime.DecodeList(list, s); len(errs) > 0 { + if errs := runtime.DecodeList(list, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } @@ -142,53 +156,65 @@ func TestArrayOfRuntimeObject(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if errs := runtime.DecodeList(list2, s); len(errs) > 0 { + if errs := runtime.DecodeList(list2, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := meta.SetList(list[3], list2); err != nil { t.Fatalf("unexpected error: %v", err) } - internal.Items[2].(*runtime.Unknown).Kind = "OtherTest" - internal.Items[2].(*runtime.Unknown).APIVersion = "unknown.group/unknown" + // 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.ObjectDiff(e, a)) + t.Errorf("mismatched decoded: %s", util.ObjectGoPrintSideBySide(e, a)) } } -func TestEmbeddedObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} +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.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) + codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) + + inner := &EmbeddedTest{ + ID: "inner", + } outer := &EmbeddedTest{ - ID: "outer", - Object: runtime.EmbeddedObject{ - Object: &EmbeddedTest{ - ID: "inner", - }, - }, + ID: "outer", + Object: runtime.NewEncodable(codec, inner), } - wire, err := s.EncodeToVersion(outer, externalGV.String()) + 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(s, 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: %#v but got %#v", e, a) + t.Errorf("Expected equal %#v %#v", e, a) } // test JSON decoding of the external object, which should preserve @@ -211,46 +237,45 @@ func TestEmbeddedObject(t *testing.T) { // the external representation var decodedViaJSON EmbeddedTest err = json.Unmarshal(wire, &decodedViaJSON) - if err != nil { + if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") { t.Fatalf("Unexpected decode error %v", err) } - if a := decodedViaJSON; a.Object.Object != nil || a.EmptyObject.Object != nil { + if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil { t.Errorf("Expected embedded objects to be nil: %#v", a) } } -// TestDeepCopyOfEmbeddedObject checks to make sure that EmbeddedObject's can be passed through DeepCopy with fidelity -func TestDeepCopyOfEmbeddedObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} +// 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.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) original := &EmbeddedTest{ ID: "outer", - Object: runtime.EmbeddedObject{ - Object: &EmbeddedTest{ - ID: "inner", - }, + Object: &EmbeddedTest{ + ID: "inner", }, } - originalData, err := s.EncodeToVersion(original, externalGV.String()) + 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 := api.Scheme.DeepCopy(original) + copyOfOriginal, err := s.DeepCopy(original) if err != nil { t.Fatalf("unexpected error: %v", err) } - copiedData, err := s.EncodeToVersion(copyOfOriginal.(runtime.Object), externalGV.String()) + copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object)) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/runtime/error.go b/pkg/runtime/error.go index 40fa2437a6d4..cbdc9b66105e 100644 --- a/pkg/runtime/error.go +++ b/pkg/runtime/error.go @@ -37,3 +37,11 @@ func IsMissingKind(err error) bool { func IsMissingVersion(err error) bool { return conversion.IsMissingVersion(err) } + +func NewMissingKindErr(data string) error { + return conversion.NewMissingKindErr(data) +} + +func NewMissingVersionErr(data string) error { + return conversion.NewMissingVersionErr(data) +} diff --git a/pkg/runtime/extension.go b/pkg/runtime/extension.go index 2194ea3a9e9c..629f675b69e7 100644 --- a/pkg/runtime/extension.go +++ b/pkg/runtime/extension.go @@ -16,7 +16,10 @@ limitations under the License. package runtime -import "errors" +import ( + "encoding/json" + "errors" +) func (re *RawExtension) UnmarshalJSON(in []byte) error { if re == nil { @@ -29,5 +32,16 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error { // Marshal may get called on pointers or values, so implement MarshalJSON on value. // 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 } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index 9b6c57cd6594..4a76e81dc9f9 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -22,8 +22,30 @@ import ( "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. @@ -48,32 +70,56 @@ func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error { return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type()) } +// 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 runtime.Unknown type. Any errors that occur are returned +// the list that have the Unknown type. Any errors that occur are returned // after the entire list is processed. Decoders are tried in order. -func DecodeList(objects []Object, decoders ...ObjectDecoder) []error { +func DecodeList(objects []Object, decoders ...Decoder) []error { errs := []error(nil) for i, obj := range objects { switch t := obj.(type) { case *Unknown: - for _, decoder := range decoders { - gv, err := unversioned.ParseGroupVersion(t.APIVersion) - if err != nil { - errs = append(errs, err) - break - } - - if !decoder.Recognizes(gv.WithKind(t.Kind)) { - continue - } - obj, err := Decode(decoder, t.RawJSON) - if err != nil { - errs = append(errs, err) - break - } - objects[i] = obj + decoded, err := decodeListItem(t, decoders) + if err != nil { + errs = append(errs, err) break } + objects[i] = decoded } } return errs @@ -84,16 +130,6 @@ type MultiObjectTyper []ObjectTyper var _ ObjectTyper = MultiObjectTyper{} -func (m MultiObjectTyper) DataKind(data []byte) (gvk unversioned.GroupVersionKind, err error) { - for _, t := range m { - gvk, err = t.DataKind(data) - if err == nil { - return - } - } - return -} - func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) { for _, t := range m { gvk, err = t.ObjectKind(obj) @@ -122,3 +158,12 @@ func (m MultiObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool { } 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 +} diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index 36434054cde3..be7f0dedda1b 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -32,7 +32,7 @@ func TestDecodeList(t *testing.T) { &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, }, } - if errs := runtime.DecodeList(pl.Items, api.Scheme); len(errs) != 0 { + if errs := runtime.DecodeList(pl.Items, testapi.Default.Codec()); len(errs) != 0 { t.Fatalf("unexpected error %v", errs) } if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" { diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index 7e6eb647686f..6a51ee05ec81 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -23,70 +23,84 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" ) -// Codec defines methods for serializing and deserializing API objects. -type Codec interface { - Decoder - Encoder -} +const ( + APIVersionInternal = "__internal" + APIVersionUnversioned = "__unversioned" +) -// Decoder defines methods for deserializing API objects into a given type -type Decoder interface { - // TODO: change the signature of this method - Decode(data []byte) (Object, error) - // DEPRECATED: This method is being removed - DecodeToVersion(data []byte, groupVersion unversioned.GroupVersion) (Object, error) - // DEPRECATED: This method is being removed - DecodeInto(data []byte, obj Object) error - // DEPRECATED: This method is being removed - DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, groupVersionKind unversioned.GroupVersionKind) error - - DecodeParametersInto(parameters url.Values, obj Object) error +// 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) } -// Encoder defines methods for serializing API objects into bytes type Encoder interface { - // DEPRECATED: This method is being removed - Encode(obj Object) (data []byte, err error) - EncodeToStream(obj Object, stream io.Writer) error + // 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 +} - // TODO: Add method for processing url parameters. - // EncodeParameters(obj Object) (url.Values, 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) } -// ObjectCodec represents the common mechanisms for converting to and from a particular -// binary representation of an object. -// TODO: Remove this interface - it is used only in CodecFor() method. -type ObjectCodec interface { +// 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 +} - // EncodeToVersion convert and serializes an object in the internal format - // to a specified output version. An error is returned if the object - // cannot be converted for any reason. - EncodeToVersion(obj Object, outVersion string) ([]byte, error) - EncodeToVersionStream(obj Object, outVersion string, stream io.Writer) error +// 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) } -// ObjectDecoder is a convenience interface for identifying serialized versions of objects -// and transforming them into Objects. It intentionally overlaps with ObjectTyper and -// Decoder for use in decode only paths. -// TODO: Consider removing this interface? -type ObjectDecoder interface { - Decoder - // DataVersionAndKind returns the group,version,kind of the provided data, or an error - // if another problem is detected. In many cases this method can be as expensive to - // invoke as the Decode method. - DataKind([]byte) (unversioned.GroupVersionKind, error) - // Recognizes returns true if the scheme is able to handle the provided group,version,kind - // of an object. - Recognizes(unversioned.GroupVersionKind) bool +// 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) } @@ -94,10 +108,6 @@ type ObjectConvertor interface { // ObjectTyper contains methods for extracting the APIVersion and Kind // of objects. type ObjectTyper interface { - // DataKind returns the group,version,kind of the provided data, or an error - // if another problem is detected. In many cases this method can be as expensive to - // invoke as the Decode method. - DataKind([]byte) (unversioned.GroupVersionKind, error) // ObjectKind returns the default group,version,kind of the provided object, or an // error if the object is not recognized (IsNotRegisteredError will return true). ObjectKind(Object) (unversioned.GroupVersionKind, error) @@ -108,6 +118,10 @@ type ObjectTyper interface { // or more precisely that the provided version is a possible conversion or decoding // 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. diff --git a/pkg/runtime/register.go b/pkg/runtime/register.go index 53186957ac02..95244913c102 100644 --- a/pkg/runtime/register.go +++ b/pkg/runtime/register.go @@ -20,16 +20,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" ) -// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed PluginBase -func (obj *PluginBase) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - _, obj.Kind = gvk.ToAPIVersionAndKind() -} - -// GroupVersionKind satisfies the ObjectKind interface for all objects that embed PluginBase -func (obj *PluginBase) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind("", obj.Kind) -} - // SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind() @@ -42,3 +32,33 @@ func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind { func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } + +// GetObjectKind implements Object for VersionedObjects, returning an empty ObjectKind +// interface if no objects are provided, or the ObjectKind interface of the object in the +// highest array position. +func (obj *VersionedObjects) GetObjectKind() unversioned.ObjectKind { + last := obj.Last() + if last == nil { + return unversioned.EmptyObjectKind + } + return last.GetObjectKind() +} + +// First returns the leftmost object in the VersionedObjects array, which is usually the +// object as serialized on the wire. +func (obj *VersionedObjects) First() Object { + if len(obj.Objects) == 0 { + return nil + } + return obj.Objects[0] +} + +// Last is the rightmost object in the VersionedObjects array, which is the object after +// all transformations have been applied. This is the same object that would be returned +// by Decode in a normal invocation (without VersionedObjects in the into argument). +func (obj *VersionedObjects) Last() Object { + if len(obj.Objects) == 0 { + return nil + } + return obj.Objects[len(obj.Objects)-1] +} diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 8b28d11a630f..474a6eb307fe 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -17,9 +17,7 @@ limitations under the License. package runtime import ( - "encoding/json" "fmt" - "io" "net/url" "reflect" @@ -36,9 +34,6 @@ type Scheme struct { fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc } -var _ Decoder = &Scheme{} -var _ ObjectTyper = &Scheme{} - // Function to convert a field selector to internal representation. type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error) @@ -55,205 +50,11 @@ func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, return inVersion, outVersion, scheme } -// emptyPlugin is used to copy the Kind field to and from plugin objects. -type emptyPlugin struct { - PluginBase `json:",inline"` -} - -// embeddedObjectToRawExtension does the conversion you would expect from the name, using the information -// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins; -// see the comment for RawExtension. -func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conversion.Scope) error { - if in.Object == nil { - out.RawJSON = []byte("null") - return nil - } - - // Figure out the type and kind of the output object. - _, outGroupVersionString, scheme := self.fromScope(s) - objKind, err := scheme.raw.ObjectKind(in.Object) - if err != nil { - return err - } - outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString) - if err != nil { - return err - } - - // Manufacture an object of this type and kind. - outObj, err := scheme.New(outVersion.WithKind(objKind.Kind)) - if err != nil { - return err - } - - // Manually do the conversion. - err = s.Convert(in.Object, outObj, 0) - if err != nil { - return err - } - - // Copy the kind field into the output object. - err = s.Convert( - &emptyPlugin{PluginBase: PluginBase{Kind: objKind.Kind}}, - outObj, - conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, - ) - if err != nil { - return err - } - // Because we provide the correct version, EncodeToVersion will not attempt a conversion. - raw, err := scheme.EncodeToVersion(outObj, outVersion.String()) - if err != nil { - // TODO: if this fails, create an Unknown-- maybe some other - // component will understand it. - return err - } - out.RawJSON = raw - return nil -} - -// rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information -// given in conversion.Scope. It's placed in all schemes as a ConversionFunc to enable plugins; -// see the comment for RawExtension. -func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conversion.Scope) error { - if len(in.RawJSON) == 0 || (len(in.RawJSON) == 4 && string(in.RawJSON) == "null") { - out.Object = nil - return nil - } - // Figure out the type and kind of the output object. - inGroupVersionString, outGroupVersionString, scheme := self.fromScope(s) - dataKind, err := scheme.raw.DataKind(in.RawJSON) - if err != nil { - return err - } - inVersion, err := unversioned.ParseGroupVersion(inGroupVersionString) - if err != nil { - return err - } - outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString) - if err != nil { - return err - } - - // We have to make this object ourselves because we don't store the version field for - // plugin objects. - inObj, err := scheme.New(inVersion.WithKind(dataKind.Kind)) - if err != nil { - return err - } - - err = DecodeInto(scheme, in.RawJSON, inObj) - if err != nil { - return err - } - - // Make the desired internal version, and do the conversion. - outObj, err := scheme.New(outVersion.WithKind(dataKind.Kind)) - if err != nil { - return err - } - err = scheme.Convert(inObj, outObj) - if err != nil { - return err - } - // Last step, clear the Kind field; that should always be blank in memory. - err = s.Convert( - &emptyPlugin{PluginBase: PluginBase{Kind: ""}}, - outObj, - conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, - ) - if err != nil { - return err - } - out.Object = outObj - return nil -} - -// runtimeObjectToRawExtensionArray takes a list of objects and encodes them as RawExtension in the output version -// defined by the conversion.Scope. If objects must be encoded to different schema versions than the default, you -// should encode them yourself with runtime.Unknown, or convert the object prior to invoking conversion. Objects -// outside of the current scheme must be added as runtime.Unknown. -func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExtension, s conversion.Scope) error { - src := *in - dest := make([]RawExtension, len(src)) - - _, outVersion, scheme := self.fromScope(s) - - for i := range src { - switch t := src[i].(type) { - case *Unknown: - // TODO: this should be decoupled from the scheme (since it is JSON specific) - dest[i].RawJSON = t.RawJSON - case *Unstructured: - // TODO: this should be decoupled from the scheme (since it is JSON specific) - data, err := json.Marshal(t.Object) - if err != nil { - return err - } - dest[i].RawJSON = data - default: - version := outVersion - // if the object exists - // this code is try to set the outputVersion, but only if the object has a non-internal group version - if inGVK, err := scheme.ObjectKind(src[i]); err == nil && !inGVK.GroupVersion().IsEmpty() { - if self.raw.InternalVersions[inGVK.Group] != inGVK.GroupVersion() { - version = inGVK.GroupVersion().String() - } - } - data, err := scheme.EncodeToVersion(src[i], version) - if err != nil { - return err - } - dest[i].RawJSON = data - } - } - *out = dest - return nil -} - -// rawExtensionToRuntimeObjectArray attempts to decode objects from the array - if they are unrecognized objects, -// they are added as Unknown. -func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]Object, s conversion.Scope) error { - src := *in - dest := make([]Object, len(src)) - - _, _, scheme := self.fromScope(s) - - for i := range src { - data := src[i].RawJSON - dataKind, err := scheme.raw.DataKind(data) - if err != nil { - return err - } - dest[i] = &Unknown{ - TypeMeta: TypeMeta{ - APIVersion: dataKind.GroupVersion().String(), - Kind: dataKind.Kind, - }, - RawJSON: data, - } - } - *out = dest - return nil -} - // NewScheme creates a new Scheme. This scheme is pluggable by default. -func NewScheme(internalGroupVersions ...unversioned.GroupVersion) *Scheme { +func NewScheme() *Scheme { s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}} + s.AddConversionFuncs(DefaultEmbeddedConversions()...) - for _, internalGV := range internalGroupVersions { - s.raw.InternalVersions[internalGV.Group] = internalGV - } - - s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"} - if err := s.raw.AddConversionFuncs( - s.embeddedObjectToRawExtension, - s.rawExtensionToEmbeddedObject, - s.runtimeObjectToRawExtensionArray, - s.rawExtensionToRuntimeObjectArray, - ); err != nil { - panic(err) - } // Enable map[string][]string conversions by default if err := s.raw.AddConversionFuncs(DefaultStringConversions...); err != nil { panic(err) @@ -267,14 +68,22 @@ func NewScheme(internalGroupVersions ...unversioned.GroupVersion) *Scheme { return s } -// AddInternalGroupVersion registers an internal GroupVersion with the scheme. This can later be -// used to lookup the internal GroupVersion for a given Group -func (s *Scheme) AddInternalGroupVersion(gv unversioned.GroupVersion) { - s.raw.InternalVersions[gv.Group] = gv +// 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 tihs method at that point. +func (s *Scheme) AddUnversionedTypes(gv unversioned.GroupVersion, types ...Object) { + interfaces := make([]interface{}, len(types)) + for i := range types { + interfaces[i] = types[i] + } + s.raw.AddUnversionedTypes(gv, interfaces...) } // AddKnownTypes registers the types of the arguments to the marshaller of the package api. -// Encode() refuses the object unless its type is registered with AddKnownTypes. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { interfaces := make([]interface{}, len(types)) for i := range types { @@ -283,6 +92,12 @@ func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { s.raw.AddKnownTypes(gv, interfaces...) } +// AddIgnoredConversionType declares a particular conversion that should be ignored - during conversion +// this method is not invoked. +func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error { + return s.raw.AddIgnoredConversionType(from, to) +} + // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should // be encoded as. Useful for testing when you don't want to make multiple packages to define // your structs. @@ -296,12 +111,6 @@ func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type return s.raw.KnownTypes(gv) } -// DataKind will return the group,version,kind of the given wire-format -// encoding of an API Object, or an error. -func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { - return s.raw.DataKind(data) -} - // ObjectKind returns the default group,version,kind of the given Object. func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) { return s.raw.ObjectKind(obj) @@ -318,7 +127,12 @@ func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { return s.raw.Recognizes(gvk) } -// New returns a new API object of the given kind, or an error if it hasn't been registered. +func (s *Scheme) IsUnversioned(obj Object) (bool, bool) { + return s.raw.IsUnversioned(obj) +} + +// New returns a new API object of the given version ("" for internal +// representation) and name, or an error if it hasn't been registered. func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) { obj, err := s.raw.NewObject(kind) if err != nil { @@ -394,11 +208,31 @@ func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error { return s.raw.AddDefaultingFuncs(defaultingFuncs...) } +// Copy does a deep copy of an API object. +func (s *Scheme) Copy(src Object) (Object, error) { + dst, err := s.raw.DeepCopy(src) + if err != nil { + return nil, err + } + return dst.(Object), nil +} + // Performs a deep copy of the given object. func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) { return s.raw.DeepCopy(src) } +// WithConversions returns an ObjectConvertor that has the additional conversion functions +// defined in fns. The current scheme is not altered. +func (s *Scheme) WithConversions(fns *conversion.ConversionFuncs) ObjectConvertor { + if fns == nil { + return s + } + copied := *s + copied.raw = s.raw.WithConversions(*fns) + return &copied +} + // Convert will attempt to convert in into out. Both must be pointers. // For easy testing of conversion functions. Returns an error if the conversion isn't // possible. @@ -423,8 +257,19 @@ func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, // version within this scheme. Will return an error if the provided version does not // 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. +// 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: + old := in.GetObjectKind().GroupVersionKind() + defer in.GetObjectKind().SetGroupVersionKind(old) + setTargetVersion(in, s.raw, gv) + return in, nil + } unknown, err := s.raw.ConvertToVersion(in, outVersion) if err != nil { return nil, err @@ -433,105 +278,16 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) if !ok { return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown) } + setTargetVersion(obj, s.raw, gv) return obj, nil } -// EncodeToVersion turns the given api object into an appropriate JSON string. -// Will return an error if the object doesn't have an embedded TypeMeta. -// Obj may be a pointer to a struct, or a struct. If a struct, a copy -// must be made. If a pointer, the object may be modified before encoding, -// but will be put back into its original state before returning. -// -// Memory/wire format differences: -// * Having to keep track of the Kind and APIVersion fields makes tests -// very annoying, so the rule is that they are set only in wire format -// (json), not when in native (memory) format. This is possible because -// both pieces of information are implicit in the go typed object. -// * An exception: note that, if there are embedded API objects of known -// type, for example, PodList{... Items []Pod ...}, these embedded -// objects must be of the same version of the object they are embedded -// within, and their APIVersion and Kind must both be empty. -// * Note that the exception does not apply to the APIObject type, which -// recursively does Encode()/Decode(), and is capable of expressing any -// API object. -// * Only versioned objects should be encoded. This means that, if you pass -// a native object, Encode will convert it to a versioned object. For -// example, an api.Pod will get converted to a v1.Pod. However, if -// you pass in an object that's already versioned (v1.Pod), Encode -// will not modify it. -// -// The purpose of the above complex conversion behavior is to allow us to -// change the memory format yet not break compatibility with any stored -// objects, whether they be in our storage layer (e.g., etcd), or in user's -// config files. -func (s *Scheme) EncodeToVersion(obj Object, destVersion string) (data []byte, err error) { - return s.raw.EncodeToVersion(obj, destVersion) -} - -func (s *Scheme) EncodeToVersionStream(obj Object, destVersion string, stream io.Writer) error { - return s.raw.EncodeToVersionStream(obj, destVersion, stream) -} - -// Decode converts a YAML or JSON string back into a pointer to an api object. -// Deduces the type based upon the APIVersion and Kind fields, which are set -// by Encode. Only versioned objects (APIVersion != "") are accepted. The object -// will be converted into the in-memory unversioned type before being returned. -func (s *Scheme) Decode(data []byte) (Object, error) { - obj, err := s.raw.Decode(data) - if err != nil { - return nil, err - } - return obj.(Object), nil -} - -// DecodeToVersion converts a YAML or JSON string back into a pointer to an api -// object. Deduces the type based upon the APIVersion and Kind fields, which -// are set by Encode. Only versioned objects (APIVersion != "") are -// accepted. The object will be converted into the in-memory versioned type -// requested before being returned. -func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) { - obj, err := s.raw.DecodeToVersion(data, gv) - if err != nil { - return nil, err - } - return obj.(Object), nil -} - -// DecodeInto parses a YAML or JSON string and stores it in obj. Returns an error -// if data.Kind is set and doesn't match the type of obj. Obj should be a -// pointer to an api type. -// If obj's APIVersion doesn't match that in data, an attempt will be made to convert -// data into obj's version. -// TODO: allow Decode/DecodeInto to take a default apiVersion and a default kind, to -// be applied if the provided object does not have either field (integrate external -// apis into the decoding scheme). -func (s *Scheme) DecodeInto(data []byte, obj Object) error { - return s.raw.DecodeInto(data, obj) -} - -// DecodeIntoWithSpecifiedVersionKind coerces the data into the obj, assuming that the data is -// of type GroupVersionKind -func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error { - return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, gvk) -} - -func (s *Scheme) DecodeParametersInto(parameters url.Values, obj Object) error { - return s.raw.DecodeParametersInto(parameters, obj) -} - -// Copy does a deep copy of an API object. Useful mostly for tests. -func (s *Scheme) Copy(src Object) (Object, error) { - dst, err := s.raw.DeepCopy(src) - if err != nil { - return nil, err - } - return dst.(Object), nil -} - -func (s *Scheme) CopyOrDie(obj Object) Object { - newObj, err := s.Copy(obj) - if err != nil { - panic(err) +func setTargetVersion(obj Object, raw *conversion.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}) } - return newObj } diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index f406585f970b..d0f9f2d08bf3 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -23,6 +23,8 @@ import ( "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" ) type TypeMeta struct { @@ -51,23 +53,19 @@ type ExternalSimple struct { } func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalSimple) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalSimple) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func TestScheme(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) 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 @@ -102,21 +100,27 @@ func TestScheme(t *testing.T) { }, ) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } + + codecs := serializer.NewCodecFactory(scheme) + codec := codecs.LegacyCodec(externalGV) + jsonserializer, _ := codecs.SerializerForFileExtension("json") + simple := &InternalSimple{ TestString: "foo", } // Test Encode, Decode, DecodeInto, and DecodeToVersion obj := runtime.Object(simple) - data, err := scheme.EncodeToVersion(obj, externalGV.String()) - obj2, err2 := runtime.Decode(scheme, data) - obj3 := &InternalSimple{} - err3 := runtime.DecodeInto(scheme, data, obj3) - obj4, err4 := scheme.DecodeToVersion(data, externalGV) - if err != nil || err2 != nil || err3 != nil || err4 != nil { - t.Fatalf("Failure: '%v' '%v' '%v' '%v'", err, err2, err3, err4) + 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") @@ -124,9 +128,22 @@ func TestScheme(t *testing.T) { 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 = 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") } @@ -135,7 +152,7 @@ func TestScheme(t *testing.T) { external := &ExternalSimple{} err = scheme.Convert(simple, external) if err != nil { - t.Errorf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: %v", err) } if e, a := simple.TestString, external.TestString; e != a { t.Errorf("Expected %v, got %v", e, a) @@ -145,36 +162,23 @@ func TestScheme(t *testing.T) { if e, a := 2, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } - // Decode and DecodeInto should each have caused an increment. + // DecodeInto and Decode should each have caused an increment because of a conversion if e, a := 2, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } } -func TestInvalidObjectValueKind(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "", Version: ""} - - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - - embedded := &runtime.EmbeddedObject{} - switch obj := embedded.Object.(type) { - default: - _, err := scheme.ObjectKind(obj) - if err == nil { - t.Errorf("Expected error on invalid kind") - } - } -} - func TestBadJSONRejection(t *testing.T) { scheme := runtime.NewScheme() + codecs := serializer.NewCodecFactory(scheme) + jsonserializer, _ := codecs.SerializerForFileExtension("json") + badJSONMissingKind := []byte(`{ }`) - if _, err := runtime.Decode(scheme, badJSONMissingKind); err == nil { + if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil { t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) } badJSONUnknownType := []byte(`{"kind": "bar"}`) - if _, err1 := runtime.Decode(scheme, badJSONUnknownType); err1 == nil { + if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) @@ -184,13 +188,13 @@ func TestBadJSONRejection(t *testing.T) { } type ExtensionA struct { - runtime.PluginBase `json:",inline"` - TestString string `json:"testString"` + TypeMeta `json:",inline"` + TestString string `json:"testString"` } type ExtensionB struct { - runtime.PluginBase `json:",inline"` - TestString string `json:"testString"` + TypeMeta `json:",inline"` + TestString string `json:"testString"` } type ExternalExtensionType struct { @@ -200,7 +204,7 @@ type ExternalExtensionType struct { type InternalExtensionType struct { TypeMeta `json:",inline"` - Extension runtime.EmbeddedObject `json:"extension"` + Extension runtime.Object `json:"extension"` } type ExternalOptionalExtensionType struct { @@ -210,143 +214,135 @@ type ExternalOptionalExtensionType struct { type InternalOptionalExtensionType struct { TypeMeta `json:",inline"` - Extension runtime.EmbeddedObject `json:"extension,omitempty"` + Extension runtime.Object `json:"extension,omitempty"` } -func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.PluginBase } -func (obj *ExtensionA) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - _, obj.PluginBase.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.PluginBase } -func (obj *ExtensionB) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - _, obj.PluginBase.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} +func (obj *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 *ExternalOptionalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalOptionalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func TestExternalToInternalMapping(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) 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: runtime.EmbeddedObject{Object: nil}}, + &InternalOptionalExtensionType{Extension: nil}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } - for _, item := range table { - gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded)) + 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) { - var eEx, aEx runtime.Object - if obj, ok := e.(*InternalOptionalExtensionType); ok { - eEx = obj.Extension.Object - } - if obj, ok := a.(*InternalOptionalExtensionType); ok { - aEx = obj.Extension.Object - } - t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx) + t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) } } } func TestExtensionMapping(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{}) scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("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 - encoded string + obj runtime.Object + expected runtime.Object + encoded string }{ { - &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionA{TestString: "foo"}}}, - `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","testString":"foo"}} + &InternalExtensionType{ + Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}), + }, + &InternalExtensionType{ + Extension: &runtime.Unknown{ + RawJSON: []byte(`{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}`), + }, + }, + // apiVersion is set in the serialized object for easier consumption by clients + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}} `, }, { - &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionB{TestString: "bar"}}}, - `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","testString":"bar"}} + &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})}, + &InternalExtensionType{ + Extension: &runtime.Unknown{ + RawJSON: []byte(`{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}`), + }, + }, + // apiVersion is set in the serialized object for easier consumption by clients + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}} `, }, { - &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, + &InternalExtensionType{Extension: nil}, + &InternalExtensionType{ + Extension: nil, + }, `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null} `, }, } - for _, item := range table { - gotEncoded, err := scheme.EncodeToVersion(item.obj, externalGV.String()) + 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(scheme, []byte(item.encoded)) + 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) { - var eEx, aEx runtime.Object - if obj, ok := e.(*InternalExtensionType); ok { - eEx = obj.Extension.Object - } - if obj, ok := a.(*InternalExtensionType); ok { - aEx = obj.Extension.Object - } - t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx) + } 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: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - codec := runtime.CodecFor(scheme, externalGV) + + codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) + test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) - obj2, err2 := runtime.Decode(codec, data) + obj2, gvk, err2 := codec.Decode(data, nil, nil) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } @@ -354,6 +350,68 @@ func TestEncode(t *testing.T) { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", &test, obj2) + t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) + } + if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) { + t.Errorf("unexpected gvk returned by decode: %#v", gvk) + } +} + +func TestUnversionedTypes(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} + otherGV := unversioned.GroupVersion{Group: "group", Version: "other"} + + scheme := runtime.NewScheme() + scheme.AddUnversionedTypes(externalGV, &InternalSimple{}) + scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) + scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) + scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{}) + + codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) + + if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok { + t.Fatal("type not unversioned and in scheme: %t %t", unv, ok) + } + + kind, err := scheme.ObjectKind(&InternalSimple{}) + if err != nil { + t.Fatal(err) + } + if kind != externalGV.WithKind("InternalSimple") { + t.Fatalf("unexpected: %#v", kind) + } + + test := &InternalSimple{ + TestString: "I'm the same", + } + obj := runtime.Object(test) + data, err := runtime.Encode(codec, obj) + if err != nil { + t.Fatal(err) + } + obj2, gvk, err := codec.Decode(data, nil, nil) + if err != nil { + t.Fatal(err) + } + if _, ok := obj2.(*InternalSimple); !ok { + t.Fatalf("Got wrong type") + } + if !reflect.DeepEqual(obj2, test) { + t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) + } + // object is serialized as an unversioned object (in the group and version it was defined in) + if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) { + t.Errorf("unexpected gvk returned by decode: %#v", gvk) + } + + // when serialized to a different group, the object is kept in its preferred name + codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV) + data, err = runtime.Encode(codec, obj) + if err != nil { + t.Fatal(err) + } + if string(data) != `{"kind":"InternalSimple","apiVersion":"test.group/testExternal","testString":"I'm the same"}`+"\n" { + t.Errorf("unexpected data: %s", data) } } diff --git a/pkg/runtime/types.go b/pkg/runtime/types.go index a09af5e14f8f..3b8cede446a6 100644 --- a/pkg/runtime/types.go +++ b/pkg/runtime/types.go @@ -36,39 +36,18 @@ type TypeMeta struct { Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` } -// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded -// except while embedded in other objects. -type PluginBase struct { - Kind string `json:"kind,omitempty"` -} - -// EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's -// stored as a []byte, but in memory, the contained object is accessible as an Object -// via the Get() function. Only valid API objects may be stored via EmbeddedObject. -// The purpose of this is to allow an API object of type known only at runtime to be -// embedded within other API objects. -// -// Note that object assumes that you've registered all of your api types with the api package. -// -// EmbeddedObject and RawExtension can be used together to allow for API object extensions: -// see the comment for RawExtension. -type EmbeddedObject struct { - Object -} - -// RawExtension is used with EmbeddedObject to do a two-phase encoding of extension objects. +// 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 EmbeddedObject in your internal struct. You also need to register your +// struct, and Object in your internal struct. You also need to register your // various plugin types. // // // Internal package: // type MyAPIObject struct { // runtime.TypeMeta `json:",inline"` -// MyPlugin runtime.EmbeddedObject `json:"myPlugin"` +// MyPlugin runtime.Object `json:"myPlugin"` // } // type PluginA struct { -// runtime.PluginBase `json:",inline"` // AOption string `json:"aOption"` // } // @@ -78,7 +57,6 @@ type EmbeddedObject struct { // MyPlugin runtime.RawExtension `json:"myPlugin"` // } // type PluginA struct { -// runtime.PluginBase `json:",inline"` // AOption string `json:"aOption"` // } // @@ -97,12 +75,16 @@ type EmbeddedObject struct { // The next step is to copy (using pkg/conversion) into the internal struct. The runtime // 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 EmbeddedObject. (TODO: In the case where the object is of an unknown type, a +// in the Object. (TODO: In the case where the object is of an unknown type, a // runtime.Unknown object will be created and stored.) // // +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 @@ -131,3 +113,13 @@ type Unstructured struct { // children. Object map[string]interface{} } + +// VersionedObjects is used by Decoders to give callers a way to access all versions +// of an object during the decoding process. +type VersionedObjects struct { + // Objects is the set of objects retrieved during decoding, in order of conversion. + // The 0 index is the object as serialized on the wire. If conversion has occured, + // other objects may be present. The right most object is the same as would be returned + // by a normal Decode call. + Objects []Object +} diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index 158961fd8efd..a236e9adb6b7 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -18,9 +18,7 @@ package runtime import ( "encoding/json" - "fmt" - "net/url" - "reflect" + "io" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" @@ -28,36 +26,19 @@ import ( // UnstructuredJSONScheme is capable of converting JSON data into the Unstructured // type, which can be used for generic access to objects without a predefined scheme. -var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{} +// TODO: move into serializer/json. +var UnstructuredJSONScheme Decoder = unstructuredJSONScheme{} type unstructuredJSONScheme struct{} -var _ Decoder = unstructuredJSONScheme{} -var _ ObjectDecoder = unstructuredJSONScheme{} +var _ Codec = unstructuredJSONScheme{} -// Recognizes returns true for any version or kind that is specified (internal -// versions are specifically excluded). -func (unstructuredJSONScheme) Recognizes(gvk unversioned.GroupVersionKind) bool { - return !gvk.GroupVersion().IsEmpty() && len(gvk.Kind) > 0 -} - -func (s unstructuredJSONScheme) Decode(data []byte) (Object, error) { +func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionKind, _ Object) (Object, *unversioned.GroupVersionKind, error) { unstruct := &Unstructured{} - if err := DecodeInto(s, data, unstruct); err != nil { - return nil, err - } - return unstruct, nil -} - -func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error { - unstruct, ok := obj.(*Unstructured) - if !ok { - return fmt.Errorf("the unstructured JSON scheme does not recognize %v", reflect.TypeOf(obj)) - } m := make(map[string]interface{}) if err := json.Unmarshal(data, &m); err != nil { - return err + return nil, nil, err } if v, ok := m["kind"]; ok { if s, ok := v.(string); ok { @@ -69,44 +50,30 @@ func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error { unstruct.APIVersion = s } } + if len(unstruct.APIVersion) == 0 { - return conversion.NewMissingVersionErr(string(data)) + return nil, nil, conversion.NewMissingVersionErr(string(data)) + } + gv, err := unversioned.ParseGroupVersion(unstruct.APIVersion) + if err != nil { + return nil, nil, err } + gvk := gv.WithKind(unstruct.Kind) if len(unstruct.Kind) == 0 { - return conversion.NewMissingKindErr(string(data)) + return nil, &gvk, conversion.NewMissingKindErr(string(data)) } unstruct.Object = m - return nil -} - -func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error { - return nil + return unstruct, &gvk, nil } -func (unstructuredJSONScheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) { - return nil, nil -} - -func (unstructuredJSONScheme) DecodeParametersInto(paramaters url.Values, obj Object) error { - return nil -} - -func (unstructuredJSONScheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { - obj := TypeMeta{} - if err := json.Unmarshal(data, &obj); err != nil { - return unversioned.GroupVersionKind{}, err - } - if len(obj.APIVersion) == 0 { - return unversioned.GroupVersionKind{}, conversion.NewMissingVersionErr(string(data)) - } - if len(obj.Kind) == 0 { - return unversioned.GroupVersionKind{}, conversion.NewMissingKindErr(string(data)) - } - - gv, err := unversioned.ParseGroupVersion(obj.APIVersion) - if err != nil { - return unversioned.GroupVersionKind{}, err +func (s 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 *Unknown: + _, err := w.Write(t.RawJSON) + return err + default: + return json.NewEncoder(w).Encode(t) } - - return gv.WithKind(obj.Kind), nil } diff --git a/pkg/runtime/unstructured_test.go b/pkg/runtime/unstructured_test.go index 120e3ff80a5e..cca0fe251194 100644 --- a/pkg/runtime/unstructured_test.go +++ b/pkg/runtime/unstructured_test.go @@ -42,7 +42,7 @@ func TestDecodeUnstructured(t *testing.T) { if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" { t.Errorf("object not converted: %#v", pl.Items[1]) } - if _, ok := pl.Items[2].(*runtime.Unknown); !ok { - t.Errorf("object should not have been converted: %#v", pl.Items[2]) + 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]) } } From 6b2f70d5532728715179882b13d8fed321d3de01 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:12:51 -0500 Subject: [PATCH 04/17] Provide a JSON and YAML serializer, and a versioning wrapper Add a recognizer that is capable of sniffing content type from data by asking each serializer to try to decode - this is for a "universal decoder/deserializer" which can be used by client logic. Add codec factory, which provides the core primitives for content type negotiation. Codec factories depend only on schemes, serializers, and groupversion pairs. --- pkg/runtime/serializer/codec_factory.go | 176 ++++++++ pkg/runtime/serializer/codec_test.go | 400 ++++++++++++++++++ pkg/runtime/serializer/json/json.go | 191 +++++++++ pkg/runtime/serializer/json/json_test.go | 269 ++++++++++++ pkg/runtime/serializer/json/meta.go | 61 +++ pkg/runtime/serializer/json/meta_test.go | 45 ++ pkg/runtime/serializer/protobuf/doc.go | 18 - .../serializer/recognizer/recognizer.go | 79 ++++ .../serializer/recognizer/recognizer_test.go | 57 +++ .../serializer/versioning/versioning.go | 243 +++++++++++ .../serializer/versioning/versioning_test.go | 300 +++++++++++++ pkg/runtime/serializer/yaml/yaml.go | 46 ++ pkg/util/yaml/decoder.go | 6 +- 13 files changed, 1870 insertions(+), 21 deletions(-) create mode 100644 pkg/runtime/serializer/codec_factory.go create mode 100644 pkg/runtime/serializer/codec_test.go create mode 100644 pkg/runtime/serializer/json/json.go create mode 100644 pkg/runtime/serializer/json/json_test.go create mode 100644 pkg/runtime/serializer/json/meta.go create mode 100644 pkg/runtime/serializer/json/meta_test.go delete mode 100644 pkg/runtime/serializer/protobuf/doc.go create mode 100644 pkg/runtime/serializer/recognizer/recognizer.go create mode 100644 pkg/runtime/serializer/recognizer/recognizer_test.go create mode 100644 pkg/runtime/serializer/versioning/versioning.go create mode 100644 pkg/runtime/serializer/versioning/versioning_test.go create mode 100644 pkg/runtime/serializer/yaml/yaml.go diff --git a/pkg/runtime/serializer/codec_factory.go b/pkg/runtime/serializer/codec_factory.go new file mode 100644 index 000000000000..6f6310d9c83a --- /dev/null +++ b/pkg/runtime/serializer/codec_factory.go @@ -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 +} diff --git a/pkg/runtime/serializer/codec_test.go b/pkg/runtime/serializer/codec_test.go new file mode 100644 index 000000000000..6c1b2ff95470 --- /dev/null +++ b/pkg/runtime/serializer/codec_test.go @@ -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") + } +} diff --git a/pkg/runtime/serializer/json/json.go b/pkg/runtime/serializer/json/json.go new file mode 100644 index 000000000000..f9fb4bbfb227 --- /dev/null +++ b/pkg/runtime/serializer/json/json.go @@ -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 +} diff --git a/pkg/runtime/serializer/json/json_test.go b/pkg/runtime/serializer/json/json_test.go new file mode 100644 index 000000000000..f9a744f8718e --- /dev/null +++ b/pkg/runtime/serializer/json/json_test.go @@ -0,0 +1,269 @@ +/* +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/conversion" + "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: conversion.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 +} diff --git a/pkg/runtime/serializer/json/meta.go b/pkg/runtime/serializer/json/meta.go new file mode 100644 index 000000000000..91df105ed6cd --- /dev/null +++ b/pkg/runtime/serializer/json/meta.go @@ -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 +} diff --git a/pkg/runtime/serializer/json/meta_test.go b/pkg/runtime/serializer/json/meta_test.go new file mode 100644 index 000000000000..4b6351286f7a --- /dev/null +++ b/pkg/runtime/serializer/json/meta_test.go @@ -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") + } +} diff --git a/pkg/runtime/serializer/protobuf/doc.go b/pkg/runtime/serializer/protobuf/doc.go deleted file mode 100644 index 3fec7197aff6..000000000000 --- a/pkg/runtime/serializer/protobuf/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -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 handles serializing API objects to and from wire formats. -package protobuf diff --git a/pkg/runtime/serializer/recognizer/recognizer.go b/pkg/runtime/serializer/recognizer/recognizer.go new file mode 100644 index 000000000000..14a2cb3e841f --- /dev/null +++ b/pkg/runtime/serializer/recognizer/recognizer.go @@ -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 +} diff --git a/pkg/runtime/serializer/recognizer/recognizer_test.go b/pkg/runtime/serializer/recognizer/recognizer_test.go new file mode 100644 index 000000000000..c83b87aa4dda --- /dev/null +++ b/pkg/runtime/serializer/recognizer/recognizer_test.go @@ -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) +} diff --git a/pkg/runtime/serializer/versioning/versioning.go b/pkg/runtime/serializer/versioning/versioning.go new file mode 100644 index 000000000000..1b501369c133 --- /dev/null +++ b/pkg/runtime/serializer/versioning/versioning.go @@ -0,0 +1,243 @@ +/* +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" +) + +// 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 { + internal.encodeVersion[v.Group] = v + } + } + if decodeVersion != nil { + internal.decodeVersion = make(map[string]unversioned.GroupVersion) + for _, v := range decodeVersion { + 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...) +} diff --git a/pkg/runtime/serializer/versioning/versioning_test.go b/pkg/runtime/serializer/versioning/versioning_test.go new file mode 100644 index 000000000000..3d7eba27e3cd --- /dev/null +++ b/pkg/runtime/serializer/versioning/versioning_test.go @@ -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 +} diff --git a/pkg/runtime/serializer/yaml/yaml.go b/pkg/runtime/serializer/yaml/yaml.go new file mode 100644 index 000000000000..637c777be922 --- /dev/null +++ b/pkg/runtime/serializer/yaml/yaml.go @@ -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) +} diff --git a/pkg/util/yaml/decoder.go b/pkg/util/yaml/decoder.go index ebe597a598c6..5846fc20ff46 100644 --- a/pkg/util/yaml/decoder.go +++ b/pkg/util/yaml/decoder.go @@ -136,7 +136,7 @@ func NewYAMLOrJSONDecoder(r io.Reader, bufferSize int) *YAMLOrJSONDecoder { // provide object, or returns an error. func (d *YAMLOrJSONDecoder) Decode(into interface{}) error { if d.decoder == nil { - buffer, isJSON := guessJSONStream(d.r, d.bufferSize) + buffer, isJSON := GuessJSONStream(d.r, d.bufferSize) if isJSON { glog.V(4).Infof("decoding stream as JSON") d.decoder = json.NewDecoder(buffer) @@ -148,10 +148,10 @@ func (d *YAMLOrJSONDecoder) Decode(into interface{}) error { return d.decoder.Decode(into) } -// guessJSONStream scans the provided reader up to size, looking +// GuessJSONStream scans the provided reader up to size, looking // for an open brace indicating this is JSON. It will return the // bufio.Reader it creates for the consumer. -func guessJSONStream(r io.Reader, size int) (io.Reader, bool) { +func GuessJSONStream(r io.Reader, size int) (io.Reader, bool) { buffer := bufio.NewReaderSize(r, size) b, _ := buffer.Peek(size) return buffer, hasJSONPrefix(b) From 125ef6fbc8cc70477071936cd93d064d71c773e6 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:15:35 -0500 Subject: [PATCH 05/17] Support content-type negotiation in the API server A NegotiatedSerializer is passed into the API installer (and ParameterCodec, which abstracts conversion of query params) that can be used to negotiate client/server request/response serialization. All error paths are now negotiation aware, and are at least minimally version aware. Watch is specially coded to only allow application/json - a follow up change will convert it to use negotiation. Ensure the swagger scheme will include supported serializations - this now includes application/yaml as a negotiated option. --- cmd/kube-apiserver/app/server.go | 1 - pkg/apiserver/api_installer.go | 54 +-- pkg/apiserver/apiserver.go | 164 ++++------ pkg/apiserver/apiserver_test.go | 223 +++++++++---- pkg/apiserver/errors.go | 39 +++ pkg/apiserver/negotiate.go | 116 +++++++ pkg/apiserver/negotiate_test.go | 252 ++++++++++++++ pkg/apiserver/proxy.go | 34 +- pkg/apiserver/resthandler.go | 309 ++++++++++-------- pkg/apiserver/resthandler_test.go | 3 +- pkg/apiserver/watch.go | 30 +- pkg/apiserver/watch_test.go | 28 ++ pkg/genericapiserver/genericapiserver.go | 13 + pkg/genericapiserver/genericapiserver_test.go | 1 + test/integration/master_test.go | 70 ++++ 15 files changed, 971 insertions(+), 366 deletions(-) create mode 100644 pkg/apiserver/negotiate.go create mode 100644 pkg/apiserver/negotiate_test.go diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index eb847e5ff75c..ed55faaa6eb4 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -34,7 +34,6 @@ import ( "k8s.io/kubernetes/cmd/kube-apiserver/app/options" "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" apiutil "k8s.io/kubernetes/pkg/api/util" "k8s.io/kubernetes/pkg/apimachinery/registered" diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 10126e5f691e..819cec6a198a 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -65,7 +65,13 @@ var errEmptyName = errors.NewBadRequest("name must be provided") func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []unversioned.APIResource, errors []error) { errors = make([]error, 0) - proxyHandler := (&ProxyHandler{a.prefix + "/proxy/", a.group.Storage, a.group.Codec, a.group.Context, a.info}) + proxyHandler := (&ProxyHandler{ + prefix: a.prefix + "/proxy/", + storage: a.group.Storage, + serializer: a.group.Serializer, + context: a.group.Context, + requestInfoResolver: a.info, + }) // Register the paths in a deterministic (sorted) order to get a deterministic swagger spec. paths := make([]string, len(a.group.Storage)) @@ -93,9 +99,11 @@ func (a *APIInstaller) NewWebService() *restful.WebService { ws.Path(a.prefix) // a.prefix contains "prefix/group/version" ws.Doc("API at " + a.prefix) - // TODO: change to restful.MIME_JSON when we set content type in client + // Backwards compatibilty, we accepted objects with empty content-type at V1. + // If we stop using go-restful, we can default empty content-type to application/json on an + // endpoint by endpoint basis ws.Consumes("*/*") - ws.Produces(restful.MIME_JSON) + ws.Produces(a.group.Serializer.SupportedMediaTypes()...) ws.ApiVersion(a.group.GroupVersion.String()) return ws @@ -262,19 +270,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag getOptions runtime.Object versionedGetOptions runtime.Object getOptionsInternalKind unversioned.GroupVersionKind - getOptionsExternalKind unversioned.GroupVersionKind getSubpath bool - getSubpathKey string ) if isGetterWithOptions { - getOptions, getSubpath, getSubpathKey = getterWithOptions.NewGetOptions() + getOptions, getSubpath, _ = getterWithOptions.NewGetOptions() getOptionsInternalKind, err = a.group.Typer.ObjectKind(getOptions) if err != nil { return nil, err } - // TODO this should be a list of all the different external versions we can coerce into the internalKind - getOptionsExternalKind = optionsExternalVersion.WithKind(getOptionsInternalKind.Kind) - versionedGetOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind(getOptionsInternalKind.Kind)) if err != nil { return nil, err @@ -286,19 +289,15 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag connectOptions runtime.Object versionedConnectOptions runtime.Object connectOptionsInternalKind unversioned.GroupVersionKind - connectOptionsExternalKind unversioned.GroupVersionKind connectSubpath bool - connectSubpathKey string ) if isConnecter { - connectOptions, connectSubpath, connectSubpathKey = connecter.NewConnectOptions() + connectOptions, connectSubpath, _ = connecter.NewConnectOptions() if connectOptions != nil { connectOptionsInternalKind, err = a.group.Typer.ObjectKind(connectOptions) if err != nil { return nil, err } - // TODO this should be a list of all the different external versions we can coerce into the internalKind - connectOptionsExternalKind = optionsExternalVersion.WithKind(connectOptionsInternalKind.Kind) versionedConnectOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind(connectOptionsInternalKind.Kind)) } @@ -434,10 +433,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag // test/integration/auth_test.go is currently the most comprehensive status code test reqScope := RequestScope{ - ContextFunc: ctxFn, - Creater: a.group.Creater, - Convertor: a.group.Convertor, - Codec: mapping.Codec, + ContextFunc: ctxFn, + Serializer: a.group.Serializer, + ParameterCodec: a.group.ParameterCodec, + Creater: a.group.Creater, + Convertor: a.group.Convertor, Resource: a.group.GroupVersion.WithResource(resource), Subresource: subresource, @@ -454,7 +454,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag case "GET": // Get a resource. var handler restful.RouteFunction if isGetterWithOptions { - handler = GetResourceWithOptions(getterWithOptions, exporter, reqScope, getOptionsInternalKind, getOptionsExternalKind, getSubpath, getSubpathKey) + handler = GetResourceWithOptions(getterWithOptions, reqScope) } else { handler = GetResource(getter, exporter, reqScope) } @@ -467,7 +467,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("read"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Writes(versionedObject) if isGetterWithOptions { @@ -492,7 +492,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("list"+namespaced+kind+strings.Title(subresource)). - Produces("application/json"). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedList). Writes(versionedList) if err := addObjectParams(ws, route, versionedListOptions); err != nil { @@ -524,7 +524,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("replace"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). Writes(versionedObject) @@ -541,7 +541,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Consumes(string(api.JSONPatchType), string(api.MergePatchType), string(api.StrategicMergePatchType)). Operation("patch"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(unversioned.Patch{}). Writes(versionedObject) @@ -563,7 +563,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("create"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). Writes(versionedObject) @@ -579,7 +579,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("delete"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Writes(versionedStatus). Returns(http.StatusOK, "OK", versionedStatus) if isGracefulDeleter { @@ -597,7 +597,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("deletecollection"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Writes(versionedStatus). Returns(http.StatusOK, "OK", versionedStatus) if err := addObjectParams(ws, route, versionedListOptions); err != nil { @@ -658,7 +658,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag doc = "connect " + method + " requests to " + subresource + " of " + kind } route := ws.Method(method).Path(action.Path). - To(ConnectResource(connecter, reqScope, admit, connectOptionsInternalKind, connectOptionsExternalKind, path, connectSubpath, connectSubpathKey)). + To(ConnectResource(connecter, reqScope, admit, path)). Filter(m). Doc(doc). Operation("connect" + strings.Title(strings.ToLower(method)) + namespaced + kind + strings.Title(subresource)). diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 2a3143f37cc0..8f3c7aa898b3 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -26,7 +26,6 @@ import ( "net/http" "path" rt "runtime" - "strconv" "strings" "time" @@ -36,7 +35,6 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/runtime" @@ -97,7 +95,9 @@ type APIGroupVersion struct { Mapper meta.RESTMapper - Codec runtime.Codec + Serializer runtime.NegotiatedSerializer + ParameterCodec runtime.ParameterCodec + Typer runtime.ObjectTyper Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor @@ -126,7 +126,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container) error { installer := g.newInstaller() ws := installer.NewWebService() apiResources, registrationErrors := installer.Install(ws) - AddSupportedResourcesWebService(ws, g.GroupVersion, apiResources) + AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, apiResources) container.Add(ws) return utilerrors.NewAggregate(registrationErrors) } @@ -150,7 +150,7 @@ func (g *APIGroupVersion) UpdateREST(container *restful.Container) error { return apierrors.NewInternalError(fmt.Errorf("unable to find an existing webservice for prefix %s", installer.prefix)) } apiResources, registrationErrors := installer.Install(ws) - AddSupportedResourcesWebService(ws, g.GroupVersion, apiResources) + AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, apiResources) return utilerrors.NewAggregate(registrationErrors) } @@ -194,12 +194,15 @@ func InstallLogsSupport(mux Mux) { mux.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))) } -func InstallRecoverHandler(container *restful.Container) { - container.RecoverHandler(logStackOnRecover) +// TODO: needs to perform response type negotiation, this is probably the wrong way to recover panics +func InstallRecoverHandler(s runtime.NegotiatedSerializer, container *restful.Container) { + container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) { + logStackOnRecover(s, panicReason, httpWriter) + }) } //TODO: Unify with RecoverPanics? -func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) { +func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason)) for i := 2; ; i += 1 { @@ -211,125 +214,110 @@ func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) } glog.Errorln(buffer.String()) - // TODO: make status unversioned or plumb enough of the request to deduce the requested API version - errorJSON(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), registered.GroupOrDie(api.GroupName).Codec, httpWriter) + headers := http.Header{} + if ct := w.Header().Get("Content-Type"); len(ct) > 0 { + headers.Set("Accept", ct) + } + errorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, w, &http.Request{Header: headers}) } -func InstallServiceErrorHandler(container *restful.Container, requestResolver *RequestInfoResolver, apiVersions []string) { +func InstallServiceErrorHandler(s runtime.NegotiatedSerializer, container *restful.Container, requestResolver *RequestInfoResolver, apiVersions []string) { container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { - serviceErrorHandler(requestResolver, apiVersions, serviceErr, request, response) + serviceErrorHandler(s, requestResolver, apiVersions, serviceErr, request, response) }) } -func serviceErrorHandler(requestResolver *RequestInfoResolver, apiVersions []string, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { - requestInfo, err := requestResolver.GetRequestInfo(request.Request) - codec := registered.GroupOrDie(api.GroupName).Codec - if err == nil && requestInfo.APIVersion != "" { - // check if the api version is valid. - for _, version := range apiVersions { - if requestInfo.APIVersion == version { - // valid api version. - codec = runtime.CodecFor(api.Scheme, unversioned.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}) - break - } - } - } - - errorJSON(apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", "", 0, false), codec, response.ResponseWriter) +func serviceErrorHandler(s runtime.NegotiatedSerializer, requestResolver *RequestInfoResolver, apiVersions []string, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { + errorNegotiated(apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, response.ResponseWriter, request.Request) } // Adds a service to return the supported api versions at the legacy /api. -func AddApiWebService(container *restful.Container, apiPrefix string, versions []string) { +func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, versions []string) { // TODO: InstallREST should register each version automatically - versionHandler := APIVersionHandler(versions[:]...) + versionHandler := APIVersionHandler(s, versions[:]...) ws := new(restful.WebService) ws.Path(apiPrefix) ws.Doc("get available API versions") ws.Route(ws.GET("/").To(versionHandler). Doc("get available API versions"). Operation("getAPIVersions"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) container.Add(ws) } // Adds a service to return the supported api versions at /apis. -func AddApisWebService(container *restful.Container, apiPrefix string, f func() []unversioned.APIGroup) { - rootAPIHandler := RootAPIHandler(f) +func AddApisWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, f func() []unversioned.APIGroup) { + rootAPIHandler := RootAPIHandler(s, f) ws := new(restful.WebService) ws.Path(apiPrefix) ws.Doc("get available API versions") ws.Route(ws.GET("/").To(rootAPIHandler). Doc("get available API versions"). Operation("getAPIVersions"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) container.Add(ws) } // Adds a service to return the supported versions, preferred version, and name // of a group. E.g., a such web service will be registered at /apis/extensions. -func AddGroupWebService(container *restful.Container, path string, group unversioned.APIGroup) { - groupHandler := GroupHandler(group) +func AddGroupWebService(s runtime.NegotiatedSerializer, container *restful.Container, path string, group unversioned.APIGroup) { + groupHandler := GroupHandler(s, group) ws := new(restful.WebService) ws.Path(path) ws.Doc("get information of a group") ws.Route(ws.GET("/").To(groupHandler). Doc("get information of a group"). Operation("getAPIGroup"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) container.Add(ws) } // Adds a service to return the supported resources, E.g., a such web service // will be registered at /apis/extensions/v1. -func AddSupportedResourcesWebService(ws *restful.WebService, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) { - resourceHandler := SupportedResourcesHandler(groupVersion, apiResources) +func AddSupportedResourcesWebService(s runtime.NegotiatedSerializer, ws *restful.WebService, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) { + resourceHandler := SupportedResourcesHandler(s, groupVersion, apiResources) ws.Route(ws.GET("/").To(resourceHandler). Doc("get available resources"). Operation("getAPIResources"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) } // handleVersion writes the server's version information. func handleVersion(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods writeRawJSON(http.StatusOK, version.Get(), resp.ResponseWriter) } // APIVersionHandler returns a handler which will list the provided versions as available. -func APIVersionHandler(versions ...string) restful.RouteFunction { +func APIVersionHandler(s runtime.NegotiatedSerializer, versions ...string) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &unversioned.APIVersions{Versions: versions}, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIVersions{Versions: versions}) } } // RootAPIHandler returns a handler which will list the provided groups and versions as available. -func RootAPIHandler(f func() []unversioned.APIGroup) restful.RouteFunction { +func RootAPIHandler(s runtime.NegotiatedSerializer, f func() []unversioned.APIGroup) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &unversioned.APIGroupList{Groups: f()}, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIGroupList{Groups: f()}) } } // GroupHandler returns a handler which will return the api.GroupAndVersion of // the group. -func GroupHandler(group unversioned.APIGroup) restful.RouteFunction { +func GroupHandler(s runtime.NegotiatedSerializer, group unversioned.APIGroup) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &group, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &group) } } // SupportedResourcesHandler returns a handler which will list the provided resources as available. -func SupportedResourcesHandler(groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) restful.RouteFunction { +func SupportedResourcesHandler(s runtime.NegotiatedSerializer, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &unversioned.APIResourceList{GroupVersion: groupVersion.String(), APIResources: apiResources}, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIResourceList{GroupVersion: groupVersion.String(), APIResources: apiResources}) } } @@ -338,11 +326,11 @@ func SupportedResourcesHandler(groupVersion unversioned.GroupVersion, apiResourc // response. The Accept header and current API version will be passed in, and the output will be copied // directly to the response body. If content type is returned it is used, otherwise the content type will // be "application/octet-stream". All other objects are sent to standard JSON serialization. -func write(statusCode int, groupVersion unversioned.GroupVersion, codec runtime.Codec, object runtime.Object, w http.ResponseWriter, req *http.Request) { +func write(statusCode int, gv unversioned.GroupVersion, s runtime.NegotiatedSerializer, object runtime.Object, w http.ResponseWriter, req *http.Request) { if stream, ok := object.(rest.ResourceStreamer); ok { - out, flush, contentType, err := stream.InputStream(groupVersion.String(), req.Header.Get("Accept")) + out, flush, contentType, err := stream.InputStream(gv.String(), req.Header.Get("Accept")) if err != nil { - errorJSONFatal(err, codec, w) + errorNegotiated(err, s, gv, w, req) return } if out == nil { @@ -372,64 +360,38 @@ func write(statusCode int, groupVersion unversioned.GroupVersion, codec runtime. io.Copy(writer, out) return } - writeJSON(statusCode, codec, object, w, isPrettyPrint(req)) + writeNegotiated(s, gv, w, req, statusCode, object) } -func isPrettyPrint(req *http.Request) bool { - pp := req.URL.Query().Get("pretty") - if len(pp) > 0 { - pretty, _ := strconv.ParseBool(pp) - return pretty - } - userAgent := req.UserAgent() - // This covers basic all browers and cli http tools - if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") { - return true +// writeNegotiated renders an object in the content type negotiated by the client +func writeNegotiated(s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) { + serializer, contentType, err := negotiateOutputSerializer(req, s) + if err != nil { + status := errToAPIStatus(err) + writeRawJSON(int(status.Code), status, w) + return } - return false -} -// writeJSON renders an object as JSON to the response. -func writeJSON(statusCode int, codec runtime.Codec, object runtime.Object, w http.ResponseWriter, pretty bool) { - w.Header().Set("Content-Type", "application/json") - // We send the status code before we encode the object, so if we error, the status code stays but there will - // still be an error object. This seems ok, the alternative is to validate the object before - // encoding, but this really should never happen, so it's wasted compute for every API request. + w.Header().Set("Content-Type", contentType) w.WriteHeader(statusCode) - if pretty { - prettyJSON(codec, object, w) - return - } - err := codec.EncodeToStream(object, w) - if err != nil { - errorJSONFatal(err, codec, w) - } -} -func prettyJSON(codec runtime.Codec, object runtime.Object, w http.ResponseWriter) { - formatted := &bytes.Buffer{} - output, err := runtime.Encode(codec, object) - if err != nil { - errorJSONFatal(err, codec, w) - } - if err := json.Indent(formatted, output, "", " "); err != nil { - errorJSONFatal(err, codec, w) - return + encoder := s.EncoderForVersion(serializer, gv) + if err := encoder.EncodeToStream(object, w); err != nil { + errorJSONFatal(err, encoder, w) } - w.Write(formatted.Bytes()) } -// errorJSON renders an error to the response. Returns the HTTP status code of the error. -func errorJSON(err error, codec runtime.Codec, w http.ResponseWriter) int { +// errorNegotiated renders an error to the response. Returns the HTTP status code of the error. +func errorNegotiated(err error, s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request) int { status := errToAPIStatus(err) code := int(status.Code) - writeJSON(code, codec, status, w, true) + writeNegotiated(s, gv, w, req, code, status) return code } // errorJSONFatal renders an error to the response, and if codec fails will render plaintext. // Returns the HTTP status code of the error. -func errorJSONFatal(err error, codec runtime.Codec, w http.ResponseWriter) int { +func errorJSONFatal(err error, codec runtime.Encoder, w http.ResponseWriter) int { util.HandleError(fmt.Errorf("apiserver was unable to write a JSON response: %v", err)) status := errToAPIStatus(err) code := int(status.Code) diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 503c6dc0253c..2a9bff8302ce 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" apierrs "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" @@ -58,18 +59,21 @@ func convert(obj runtime.Object) (runtime.Object, error) { // This creates fake API versions, similar to api/latest.go. var testAPIGroup = "test.group" -var testInternalGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: ""} +var testInternalGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: runtime.APIVersionInternal} var testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"} var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"} var prefix = "apis" var grouplessGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"} +var grouplessInternalGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal} var grouplessPrefix = "api" -var grouplessCodec = runtime.CodecFor(api.Scheme, grouplessGroupVersion) var groupVersions = []unversioned.GroupVersion{grouplessGroupVersion, testGroupVersion, newGroupVersion} -var codec = runtime.CodecFor(api.Scheme, testGroupVersion) -var newCodec = runtime.CodecFor(api.Scheme, newGroupVersion) + +var codec = latest.Codecs.LegacyCodec(groupVersions...) +var grouplessCodec = latest.Codecs.LegacyCodec(grouplessGroupVersion) +var testCodec = latest.Codecs.LegacyCodec(testGroupVersion) +var newCodec = latest.Codecs.LegacyCodec(newGroupVersion) var accessor = meta.NewAccessor() var versioner runtime.ResourceVersioner = accessor @@ -82,19 +86,16 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case testGroupVersion: return &meta.VersionInterfaces{ - Codec: codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil case newGroupVersion: return &meta.VersionInterfaces{ - Codec: newCodec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil case grouplessGroupVersion: return &meta.VersionInterfaces{ - Codec: grouplessCodec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil @@ -117,10 +118,13 @@ func addGrouplessTypes() { ResourceVersion string `json:"resourceVersion,omitempty"` TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"` } - api.Scheme.AddKnownTypes( - grouplessGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) + api.Scheme.AddKnownTypes(grouplessGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{}, + &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) api.Scheme.AddKnownTypes(grouplessGroupVersion, &api.Pod{}) + api.Scheme.AddKnownTypes(grouplessInternalGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{}, + &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) } func addTestTypes() { @@ -133,11 +137,13 @@ func addTestTypes() { ResourceVersion string `json:"resourceVersion,omitempty"` TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"` } - api.Scheme.AddKnownTypes( - testGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}, - &unversioned.ExportOptions{}) + api.Scheme.AddKnownTypes(testGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{}, + &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) api.Scheme.AddKnownTypes(testGroupVersion, &api.Pod{}) + api.Scheme.AddKnownTypes(testInternalGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{}, + &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) } func addNewTestTypes() { @@ -150,21 +156,15 @@ func addNewTestTypes() { ResourceVersion string `json:"resourceVersion,omitempty"` TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"` } - api.Scheme.AddKnownTypes( - newGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}, - &unversioned.ExportOptions{}) + api.Scheme.AddKnownTypes(newGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{}, + &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) } func init() { // Certain API objects are returned regardless of the contents of storage: // api.Status is returned in errors - // "internal" version - api.Scheme.AddKnownTypes( - testInternalGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &api.ListOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) - api.Scheme.AddInternalGroupVersion(testInternalGroupVersion) addGrouplessTypes() addTestTypes() addNewTestTypes() @@ -253,6 +253,8 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. Linker: selfLinker, Mapper: namespaceMapper, + ParameterCodec: api.ParameterCodec, + Admit: admissionControl, Context: requestContextMapper, } @@ -263,7 +265,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + grouplessPrefix group.GroupVersion = grouplessGroupVersion group.OptionsExternalVersion = &grouplessGroupVersion - group.Codec = grouplessCodec + group.Serializer = latest.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -275,7 +277,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + prefix group.GroupVersion = testGroupVersion group.OptionsExternalVersion = &testGroupVersion - group.Codec = codec + group.Serializer = latest.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -287,7 +289,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + prefix group.GroupVersion = newGroupVersion group.OptionsExternalVersion = &newGroupVersion - group.Codec = newCodec + group.Serializer = latest.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -430,7 +432,11 @@ func (storage *SimpleRESTStorage) Get(ctx api.Context, id string) (runtime.Objec if id == "binary" { return storage.stream, storage.errors["get"] } - return api.Scheme.CopyOrDie(&storage.item), storage.errors["get"] + copied, err := api.Scheme.Copy(&storage.item) + if err != nil { + panic(err) + } + return copied, storage.errors["get"] } func (storage *SimpleRESTStorage) checkContext(ctx api.Context) { @@ -645,7 +651,11 @@ func (storage *SimpleTypedStorage) New() runtime.Object { func (storage *SimpleTypedStorage) Get(ctx api.Context, id string) (runtime.Object, error) { storage.checkContext(ctx) - return api.Scheme.CopyOrDie(storage.item), storage.errors["get"] + copied, err := api.Scheme.Copy(storage.item) + if err != nil { + panic(err) + } + return copied, storage.errors["get"] } func (storage *SimpleTypedStorage) checkContext(ctx api.Context) { @@ -653,13 +663,16 @@ func (storage *SimpleTypedStorage) checkContext(ctx api.Context) { } func extractBody(response *http.Response, object runtime.Object) (string, error) { + return extractBodyDecoder(response, object, codec) +} + +func extractBodyDecoder(response *http.Response, object runtime.Object, decoder runtime.Decoder) (string, error) { defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return string(body), err } - err = runtime.DecodeInto(codec, body, object) - return string(body), err + return string(body), runtime.DecodeInto(decoder, body, object) } func TestNotFound(t *testing.T) { @@ -1196,7 +1209,11 @@ func TestMetadata(t *testing.T) { matches[s] = i + 1 } } - if matches["text/plain,application/json"] == 0 || matches["application/json"] == 0 || matches["*/*"] == 0 || len(matches) != 3 { + if matches["text/plain,application/json,application/yaml"] == 0 || + matches["application/json,application/yaml"] == 0 || + matches["application/json"] == 0 || + matches["*/*"] == 0 || + len(matches) != 4 { t.Errorf("unexpected mime types: %v", matches) } } @@ -2060,7 +2077,7 @@ func TestUpdate(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2098,7 +2115,7 @@ func TestUpdateInvokesAdmissionControl(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2128,7 +2145,7 @@ func TestUpdateRequiresMatchingName(t *testing.T) { item := &apiservertesting.Simple{ Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2161,7 +2178,7 @@ func TestUpdateAllowsMissingNamespace(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2200,7 +2217,7 @@ func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2238,7 +2255,7 @@ func TestUpdatePreventsMismatchedNamespace(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2274,7 +2291,7 @@ func TestUpdateMissing(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2304,7 +2321,7 @@ func TestCreateNotFound(t *testing.T) { client := http.Client{} simple := &apiservertesting.Simple{Other: "foo"} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2331,7 +2348,7 @@ func TestCreateChecksDecode(t *testing.T) { client := http.Client{} simple := &api.Pod{} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(codec, simple, testGroupVersion) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2373,7 +2390,9 @@ func TestUpdateREST(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Codec: newCodec, + + Serializer: latest.Codecs, + ParameterCodec: api.ParameterCodec, } } @@ -2455,7 +2474,9 @@ func TestParentResourceIsRequired(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Codec: newCodec, + + Serializer: latest.Codecs, + ParameterCodec: api.ParameterCodec, } container := restful.NewContainer() if err := group.InstallREST(container); err == nil { @@ -2484,7 +2505,9 @@ func TestParentResourceIsRequired(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Codec: newCodec, + + Serializer: latest.Codecs, + ParameterCodec: api.ParameterCodec, } container = restful.NewContainer() if err := group.InstallREST(container); err != nil { @@ -2522,7 +2545,7 @@ func TestCreateWithName(t *testing.T) { client := http.Client{} simple := &apiservertesting.Simple{Other: "foo"} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2550,7 +2573,7 @@ func TestUpdateChecksDecode(t *testing.T) { client := http.Client{} simple := &api.Pod{} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(codec, simple, testGroupVersion) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2563,7 +2586,7 @@ func TestUpdateChecksDecode(t *testing.T) { t.Errorf("unexpected error: %v", err) } if response.StatusCode != http.StatusBadRequest { - t.Errorf("Unexpected response %#v", response) + t.Errorf("Unexpected response %#v\n%s", response, readBodyOrDie(response.Body)) } b, err := ioutil.ReadAll(response.Body) if err != nil { @@ -2627,7 +2650,7 @@ func TestCreate(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2651,7 +2674,7 @@ func TestCreate(t *testing.T) { var itemOut apiservertesting.Simple body, err := extractBody(response, &itemOut) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Errorf("unexpected error: %v %#v", err, response) } if !reflect.DeepEqual(&itemOut, simple) { @@ -2665,6 +2688,74 @@ func TestCreate(t *testing.T) { } } +func TestCreateYAML(t *testing.T) { + storage := SimpleRESTStorage{ + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { + time.Sleep(5 * time.Millisecond) + return obj, nil + }, + } + selfLinker := &setTestSelfLinker{ + t: t, + name: "bar", + namespace: "default", + expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar", + } + handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker) + server := httptest.NewServer(handler) + defer server.Close() + client := http.Client{} + + // yaml encoder + simple := &apiservertesting.Simple{ + Other: "bar", + } + serializer, ok := latest.Codecs.SerializerForMediaType("application/yaml", nil) + if !ok { + t.Fatal("No yaml serializer") + } + encoder := latest.Codecs.EncoderForVersion(serializer, testGroupVersion) + decoder := latest.Codecs.DecoderToVersion(serializer, testInternalGroupVersion) + + data, err := runtime.Encode(encoder, simple) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + request.Header.Set("Accept", "application/yaml, application/json") + request.Header.Set("Content-Type", "application/yaml") + + wg := sync.WaitGroup{} + wg.Add(1) + var response *http.Response + go func() { + response, err = client.Do(request) + wg.Done() + }() + wg.Wait() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + var itemOut apiservertesting.Simple + body, err := extractBodyDecoder(response, &itemOut, decoder) + if err != nil { + t.Fatalf("unexpected error: %v %#v", err, response) + } + + if !reflect.DeepEqual(&itemOut, simple) { + t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body)) + } + if response.StatusCode != http.StatusCreated { + t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response) + } + if !selfLinker.called { + t.Errorf("Never set self link") + } +} func TestCreateInNamespace(t *testing.T) { storage := SimpleRESTStorage{ injectedFunction: func(obj runtime.Object) (runtime.Object, error) { @@ -2687,13 +2778,13 @@ func TestCreateInNamespace(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/other/foo", bytes.NewBuffer(data)) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } wg := sync.WaitGroup{} @@ -2705,13 +2796,13 @@ func TestCreateInNamespace(t *testing.T) { }() wg.Wait() if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } var itemOut apiservertesting.Simple body, err := extractBody(response, &itemOut) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v\n%s", err, data) } if !reflect.DeepEqual(&itemOut, simple) { @@ -2747,7 +2838,7 @@ func TestCreateInvokesAdmissionControl(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2822,7 +2913,7 @@ func (obj *UnregisteredAPIObject) GetObjectKind() unversioned.ObjectKind { func TestWriteJSONDecodeError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - writeJSON(http.StatusOK, codec, &UnregisteredAPIObject{"Undecodable"}, w, false) + writeNegotiated(latest.Codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"}) })) // TODO: Uncomment when fix #19254 // defer server.Close() @@ -2833,7 +2924,7 @@ func TestWriteJSONDecodeError(t *testing.T) { if status.Reason != unversioned.StatusReasonUnknown { t.Errorf("unexpected reason %#v", status) } - if !strings.Contains(status.Message, "type apiserver.UnregisteredAPIObject is not registered") { + if !strings.Contains(status.Message, "no kind is registered for the type apiserver.UnregisteredAPIObject") { t.Errorf("unexpected message %#v", status) } } @@ -2881,7 +2972,7 @@ func TestCreateTimeout(t *testing.T) { // defer server.Close() simple := &apiservertesting.Simple{Other: "foo"} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2993,7 +3084,7 @@ func TestCreateChecksAPIVersion(t *testing.T) { b, err := ioutil.ReadAll(response.Body) if err != nil { t.Errorf("unexpected error: %v", err) - } else if !strings.Contains(string(b), "does not match the specified apiVersion") { + } else if !strings.Contains(string(b), "does not match the expected API version") { t.Errorf("unexpected response: %s", string(b)) } } @@ -3044,15 +3135,15 @@ func TestUpdateChecksAPIVersion(t *testing.T) { simple := &apiservertesting.Simple{ObjectMeta: api.ObjectMeta{Name: "bar"}} data, err := runtime.Encode(newCodec, simple) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data)) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } response, err := client.Do(request) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } if response.StatusCode != http.StatusBadRequest { t.Errorf("Unexpected response %#v", response) @@ -3060,7 +3151,15 @@ func TestUpdateChecksAPIVersion(t *testing.T) { b, err := ioutil.ReadAll(response.Body) if err != nil { t.Errorf("unexpected error: %v", err) - } else if !strings.Contains(string(b), "does not match the specified apiVersion") { + } else if !strings.Contains(string(b), "does not match the expected API version") { t.Errorf("unexpected response: %s", string(b)) } } + +func readBodyOrDie(r io.Reader) []byte { + body, err := ioutil.ReadAll(r) + if err != nil { + panic(err) + } + return body +} diff --git a/pkg/apiserver/errors.go b/pkg/apiserver/errors.go index d3d485e7d097..c7c5e9a90f16 100644 --- a/pkg/apiserver/errors.go +++ b/pkg/apiserver/errors.go @@ -19,6 +19,7 @@ package apiserver import ( "fmt" "net/http" + "strings" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/storage" @@ -105,3 +106,41 @@ func IsAPIPrefixNotFound(err error) bool { _, ok := err.(*errAPIPrefixNotFound) return ok } + +// errNotAcceptable indicates Accept negotiation has failed +// TODO: move to api/errors if other code needs to return this +type errNotAcceptable struct { + accepted []string +} + +func (e errNotAcceptable) Error() string { + return fmt.Sprintf("only the following media types are accepted: %v", strings.Join(e.accepted, ", ")) +} + +func (e errNotAcceptable) Status() unversioned.Status { + return unversioned.Status{ + Status: unversioned.StatusFailure, + Code: http.StatusNotAcceptable, + Reason: unversioned.StatusReason("NotAcceptable"), + Message: e.Error(), + } +} + +// errNotAcceptable indicates Content-Type is not recognized +// TODO: move to api/errors if other code needs to return this +type errUnsupportedMediaType struct { + accepted []string +} + +func (e errUnsupportedMediaType) Error() string { + return fmt.Sprintf("the body of the request was in an unknown format - accepted media types include: %v", strings.Join(e.accepted, ", ")) +} + +func (e errUnsupportedMediaType) Status() unversioned.Status { + return unversioned.Status{ + Status: unversioned.StatusFailure, + Code: http.StatusUnsupportedMediaType, + Reason: unversioned.StatusReason("UnsupportedMediaType"), + Message: e.Error(), + } +} diff --git a/pkg/apiserver/negotiate.go b/pkg/apiserver/negotiate.go new file mode 100644 index 000000000000..1457addbfc2c --- /dev/null +++ b/pkg/apiserver/negotiate.go @@ -0,0 +1,116 @@ +/* +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 apiserver + +import ( + "mime" + "net/http" + "strconv" + "strings" + + "bitbucket.org/ww/goautoneg" + + "k8s.io/kubernetes/pkg/runtime" +) + +func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.Serializer, string, error) { + acceptHeader := req.Header.Get("Accept") + supported := ns.SupportedMediaTypes() + if len(acceptHeader) == 0 && len(supported) > 0 { + acceptHeader = supported[0] + } + accept, ok := negotiate(acceptHeader, supported) + if !ok { + return nil, "", errNotAcceptable{supported} + } + + pretty := isPrettyPrint(req) + if _, ok := accept.Params["pretty"]; !ok && pretty { + accept.Params["pretty"] = "1" + } + mediaType := accept.Type + if len(accept.SubType) > 0 { + mediaType += "/" + accept.SubType + } + if s, ok := ns.SerializerForMediaType(mediaType, accept.Params); ok { + return s, mediaType, nil + } + + return nil, "", errNotAcceptable{supported} +} + +func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer) (runtime.Serializer, error) { + supported := s.SupportedMediaTypes() + mediaType := req.Header.Get("Content-Type") + if len(mediaType) == 0 { + mediaType = supported[0] + } + mediaType, options, err := mime.ParseMediaType(mediaType) + if err != nil { + return nil, errUnsupportedMediaType{supported} + } + out, ok := s.SerializerForMediaType(mediaType, options) + if !ok { + return nil, errUnsupportedMediaType{supported} + } + return out, nil +} + +// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent +// matches known "human" clients. +func isPrettyPrint(req *http.Request) bool { + // DEPRECATED: should be part of the content type + if req.URL != nil { + pp := req.URL.Query().Get("pretty") + if len(pp) > 0 { + pretty, _ := strconv.ParseBool(pp) + return pretty + } + } + userAgent := req.UserAgent() + // This covers basic all browers and cli http tools + if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") { + return true + } + return false +} + +// negotiate the most appropriate content type given the accept header and a list of +// alternatives. +func negotiate(header string, alternatives []string) (goautoneg.Accept, bool) { + alternates := make([][]string, 0, len(alternatives)) + for _, alternate := range alternatives { + alternates = append(alternates, strings.SplitN(alternate, "/", 2)) + } + for _, clause := range goautoneg.ParseAccept(header) { + for _, alternate := range alternates { + if clause.Type == alternate[0] && clause.SubType == alternate[1] { + return clause, true + } + if clause.Type == alternate[0] && clause.SubType == "*" { + clause.SubType = alternate[1] + return clause, true + } + if clause.Type == "*" && clause.SubType == "*" { + clause.Type = alternate[0] + clause.SubType = alternate[1] + return clause, true + } + } + } + return goautoneg.Accept{}, false +} diff --git a/pkg/apiserver/negotiate_test.go b/pkg/apiserver/negotiate_test.go new file mode 100644 index 000000000000..8e59d6d69cc0 --- /dev/null +++ b/pkg/apiserver/negotiate_test.go @@ -0,0 +1,252 @@ +/* +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 apiserver + +import ( + "net/http" + "net/url" + "reflect" + "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" +) + +type fakeNegotiater struct { + serializer runtime.Serializer + types []string + mediaType string + options map[string]string +} + +func (n *fakeNegotiater) SupportedMediaTypes() []string { + return n.types +} + +func (n *fakeNegotiater) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) { + n.mediaType = mediaType + if len(options) > 0 { + n.options = options + } + return n.serializer, n.serializer != nil +} + +func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder { + return n.serializer +} + +func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder { + return n.serializer +} + +var fakeCodec = runtime.NewCodec(runtime.NoopEncoder{}, runtime.NoopDecoder{}) + +func TestNegotiate(t *testing.T) { + testCases := []struct { + accept string + req *http.Request + ns *fakeNegotiater + serializer runtime.Serializer + contentType string + params map[string]string + errFn func(error) bool + }{ + // pick a default + { + req: &http.Request{}, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "*/*", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "application/*", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "application/json", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "application/json", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json", "application/protobuf"}}, + serializer: fakeCodec, + }, + { + accept: "application/protobuf", + contentType: "application/protobuf", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json", "application/protobuf"}}, + serializer: fakeCodec, + }, + { + accept: "application/json; pretty=1", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + { + accept: "unrecognized/stuff,application/json; pretty=1", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + + // query param triggers pretty + { + req: &http.Request{ + Header: http.Header{"Accept": []string{"application/json"}}, + URL: &url.URL{RawQuery: "pretty=1"}, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + + // certain user agents trigger pretty + { + req: &http.Request{ + Header: http.Header{ + "Accept": []string{"application/json"}, + "User-Agent": []string{"curl"}, + }, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + { + req: &http.Request{ + Header: http.Header{ + "Accept": []string{"application/json"}, + "User-Agent": []string{"Wget"}, + }, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + { + req: &http.Request{ + Header: http.Header{ + "Accept": []string{"application/json"}, + "User-Agent": []string{"Mozilla/5.0"}, + }, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + + // "application" is not a valid media type, so the server will reject the response during + // negotiation (the server, in error, has specified an invalid media type) + { + accept: "application", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application"}}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: application" + }, + }, + { + ns: &fakeNegotiater{types: []string{"a/b/c"}}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: a/b/c" + }, + }, + { + ns: &fakeNegotiater{}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: " + }, + }, + { + accept: "*/*", + ns: &fakeNegotiater{}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: " + }, + }, + { + accept: "application/json", + ns: &fakeNegotiater{types: []string{"application/json"}}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: application/json" + }, + }, + } + + for i, test := range testCases { + req := test.req + if req == nil { + req = &http.Request{Header: http.Header{}} + req.Header.Set("Accept", test.accept) + } + s, contentType, err := negotiateOutputSerializer(req, test.ns) + switch { + case err == nil && test.errFn != nil: + t.Errorf("%d: failed: expected error", i) + 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) + } + status, ok := err.(statusError) + if !ok { + t.Errorf("%d: failed, error should be statusError: %v", i, err) + continue + } + if status.Status().Status != unversioned.StatusFailure || status.Status().Code != http.StatusNotAcceptable { + t.Errorf("%d: failed: %v", i, err) + continue + } + continue + } + if test.contentType != contentType { + t.Errorf("%d: unexpected %s %s", i, test.contentType, contentType) + } + if s != test.serializer { + t.Errorf("%d: unexpected %s %s", i, test.serializer, s) + } + if !reflect.DeepEqual(test.params, test.ns.options) { + t.Errorf("%d: unexpected %#v %#v", i, test.params, test.ns.options) + } + } +} diff --git a/pkg/apiserver/proxy.go b/pkg/apiserver/proxy.go index 32474daaa9c2..d640cb957f64 100644 --- a/pkg/apiserver/proxy.go +++ b/pkg/apiserver/proxy.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/rest" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/httplog" "k8s.io/kubernetes/pkg/runtime" @@ -44,7 +45,7 @@ import ( type ProxyHandler struct { prefix string storage map[string]rest.Storage - codec runtime.Codec + serializer runtime.NegotiatedSerializer context api.RequestContextMapper requestInfoResolver *RequestInfoResolver } @@ -98,20 +99,19 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } apiResource = resource + gv := unversioned.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} + redirector, ok := storage.(rest.Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) - httpCode = errorJSON(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.codec, w) + httpCode = errorNegotiated(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.serializer, gv, w, req) return } location, roundTripper, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) - httpCode = code + httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } if location == nil { @@ -144,11 +144,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { newReq, err := http.NewRequest(req.Method, location.String(), req.Body) if err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) - notFound(w, req) - httpCode = code + httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } httpCode = http.StatusOK @@ -161,7 +157,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. - if r.tryUpgrade(w, req, newReq, location, roundTripper) { + if r.tryUpgrade(w, req, newReq, location, roundTripper, gv) { return } @@ -210,15 +206,13 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // tryUpgrade returns true if the request was handled. -func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper) bool { +func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper, gv unversioned.GroupVersion) bool { if !httpstream.IsUpgradeRequest(req) { return false } backendConn, err := proxyutil.DialURL(location, transport) if err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) + errorNegotiated(err, r.serializer, gv, w, req) return true } defer backendConn.Close() @@ -228,17 +222,13 @@ func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Reque // hijack, just for reference... requestHijackedConn, _, err := w.(http.Hijacker).Hijack() if err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) + errorNegotiated(err, r.serializer, gv, w, req) return true } defer requestHijackedConn.Close() if err = newReq.Write(backendConn); err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) + errorNegotiated(err, r.serializer, gv, w, req) return true } diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 41032066af1c..43674b933ef8 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -32,7 +32,6 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" @@ -70,7 +69,8 @@ type ScopeNamer interface { type RequestScope struct { Namer ScopeNamer ContextFunc - runtime.Codec + Serializer runtime.NegotiatedSerializer + runtime.ParameterCodec Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor @@ -79,6 +79,10 @@ type RequestScope struct { Subresource string } +func (scope *RequestScope) err(err error, req *restful.Request, res *restful.Response) { + errorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), res.ResponseWriter, req.Request) +} + // getterFunc performs a get request with the given context and object name. The request // may be used to deserialize an options object to pass to the getter. type getterFunc func(ctx api.Context, name string, req *restful.Request) (runtime.Object, error) @@ -93,7 +97,7 @@ func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunc w := res.ResponseWriter namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) @@ -101,14 +105,14 @@ func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunc result, err := getter(ctx, name, req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -119,44 +123,41 @@ func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) restful.Rou // For performance tracking purposes. trace := util.NewTrace("Get " + req.Request.URL.Path) defer trace.LogIfLong(250 * time.Millisecond) - opts := v1.ExportOptions{} - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), &opts); err != nil { - return nil, err - } - internalOpts := unversioned.ExportOptions{} - scope.Convertor.Convert(&opts, &internalOpts) - if internalOpts.Export { - if e == nil { - return nil, errors.NewBadRequest("export unsupported") + + // check for export + if values := req.Request.URL.Query(); len(values) > 0 { + // TODO: this is internal version, not unversioned + exports := unversioned.ExportOptions{} + if err := scope.ParameterCodec.DecodeParameters(values, unversioned.GroupVersion{Version: "v1"}, &exports); err != nil { + return nil, err + } + if exports.Export { + if e == nil { + return nil, errors.NewBadRequest(fmt.Sprintf("export of %q is not supported", scope.Resource.Resource)) + } + return e.Export(ctx, name, exports) } - return e.Export(ctx, name, internalOpts) } + return r.Get(ctx, name) }) } // GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object. -func GetResourceWithOptions(r rest.GetterWithOptions, e rest.Exporter, scope RequestScope, internalKind, externalKind unversioned.GroupVersionKind, subpath bool, subpathKey string) restful.RouteFunction { +func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope) restful.RouteFunction { return getResourceHandler(scope, func(ctx api.Context, name string, req *restful.Request) (runtime.Object, error) { - opts, err := getRequestOptions(req, scope, internalKind, externalKind, subpath, subpathKey) - if err != nil { + opts, subpath, subpathKey := r.NewGetOptions() + if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil { return nil, err } - exportOpts := unversioned.ExportOptions{} - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), &exportOpts); err != nil { - return nil, err - } - if exportOpts.Export { - return nil, errors.NewBadRequest("export unsupported") - } return r.Get(ctx, name, opts) }) } -func getRequestOptions(req *restful.Request, scope RequestScope, internalKind, externalKind unversioned.GroupVersionKind, subpath bool, subpathKey string) (runtime.Object, error) { - if internalKind.IsEmpty() { - return nil, nil +func getRequestOptions(req *restful.Request, scope RequestScope, into runtime.Object, subpath bool, subpathKey string) error { + if into == nil { + return nil } query := req.Request.URL.Query() @@ -168,37 +169,23 @@ func getRequestOptions(req *restful.Request, scope RequestScope, internalKind, e newQuery[subpathKey] = []string{req.PathParameter("path")} query = newQuery } - - versioned, err := scope.Creater.New(externalKind) - if err != nil { - return nil, err - } - - if err := scope.Codec.DecodeParametersInto(query, versioned); err != nil { - return nil, errors.NewBadRequest(err.Error()) - } - out, err := scope.Convertor.ConvertToVersion(versioned, internalKind.GroupVersion().String()) - if err != nil { - // programmer error - return nil, err - } - return out, nil + return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into) } // ConnectResource returns a function that handles a connect request on a rest.Storage object. -func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, internalKind, externalKind unversioned.GroupVersionKind, restPath string, subpath bool, subpathKey string) restful.RouteFunction { +func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string) restful.RouteFunction { return func(req *restful.Request, res *restful.Response) { w := res.ResponseWriter namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) ctx = api.WithNamespace(ctx, namespace) - opts, err := getRequestOptions(req, scope, internalKind, externalKind, subpath, subpathKey) - if err != nil { - errorJSON(err, scope.Codec, w) + opts, subpath, subpathKey := connecter.NewConnectOptions() + if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil { + scope.err(err, req, res) return } if admit.Handles(admission.Connect) { @@ -211,13 +198,13 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi err = admit.Admit(admission.NewAttributesRecord(connectRequest, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Connect, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } - handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req.Request, w: w}) + handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, res: res}) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } handler.ServeHTTP(w, req.Request) @@ -227,16 +214,16 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi // responder implements rest.Responder for assisting a connector in writing objects or errors. type responder struct { scope RequestScope - req *http.Request - w http.ResponseWriter + req *restful.Request + res *restful.Response } func (r *responder) Object(statusCode int, obj runtime.Object) { - write(statusCode, r.scope.Kind.GroupVersion(), r.scope.Codec, obj, r.w, r.req) + write(statusCode, r.scope.Kind.GroupVersion(), r.scope.Serializer, obj, r.res.ResponseWriter, r.req.Request) } func (r *responder) Error(err error) { - errorJSON(err, r.scope.Codec, r.w) + r.scope.err(err, r.req, r.res) } // ListResource returns a function that handles retrieving a list of resources from a rest.Storage object. @@ -249,7 +236,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch namespace, err := scope.Namer.Namespace(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -264,19 +251,9 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch ctx := scope.ContextFunc(req) ctx = api.WithNamespace(ctx, namespace) - listOptionsGVK := scope.Kind.GroupVersion().WithKind("ListOptions") - versioned, err := scope.Creater.New(listOptionsGVK) - if err != nil { - errorJSON(err, scope.Codec, w) - return - } - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), versioned); err != nil { - errorJSON(err, scope.Codec, w) - return - } opts := api.ListOptions{} - if err := scope.Convertor.Convert(versioned, &opts); err != nil { - errorJSON(err, scope.Codec, w) + if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &opts); err != nil { + scope.err(err, req, res) return } @@ -289,7 +266,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch if opts.FieldSelector, err = opts.FieldSelector.Transform(fn); err != nil { // TODO: allow bad request to set field causes based on query parameters err = errors.NewBadRequest(err.Error()) - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -305,11 +282,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch // and a field selector, since just the name is // sufficient to narrow down the request to a // single object. - errorJSON( - errors.NewBadRequest("both a name and a field selector provided; please provide one or the other."), - scope.Codec, - w, - ) + scope.err(errors.NewBadRequest("both a name and a field selector provided; please provide one or the other."), req, res) return } opts.FieldSelector = nameSelector @@ -318,7 +291,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch if (opts.Watch || forceWatch) && rw != nil { watcher, err := rw.Watch(ctx, &opts) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } // TODO: Currently we explicitly ignore ?timeout= and use only ?timeoutSeconds=. @@ -329,7 +302,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch if timeout == 0 && minRequestTimeout > 0 { timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0)) } - serveWatch(watcher, scope, w, req, timeout) + serveWatch(watcher, scope, req, res, timeout) return } @@ -338,17 +311,17 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch trace.Step("About to List from storage") result, err := r.List(ctx, &opts) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Listing from storage done") numberOfItems, err := setListSelfLink(result, req, scope.Namer) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Self-linking done") - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) trace.Step(fmt.Sprintf("Writing http response done (%d items)", numberOfItems)) } } @@ -374,25 +347,39 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object namespace, err = scope.Namer.Namespace(req) } if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) ctx = api.WithNamespace(ctx, namespace) + gv := scope.Kind.GroupVersion() + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + decoder := scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}) + body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - obj := r.New() + defaultGVK := scope.Kind + original := r.New() trace.Step("About to convert to expected version") - // TODO this cleans up with proper typing - if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.Kind); err != nil { - err = transformDecodeError(typer, err, obj, body) - errorJSON(err, scope.Codec, w) + obj, gvk, err := decoder.Decode(body, &defaultGVK, original) + if err != nil { + err = transformDecodeError(typer, err, original, gvk) + scope.err(err, req, res) + return + } + if gvk.GroupVersion() != gv { + err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String())) + scope.err(err, req, res) return } trace.Step("Conversion done") @@ -402,7 +389,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object err = admit.Admit(admission.NewAttributesRecord(obj, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Create, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -416,18 +403,18 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object return out, err }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Object stored in database") if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Self-link added") - write(http.StatusCreated, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusCreated, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -462,7 +449,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -471,10 +458,11 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion().String()) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } + // TODO: handle this in negotiation contentType := req.HeaderParameter("Content-Type") // Remove "; charset=" if included in header. if idx := strings.Index(contentType, ";"); idx > 0 { @@ -484,9 +472,20 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper patchJS, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) + return + } + + s, ok := scope.Serializer.SerializerForMediaType("application/json", nil) + if !ok { + scope.err(fmt.Errorf("no serializer defined for JSON"), req, res) return } + gv := scope.Kind.GroupVersion() + codec := runtime.NewCodec( + scope.Serializer.EncoderForVersion(s, gv), + scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}), + ) updateAdmit := func(updatedObject runtime.Object) error { if admit != nil && admit.Handles(admission.Update) { @@ -497,18 +496,18 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper return nil } - result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, scope.Namer, scope.Codec) + result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, scope.Namer, codec) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -625,7 +624,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) @@ -633,21 +632,33 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - obj := r.New() + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + defaultGVK := scope.Kind + original := r.New() trace.Step("About to convert to expected version") - if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.Kind); err != nil { - err = transformDecodeError(typer, err, obj, body) - errorJSON(err, scope.Codec, w) + obj, gvk, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original) + if err != nil { + err = transformDecodeError(typer, err, original, gvk) + scope.err(err, req, res) + return + } + if gvk.GroupVersion() != defaultGVK.GroupVersion() { + err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion())) + scope.err(err, req, res) return } trace.Step("Conversion done") if err := checkName(obj, name, namespace, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -656,7 +667,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType err = admit.Admit(admission.NewAttributesRecord(obj, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Update, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -669,13 +680,13 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType return obj, err }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Object stored in database") if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Self-link added") @@ -684,7 +695,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType if wasCreated { status = http.StatusCreated } - writeJSON(status, scope.Codec, result, w, isPrettyPrint(req.Request)) + write(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -702,7 +713,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) @@ -712,12 +723,23 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, if checkBody { body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if len(body) > 0 { - if err := scope.Codec.DecodeInto(body, options); err != nil { - errorJSON(err, scope.Codec, w) + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions") + obj, _, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options) + if err != nil { + scope.err(err, req, res) + return + } + if obj != options { + scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), req, res) return } } @@ -728,7 +750,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, err = admit.Admit(admission.NewAttributesRecord(nil, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Delete, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -738,7 +760,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, return r.Delete(ctx, name, options) }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Object deleted from database") @@ -758,12 +780,12 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, // when a non-status response is returned, set the self link if _, ok := result.(*unversioned.Status); !ok { if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -777,7 +799,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco namespace, err := scope.Namer.Namespace(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -789,24 +811,14 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco err = admit.Admit(admission.NewAttributesRecord(nil, scope.Kind.GroupKind(), namespace, "", scope.Resource.GroupResource(), scope.Subresource, admission.Delete, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } - listOptionsGVK := scope.Kind.GroupVersion().WithKind("ListOptions") - versioned, err := scope.Creater.New(listOptionsGVK) - if err != nil { - errorJSON(err, scope.Codec, w) - return - } - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), versioned); err != nil { - errorJSON(err, scope.Codec, w) - return - } listOptions := api.ListOptions{} - if err := scope.Convertor.Convert(versioned, &listOptions); err != nil { - errorJSON(err, scope.Codec, w) + if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &listOptions); err != nil { + scope.err(err, req, res) return } @@ -819,7 +831,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco if listOptions.FieldSelector, err = listOptions.FieldSelector.Transform(fn); err != nil { // TODO: allow bad request to set field causes based on query parameters err = errors.NewBadRequest(err.Error()) - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -828,12 +840,23 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco if checkBody { body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if len(body) > 0 { - if err := scope.Codec.DecodeInto(body, options); err != nil { - errorJSON(err, scope.Codec, w) + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions") + obj, _, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options) + if err != nil { + scope.err(err, req, res) + return + } + if obj != options { + scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), req, res) return } } @@ -843,7 +866,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco return r.DeleteCollection(ctx, options, &listOptions) }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -861,12 +884,12 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco // when a non-status response is returned, set the self link if _, ok := result.(*unversioned.Status); !ok { if _, err := setListSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + writeNegotiated(scope.Serializer, scope.Kind.GroupVersion(), w, req.Request, http.StatusOK, result) } } @@ -911,15 +934,15 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, } // transformDecodeError adds additional information when a decode fails. -func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, body []byte) error { - objectGroupVersionKind, err := typer.ObjectKind(into) +func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, gvk *unversioned.GroupVersionKind) error { + objGVK, err := typer.ObjectKind(into) if err != nil { return err } - if dataGroupVersionKind, err := typer.DataKind(body); err == nil && len(dataGroupVersionKind.Kind) > 0 { - return errors.NewBadRequest(fmt.Sprintf("%s in version %v cannot be handled as a %s: %v", dataGroupVersionKind.Kind, dataGroupVersionKind.GroupVersion(), objectGroupVersionKind.Kind, baseErr)) + if gvk != nil && len(gvk.Kind) > 0 { + return errors.NewBadRequest(fmt.Sprintf("%s in version %q cannot be handled as a %s: %v", gvk.Kind, gvk.Version, objGVK.Kind, baseErr)) } - return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v", objectGroupVersionKind.Kind, baseErr)) + return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v", objGVK.Kind, baseErr)) } // setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index 53d6f0c86c0a..0ddf0056b727 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/kubernetes/pkg/api" apierrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" @@ -155,7 +156,7 @@ func (tc *patchTestCase) Run(t *testing.T) { namespace := tc.startingPod.Namespace name := tc.startingPod.Name - codec := registered.GroupOrDie(api.GroupName).Codec + codec := testapi.Default.Codec() admit := tc.admit if admit == nil { admit = func(updatedObject runtime.Object) error { diff --git a/pkg/apiserver/watch.go b/pkg/apiserver/watch.go index 3519e729079e..8b951d9e5390 100644 --- a/pkg/apiserver/watch.go +++ b/pkg/apiserver/watch.go @@ -65,23 +65,34 @@ func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) { } // serveWatch handles serving requests to the server -func serveWatch(watcher watch.Interface, scope RequestScope, w http.ResponseWriter, req *restful.Request, timeout time.Duration) { - watchServer := &WatchServer{watcher, scope.Codec, func(obj runtime.Object) { +func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) { + s, mediaType, err := negotiateOutputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + // TODO: replace with typed serialization + if mediaType != "application/json" { + writeRawJSON(http.StatusNotAcceptable, (errNotAcceptable{[]string{"application/json"}}).Status(), res.ResponseWriter) + return + } + encoder := scope.Serializer.EncoderForVersion(s, scope.Kind.GroupVersion()) + watchServer := &WatchServer{watcher, encoder, func(obj runtime.Object) { if err := setSelfLink(obj, req, scope.Namer); err != nil { glog.V(5).Infof("Failed to set self link for object %v: %v", reflect.TypeOf(obj), err) } }, &realTimeoutFactory{timeout}} if isWebsocketRequest(req.Request) { - websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req.Request) + websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(res.ResponseWriter), req.Request) } else { - watchServer.ServeHTTP(w, req.Request) + watchServer.ServeHTTP(res.ResponseWriter, req.Request) } } // WatchServer serves a watch.Interface over a websocket or vanilla HTTP. type WatchServer struct { watching watch.Interface - codec runtime.Codec + encoder runtime.Encoder fixup func(runtime.Object) t timeoutFactory } @@ -108,7 +119,7 @@ func (w *WatchServer) HandleWS(ws *websocket.Conn) { return } w.fixup(event.Object) - obj, err := watchjson.Object(w.codec, &event) + obj, err := watchjson.Object(w.encoder, &event) if err != nil { // Client disconnect. w.watching.Stop() @@ -134,20 +145,21 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { cn, ok := w.(http.CloseNotifier) if !ok { - loggedW.Addf("unable to get CloseNotifier") + loggedW.Addf("unable to get CloseNotifier: %#v", w) http.NotFound(w, req) return } flusher, ok := w.(http.Flusher) if !ok { - loggedW.Addf("unable to get Flusher") + loggedW.Addf("unable to get Flusher: %#v", w) http.NotFound(w, req) return } w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) flusher.Flush() - encoder := watchjson.NewEncoder(w, self.codec) + // TODO: use arbitrary serialization on watch + encoder := watchjson.NewEncoder(w, self.encoder) for { select { case <-cn.CloseNotify(): diff --git a/pkg/apiserver/watch_test.go b/pkg/apiserver/watch_test.go index 130a799c19af..4ff824e000c3 100644 --- a/pkg/apiserver/watch_test.go +++ b/pkg/apiserver/watch_test.go @@ -169,6 +169,34 @@ func TestWatchHTTP(t *testing.T) { } } +func TestWatchHTTPAccept(t *testing.T) { + simpleStorage := &SimpleRESTStorage{} + handler := handle(map[string]rest.Storage{"simples": simpleStorage}) + server := httptest.NewServer(handler) + defer server.Close() + client := http.Client{} + + dest, _ := url.Parse(server.URL) + dest.Path = "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simples" + dest.RawQuery = "" + + request, err := http.NewRequest("GET", dest.String(), nil) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + request.Header.Set("Accept", "application/yaml") + response, err := client.Do(request) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // TODO: once this is fixed, this test will change + if response.StatusCode != http.StatusNotAcceptable { + t.Errorf("Unexpected response %#v", response) + } +} + func TestWatchParamParsing(t *testing.T) { simpleStorage := &SimpleRESTStorage{} handler := handle(map[string]rest.Storage{ diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index c538d557b28c..18be54f5cae4 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -148,6 +148,15 @@ type APIGroupInfo struct { // If nil, defaults to groupMeta.GroupVersion. // TODO: Remove this when https://github.com/kubernetes/kubernetes/issues/19018 is fixed. OptionsExternalVersion *unversioned.GroupVersion + + // Scheme includes all of the types used by this group and how to convert between them (or + // to convert objects from outside of this group that are accepted in this API). + // TODO: replace with interfaces + Scheme *runtime.Scheme + // NegotiatedSerializer controls how this group encodes and decodes data + NegotiatedSerializer runtime.NegotiatedSerializer + // ParameterCodec performs conversions for query parameters passed to API calls + ParameterCodec runtime.ParameterCodec } // Config is a structure used to configure a GenericAPIServer. @@ -275,6 +284,10 @@ type GenericAPIServer struct { // storage contains the RESTful endpoints exposed by this GenericAPIServer storage map[string]rest.Storage + // Serializer controls how common API objects not in a group/version prefix are serialized for this server. + // Individual APIGroups may define their own serializers. + Serializer runtime.NegotiatedSerializer + // "Outputs" Handler http.Handler InsecureHandler http.Handler diff --git a/pkg/genericapiserver/genericapiserver_test.go b/pkg/genericapiserver/genericapiserver_test.go index 671f6cccce40..e12e7e9b2a07 100644 --- a/pkg/genericapiserver/genericapiserver_test.go +++ b/pkg/genericapiserver/genericapiserver_test.go @@ -94,6 +94,7 @@ func TestInstallAPIGroups(t *testing.T) { config.ProxyTLSClientConfig = &tls.Config{} config.APIPrefix = "/apiPrefix" config.APIGroupPrefix = "/apiGroupPrefix" + config.Serializer = latest.Codecs s := New(&config) apiGroupMeta := registered.GroupOrDie(api.GroupName) diff --git a/test/integration/master_test.go b/test/integration/master_test.go index cfd95db08c6e..bd389fa3b88f 100644 --- a/test/integration/master_test.go +++ b/test/integration/master_test.go @@ -51,3 +51,73 @@ func TestWatchSucceedsWithoutArgs(t *testing.T) { } resp.Body.Close() } + +func TestAccept(t *testing.T) { + _, s := framework.RunAMaster(t) + defer s.Close() + + resp, err := http.Get(s.URL + "/api/") + if err != nil { + t.Fatalf("unexpected error getting api: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("got status %v instead of 200 OK", resp.StatusCode) + } + + body, _ := ioutil.ReadAll(resp.Body) + if resp.Header.Get("Content-Type") != "application/json" { + t.Errorf("unexpected content: %s", body) + } + if err := json.Unmarshal(body, &map[string]interface{}{}); err != nil { + t.Fatal(err) + } + + req, err := http.NewRequest("GET", s.URL+"/api/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept", "application/yaml") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + body, _ = ioutil.ReadAll(resp.Body) + if resp.Header.Get("Content-Type") != "application/yaml" { + t.Errorf("unexpected content: %s", body) + } + t.Logf("body: %s", body) + if err := yaml.Unmarshal(body, &map[string]interface{}{}); err != nil { + t.Fatal(err) + } + + req, err = http.NewRequest("GET", s.URL+"/api/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept", "application/json, application/yaml") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + body, _ = ioutil.ReadAll(resp.Body) + if resp.Header.Get("Content-Type") != "application/json" { + t.Errorf("unexpected content: %s", body) + } + t.Logf("body: %s", body) + if err := yaml.Unmarshal(body, &map[string]interface{}{}); err != nil { + t.Fatal(err) + } + + req, err = http.NewRequest("GET", s.URL+"/api/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept", "application") // not a valid media type + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusNotAcceptable { + t.Errorf("unexpected error from the server") + } +} From c1d932e44a1a16890791df4f5d3f0d5f75b13b53 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:21:26 -0500 Subject: [PATCH 06/17] Switch API objects to not register per version codecs Remove Codec from versionInterfaces in meta (RESTMapper is now agnostic to codec and serialization). Register api/latest.Codecs as the codec factory and use latest.Codecs.LegacyCodec(version) as an equvialent to the previous codec. --- .../apis/testgroup/install/install.go | 2 - .../testdata/apis/testgroup/register.go | 2 +- .../testdata/apis/testgroup/v1/register.go | 3 - pkg/api/conversion.go | 5 -- pkg/api/conversion_test.go | 13 ++-- pkg/api/copy_test.go | 2 +- pkg/api/deep_copy_test.go | 8 ++- pkg/api/install/install.go | 9 +-- pkg/api/install/install_test.go | 19 +++++- pkg/api/meta/help.go | 2 +- pkg/api/meta/interfaces.go | 2 - pkg/api/meta/meta_test.go | 12 ++-- pkg/api/meta/restmapper.go | 11 ++-- pkg/api/meta/restmapper_test.go | 39 +----------- pkg/api/register.go | 33 +++++++--- pkg/api/serialization_test.go | 48 ++++++++++---- pkg/api/testapi/testapi.go | 17 +++-- .../testing/compat/compatibility_tester.go | 3 +- pkg/api/testing/fuzzer.go | 9 +-- pkg/api/unversioned/group_version.go | 8 ++- pkg/api/v1/defaults_test.go | 5 +- pkg/api/v1/register.go | 4 -- pkg/api/validation/schema_test.go | 2 +- .../registered/registered_test.go | 62 +++++++++++++++++++ pkg/apis/abac/latest/latest.go | 7 +-- pkg/apis/abac/register.go | 9 ++- pkg/apis/abac/types.go | 2 +- pkg/apis/abac/v0/conversion.go | 2 +- pkg/apis/abac/v0/conversion_test.go | 2 +- pkg/apis/abac/v0/register.go | 6 +- pkg/apis/abac/v1beta1/register.go | 6 +- pkg/apis/authorization/install/install.go | 2 - pkg/apis/authorization/register.go | 2 +- pkg/apis/authorization/v1beta1/register.go | 3 - pkg/apis/componentconfig/decode.go | 33 ---------- pkg/apis/componentconfig/install/install.go | 2 - .../componentconfig/install/install_test.go | 7 ++- pkg/apis/componentconfig/register.go | 2 +- pkg/apis/componentconfig/v1alpha1/register.go | 3 - pkg/apis/extensions/install/install.go | 2 - pkg/apis/extensions/install/install_test.go | 9 ++- pkg/apis/extensions/register.go | 2 +- pkg/apis/extensions/v1beta1/defaults_test.go | 4 +- pkg/apis/extensions/v1beta1/register.go | 3 - pkg/apis/metrics/install/install.go | 2 - pkg/apis/metrics/register.go | 2 +- pkg/apis/metrics/v1alpha1/register.go | 3 - pkg/apiserver/apiserver_test.go | 29 +++++---- pkg/auth/authorizer/abac/abac.go | 3 +- pkg/conversion/unversioned_test.go | 2 +- pkg/genericapiserver/genericapiserver_test.go | 2 +- plugin/pkg/scheduler/api/latest/latest.go | 17 +++-- plugin/pkg/scheduler/api/register.go | 5 +- plugin/pkg/scheduler/api/v1/register.go | 4 -- 54 files changed, 258 insertions(+), 239 deletions(-) delete mode 100644 pkg/apis/componentconfig/decode.go diff --git a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go index 39a4b1bbd16f..a3e6ec2c1816 100644 --- a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go +++ b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go @@ -68,7 +68,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -97,7 +96,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go index ec99f0cfe451..f5f7b3192b17 100644 --- a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go +++ b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go @@ -22,7 +22,7 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) -var SchemeGroupVersion = unversioned.GroupVersion{Group: "testgroup", Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: "testgroup", Version: runtime.APIVersionInternal} func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. diff --git a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go index 9f6e96ae0a78..6d4698ebdaf8 100644 --- a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go +++ b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -25,8 +24,6 @@ import ( var SchemeGroupVersion = unversioned.GroupVersion{Group: "testgroup", Version: "v1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/api/conversion.go b/pkg/api/conversion.go index 46e33a8c1831..f6c95d0493fe 100644 --- a/pkg/api/conversion.go +++ b/pkg/api/conversion.go @@ -22,14 +22,9 @@ import ( "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" ) -// Codec is the identity codec for this package - it can only convert itself -// to itself. -var Codec = runtime.CodecFor(Scheme, unversioned.GroupVersion{}) - func init() { Scheme.AddDefaultingFuncs( func(obj *ListOptions) { diff --git a/pkg/api/conversion_test.go b/pkg/api/conversion_test.go index 5ac3fac91d1f..1af454ef7557 100644 --- a/pkg/api/conversion_test.go +++ b/pkg/api/conversion_test.go @@ -22,6 +22,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/runtime" ) func BenchmarkPodConversion(b *testing.B) { @@ -30,7 +31,7 @@ func BenchmarkPodConversion(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var pod api.Pod - if err := api.Scheme.DecodeInto(data, &pod); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &pod); err != nil { b.Fatalf("Unexpected error decoding pod: %v", err) } @@ -41,7 +42,7 @@ func BenchmarkPodConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.GroupVersion().Group].String()) + obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) if err != nil { b.Fatalf("Conversion error: %v", err) } @@ -58,7 +59,7 @@ func BenchmarkNodeConversion(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var node api.Node - if err := api.Scheme.DecodeInto(data, &node); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } @@ -69,7 +70,7 @@ func BenchmarkNodeConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.GroupVersion().Group].String()) + obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) if err != nil { b.Fatalf("Conversion error: %v", err) } @@ -86,7 +87,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var replicationController api.ReplicationController - if err := api.Scheme.DecodeInto(data, &replicationController); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } @@ -97,7 +98,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.GroupVersion().Group].String()) + obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) if err != nil { b.Fatalf("Conversion error: %v", err) } diff --git a/pkg/api/copy_test.go b/pkg/api/copy_test.go index 94575c8853aa..194b5318a42a 100644 --- a/pkg/api/copy_test.go +++ b/pkg/api/copy_test.go @@ -54,7 +54,7 @@ func doDeepCopyTest(t *testing.T, kind unversioned.GroupVersionKind, f *fuzz.Fuz } if !reflect.DeepEqual(item, itemCopy) { - t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectDiff(item, itemCopy)) + t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectGoPrintSideBySide(item, itemCopy)) } } diff --git a/pkg/api/deep_copy_test.go b/pkg/api/deep_copy_test.go index 606e3ab080d7..a251623a0547 100644 --- a/pkg/api/deep_copy_test.go +++ b/pkg/api/deep_copy_test.go @@ -21,6 +21,8 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/runtime" ) func BenchmarkPodCopy(b *testing.B) { @@ -29,7 +31,7 @@ func BenchmarkPodCopy(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var pod api.Pod - if err := api.Scheme.DecodeInto(data, &pod); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &pod); err != nil { b.Fatalf("Unexpected error decoding pod: %v", err) } @@ -52,7 +54,7 @@ func BenchmarkNodeCopy(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var node api.Node - if err := api.Scheme.DecodeInto(data, &node); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } @@ -75,7 +77,7 @@ func BenchmarkReplicationControllerCopy(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var replicationController api.ReplicationController - if err := api.Scheme.DecodeInto(data, &replicationController); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } diff --git a/pkg/api/install/install.go b/pkg/api/install/install.go index 1a660f45d068..14cf55519262 100644 --- a/pkg/api/install/install.go +++ b/pkg/api/install/install.go @@ -24,14 +24,13 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/util/sets" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" ) const importPrefix = "k8s.io/kubernetes/pkg/api" @@ -75,7 +74,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -127,7 +125,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/api/install/install_test.go b/pkg/api/install/install_test.go index ad051eb218af..0b161535023e 100644 --- a/pkg/api/install/install_test.go +++ b/pkg/api/install/install_test.go @@ -18,11 +18,13 @@ package install import ( "encoding/json" + "reflect" "testing" internal "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" ) func TestResourceVersioner(t *testing.T) { @@ -49,7 +51,7 @@ func TestCodec(t *testing.T) { pod := internal.Pod{} // We do want to use package registered rather than testapi here, because we // want to test if the package install and package registered work as expected. - data, err := registered.GroupOrDie(internal.GroupName).Codec.Encode(&pod) + data, err := runtime.Encode(internal.Codecs.LegacyCodec(registered.GroupOrDie(internal.GroupName).GroupVersion), &pod) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -100,8 +102,8 @@ func TestRESTMapper(t *testing.T) { } interfaces, _ := registered.GroupOrDie(internal.GroupName).InterfacesFor(version) - if mapping.Codec != interfaces.Codec { - t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) + if mapping.ObjectConvertor != interfaces.ObjectConvertor { + t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } rc := &internal.ReplicationController{ObjectMeta: internal.ObjectMeta{Name: "foo"}} @@ -114,3 +116,14 @@ func TestRESTMapper(t *testing.T) { } } } + +func TestUnversioned(t *testing.T) { + for _, obj := range []runtime.Object{ + &unversioned.Status{}, + &unversioned.ExportOptions{}, + } { + if unversioned, ok := internal.Scheme.IsUnversioned(obj); !unversioned || !ok { + t.Errorf("%v is expected to be unversioned", reflect.TypeOf(obj)) + } + } +} diff --git a/pkg/api/meta/help.go b/pkg/api/meta/help.go index 3ddcfef87e6e..3812f240fb8f 100644 --- a/pkg/api/meta/help.go +++ b/pkg/api/meta/help.go @@ -120,7 +120,7 @@ func SetList(list runtime.Object, objects []runtime.Object) error { } else if src.Type().ConvertibleTo(dest.Type()) { dest.Set(src.Convert(dest.Type())) } else { - return fmt.Errorf("item[%d]: Type mismatch: Expected %v, got %v", i, dest.Type(), src.Type()) + return fmt.Errorf("item[%d]: can't assign or convert %v into %v", i, src.Type(), dest.Type()) } } items.Set(slice) diff --git a/pkg/api/meta/interfaces.go b/pkg/api/meta/interfaces.go index 8f14a3fab50a..cc81aafa1371 100644 --- a/pkg/api/meta/interfaces.go +++ b/pkg/api/meta/interfaces.go @@ -24,7 +24,6 @@ import ( // VersionInterfaces contains the interfaces one should use for dealing with types of a particular version. type VersionInterfaces struct { - runtime.Codec runtime.ObjectConvertor MetadataAccessor } @@ -142,7 +141,6 @@ type RESTMapping struct { // Scope contains the information needed to deal with REST Resources that are in a resource hierarchy Scope RESTScope - runtime.Codec runtime.ObjectConvertor MetadataAccessor } diff --git a/pkg/api/meta/meta_test.go b/pkg/api/meta/meta_test.go index 9d6c7abd62fb..5b6c35230e41 100644 --- a/pkg/api/meta/meta_test.go +++ b/pkg/api/meta/meta_test.go @@ -73,7 +73,7 @@ func TestAPIObjectMeta(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if e, a := "/a", typeAccessor.GetAPIVersion(); e != a { + if e, a := "a", typeAccessor.GetAPIVersion(); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "b", typeAccessor.GetKind(); e != a { @@ -102,7 +102,7 @@ func TestAPIObjectMeta(t *testing.T) { if e, a := types.UID("other"), j.UID; e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "/c", j.APIVersion; e != a { + if e, a := "c", j.APIVersion; e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "d", j.Kind; e != a { @@ -117,7 +117,7 @@ func TestAPIObjectMeta(t *testing.T) { typeAccessor.SetAPIVersion("d") typeAccessor.SetKind("e") - if e, a := "/d", j.APIVersion; e != a { + if e, a := "d", j.APIVersion; e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "e", j.Kind; e != a { @@ -308,7 +308,7 @@ func TestGenericTypeMetaAccessor(t *testing.T) { if err != nil { t.Errorf("unexpected error: %v", err) } - if e, a := "/a", apiVersion; e != a { + if e, a := "a", apiVersion; e != a { t.Errorf("expected %v, got %v", e, a) } kind, err := accessor.Kind(j) @@ -392,7 +392,7 @@ func TestGenericTypeMetaAccessor(t *testing.T) { if e, a := "other", j.TypeMeta.UID; e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "/c", j.TypeMeta.APIVersion; e != a { + if e, a := "c", j.TypeMeta.APIVersion; e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "d", j.TypeMeta.Kind; e != a { @@ -757,7 +757,7 @@ func BenchmarkAccessorSetReflection(b *testing.B) { Name: "foo", GenerateName: "prefix", UID: "uid", - APIVersion: "/a", + APIVersion: "a", Kind: "b", ResourceVersion: "1", SelfLink: "some/place/only/we/know", diff --git a/pkg/api/meta/restmapper.go b/pkg/api/meta/restmapper.go index 449f519afca0..f1a4d4113bfe 100644 --- a/pkg/api/meta/restmapper.go +++ b/pkg/api/meta/restmapper.go @@ -23,6 +23,7 @@ import ( "strings" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/sets" ) @@ -84,7 +85,7 @@ type DefaultRESTMapper struct { var _ RESTMapper = &DefaultRESTMapper{} -// VersionInterfacesFunc returns the appropriate codec, typer, and metadata accessor for a +// VersionInterfacesFunc returns the appropriate typer, and metadata accessor for a // given api version, or an error if no such api version exists. type VersionInterfacesFunc func(version unversioned.GroupVersion) (*VersionInterfaces, error) @@ -92,7 +93,7 @@ type VersionInterfacesFunc func(version unversioned.GroupVersion) (*VersionInter // to a resource name and back based on the objects in a runtime.Scheme // and the Kubernetes API conventions. Takes a group name, a priority list of the versions // to search when an object has no default version (set empty to return an error), -// and a function that retrieves the correct codec and metadata for a given version. +// and a function that retrieves the correct metadata for a given version. func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, f VersionInterfacesFunc) *DefaultRESTMapper { resourceToKind := make(map[unversioned.GroupVersionResource]unversioned.GroupVersionKind) kindToPluralResource := make(map[unversioned.GroupVersionKind]unversioned.GroupVersionResource) @@ -251,6 +252,9 @@ func (m *DefaultRESTMapper) ResourceFor(resource unversioned.GroupVersionResourc func (m *DefaultRESTMapper) KindsFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) { resource := input.GroupVersion().WithResource(strings.ToLower(input.Resource)) + if resource.Version == runtime.APIVersionInternal { + resource.Version = "" + } hasResource := len(resource.Resource) > 0 hasGroup := len(resource.Group) > 0 @@ -412,7 +416,7 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st var gvk *unversioned.GroupVersionKind hadVersion := false for _, version := range versions { - if len(version) == 0 { + if len(version) == 0 || version == runtime.APIVersionInternal { continue } @@ -472,7 +476,6 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st GroupVersionKind: *gvk, Scope: scope, - Codec: interfaces.Codec, ObjectConvertor: interfaces.ObjectConvertor, MetadataAccessor: interfaces.MetadataAccessor, } diff --git a/pkg/api/meta/restmapper_test.go b/pkg/api/meta/restmapper_test.go index 603848d5c874..17525df502e5 100644 --- a/pkg/api/meta/restmapper_test.go +++ b/pkg/api/meta/restmapper_test.go @@ -18,8 +18,6 @@ package meta import ( "errors" - "io" - "net/url" "reflect" "strings" "testing" @@ -28,38 +26,6 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) -type fakeCodec struct{} - -var _ runtime.Decoder = fakeCodec{} - -func (fakeCodec) Encode(runtime.Object) ([]byte, error) { - return []byte{}, nil -} - -func (fakeCodec) EncodeToStream(runtime.Object, io.Writer) error { - return nil -} - -func (fakeCodec) Decode([]byte) (runtime.Object, error) { - return nil, nil -} - -func (fakeCodec) DecodeToVersion([]byte, unversioned.GroupVersion) (runtime.Object, error) { - return nil, nil -} - -func (fakeCodec) DecodeInto([]byte, runtime.Object) error { - return nil -} - -func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, unversioned.GroupVersionKind) error { - return nil -} - -func (fakeCodec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error { - return nil -} - type fakeConvertor struct{} func (fakeConvertor) Convert(in, out interface{}) error { @@ -74,12 +40,11 @@ func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (stri return label, value, nil } -var validCodec = fakeCodec{} var validAccessor = resourceAccessor{} var validConvertor = fakeConvertor{} func fakeInterfaces(version unversioned.GroupVersion) (*VersionInterfaces, error) { - return &VersionInterfaces{Codec: validCodec, ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil + return &VersionInterfaces{ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil } var unmatchedErr = errors.New("no version") @@ -512,7 +477,7 @@ func TestRESTMapperRESTMapping(t *testing.T) { t.Errorf("%d: unexpected resource: %#v", i, mapping) } - if mapping.Codec == nil || mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil { + if mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil { t.Errorf("%d: missing codec and accessor: %#v", i, mapping) } diff --git a/pkg/api/register.go b/pkg/api/register.go index c1e9e9157aa3..4aff2256be08 100644 --- a/pkg/api/register.go +++ b/pkg/api/register.go @@ -20,16 +20,27 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" ) // Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered. var Scheme = runtime.NewScheme() +// Codecs provides access to encoding and decoding for the scheme +var Codecs = serializer.NewCodecFactory(Scheme) + // GroupName is the group name use in this package const GroupName = "" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Unversiond is group version for unversioned API objects +// TODO: this should be v1 probably +var Unversioned = unversioned.GroupVersion{Group: "", Version: "v1"} + +// ParameterCodec handles versioning of objects that are converted to query parameters. +var ParameterCodec = runtime.NewParameterCodec(Scheme) // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { @@ -42,6 +53,9 @@ func Resource(resource string) unversioned.GroupResource { } func AddToScheme(scheme *runtime.Scheme) { + if err := Scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { + panic(err) + } scheme.AddKnownTypes(SchemeGroupVersion, &Pod{}, &PodList{}, @@ -86,14 +100,15 @@ func AddToScheme(scheme *runtime.Scheme) { &RangeAllocation{}, ) - // Add the Unversioned types to scheme. - // TODO this should not be done here - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.ExportOptions{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.Status{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIVersions{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIGroupList{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIGroup{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIResourceList{}) + // Register Unversioned types under their own special group + Scheme.AddUnversionedTypes(Unversioned, + &unversioned.ExportOptions{}, + &unversioned.Status{}, + &unversioned.APIVersions{}, + &unversioned.APIGroupList{}, + &unversioned.APIGroup{}, + &unversioned.APIResourceList{}, + ) } func (obj *Pod) GetObjectMeta() meta.Object { return &obj.ObjectMeta } diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index bcae9883c95c..0f69b8be81da 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -61,6 +61,8 @@ func fuzzInternalObject(t *testing.T, forVersion unversioned.GroupVersion, item } func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { + t.Logf("codec: %#v", codec) + printer := spew.ConfigState{DisableMethods: true} name := reflect.TypeOf(item).Elem().Name() @@ -81,8 +83,7 @@ func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) - err = runtime.DecodeInto(codec, data, obj3) - if err != nil { + if err := runtime.DecodeInto(codec, data, obj3); err != nil { t.Errorf("2: %v: %v", name, err) return } @@ -122,7 +123,7 @@ func TestSpecificKind(t *testing.T) { // api.Scheme.Log(t) // defer api.Scheme.Log(nil) - kind := "Pod" + kind := "List" for i := 0; i < *fuzzIters; i++ { doRoundTripTest(kind, t) if t.Failed() { @@ -180,7 +181,7 @@ func doRoundTripTest(kind string, t *testing.T) { roundTripSame(t, item, nonRoundTrippableTypesByVersion[kind]...) } if !nonInternalRoundTrippableTypes.Has(kind) { - roundTrip(t, api.Codec, fuzzInternalObject(t, testapi.Default.InternalGroupVersion(), item, rand.Int63())) + roundTrip(t, testapi.Default.Codec(), fuzzInternalObject(t, testapi.Default.InternalGroupVersion(), item, rand.Int63())) } } @@ -200,8 +201,8 @@ func TestEncode_Ptr(t *testing.T) { }, } obj := runtime.Object(pod) - data, err := testapi.Default.Codec().Encode(obj) - obj2, err2 := testapi.Default.Codec().Decode(data) + data, err := runtime.Encode(testapi.Default.Codec(), obj) + obj2, err2 := runtime.Decode(testapi.Default.Codec(), data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } @@ -216,11 +217,11 @@ func TestEncode_Ptr(t *testing.T) { func TestBadJSONRejection(t *testing.T) { badJSONMissingKind := []byte(`{ }`) - if _, err := testapi.Default.Codec().Decode(badJSONMissingKind); err == nil { + if _, err := runtime.Decode(testapi.Default.Codec(), badJSONMissingKind); err == nil { t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) } badJSONUnknownType := []byte(`{"kind": "bar"}`) - if _, err1 := testapi.Default.Codec().Decode(badJSONUnknownType); err1 == nil { + if _, err1 := runtime.Decode(testapi.Default.Codec(), badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) @@ -240,14 +241,14 @@ func TestUnversionedTypes(t *testing.T) { for _, obj := range testcases { // Make sure the unversioned codec can encode - unversionedJSON, err := api.Codec.Encode(obj) + unversionedJSON, err := runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("%v: unexpected error: %v", obj, err) continue } // Make sure the versioned codec under test can decode - versionDecodedObject, err := testapi.Default.Codec().Decode(unversionedJSON) + versionDecodedObject, err := runtime.Decode(testapi.Default.Codec(), unversionedJSON) if err != nil { t.Errorf("%v: unexpected error: %v", obj, err) continue @@ -278,7 +279,7 @@ func BenchmarkEncodeCodec(b *testing.B) { width := len(items) b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := testapi.Default.Codec().Encode(&items[i%width]); err != nil { + if _, err := runtime.Encode(testapi.Default.Codec(), &items[i%width]); err != nil { b.Fatal(err) } } @@ -320,7 +321,7 @@ func BenchmarkDecodeCodec(b *testing.B) { b.StopTimer() } -func BenchmarkDecodeIntoCodec(b *testing.B) { +func BenchmarkDecodeIntoExternalCodec(b *testing.B) { codec := testapi.Default.Codec() items := benchmarkItems() width := len(items) @@ -343,6 +344,29 @@ func BenchmarkDecodeIntoCodec(b *testing.B) { b.StopTimer() } +func BenchmarkDecodeIntoInternalCodec(b *testing.B) { + codec := testapi.Default.Codec() + items := benchmarkItems() + width := len(items) + encoded := make([][]byte, width) + for i := range items { + data, err := runtime.Encode(codec, &items[i]) + if err != nil { + b.Fatal(err) + } + encoded[i] = data + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + obj := api.Pod{} + if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil { + b.Fatal(err) + } + } + b.StopTimer() +} + // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance func BenchmarkDecodeIntoJSON(b *testing.B) { codec := testapi.Default.Codec() diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index 09a5d1b4a98b..eb5cd8aa163e 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -47,7 +47,7 @@ type TestGroup struct { func init() { kubeTestAPI := os.Getenv("KUBE_TEST_API") - if kubeTestAPI != "" { + if len(kubeTestAPI) != 0 { testGroupVersions := strings.Split(kubeTestAPI, ",") for _, gvString := range testGroupVersions { groupVersion, err := unversioned.ParseGroupVersion(gvString) @@ -57,7 +57,7 @@ func init() { Groups[groupVersion.Group] = TestGroup{ externalGroupVersion: groupVersion, - internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group}, + internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}, } } } @@ -93,12 +93,7 @@ func (g TestGroup) InternalGroupVersion() unversioned.GroupVersion { // Codec returns the codec for the API version to test against, as set by the // KUBE_TEST_API env var. func (g TestGroup) Codec() runtime.Codec { - // TODO: caesarxuchao: Restructure the body once we have a central `registered`. - interfaces, err := registered.GroupOrDie(g.externalGroupVersion.Group).InterfacesFor(g.externalGroupVersion) - if err != nil { - panic(err) - } - return interfaces.Codec + return api.Codecs.LegacyCodec(g.externalGroupVersion) } // Converter returns the api.Scheme for the API version to test against, as set by the @@ -199,7 +194,11 @@ func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) { } // Codec used for unversioned types if api.Scheme.Recognizes(kind) { - return api.Codec, nil + serializer, ok := api.Codecs.SerializerForFileExtension("json") + if !ok { + return nil, fmt.Errorf("no serializer registered for json") + } + return serializer, nil } return nil, fmt.Errorf("unexpected kind: %v", kind) } diff --git a/pkg/api/testing/compat/compatibility_tester.go b/pkg/api/testing/compat/compatibility_tester.go index 48174fc3d82e..17db89ad2cb5 100644 --- a/pkg/api/testing/compat/compatibility_tester.go +++ b/pkg/api/testing/compat/compatibility_tester.go @@ -26,7 +26,6 @@ import ( "strings" "testing" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/runtime" @@ -48,7 +47,7 @@ func TestCompatibility( ) { // Decode - codec := runtime.CodecFor(api.Scheme, version) + codec := api.Codecs.LegacyCodec(version) obj, err := runtime.Decode(codec, input) if err != nil { t.Fatalf("Unexpected error: %v", err) diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index c34f07effadb..30a2ed18746a 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -59,9 +59,6 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) func(q *resource.Quantity, c fuzz.Continue) { *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) }, - func(j *runtime.PluginBase, c fuzz.Continue) { - // Do nothing; this struct has only a Kind field and it must stay blank in memory. - }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. @@ -177,10 +174,8 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) // TODO: uncomment when round trip starts from a versioned object if true { //c.RandBool() { *j = &runtime.Unknown{ - // apiVersion has rules now. Since it includes / and only `v1` can be bare, - // then this must choose a valid format to deserialize - TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown.group/unknown"}, - RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), + // We do not set TypeMeta here because it is not carried through a round trip + RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index a77ea9ef7ce3..5b684915afc8 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -141,11 +141,13 @@ func (gv GroupVersion) String() string { } // special case of "v1" for backward compatibility - if gv.Group == "" && gv.Version == "v1" { + if len(gv.Group) == 0 && gv.Version == "v1" { return gv.Version - } else { + } + if len(gv.Group) > 0 { return gv.Group + "/" + gv.Version } + return gv.Version } // ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error @@ -163,6 +165,8 @@ func ParseGroupVersion(gv string) (GroupVersion, error) { switch { case len(s) == 1 && gv == "v1": return GroupVersion{"", "v1"}, nil + case len(s) == 1: + return GroupVersion{"", s[0]}, nil case len(s) == 2: return GroupVersion{s[0], s[1]}, nil default: diff --git a/pkg/api/v1/defaults_test.go b/pkg/api/v1/defaults_test.go index 0c2b5b298847..b1a78e758e37 100644 --- a/pkg/api/v1/defaults_test.go +++ b/pkg/api/v1/defaults_test.go @@ -28,12 +28,13 @@ import ( ) func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { - data, err := versioned.Codec.Encode(obj) + codec := api.Codecs.LegacyCodec(versioned.SchemeGroupVersion) + data, err := runtime.Encode(codec, obj) if err != nil { t.Errorf("%v\n %#v", err, obj) return nil } - obj2, err := api.Codec.Decode(data) + obj2, err := runtime.Decode(codec, data) if err != nil { t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) return nil diff --git a/pkg/api/v1/register.go b/pkg/api/v1/register.go index 14b2e5adfe22..f39ae8ff12a5 100644 --- a/pkg/api/v1/register.go +++ b/pkg/api/v1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -28,9 +27,6 @@ const GroupName = "" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"} -// Codec encodes internal objects to the v1 scheme -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/api/validation/schema_test.go b/pkg/api/validation/schema_test.go index f35df2bd84dd..be6fc83e7a0f 100644 --- a/pkg/api/validation/schema_test.go +++ b/pkg/api/validation/schema_test.go @@ -72,7 +72,7 @@ func TestValidateOk(t *testing.T) { for _, test := range tests { testObj := test.obj apiObjectFuzzer.Fuzz(testObj) - data, err := testapi.Default.Codec().Encode(testObj) + data, err := runtime.Encode(testapi.Default.Codec(), testObj) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/apimachinery/registered/registered_test.go b/pkg/apimachinery/registered/registered_test.go index e7466fcc2b8a..de40d2e2ac03 100644 --- a/pkg/apimachinery/registered/registered_test.go +++ b/pkg/apimachinery/registered/registered_test.go @@ -17,10 +17,14 @@ limitations under the License. package registered import ( + "encoding/json" + "reflect" "testing" + "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/runtime" ) func TestAllPreferredGroupVersions(t *testing.T) { @@ -66,3 +70,61 @@ func TestAllPreferredGroupVersions(t *testing.T) { reset() } } + +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) { + extensionCodec := testapi.Extensions.Codec() + encoded, err := runtime.Encode(extensionCodec, 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(extensionCodec, encoded) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(status, decoded) { + t.Errorf("expected: %v, got: %v", status, decoded) + } +} diff --git a/pkg/apis/abac/latest/latest.go b/pkg/apis/abac/latest/latest.go index bcbef4856805..2618fc4fc248 100644 --- a/pkg/apis/abac/latest/latest.go +++ b/pkg/apis/abac/latest/latest.go @@ -17,8 +17,7 @@ limitations under the License. package latest import ( - "k8s.io/kubernetes/pkg/apis/abac/v1beta1" + _ "k8s.io/kubernetes/pkg/apis/abac" + _ "k8s.io/kubernetes/pkg/apis/abac/v0" + _ "k8s.io/kubernetes/pkg/apis/abac/v1beta1" ) - -// Codec is the default codec for serializing input that should use the latest supported version. -var Codec = v1beta1.Codec diff --git a/pkg/apis/abac/register.go b/pkg/apis/abac/register.go index bc99f922b181..c555d5aa979b 100644 --- a/pkg/apis/abac/register.go +++ b/pkg/apis/abac/register.go @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package api +package abac import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" ) // Group is the API group for abac @@ -27,9 +28,11 @@ const Group = "abac.authorization.kubernetes.io" // Scheme is the default instance of runtime.Scheme to which types in the abac API group are registered. var Scheme = runtime.NewScheme() +// Codecs provides access to encoding and decoding for the scheme +var Codecs = serializer.NewCodecFactory(Scheme) + func init() { - Scheme.AddInternalGroupVersion(unversioned.GroupVersion{Group: Group, Version: ""}) - Scheme.AddKnownTypes(unversioned.GroupVersion{Group: Group, Version: ""}, + Scheme.AddKnownTypes(unversioned.GroupVersion{Group: Group, Version: runtime.APIVersionInternal}, &Policy{}, ) } diff --git a/pkg/apis/abac/types.go b/pkg/apis/abac/types.go index 1b694c0481bf..024c7ee2417a 100644 --- a/pkg/apis/abac/types.go +++ b/pkg/apis/abac/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package api +package abac import "k8s.io/kubernetes/pkg/api/unversioned" diff --git a/pkg/apis/abac/v0/conversion.go b/pkg/apis/abac/v0/conversion.go index 56b7a31b0c4a..c0fda4bd5555 100644 --- a/pkg/apis/abac/v0/conversion.go +++ b/pkg/apis/abac/v0/conversion.go @@ -17,7 +17,7 @@ limitations under the License. package v0 import ( - "k8s.io/kubernetes/pkg/apis/abac" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/conversion" ) diff --git a/pkg/apis/abac/v0/conversion_test.go b/pkg/apis/abac/v0/conversion_test.go index 256b4e251001..ffdbd398d972 100644 --- a/pkg/apis/abac/v0/conversion_test.go +++ b/pkg/apis/abac/v0/conversion_test.go @@ -20,7 +20,7 @@ import ( "reflect" "testing" - "k8s.io/kubernetes/pkg/apis/abac" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/apis/abac/v0" ) diff --git a/pkg/apis/abac/v0/register.go b/pkg/apis/abac/v0/register.go index 3aeb73df5b2c..d5338045a984 100644 --- a/pkg/apis/abac/v0/register.go +++ b/pkg/apis/abac/v0/register.go @@ -18,16 +18,12 @@ package v0 import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/runtime" + api "k8s.io/kubernetes/pkg/apis/abac" ) // GroupVersion is the API group and version for abac v0 var GroupVersion = unversioned.GroupVersion{Group: api.Group, Version: "v0"} -// Codec encodes internal objects to the v0 version for the abac group -var Codec = runtime.CodecFor(api.Scheme, GroupVersion) - func init() { api.Scheme.AddKnownTypes(GroupVersion, &Policy{}, diff --git a/pkg/apis/abac/v1beta1/register.go b/pkg/apis/abac/v1beta1/register.go index 6312c762a2ea..95fd6b3ef18b 100644 --- a/pkg/apis/abac/v1beta1/register.go +++ b/pkg/apis/abac/v1beta1/register.go @@ -18,16 +18,12 @@ package v1beta1 import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/runtime" + api "k8s.io/kubernetes/pkg/apis/abac" ) // GroupVersion is the API group and version for abac v1beta1 var GroupVersion = unversioned.GroupVersion{Group: api.Group, Version: "v1beta1"} -// Codec encodes internal objects to the v1beta1 version for the abac group -var Codec = runtime.CodecFor(api.Scheme, GroupVersion) - func init() { api.Scheme.AddKnownTypes(GroupVersion, &Policy{}, diff --git a/pkg/apis/authorization/install/install.go b/pkg/apis/authorization/install/install.go index 83675ea6090f..bf8814dd5e92 100644 --- a/pkg/apis/authorization/install/install.go +++ b/pkg/apis/authorization/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -119,7 +118,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1beta1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1beta1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/authorization/register.go b/pkg/apis/authorization/register.go index 317e44c9131e..b286526131a5 100644 --- a/pkg/apis/authorization/register.go +++ b/pkg/apis/authorization/register.go @@ -25,7 +25,7 @@ import ( const GroupName = "authorization.k8s.io" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/authorization/v1beta1/register.go b/pkg/apis/authorization/v1beta1/register.go index eee6eaa41c14..d9e33ed5a22d 100644 --- a/pkg/apis/authorization/v1beta1/register.go +++ b/pkg/apis/authorization/v1beta1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1beta1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -28,8 +27,6 @@ const GroupName = "authorization.k8s.io" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1beta1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/apis/componentconfig/decode.go b/pkg/apis/componentconfig/decode.go deleted file mode 100644 index 7fe927c32728..000000000000 --- a/pkg/apis/componentconfig/decode.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 componentconfig - -import ( - "io/ioutil" - - "k8s.io/kubernetes/pkg/runtime" -) - -func DecodeFromPathInto(obj runtime.Object, c runtime.Codec, filename string) error { - b, err := ioutil.ReadFile(filename) - c = runtime.YAMLDecoder(c) - - if err != nil { - return err - } - return c.DecodeInto(b, obj) -} diff --git a/pkg/apis/componentconfig/install/install.go b/pkg/apis/componentconfig/install/install.go index 49b382dea5dd..ec55542737da 100644 --- a/pkg/apis/componentconfig/install/install.go +++ b/pkg/apis/componentconfig/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -104,7 +103,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1alpha1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1alpha1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/componentconfig/install/install_test.go b/pkg/apis/componentconfig/install/install_test.go index 82bae567a937..20536d57928e 100644 --- a/pkg/apis/componentconfig/install/install_test.go +++ b/pkg/apis/componentconfig/install/install_test.go @@ -23,13 +23,14 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/componentconfig" + "k8s.io/kubernetes/pkg/runtime" ) func TestCodec(t *testing.T) { daemonSet := componentconfig.KubeProxyConfiguration{} // We do want to use package registered rather than testapi here, because we // want to test if the package install and package registered work as expected. - data, err := registered.GroupOrDie(componentconfig.GroupName).Codec.Encode(&daemonSet) + data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(componentconfig.GroupName).GroupVersion), &daemonSet) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -80,8 +81,8 @@ func TestRESTMapper(t *testing.T) { } interfaces, _ := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(version) - if mapping.Codec != interfaces.Codec { - t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) + if mapping.ObjectConvertor != interfaces.ObjectConvertor { + t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } } } diff --git a/pkg/apis/componentconfig/register.go b/pkg/apis/componentconfig/register.go index ce29ed8baf40..eaf675809967 100644 --- a/pkg/apis/componentconfig/register.go +++ b/pkg/apis/componentconfig/register.go @@ -29,7 +29,7 @@ func AddToScheme(scheme *runtime.Scheme) { const GroupName = "componentconfig" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/componentconfig/v1alpha1/register.go b/pkg/apis/componentconfig/v1alpha1/register.go index aeec8975ab4b..ab0bdd5fa8ff 100644 --- a/pkg/apis/componentconfig/v1alpha1/register.go +++ b/pkg/apis/componentconfig/v1alpha1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -28,8 +27,6 @@ const GroupName = "componentconfig" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { addKnownTypes(scheme) addDefaultingFuncs(scheme) diff --git a/pkg/apis/extensions/install/install.go b/pkg/apis/extensions/install/install.go index bcc10f36003c..4e1477d2d0a6 100644 --- a/pkg/apis/extensions/install/install.go +++ b/pkg/apis/extensions/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -104,7 +103,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1beta1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1beta1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/extensions/install/install_test.go b/pkg/apis/extensions/install/install_test.go index 4196d2b669c8..61aa91beff6f 100644 --- a/pkg/apis/extensions/install/install_test.go +++ b/pkg/apis/extensions/install/install_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/runtime" ) func TestResourceVersioner(t *testing.T) { @@ -51,7 +52,7 @@ func TestCodec(t *testing.T) { daemonSet := extensions.DaemonSet{} // We do want to use package registered rather than testapi here, because we // want to test if the package install and package registered work as expected. - data, err := registered.GroupOrDie(extensions.GroupName).Codec.Encode(&daemonSet) + data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(extensions.GroupName).GroupVersion), &daemonSet) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -101,9 +102,15 @@ func TestRESTMapper(t *testing.T) { t.Errorf("incorrect groupVersion: %v", mapping) } +<<<<<<< HEAD interfaces, _ := registered.GroupOrDie(extensions.GroupName).InterfacesFor(version) if mapping.Codec != interfaces.Codec { t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) +======= + interfaces, _ := latest.GroupOrDie(extensions.GroupName).InterfacesFor(version) + if mapping.ObjectConvertor != interfaces.ObjectConvertor { + t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) +>>>>>>> e776ada... Switch API objects to not register per version codecs } rc := &extensions.HorizontalPodAutoscaler{ObjectMeta: api.ObjectMeta{Name: "foo"}} diff --git a/pkg/apis/extensions/register.go b/pkg/apis/extensions/register.go index f33957292e4a..d02dd6f6ea6c 100644 --- a/pkg/apis/extensions/register.go +++ b/pkg/apis/extensions/register.go @@ -26,7 +26,7 @@ import ( const GroupName = "extensions" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/extensions/v1beta1/defaults_test.go b/pkg/apis/extensions/v1beta1/defaults_test.go index 082fc2adb41b..bce6e1997de7 100644 --- a/pkg/apis/extensions/v1beta1/defaults_test.go +++ b/pkg/apis/extensions/v1beta1/defaults_test.go @@ -588,12 +588,12 @@ func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) { } func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { - data, err := runtime.Encode(Codec, obj) + data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) if err != nil { t.Errorf("%v\n %#v", err, obj) return nil } - obj2, err := api.Codec.Decode(data) + obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) if err != nil { t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) return nil diff --git a/pkg/apis/extensions/v1beta1/register.go b/pkg/apis/extensions/v1beta1/register.go index e4bbf61da8c1..8ffbc48e6355 100644 --- a/pkg/apis/extensions/v1beta1/register.go +++ b/pkg/apis/extensions/v1beta1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1beta1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -29,8 +28,6 @@ const GroupName = "extensions" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1beta1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { addKnownTypes(scheme) addDefaultingFuncs(scheme) diff --git a/pkg/apis/metrics/install/install.go b/pkg/apis/metrics/install/install.go index 75709fa6e215..e83c91cc4a20 100644 --- a/pkg/apis/metrics/install/install.go +++ b/pkg/apis/metrics/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -104,7 +103,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1alpha1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1alpha1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/metrics/register.go b/pkg/apis/metrics/register.go index 99bcb529c14e..29ad86a09f37 100644 --- a/pkg/apis/metrics/register.go +++ b/pkg/apis/metrics/register.go @@ -30,7 +30,7 @@ func AddToScheme(scheme *runtime.Scheme) { const GroupName = "metrics" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/metrics/v1alpha1/register.go b/pkg/apis/metrics/v1alpha1/register.go index 1643f6e7e474..c943d54687d0 100644 --- a/pkg/apis/metrics/v1alpha1/register.go +++ b/pkg/apis/metrics/v1alpha1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -29,8 +28,6 @@ const GroupName = "metrics" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 2a9bff8302ce..6141edd47e8a 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -35,7 +35,6 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" apierrs "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" @@ -70,10 +69,10 @@ var grouplessPrefix = "api" var groupVersions = []unversioned.GroupVersion{grouplessGroupVersion, testGroupVersion, newGroupVersion} -var codec = latest.Codecs.LegacyCodec(groupVersions...) -var grouplessCodec = latest.Codecs.LegacyCodec(grouplessGroupVersion) -var testCodec = latest.Codecs.LegacyCodec(testGroupVersion) -var newCodec = latest.Codecs.LegacyCodec(newGroupVersion) +var codec = api.Codecs.LegacyCodec(groupVersions...) +var grouplessCodec = api.Codecs.LegacyCodec(grouplessGroupVersion) +var testCodec = api.Codecs.LegacyCodec(testGroupVersion) +var newCodec = api.Codecs.LegacyCodec(newGroupVersion) var accessor = meta.NewAccessor() var versioner runtime.ResourceVersioner = accessor @@ -265,7 +264,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + grouplessPrefix group.GroupVersion = grouplessGroupVersion group.OptionsExternalVersion = &grouplessGroupVersion - group.Serializer = latest.Codecs + group.Serializer = api.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -277,7 +276,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + prefix group.GroupVersion = testGroupVersion group.OptionsExternalVersion = &testGroupVersion - group.Serializer = latest.Codecs + group.Serializer = api.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -289,7 +288,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + prefix group.GroupVersion = newGroupVersion group.OptionsExternalVersion = &newGroupVersion - group.Serializer = latest.Codecs + group.Serializer = api.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -2391,7 +2390,7 @@ func TestUpdateREST(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Serializer: latest.Codecs, + Serializer: api.Codecs, ParameterCodec: api.ParameterCodec, } } @@ -2475,7 +2474,7 @@ func TestParentResourceIsRequired(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Serializer: latest.Codecs, + Serializer: api.Codecs, ParameterCodec: api.ParameterCodec, } container := restful.NewContainer() @@ -2506,7 +2505,7 @@ func TestParentResourceIsRequired(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Serializer: latest.Codecs, + Serializer: api.Codecs, ParameterCodec: api.ParameterCodec, } container = restful.NewContainer() @@ -2710,12 +2709,12 @@ func TestCreateYAML(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - serializer, ok := latest.Codecs.SerializerForMediaType("application/yaml", nil) + serializer, ok := api.Codecs.SerializerForMediaType("application/yaml", nil) if !ok { t.Fatal("No yaml serializer") } - encoder := latest.Codecs.EncoderForVersion(serializer, testGroupVersion) - decoder := latest.Codecs.DecoderToVersion(serializer, testInternalGroupVersion) + encoder := api.Codecs.EncoderForVersion(serializer, testGroupVersion) + decoder := api.Codecs.DecoderToVersion(serializer, testInternalGroupVersion) data, err := runtime.Encode(encoder, simple) if err != nil { @@ -2913,7 +2912,7 @@ func (obj *UnregisteredAPIObject) GetObjectKind() unversioned.ObjectKind { func TestWriteJSONDecodeError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - writeNegotiated(latest.Codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"}) + writeNegotiated(api.Codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"}) })) // TODO: Uncomment when fix #19254 // defer server.Close() diff --git a/pkg/auth/authorizer/abac/abac.go b/pkg/auth/authorizer/abac/abac.go index 0ef8768714aa..9b367cf57fb1 100644 --- a/pkg/auth/authorizer/abac/abac.go +++ b/pkg/auth/authorizer/abac/abac.go @@ -28,8 +28,7 @@ import ( "github.com/golang/glog" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/apis/abac/latest" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/apis/abac/v0" _ "k8s.io/kubernetes/pkg/apis/abac/v1beta1" "k8s.io/kubernetes/pkg/auth/authorizer" diff --git a/pkg/conversion/unversioned_test.go b/pkg/conversion/unversioned_test.go index eee3e10c5071..308549edc53a 100644 --- a/pkg/conversion/unversioned_test.go +++ b/pkg/conversion/unversioned_test.go @@ -67,7 +67,7 @@ func TestV1EncodeDecodeStatus(t *testing.T) { 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 := latest.Codecs.LegacyCodec(extensions.SchemeGroupVersion) + expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion) encoded, err := runtime.Encode(expCodec, status) if err != nil { t.Errorf("unexpected error: %v", err) diff --git a/pkg/genericapiserver/genericapiserver_test.go b/pkg/genericapiserver/genericapiserver_test.go index e12e7e9b2a07..e1e963dc187b 100644 --- a/pkg/genericapiserver/genericapiserver_test.go +++ b/pkg/genericapiserver/genericapiserver_test.go @@ -94,7 +94,7 @@ func TestInstallAPIGroups(t *testing.T) { config.ProxyTLSClientConfig = &tls.Config{} config.APIPrefix = "/apiPrefix" config.APIGroupPrefix = "/apiGroupPrefix" - config.Serializer = latest.Codecs + config.Serializer = api.Codecs s := New(&config) apiGroupMeta := registered.GroupOrDie(api.GroupName) diff --git a/plugin/pkg/scheduler/api/latest/latest.go b/plugin/pkg/scheduler/api/latest/latest.go index cee44028f246..c01d91d32ed0 100644 --- a/plugin/pkg/scheduler/api/latest/latest.go +++ b/plugin/pkg/scheduler/api/latest/latest.go @@ -17,7 +17,12 @@ limitations under the License. package latest import ( - "k8s.io/kubernetes/plugin/pkg/scheduler/api/v1" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" + "k8s.io/kubernetes/plugin/pkg/scheduler/api" + _ "k8s.io/kubernetes/plugin/pkg/scheduler/api/v1" ) // Version is the string that represents the current external default version. @@ -33,6 +38,10 @@ const OldestVersion = "v1" var Versions = []string{"v1"} // Codec is the default codec for serializing input that should use -// the latest supported version. -// This codec can decode any object that Kubernetes is aware of. -var Codec = v1.Codec +// the latest supported version. It supports JSON by default. +var Codec = versioning.NewCodecForScheme( + api.Scheme, + json.NewSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), true), + []unversioned.GroupVersion{{Version: Version}}, + []unversioned.GroupVersion{{Version: runtime.APIVersionInternal}}, +) diff --git a/plugin/pkg/scheduler/api/register.go b/plugin/pkg/scheduler/api/register.go index 53a19cedafb2..3b988f25bfae 100644 --- a/plugin/pkg/scheduler/api/register.go +++ b/plugin/pkg/scheduler/api/register.go @@ -26,9 +26,12 @@ var Scheme = runtime.NewScheme() // SchemeGroupVersion is group version used to register these objects // TODO this should be in the "scheduler" group -var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal} func init() { + if err := Scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { + panic(err) + } Scheme.AddKnownTypes(SchemeGroupVersion, &Policy{}, ) diff --git a/plugin/pkg/scheduler/api/v1/register.go b/plugin/pkg/scheduler/api/v1/register.go index 110d1ec2604c..8e6d754376cf 100644 --- a/plugin/pkg/scheduler/api/v1/register.go +++ b/plugin/pkg/scheduler/api/v1/register.go @@ -18,7 +18,6 @@ package v1 import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/plugin/pkg/scheduler/api" ) @@ -26,9 +25,6 @@ import ( // TODO this should be in the "scheduler" group var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"} -// Codec encodes internal objects to the v1 scheme -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func init() { api.Scheme.AddKnownTypes(SchemeGroupVersion, &Policy{}, From c49cd4edf97d8669385bc538282cac465a525a38 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:25:12 -0500 Subject: [PATCH 07/17] Alter the build to generate for __internal correctly --- cmd/gendeepcopy/deep_copy.go | 2 +- hack/after-build/update-generated-deep-copies.sh | 4 ++-- hack/lib/util.sh | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/gendeepcopy/deep_copy.go b/cmd/gendeepcopy/deep_copy.go index 56fa74b03dc2..70672df3a369 100644 --- a/cmd/gendeepcopy/deep_copy.go +++ b/cmd/gendeepcopy/deep_copy.go @@ -61,7 +61,7 @@ func pkgPath(group, version string) string { group = "api" } gv := group - if version != "" { + if version != "__internal" { gv = path.Join(group, version) } switch { diff --git a/hack/after-build/update-generated-deep-copies.sh b/hack/after-build/update-generated-deep-copies.sh index 83740149bf48..c3089be5e711 100755 --- a/hack/after-build/update-generated-deep-copies.sh +++ b/hack/after-build/update-generated-deep-copies.sh @@ -54,7 +54,7 @@ function generate_deep_copies() { else apiVersions="${ver}" fi - KUBE_API_VERSIONS="${apiVersions}" generate_version "${ver}" + KUBE_API_VERSIONS="${apiVersions:-}" generate_version "${ver}" done } @@ -62,6 +62,6 @@ function generate_deep_copies() { # Currently pkg/api/deep_copy_generated.go is generated by the new go2idl generator. # All others (mentioned above) are still generated by the old reflection-based generator. # TODO: Migrate these to the new generator. -DEFAULT_VERSIONS="v1 authorization/ authorization/v1beta1 extensions/ extensions/v1beta1 componentconfig/ componentconfig/v1alpha1 metrics/ metrics/v1alpha1" +DEFAULT_VERSIONS="v1 authorization/__internal authorization/v1beta1 extensions/__internal extensions/v1beta1 componentconfig/__internal componentconfig/v1alpha1 metrics/__internal metrics/v1alpha1" VERSIONS=${VERSIONS:-$DEFAULT_VERSIONS} generate_deep_copies "$VERSIONS" diff --git a/hack/lib/util.sh b/hack/lib/util.sh index b4ebec1ecf82..5208a954c752 100755 --- a/hack/lib/util.sh +++ b/hack/lib/util.sh @@ -282,7 +282,7 @@ kube::util::group-version-to-pkg-path() { # moving the results to pkg/apis/api. case "${group_version}" in # both group and version are "", this occurs when we generate deep copies for internal objects of the legacy v1 API. - /) + __internal) echo "api" ;; v1) @@ -292,7 +292,7 @@ kube::util::group-version-to-pkg-path() { echo "api/unversioned" ;; *) - echo "apis/${group_version}" + echo "apis/${group_version%__internal}" ;; esac } From 4d127dc969295c5043fbe5daf1141113689eae23 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:27:49 -0500 Subject: [PATCH 08/17] Initialize API servers with negotiated serializers Pass down into the server initialization the necessary interface for handling client/server content type negotiation. Add integration tests for the negotiation. --- cmd/kube-apiserver/app/server.go | 20 ++++++------ cmd/kube-apiserver/app/server_test.go | 4 +-- pkg/apis/extensions/install/install_test.go | 6 ---- pkg/conversion/unversioned_test.go | 1 - pkg/genericapiserver/genericapiserver.go | 31 ++++++++++--------- pkg/genericapiserver/genericapiserver_test.go | 10 ++++-- pkg/master/master.go | 23 ++++++++++---- pkg/master/master_test.go | 2 +- test/integration/etcd_tools_test.go | 4 +-- test/integration/framework/master_utils.go | 8 +++-- test/integration/master_test.go | 4 +++ 11 files changed, 65 insertions(+), 48 deletions(-) diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index ed55faaa6eb4..71cdf66cca4e 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -47,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/genericapiserver" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/storage" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" @@ -81,9 +82,9 @@ func verifyClusterIPFlags(s *options.APIServer) { } } -type newEtcdFunc func([]string, meta.VersionInterfacesFunc, string, string) (storage.Interface, error) +type newEtcdFunc func([]string, runtime.NegotiatedSerializer, string, string) (storage.Interface, error) -func newEtcd(etcdServerList []string, interfacesFunc meta.VersionInterfacesFunc, storageGroupVersionString, pathPrefix string) (etcdStorage storage.Interface, err error) { +func newEtcd(etcdServerList []string, ns runtime.NegotiatedSerializer, storageGroupVersionString, pathPrefix string) (etcdStorage storage.Interface, err error) { if storageGroupVersionString == "" { return etcdStorage, fmt.Errorf("storageVersion is required to create a etcd storage") } @@ -95,11 +96,11 @@ func newEtcd(etcdServerList []string, interfacesFunc meta.VersionInterfacesFunc, var storageConfig etcdstorage.EtcdConfig storageConfig.ServerList = etcdServerList storageConfig.Prefix = pathPrefix - versionedInterface, err := interfacesFunc(storageVersion) - if err != nil { - return nil, err + s, ok := ns.SerializerForMediaType("application/json", nil) + if !ok { + return nil, fmt.Errorf("unable to find serializer for JSON") } - storageConfig.Codec = versionedInterface.Codec + storageConfig.Codec = runtime.NewCodec(ns.EncoderForVersion(s, storageVersion), ns.DecoderToVersion(s, unversioned.GroupVersion{Group: storageVersion.Group, Version: runtime.APIVersionInternal})) return storageConfig.NewStorage() } @@ -148,7 +149,7 @@ func updateEtcdOverrides(overrides []string, storageVersions map[string]string, } servers := strings.Split(tokens[1], ";") - etcdOverrideStorage, err := newEtcdFn(servers, apigroup.InterfacesFor, storageVersions[apigroup.GroupVersion.Group], prefix) + etcdOverrideStorage, err := newEtcdFn(servers, api.Codecs, storageVersions[apigroup.GroupVersion.Group], prefix) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd for %s: %v", tokens[0], err) } @@ -259,7 +260,7 @@ func Run(s *options.APIServer) error { if _, found := storageVersions[legacyV1Group.GroupVersion.Group]; !found { glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", legacyV1Group.GroupVersion.Group, storageVersions) } - etcdStorage, err := newEtcd(s.EtcdServerList, legacyV1Group.InterfacesFor, storageVersions[legacyV1Group.GroupVersion.Group], s.EtcdPathPrefix) + etcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[legacyV1Group.GroupVersion.Group], s.EtcdPathPrefix) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err) } @@ -273,7 +274,7 @@ func Run(s *options.APIServer) error { if _, found := storageVersions[expGroup.GroupVersion.Group]; !found { glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", expGroup.GroupVersion.Group, storageVersions) } - expEtcdStorage, err := newEtcd(s.EtcdServerList, expGroup.InterfacesFor, storageVersions[expGroup.GroupVersion.Group], s.EtcdPathPrefix) + expEtcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[expGroup.GroupVersion.Group], s.EtcdPathPrefix) if err != nil { glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err) } @@ -380,6 +381,7 @@ func Run(s *options.APIServer) error { ProxyTLSClientConfig: proxyTLSClientConfig, ServiceNodePortRange: s.ServiceNodePortRange, KubernetesServiceNodePort: s.KubernetesServiceNodePort, + Serializer: api.Codecs, }, EnableCoreControllers: true, EventTTL: s.EventTTL, diff --git a/cmd/kube-apiserver/app/server_test.go b/cmd/kube-apiserver/app/server_test.go index 165256dfb3a4..ed98edbf5e88 100644 --- a/cmd/kube-apiserver/app/server_test.go +++ b/cmd/kube-apiserver/app/server_test.go @@ -24,9 +24,9 @@ import ( "k8s.io/kubernetes/cmd/kube-apiserver/app/options" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/genericapiserver" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" ) @@ -133,7 +133,7 @@ func TestUpdateEtcdOverrides(t *testing.T) { } for _, test := range testCases { - newEtcd := func(serverList []string, _ meta.VersionInterfacesFunc, _, _ string) (storage.Interface, error) { + newEtcd := func(serverList []string, _ runtime.NegotiatedSerializer, _, _ string) (storage.Interface, error) { if !reflect.DeepEqual(test.servers, serverList) { t.Errorf("unexpected server list, expected: %#v, got: %#v", test.servers, serverList) } diff --git a/pkg/apis/extensions/install/install_test.go b/pkg/apis/extensions/install/install_test.go index 61aa91beff6f..311fad56da92 100644 --- a/pkg/apis/extensions/install/install_test.go +++ b/pkg/apis/extensions/install/install_test.go @@ -102,15 +102,9 @@ func TestRESTMapper(t *testing.T) { t.Errorf("incorrect groupVersion: %v", mapping) } -<<<<<<< HEAD interfaces, _ := registered.GroupOrDie(extensions.GroupName).InterfacesFor(version) - if mapping.Codec != interfaces.Codec { - t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) -======= - interfaces, _ := latest.GroupOrDie(extensions.GroupName).InterfacesFor(version) if mapping.ObjectConvertor != interfaces.ObjectConvertor { t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) ->>>>>>> e776ada... Switch API objects to not register per version codecs } rc := &extensions.HorizontalPodAutoscaler{ObjectMeta: api.ObjectMeta{Name: "foo"}} diff --git a/pkg/conversion/unversioned_test.go b/pkg/conversion/unversioned_test.go index 308549edc53a..ae6b89c7613d 100644 --- a/pkg/conversion/unversioned_test.go +++ b/pkg/conversion/unversioned_test.go @@ -23,7 +23,6 @@ import ( // 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/latest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index 18be54f5cae4..538094b21537 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/registry/generic" genericetcd "k8s.io/kubernetes/pkg/registry/generic/etcd" ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/ui" "k8s.io/kubernetes/pkg/util" @@ -188,6 +189,9 @@ type Config struct { // Map requests to contexts. Exported so downstream consumers can provider their own mappers RequestContextMapper api.RequestContextMapper + // Required, the interface for serializing and converting objects to and from the wire + Serializer runtime.NegotiatedSerializer + // If specified, all web services will be registered into this container RestfulContainer *restful.Container @@ -394,6 +398,7 @@ func New(c *Config) *GenericAPIServer { AdmissionControl: c.AdmissionControl, ApiGroupVersionOverrides: c.APIGroupVersionOverrides, RequestContextMapper: c.RequestContextMapper, + Serializer: c.Serializer, cacheTimeout: c.CacheTimeout, MinRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second, @@ -418,7 +423,7 @@ func New(c *Config) *GenericAPIServer { } else { mux := http.NewServeMux() s.mux = mux - handlerContainer = NewHandlerContainer(mux) + handlerContainer = NewHandlerContainer(mux, c.Serializer) } s.HandlerContainer = handlerContainer // Use CurlyRouter to be able to use regular expressions in paths. Regular expressions are required in paths for example for proxy (where the path is proxy/{kind}/{name}/{*}) @@ -457,10 +462,10 @@ func (s *GenericAPIServer) HandleFuncWithAuth(pattern string, handler func(http. s.MuxHelper.HandleFunc(pattern, handler) } -func NewHandlerContainer(mux *http.ServeMux) *restful.Container { +func NewHandlerContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer) *restful.Container { container := restful.NewContainer() container.ServeMux = mux - apiserver.InstallRecoverHandler(container) + apiserver.InstallRecoverHandler(s, container) return container } @@ -667,7 +672,7 @@ func (s *GenericAPIServer) installAPIGroup(apiGroupInfo *APIGroupInfo) error { // Install the version handler. if apiGroupInfo.IsLegacyGroup { // Add a handler at /api to enumerate the supported api versions. - apiserver.AddApiWebService(s.HandlerContainer, apiPrefix, apiVersions) + apiserver.AddApiWebService(s.Serializer, s.HandlerContainer, apiPrefix, apiVersions) } else { // Add a handler at /apis/ to enumerate all versions supported by this group. apiVersionsForDiscovery := []unversioned.GroupVersionForDiscovery{} @@ -686,9 +691,9 @@ func (s *GenericAPIServer) installAPIGroup(apiGroupInfo *APIGroupInfo) error { Versions: apiVersionsForDiscovery, PreferredVersion: preferedVersionForDiscovery, } - apiserver.AddGroupWebService(s.HandlerContainer, apiPrefix+"/"+apiGroup.Name, apiGroup) + apiserver.AddGroupWebService(s.Serializer, s.HandlerContainer, apiPrefix+"/"+apiGroup.Name, apiGroup) } - apiserver.InstallServiceErrorHandler(s.HandlerContainer, s.NewRequestInfoResolver(), apiVersions) + apiserver.InstallServiceErrorHandler(s.Serializer, s.HandlerContainer, s.NewRequestInfoResolver(), apiVersions) return nil } @@ -700,25 +705,21 @@ func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV version, err := s.newAPIGroupVersion(apiGroupInfo.GroupMeta, groupVersion) version.Root = apiPrefix version.Storage = storage + version.ParameterCodec = apiGroupInfo.ParameterCodec + version.Serializer = apiGroupInfo.NegotiatedSerializer + version.Creater = apiGroupInfo.Scheme + version.Convertor = apiGroupInfo.Scheme + version.Typer = apiGroupInfo.Scheme return version, err } func (s *GenericAPIServer) newAPIGroupVersion(groupMeta apimachinery.GroupMeta, groupVersion unversioned.GroupVersion) (*apiserver.APIGroupVersion, error) { - versionInterface, err := groupMeta.InterfacesFor(groupVersion) - if err != nil { - return nil, err - } return &apiserver.APIGroupVersion{ RequestInfoResolver: s.NewRequestInfoResolver(), - Creater: api.Scheme, - Convertor: api.Scheme, - Typer: api.Scheme, - GroupVersion: groupVersion, Linker: groupMeta.SelfLinker, Mapper: groupMeta.RESTMapper, - Codec: versionInterface.Codec, Admit: s.AdmissionControl, Context: s.RequestContextMapper, diff --git a/pkg/genericapiserver/genericapiserver_test.go b/pkg/genericapiserver/genericapiserver_test.go index e1e963dc187b..be22c99e3427 100644 --- a/pkg/genericapiserver/genericapiserver_test.go +++ b/pkg/genericapiserver/genericapiserver_test.go @@ -105,12 +105,16 @@ func TestInstallAPIGroups(t *testing.T) { GroupMeta: *apiGroupMeta, VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, IsLegacyGroup: true, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, }, { // extensions group version GroupMeta: *extensionsGroupMeta, VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, OptionsExternalVersion: &apiGroupMeta.GroupVersion, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, }, } s.InstallAPIGroups(apiGroupsInfo) @@ -140,7 +144,7 @@ func TestInstallAPIGroups(t *testing.T) { func TestNewHandlerContainer(t *testing.T) { assert := assert.New(t) mux := http.NewServeMux() - container := NewHandlerContainer(mux) + container := NewHandlerContainer(mux, nil) assert.Equal(mux, container.ServeMux, "ServerMux's do not match") } @@ -179,7 +183,7 @@ func TestInstallSwaggerAPI(t *testing.T) { defer etcdserver.Terminate(t) mux := http.NewServeMux() - server.HandlerContainer = NewHandlerContainer(mux) + server.HandlerContainer = NewHandlerContainer(mux, nil) // Ensure swagger isn't installed without the call ws := server.HandlerContainer.RegisteredWebServices() @@ -198,7 +202,7 @@ func TestInstallSwaggerAPI(t *testing.T) { // Empty externalHost verification mux = http.NewServeMux() - server.HandlerContainer = NewHandlerContainer(mux) + server.HandlerContainer = NewHandlerContainer(mux, nil) server.externalHost = "" server.ClusterIP = net.IPv4(10, 10, 10, 10) server.PublicReadWritePort = 1010 diff --git a/pkg/master/master.go b/pkg/master/master.go index de8bc0925e92..64cb3ef2c7de 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -66,6 +66,7 @@ import ( thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd" "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata" thirdpartyresourcedataetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata/etcd" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" etcdutil "k8s.io/kubernetes/pkg/storage/etcd/util" "k8s.io/kubernetes/pkg/util" @@ -183,7 +184,10 @@ func (m *Master) InstallAPIs(c *Config) { VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ "v1": m.v1ResourcesStorage, }, - IsLegacyGroup: true, + IsLegacyGroup: true, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, } apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) } @@ -217,6 +221,9 @@ func (m *Master) InstallAPIs(c *Config) { "v1beta1": extensionResources, }, OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, } apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) @@ -237,7 +244,7 @@ func (m *Master) InstallAPIs(c *Config) { // This should be done after all groups are registered // TODO: replace the hardcoded "apis". - apiserver.AddApisWebService(m.HandlerContainer, "/apis", func() []unversioned.APIGroup { + apiserver.AddApisWebService(m.Serializer, m.HandlerContainer, "/apis", func() []unversioned.APIGroup { groups := []unversioned.APIGroup{} for ix := range allGroups { groups = append(groups, allGroups[ix]) @@ -517,9 +524,9 @@ func (m *Master) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource) Name: group, Versions: []unversioned.GroupVersionForDiscovery{groupVersion}, } - apiserver.AddGroupWebService(m.HandlerContainer, path, apiGroup) + apiserver.AddGroupWebService(api.Codecs, m.HandlerContainer, path, apiGroup) m.addThirdPartyResourceStorage(path, thirdparty.Storage[strings.ToLower(kind)+"s"].(*thirdpartyresourcedataetcd.REST), apiGroup) - apiserver.InstallServiceErrorHandler(m.HandlerContainer, m.NewRequestInfoResolver(), []string{thirdparty.GroupVersion.String()}) + apiserver.InstallServiceErrorHandler(api.Codecs, m.HandlerContainer, m.NewRequestInfoResolver(), []string{thirdparty.GroupVersion.String()}) return nil } @@ -533,10 +540,12 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV } optionsExternalVersion := registered.GroupOrDie(api.GroupName).GroupVersion + internalVersion := unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal} + externalVersion := unversioned.GroupVersion{Group: group, Version: version} return &apiserver.APIGroupVersion{ Root: apiRoot, - GroupVersion: unversioned.GroupVersion{Group: group, Version: version}, + GroupVersion: externalVersion, RequestInfoResolver: m.NewRequestInfoResolver(), Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme), @@ -544,11 +553,13 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV Typer: api.Scheme, Mapper: thirdpartyresourcedata.NewMapper(registered.GroupOrDie(extensions.GroupName).RESTMapper, kind, version, group), - Codec: thirdpartyresourcedata.NewCodec(registered.GroupOrDie(extensions.GroupName).Codec, kind), Linker: registered.GroupOrDie(extensions.GroupName).SelfLinker, Storage: storage, OptionsExternalVersion: &optionsExternalVersion, + Serializer: thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, kind, externalVersion, internalVersion), + ParameterCodec: api.ParameterCodec, + Context: m.RequestContextMapper, MinRequestTimeout: m.MinRequestTimeout, diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index 9147f951283d..b1996bd96047 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -83,6 +83,7 @@ func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.A func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) { _, etcdserver, config, assert := setUp(t) + config.Serializer = api.Codecs config.KubeletClient = client.FakeKubeletClient{} config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil } @@ -496,7 +497,6 @@ func decodeResponse(resp *http.Response, obj interface{}) error { if err != nil { return err } - if err := json.Unmarshal(data, obj); err != nil { return err } diff --git a/test/integration/etcd_tools_test.go b/test/integration/etcd_tools_test.go index 720fe278d60d..a8e8b0a9d64f 100644 --- a/test/integration/etcd_tools_test.go +++ b/test/integration/etcd_tools_test.go @@ -49,7 +49,7 @@ func TestSet(t *testing.T) { if err != nil || resp.Node == nil { t.Fatalf("unexpected error: %v %v", err, resp) } - decoded, err := testapi.Default.Codec().Decode([]byte(resp.Node.Value)) + decoded, err := runtime.Decode(testapi.Default.Codec(), []byte(resp.Node.Value)) if err != nil { t.Fatalf("unexpected response: %#v", resp.Node) } @@ -67,7 +67,7 @@ func TestGet(t *testing.T) { ctx := context.TODO() framework.WithEtcdKey(func(key string) { testObject := api.ServiceAccount{ObjectMeta: api.ObjectMeta{Name: "foo"}} - coded, err := testapi.Default.Codec().Encode(&testObject) + coded, err := runtime.Encode(testapi.Default.Codec(), &testObject) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index 73a9e96aa4a5..5a7dab1f15f1 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -21,7 +21,7 @@ import ( "net" "net/http" "net/http/httptest" - "runtime" + goruntime "runtime" "sync" "testing" "time" @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" "k8s.io/kubernetes/plugin/pkg/admission/admit" @@ -106,7 +107,7 @@ func NewMasterComponents(c *Config) *MasterComponents { // TODO: Support events once we can cleanly shutdown an event recorder. controllerManager.SetEventRecorder(&record.FakeRecorder{}) if c.StartReplicationManager { - go controllerManager.Run(runtime.NumCPU(), rcStopCh) + go controllerManager.Run(goruntime.NumCPU(), rcStopCh) } var once sync.Once return &MasterComponents{ @@ -157,6 +158,7 @@ func NewMasterConfig() *master.Config { APIGroupPrefix: "/apis", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), + Serializer: api.Codecs, }, KubeletClient: kubeletclient.FakeKubeletClient{}, } @@ -195,7 +197,7 @@ func RCFromManifest(fileName string) *api.ReplicationController { glog.Fatalf("Unexpected error reading rc manifest %v", err) } var controller api.ReplicationController - if err := api.Scheme.DecodeInto(data, &controller); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &controller); err != nil { glog.Fatalf("Unexpected error reading rc manifest %v", err) } return &controller diff --git a/test/integration/master_test.go b/test/integration/master_test.go index bd389fa3b88f..9e74782ce691 100644 --- a/test/integration/master_test.go +++ b/test/integration/master_test.go @@ -19,9 +19,13 @@ limitations under the License. package integration import ( + "encoding/json" + "io/ioutil" "net/http" "testing" + "github.com/ghodss/yaml" + "k8s.io/kubernetes/test/integration/framework" ) From 4386e8cc38d33b153808d2462ccbe5d16a143b9d Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:28:55 -0500 Subject: [PATCH 09/17] Change legacy ABAC decode to use new Decoder The new Decode() method is able to deserialize an unknown type when an explicit Into is provided. --- pkg/auth/authorizer/abac/abac.go | 34 +++++++++++++-------------- pkg/auth/authorizer/abac/abac_test.go | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/pkg/auth/authorizer/abac/abac.go b/pkg/auth/authorizer/abac/abac.go index 9b367cf57fb1..c3bfedcc4d8e 100644 --- a/pkg/auth/authorizer/abac/abac.go +++ b/pkg/auth/authorizer/abac/abac.go @@ -29,9 +29,10 @@ import ( "github.com/golang/glog" api "k8s.io/kubernetes/pkg/apis/abac" + _ "k8s.io/kubernetes/pkg/apis/abac/latest" "k8s.io/kubernetes/pkg/apis/abac/v0" - _ "k8s.io/kubernetes/pkg/apis/abac/v1beta1" "k8s.io/kubernetes/pkg/auth/authorizer" + "k8s.io/kubernetes/pkg/runtime" ) type policyLoadError struct { @@ -63,6 +64,8 @@ func NewFromFile(path string) (policyList, error) { scanner := bufio.NewScanner(file) pl := make(policyList, 0) + decoder := api.Codecs.UniversalDecoder() + i := 0 unversionedLines := 0 for scanner.Scan() { @@ -76,34 +79,29 @@ func NewFromFile(path string) (policyList, error) { continue } - dataKind, err := api.Scheme.DataKind(b) + decodedObj, _, err := decoder.Decode(b, nil, nil) if err != nil { - return nil, policyLoadError{path, i, b, err} - } - - if dataKind.IsEmpty() { + if !(runtime.IsMissingVersion(err) || runtime.IsMissingKind(err) || runtime.IsNotRegisteredError(err)) { + return nil, policyLoadError{path, i, b, err} + } unversionedLines++ // Migrate unversioned policy object oldPolicy := &v0.Policy{} - if err := latest.Codec.DecodeInto(b, oldPolicy); err != nil { + if err := runtime.DecodeInto(decoder, b, oldPolicy); err != nil { return nil, policyLoadError{path, i, b, err} } if err := api.Scheme.Convert(oldPolicy, p); err != nil { return nil, policyLoadError{path, i, b, err} } - } else { - decodedObj, err := latest.Codec.Decode(b) - if err != nil { - return nil, policyLoadError{path, i, b, err} - } - decodedPolicy, ok := decodedObj.(*api.Policy) - if !ok { - return nil, policyLoadError{path, i, b, fmt.Errorf("unrecognized object: %#v", decodedObj)} - } - p = decodedPolicy + pl = append(pl, p) + continue } - pl = append(pl, p) + decodedPolicy, ok := decodedObj.(*api.Policy) + if !ok { + return nil, policyLoadError{path, i, b, fmt.Errorf("unrecognized object: %#v", decodedObj)} + } + pl = append(pl, decodedPolicy) } if unversionedLines > 0 { diff --git a/pkg/auth/authorizer/abac/abac_test.go b/pkg/auth/authorizer/abac/abac_test.go index 5fa1154fd3f6..8b4e3b75ba2f 100644 --- a/pkg/auth/authorizer/abac/abac_test.go +++ b/pkg/auth/authorizer/abac/abac_test.go @@ -21,7 +21,7 @@ import ( "os" "testing" - "k8s.io/kubernetes/pkg/apis/abac" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/apis/abac/v0" "k8s.io/kubernetes/pkg/apis/abac/v1beta1" "k8s.io/kubernetes/pkg/auth/authorizer" From 24a7919002b9c58d0376168ffaec371b523785f9 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:31:34 -0500 Subject: [PATCH 10/17] Update clientcmd.Config to use new Decode methods --- .../clientcmd/api/latest/latest.go | 16 +++++++++------ .../unversioned/clientcmd/api/register.go | 2 +- pkg/client/unversioned/clientcmd/api/types.go | 20 +++++++++---------- .../clientcmd/api/v1/conversion.go | 10 +++++----- .../unversioned/clientcmd/api/v1/register.go | 4 ---- pkg/client/unversioned/clientcmd/loader.go | 19 ++++++------------ .../unversioned/clientcmd/loader_test.go | 7 ++++--- 7 files changed, 36 insertions(+), 42 deletions(-) diff --git a/pkg/client/unversioned/clientcmd/api/latest/latest.go b/pkg/client/unversioned/clientcmd/api/latest/latest.go index ecce3cbdd512..90d5c5380b1c 100644 --- a/pkg/client/unversioned/clientcmd/api/latest/latest.go +++ b/pkg/client/unversioned/clientcmd/api/latest/latest.go @@ -18,8 +18,11 @@ package latest import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1" + "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" + _ "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" ) // Version is the string that represents the current external default version. @@ -37,8 +40,9 @@ const OldestVersion = "v1" // with a set of versions to choose. var Versions = []string{"v1"} -// Codec is the default codec for serializing output that should use -// the latest supported version. Use this Codec when writing to -// disk, a data store that is not dynamically versioned, or in tests. -// This codec can decode any object that Kubernetes is aware of. -var Codec = runtime.YAMLDecoder(v1.Codec) +var Codec = versioning.NewCodecForScheme( + api.Scheme, + json.NewYAMLSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme)), + []unversioned.GroupVersion{{Version: Version}}, + []unversioned.GroupVersion{{Version: runtime.APIVersionInternal}}, +) diff --git a/pkg/client/unversioned/clientcmd/api/register.go b/pkg/client/unversioned/clientcmd/api/register.go index f3be591718bf..e4e23998abb8 100644 --- a/pkg/client/unversioned/clientcmd/api/register.go +++ b/pkg/client/unversioned/clientcmd/api/register.go @@ -26,7 +26,7 @@ var Scheme = runtime.NewScheme() // SchemeGroupVersion is group version used to register these objects // TODO this should be in the "kubeconfig" group -var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal} func init() { Scheme.AddKnownTypes(SchemeGroupVersion, diff --git a/pkg/client/unversioned/clientcmd/api/types.go b/pkg/client/unversioned/clientcmd/api/types.go index 818ab25b2a83..4a1dc364f43d 100644 --- a/pkg/client/unversioned/clientcmd/api/types.go +++ b/pkg/client/unversioned/clientcmd/api/types.go @@ -42,14 +42,14 @@ type Config struct { // CurrentContext is the name of the context that you would like to use by default CurrentContext string `json:"current-context"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // IMPORTANT if you add fields to this struct, please update IsConfigEmpty() type Preferences struct { Colors bool `json:"colors,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // Cluster contains information about how to communicate with a kubernetes cluster @@ -67,7 +67,7 @@ type Cluster struct { // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // AuthInfo contains information that describes identity information. This is use to tell the kubernetes cluster who you are. @@ -89,7 +89,7 @@ type AuthInfo struct { // Password is the password for basic authentication to the kubernetes cluster. Password string `json:"password,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with) @@ -103,7 +103,7 @@ type Context struct { // Namespace is the default namespace to use on unspecified requests Namespace string `json:"namespace,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // NewConfig is a convenience function that returns a new Config object with non-nil maps @@ -113,26 +113,26 @@ func NewConfig() *Config { Clusters: make(map[string]*Cluster), AuthInfos: make(map[string]*AuthInfo), Contexts: make(map[string]*Context), - Extensions: make(map[string]*runtime.EmbeddedObject), + Extensions: make(map[string]runtime.Object), } } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewContext() *Context { - return &Context{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &Context{Extensions: make(map[string]runtime.Object)} } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewCluster() *Cluster { - return &Cluster{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &Cluster{Extensions: make(map[string]runtime.Object)} } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewAuthInfo() *AuthInfo { - return &AuthInfo{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &AuthInfo{Extensions: make(map[string]runtime.Object)} } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewPreferences() *Preferences { - return &Preferences{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &Preferences{Extensions: make(map[string]runtime.Object)} } diff --git a/pkg/client/unversioned/clientcmd/api/v1/conversion.go b/pkg/client/unversioned/clientcmd/api/v1/conversion.go index cffdeb6ea9ec..e03fc60b164b 100644 --- a/pkg/client/unversioned/clientcmd/api/v1/conversion.go +++ b/pkg/client/unversioned/clientcmd/api/v1/conversion.go @@ -69,7 +69,7 @@ func init() { if err := s.Convert(&in.Contexts, &out.Contexts, 0); err != nil { return err } - out.Extensions = make(map[string]*runtime.EmbeddedObject) + out.Extensions = make(map[string]runtime.Object) if err := s.Convert(&in.Extensions, &out.Extensions, 0); err != nil { return err } @@ -192,10 +192,10 @@ func init() { return nil }, - func(in *[]NamedExtension, out *map[string]*runtime.EmbeddedObject, s conversion.Scope) error { + func(in *[]NamedExtension, out *map[string]runtime.Object, s conversion.Scope) error { for _, curr := range *in { - newExtension := &runtime.EmbeddedObject{} - if err := s.Convert(&curr.Extension, newExtension, 0); err != nil { + var newExtension runtime.Object + if err := s.Convert(&curr.Extension, &newExtension, 0); err != nil { return err } (*out)[curr.Name] = newExtension @@ -203,7 +203,7 @@ func init() { return nil }, - func(in *map[string]*runtime.EmbeddedObject, out *[]NamedExtension, s conversion.Scope) error { + func(in *map[string]runtime.Object, out *[]NamedExtension, s conversion.Scope) error { allKeys := make([]string, 0, len(*in)) for key := range *in { allKeys = append(allKeys, key) diff --git a/pkg/client/unversioned/clientcmd/api/v1/register.go b/pkg/client/unversioned/clientcmd/api/v1/register.go index 055ce52d3ae1..edf9fe1a70a5 100644 --- a/pkg/client/unversioned/clientcmd/api/v1/register.go +++ b/pkg/client/unversioned/clientcmd/api/v1/register.go @@ -19,16 +19,12 @@ package v1 import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/runtime" ) // SchemeGroupVersion is group version used to register these objects // TODO this should be in the "kubeconfig" group var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"} -// Codec encodes internal objects to the v1 scheme -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func init() { api.Scheme.AddKnownTypes(SchemeGroupVersion, &Config{}, diff --git a/pkg/client/unversioned/clientcmd/loader.go b/pkg/client/unversioned/clientcmd/loader.go index 42f1210d5b00..55b3dbcd2ce0 100644 --- a/pkg/client/unversioned/clientcmd/loader.go +++ b/pkg/client/unversioned/clientcmd/loader.go @@ -25,12 +25,13 @@ import ( "path/filepath" "strings" - "github.com/ghodss/yaml" "github.com/golang/glog" "github.com/imdario/mergo" + "k8s.io/kubernetes/pkg/api/unversioned" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" + "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" ) @@ -263,11 +264,11 @@ func Load(data []byte) (*clientcmdapi.Config, error) { if len(data) == 0 { return config, nil } - - if err := clientcmdlatest.Codec.DecodeInto(data, config); err != nil { + decoded, _, err := clientcmdlatest.Codec.Decode(data, &unversioned.GroupVersionKind{Version: clientcmdlatest.Version, Kind: "Config"}, config) + if err != nil { return nil, err } - return config, nil + return decoded.(*clientcmdapi.Config), nil } // WriteToFile serializes the config to yaml and writes it out to a file. If not present, it creates the file with the mode 0600. If it is present @@ -292,15 +293,7 @@ func WriteToFile(config clientcmdapi.Config, filename string) error { // Write serializes the config to yaml. // Encapsulates serialization without assuming the destination is a file. func Write(config clientcmdapi.Config) ([]byte, error) { - json, err := clientcmdlatest.Codec.Encode(&config) - if err != nil { - return nil, err - } - content, err := yaml.JSONToYAML(json) - if err != nil { - return nil, err - } - return content, nil + return runtime.Encode(clientcmdlatest.Codec, &config) } func (rules ClientConfigLoadingRules) ResolvePaths() bool { diff --git a/pkg/client/unversioned/clientcmd/loader_test.go b/pkg/client/unversioned/clientcmd/loader_test.go index 22926e7752a3..0921f7043aee 100644 --- a/pkg/client/unversioned/clientcmd/loader_test.go +++ b/pkg/client/unversioned/clientcmd/loader_test.go @@ -30,6 +30,7 @@ import ( clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" + "k8s.io/kubernetes/pkg/runtime" ) var ( @@ -391,7 +392,7 @@ func ExampleNoMergingOnExplicitPaths() { mergedConfig, err := loadingRules.Load() - json, err := clientcmdlatest.Codec.Encode(mergedConfig) + json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) if err != nil { fmt.Printf("Unexpected error: %v", err) } @@ -437,7 +438,7 @@ func ExampleMergingSomeWithConflict() { mergedConfig, err := loadingRules.Load() - json, err := clientcmdlatest.Codec.Encode(mergedConfig) + json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) if err != nil { fmt.Printf("Unexpected error: %v", err) } @@ -496,7 +497,7 @@ func ExampleMergingEverythingNoConflicts() { mergedConfig, err := loadingRules.Load() - json, err := clientcmdlatest.Codec.Encode(mergedConfig) + json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) if err != nil { fmt.Printf("Unexpected error: %v", err) } From efe88e08186a4bb75b7665028982afe3920b50a0 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:32:52 -0500 Subject: [PATCH 11/17] Update client library to react to changes in Decoding --- .../generators/generator-for-group.go | 11 ++++------ .../testgroup/unversioned/testgroup_client.go | 9 ++------ .../testgroup/unversioned/testgroup_test.go | 2 +- pkg/client/testing/core/fixture.go | 4 ++-- pkg/client/testing/fake/clientset.go | 2 +- .../unversioned/extensions_client.go | 8 ++----- .../legacy/unversioned/legacy_client.go | 8 ++----- pkg/client/unversioned/client_test.go | 6 ++++-- pkg/client/unversioned/discovery_client.go | 4 ++-- pkg/client/unversioned/extensions.go | 10 ++------- pkg/client/unversioned/helper.go | 6 +----- .../unversioned/helper_blackbox_test.go | 10 ++++----- pkg/client/unversioned/helper_test.go | 2 +- pkg/client/unversioned/request.go | 21 +++++++++++-------- pkg/client/unversioned/request_test.go | 20 +++++++++--------- pkg/client/unversioned/restclient_test.go | 11 +++++----- pkg/client/unversioned/testclient/fixture.go | 9 +++----- .../testclient/simple/simple_testclient.go | 2 +- .../unversioned/testclient/testclient.go | 2 +- .../unversioned/testclient/testclient_test.go | 6 +++--- 20 files changed, 65 insertions(+), 88 deletions(-) diff --git a/cmd/libs/go2idl/client-gen/generators/generator-for-group.go b/cmd/libs/go2idl/client-gen/generators/generator-for-group.go index 22a4ed18c2fe..75b3edc51eac 100644 --- a/cmd/libs/go2idl/client-gen/generators/generator-for-group.go +++ b/cmd/libs/go2idl/client-gen/generators/generator-for-group.go @@ -48,13 +48,14 @@ func (g *genGroup) Namers(c *generator.Context) namer.NameSystems { } func (g *genGroup) Imports(c *generator.Context) (imports []string) { - return append(g.imports.ImportLines(), "fmt") + return g.imports.ImportLines() } func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") const pkgUnversioned = "k8s.io/kubernetes/pkg/client/unversioned" const pkgRegistered = "k8s.io/kubernetes/pkg/apimachinery/registered" + const pkgAPI = "k8s.io/kubernetes/pkg/api" apiPath := func(group string) string { if group == "legacy" { return `"/api"` @@ -81,6 +82,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer "latestGroup": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "Group"}), "GroupOrDie": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "GroupOrDie"}), "apiPath": apiPath(g.group), + "latestCodecs": c.Universe.Variable(types.Name{Package: pkgAPI, Name: "Codecs"}), } sw.Do(groupInterfaceTemplate, m) sw.Do(groupClientTemplate, m) @@ -181,12 +183,7 @@ func setConfigDefaults(config *$.Config|raw$) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("$.Group$ API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = $.latestCodecs|raw$.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go index b011ed8a6682..a80e27299f31 100644 --- a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go +++ b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go @@ -17,7 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" + api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -79,12 +79,7 @@ func setConfigDefaults(config *unversioned.Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Testgroup API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go index fbb577e89f3a..af504efad1dc 100644 --- a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go +++ b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go @@ -40,7 +40,7 @@ func init() { return } testapi.Groups[testgroup.SchemeGroupVersion.Group] = testapi.NewTestGroup( - unversioned.GroupVersion{Group: testgroup.SchemeGroupVersion.Group, Version: registered.GroupOrDie(testgroup.SchemeGroupVersion.Group).GroupVersion.Version}, + registered.GroupOrDie(testgroup.SchemeGroupVersion.Group).GroupVersion, testgroup.SchemeGroupVersion) testHelper = testapi.Groups[testgroup.SchemeGroupVersion.Group] } diff --git a/pkg/client/testing/core/fixture.go b/pkg/client/testing/core/fixture.go index 19824aa453a3..6bac2b337b87 100644 --- a/pkg/client/testing/core/fixture.go +++ b/pkg/client/testing/core/fixture.go @@ -128,7 +128,7 @@ type objects struct { types map[string][]runtime.Object last map[string]int scheme ObjectScheme - decoder runtime.ObjectDecoder + decoder runtime.Decoder } var _ ObjectRetriever = &objects{} @@ -143,7 +143,7 @@ var _ ObjectRetriever = &objects{} // as a runtime.Object if Status == Success). If multiple PodLists are provided, they // will be returned in order by the Kind call, and the last PodList will be reused for // subsequent calls. -func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetriever { +func NewObjects(scheme ObjectScheme, decoder runtime.Decoder) ObjectRetriever { return objects{ types: make(map[string][]runtime.Object), last: make(map[string]int), diff --git a/pkg/client/testing/fake/clientset.go b/pkg/client/testing/fake/clientset.go index 3ddacfea1718..e14b0dc08bd0 100644 --- a/pkg/client/testing/fake/clientset.go +++ b/pkg/client/testing/fake/clientset.go @@ -30,7 +30,7 @@ import ( // Clientset returns a clientset that will respond with the provided objects func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := core.NewObjects(api.Scheme, api.Scheme) + o := core.NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) for _, obj := range objects { if err := o.Add(obj); err != nil { panic(err) diff --git a/pkg/client/typed/generated/extensions/unversioned/extensions_client.go b/pkg/client/typed/generated/extensions/unversioned/extensions_client.go index bfbf8aefd304..e4db05c0f529 100644 --- a/pkg/client/typed/generated/extensions/unversioned/extensions_client.go +++ b/pkg/client/typed/generated/extensions/unversioned/extensions_client.go @@ -18,6 +18,7 @@ package unversioned import ( "fmt" + registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -109,12 +110,7 @@ func setConfigDefaults(config *unversioned.Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Extensions API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/pkg/client/typed/generated/legacy/unversioned/legacy_client.go b/pkg/client/typed/generated/legacy/unversioned/legacy_client.go index afeedab1789d..6c4cd5a93d1a 100644 --- a/pkg/client/typed/generated/legacy/unversioned/legacy_client.go +++ b/pkg/client/typed/generated/legacy/unversioned/legacy_client.go @@ -18,6 +18,7 @@ package unversioned import ( "fmt" + registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -149,12 +150,7 @@ func setConfigDefaults(config *unversioned.Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Legacy API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/pkg/client/unversioned/client_test.go b/pkg/client/unversioned/client_test.go index 64bf256aade5..9ee779a0d2ce 100644 --- a/pkg/client/unversioned/client_test.go +++ b/pkg/client/unversioned/client_test.go @@ -30,6 +30,8 @@ import ( "k8s.io/kubernetes/pkg/version" ) +const nameRequiredError = "resource name may not be empty" + func TestGetServerVersion(t *testing.T) { expect := version.Info{ Major: "foo", @@ -75,7 +77,7 @@ func TestGetServerGroupsWithV1Server(t *testing.T) { } output, err := json.Marshal(obj) if err != nil { - t.Errorf("unexpected encoding error: %v", err) + t.Fatalf("unexpected encoding error: %v", err) return } w.Header().Set("Content-Type", "application/json") @@ -88,7 +90,7 @@ func TestGetServerGroupsWithV1Server(t *testing.T) { // ServerGroups should not return an error even if server returns error at /api and /apis apiGroupList, err := client.Discovery().ServerGroups() if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } groupVersions := ExtractGroupVersions(apiGroupList) if !reflect.DeepEqual(groupVersions, []string{"v1"}) { diff --git a/pkg/client/unversioned/discovery_client.go b/pkg/client/unversioned/discovery_client.go index dc0167e20e2f..ccc1c1ce8bd4 100644 --- a/pkg/client/unversioned/discovery_client.go +++ b/pkg/client/unversioned/discovery_client.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/version" ) @@ -209,8 +210,7 @@ func (d *DiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swag func setDiscoveryDefaults(config *Config) error { config.APIPath = "" config.GroupVersion = nil - // Discovery client deals with unversioned objects, so we use api.Codec. - config.Codec = api.Codec + config.Codec = runtime.NoopEncoder{api.Codecs.UniversalDecoder()} return nil } diff --git a/pkg/client/unversioned/extensions.go b/pkg/client/unversioned/extensions.go index e6be5415ee58..fe041bc987e3 100644 --- a/pkg/client/unversioned/extensions.go +++ b/pkg/client/unversioned/extensions.go @@ -17,8 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" - + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/extensions" ) @@ -121,12 +120,7 @@ func setExtensionsDefaults(config *Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Extensions API group/version '%v' is not recognized (valid values: %v)", - config.GroupVersion, registered.GroupOrDie(extensions.GroupName).GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index ea315c2008da..fad07b60a761 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -378,12 +378,8 @@ func SetKubernetesDefaults(config *Config) error { // TODO: Unconditionally set the config.Version, until we fix the config. copyGroupVersion := g.GroupVersion config.GroupVersion = ©GroupVersion - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("API version '%v' is not recognized (valid values: %v)", *config.GroupVersion, registered.GroupOrDie(api.GroupName).GroupVersions) - } if config.Codec == nil { - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) } if config.QPS == 0.0 { config.QPS = 5.0 diff --git a/pkg/client/unversioned/helper_blackbox_test.go b/pkg/client/unversioned/helper_blackbox_test.go index a913886488f6..de2df14fbb14 100644 --- a/pkg/client/unversioned/helper_blackbox_test.go +++ b/pkg/client/unversioned/helper_blackbox_test.go @@ -56,7 +56,7 @@ func TestNegotiateVersion(t *testing.T) { name: "server supports client default", version: &uapi.GroupVersion{Version: "version1"}, config: &unversioned.Config{}, - serverVersions: []string{"/version1", testapi.Default.GroupVersion().String()}, + serverVersions: []string{"version1", testapi.Default.GroupVersion().String()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, }, @@ -64,28 +64,28 @@ func TestNegotiateVersion(t *testing.T) { name: "server falls back to client supported", version: testapi.Default.GroupVersion(), config: &unversioned.Config{}, - serverVersions: []string{"/version1"}, + serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, }, { name: "explicit version supported", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, - serverVersions: []string{"/version1", testapi.Default.GroupVersion().String()}, + serverVersions: []string{"/ersion1", testapi.Default.GroupVersion().String()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectedVersion: testapi.Default.GroupVersion(), }, { name: "explicit version not supported", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, - serverVersions: []string{"/version1"}, + serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) }, }, { name: "connection refused error", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, - serverVersions: []string{"/version1"}, + serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, sendErr: errors.New("connection refused"), expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") }, diff --git a/pkg/client/unversioned/helper_test.go b/pkg/client/unversioned/helper_test.go index 8921bd04358e..8f9b6b3e7b27 100644 --- a/pkg/client/unversioned/helper_test.go +++ b/pkg/client/unversioned/helper_test.go @@ -222,7 +222,7 @@ func TestSetsCodec(t *testing.T) { if e, a := expected.Prefix, client.RESTClient.versionedAPIPath; e != a { t.Errorf("expected %#v, got %#v", e, a) } - if e, a := expected.Codec, client.RESTClient.Codec; e != a { + if e, a := expected.Codec, client.RESTClient.Codec; !reflect.DeepEqual(e, a) { t.Errorf("expected %#v, got %#v", e, a) } } diff --git a/pkg/client/unversioned/request.go b/pkg/client/unversioned/request.go index 371afe3817a4..2bcb38fa581f 100644 --- a/pkg/client/unversioned/request.go +++ b/pkg/client/unversioned/request.go @@ -452,7 +452,7 @@ func (r *Request) VersionedParams(obj runtime.Object, convertor runtime.ObjectCo continue } if k == unversioned.FieldSelectorQueryParam(r.groupVersion.String()) { - if value == "" { + if len(value) == 0 { // Don't set an empty selector for backward compatibility. // Since there is no way to get the difference between empty // and unspecified string, we don't set it to avoid having @@ -717,7 +717,7 @@ func (r *Request) Stream() (io.ReadCloser, error) { return nil, fmt.Errorf("%v while accessing %v", resp.Status, url) } - if runtimeObject, err := r.codec.Decode(bodyBytes); err == nil { + if runtimeObject, err := runtime.Decode(r.codec, bodyBytes); err == nil { statusError := errors.FromObject(runtimeObject) if _, ok := statusError.(errors.APIStatus); ok { @@ -846,8 +846,10 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request) Resu // Did the server give us a status response? isStatusResponse := false - var status unversioned.Status - if err := r.codec.DecodeInto(body, &status); err == nil && status.Status != "" { + var status *unversioned.Status + result, err := runtime.Decode(r.codec, body) + if out, ok := result.(*unversioned.Status); err == nil && ok && len(out.Status) > 0 { + status = out isStatusResponse = true } @@ -858,14 +860,14 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request) Resu if !isStatusResponse { return Result{err: r.transformUnstructuredResponseError(resp, req, body)} } - return Result{err: errors.FromObject(&status)} + return Result{err: errors.FromObject(status)} } // If the server gave us a status back, look at what it was. success := resp.StatusCode >= http.StatusOK && resp.StatusCode <= http.StatusPartialContent if isStatusResponse && (status.Status != unversioned.StatusSuccess && !success) { // "Failed" requests are clearly just an error and it makes sense to return them as such. - return Result{err: errors.FromObject(&status)} + return Result{err: errors.FromObject(status)} } return Result{ @@ -965,7 +967,8 @@ func (r Result) Get() (runtime.Object, error) { if r.err != nil { return nil, r.err } - return r.codec.Decode(r.body) + obj, err := runtime.Decode(r.codec, r.body) + return obj, err } // StatusCode returns the HTTP status code of the request. (Only valid if no @@ -975,12 +978,12 @@ func (r Result) StatusCode(statusCode *int) Result { return r } -// Into stores the result into obj, if possible. +// Into stores the result into obj, if possible. If obj is nil it is ignored. func (r Result) Into(obj runtime.Object) error { if r.err != nil { return r.err } - return r.codec.DecodeInto(r.body, obj) + return runtime.DecodeInto(r.codec, r.body, obj) } // WasCreated updates the provided bool pointer to whether the server returned diff --git a/pkg/client/unversioned/request_test.go b/pkg/client/unversioned/request_test.go index 77d71e8170a6..52203af9ad52 100644 --- a/pkg/client/unversioned/request_test.go +++ b/pkg/client/unversioned/request_test.go @@ -698,7 +698,7 @@ func TestDoRequestNewWay(t *testing.T) { Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -861,13 +861,13 @@ func BenchmarkCheckRetryClosesBody(b *testing.B) { func TestDoRequestNewWayReader(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, _ := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, _ := runtime.Encode(testapi.Default.Codec(), reqObj) expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -902,13 +902,13 @@ func TestDoRequestNewWayReader(t *testing.T) { func TestDoRequestNewWayObj(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, _ := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, _ := runtime.Encode(testapi.Default.Codec(), reqObj) expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -943,7 +943,7 @@ func TestDoRequestNewWayObj(t *testing.T) { func TestDoRequestNewWayFile(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, err := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, err := runtime.Encode(testapi.Default.Codec(), reqObj) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -964,7 +964,7 @@ func TestDoRequestNewWayFile(t *testing.T) { Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -1000,7 +1000,7 @@ func TestDoRequestNewWayFile(t *testing.T) { func TestWasCreated(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, err := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, err := runtime.Encode(testapi.Default.Codec(), reqObj) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -1010,7 +1010,7 @@ func TestWasCreated(t *testing.T) { Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 201, ResponseBody: string(expectedBody), @@ -1138,7 +1138,7 @@ func TestBody(t *testing.T) { const data = "test payload" obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - bodyExpected, _ := testapi.Default.Codec().Encode(obj) + bodyExpected, _ := runtime.Encode(testapi.Default.Codec(), obj) f, err := ioutil.TempFile("", "test_body") if err != nil { diff --git a/pkg/client/unversioned/restclient_test.go b/pkg/client/unversioned/restclient_test.go index 69884d86a8ae..b59da0fe7aca 100644 --- a/pkg/client/unversioned/restclient_test.go +++ b/pkg/client/unversioned/restclient_test.go @@ -28,13 +28,14 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" utiltesting "k8s.io/kubernetes/pkg/util/testing" ) func TestDoRequestSuccess(t *testing.T) { status := &unversioned.Status{Status: unversioned.StatusSuccess} - expectedBody, _ := testapi.Default.Codec().Encode(status) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -60,7 +61,7 @@ func TestDoRequestSuccess(t *testing.T) { if fakeHandler.RequestReceived.Header["Authorization"] == nil { t.Errorf("Request is missing authorization header: %#v", fakeHandler.RequestReceived) } - statusOut, err := testapi.Default.Codec().Decode(body) + statusOut, err := runtime.Decode(testapi.Default.Codec(), body) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -78,7 +79,7 @@ func TestDoRequestFailed(t *testing.T) { Message: " \"\" not found", Details: &unversioned.StatusDetails{}, } - expectedBody, _ := testapi.Default.Codec().Encode(status) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 404, ResponseBody: string(expectedBody), @@ -111,7 +112,7 @@ func TestDoRequestFailed(t *testing.T) { func TestDoRequestCreated(t *testing.T) { status := &unversioned.Status{Status: unversioned.StatusSuccess} - expectedBody, _ := testapi.Default.Codec().Encode(status) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 201, ResponseBody: string(expectedBody), @@ -138,7 +139,7 @@ func TestDoRequestCreated(t *testing.T) { if !created { t.Errorf("Expected object to be created") } - statusOut, err := testapi.Default.Codec().Decode(body) + statusOut, err := runtime.Decode(testapi.Default.Codec(), body) if err != nil { t.Errorf("Unexpected error %#v", err) } diff --git a/pkg/client/unversioned/testclient/fixture.go b/pkg/client/unversioned/testclient/fixture.go index fad088e99de2..64388fb63819 100644 --- a/pkg/client/unversioned/testclient/fixture.go +++ b/pkg/client/unversioned/testclient/fixture.go @@ -128,7 +128,7 @@ type objects struct { types map[string][]runtime.Object last map[string]int scheme ObjectScheme - decoder runtime.ObjectDecoder + decoder runtime.Decoder } var _ ObjectRetriever = &objects{} @@ -143,7 +143,7 @@ var _ ObjectRetriever = &objects{} // as a runtime.Object if Status == Success). If multiple PodLists are provided, they // will be returned in order by the Kind call, and the last PodList will be reused for // subsequent calls. -func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetriever { +func NewObjects(scheme ObjectScheme, decoder runtime.Decoder) ObjectRetriever { return objects{ types: make(map[string][]runtime.Object), last: make(map[string]int), @@ -153,10 +153,7 @@ func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetrie } func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.Object, error) { - // TODO our test clients deal in internal versions. We need to plumb that knowledge down here - // we might do this via an extra function to the scheme to allow getting internal group versions - // I'm punting for now - kind.Version = "" + kind.Version = runtime.APIVersionInternal empty, _ := o.scheme.New(kind) nilValue := reflect.Zero(reflect.TypeOf(empty)).Interface().(runtime.Object) diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index ec1bc0bc78c6..60a74963d1aa 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -213,7 +213,7 @@ func body(t *testing.T, obj runtime.Object, raw *string) *string { if !found { t.Errorf("Group %s is not registered in testapi", fqKind.GroupVersion().Group) } - bs, err = g.Codec().Encode(obj) + bs, err = runtime.Encode(g.Codec(), obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } diff --git a/pkg/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 8d8d1e92d71f..34c2dfb1cb22 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -33,7 +33,7 @@ import ( // NewSimpleFake returns a client that will respond with the provided objects func NewSimpleFake(objects ...runtime.Object) *Fake { - o := NewObjects(api.Scheme, api.Scheme) + o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) for _, obj := range objects { if err := o.Add(obj); err != nil { panic(err) diff --git a/pkg/client/unversioned/testclient/testclient_test.go b/pkg/client/unversioned/testclient/testclient_test.go index 6c7075e2846e..b31bdc213ee5 100644 --- a/pkg/client/unversioned/testclient/testclient_test.go +++ b/pkg/client/unversioned/testclient/testclient_test.go @@ -26,8 +26,8 @@ import ( ) func TestNewClient(t *testing.T) { - o := NewObjects(api.Scheme, api.Scheme) - if err := AddObjectsFromPath("../../../../examples/guestbook/frontend-service.yaml", o, api.Scheme); err != nil { + o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) + if err := AddObjectsFromPath("../../../../examples/guestbook/frontend-service.yaml", o, api.Codecs.UniversalDecoder()); err != nil { t.Fatal(err) } client := &Fake{} @@ -52,7 +52,7 @@ func TestNewClient(t *testing.T) { } func TestErrors(t *testing.T) { - o := NewObjects(api.Scheme, api.Scheme) + o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) o.Add(&api.List{ Items: []runtime.Object{ // This first call to List will return this error From fb4ea845f1f0dc51bb88b3487e911ff1058a1196 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:34:11 -0500 Subject: [PATCH 12/17] resource_printer was not leveraging runtime interfaces In general, everything in kubectl/* needs to be ignorant of api/* unless it deals with a concrete type - this change forces resource_printer to accept interface abstractions (that are already part of kubectl). --- pkg/kubectl/resource_printer.go | 91 ++++++++++++---------------- pkg/kubectl/resource_printer_test.go | 20 +++--- pkg/kubectl/sorting_printer.go | 8 +-- pkg/kubectl/sorting_printer_test.go | 5 +- 4 files changed, 57 insertions(+), 67 deletions(-) diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index bd5a42663615..637fc1d7ba70 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -19,7 +19,6 @@ package kubectl import ( "bytes" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -34,13 +33,14 @@ import ( "github.com/ghodss/yaml" "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" + utilerrors "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/jsonpath" "k8s.io/kubernetes/pkg/util/sets" ) @@ -66,7 +66,10 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) { case "yaml": printer = &YAMLPrinter{} case "name": - printer = &NamePrinter{} + printer = &NamePrinter{ + Typer: runtime.ObjectTyperToTyper(api.Scheme), + Decoder: latest.Codecs.UniversalDecoder(), + } case "template", "go-template": if len(formatArgument) == 0 { return nil, false, fmt.Errorf("template format specified but no template given") @@ -197,62 +200,47 @@ func (p *VersionedPrinter) HandledResources() []string { // NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object. type NamePrinter struct { + Decoder runtime.Decoder + Typer runtime.Typer } // PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object // and print "resource/name" pair. If the object is a List, print all items in it. func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { - objvalue := reflect.ValueOf(obj).Elem() - kindString := objvalue.FieldByName("Kind") - groupVersionString := objvalue.FieldByName("APIVersion") - kind := unversioned.GroupVersionKind{} - if !kindString.IsValid() { - kindString = reflect.ValueOf("") - } - kind.Kind = kindString.String() + gvk, _, _ := p.Typer.ObjectKind(obj) - if !groupVersionString.IsValid() { - groupVersionString = reflect.ValueOf("/") - } - gv, err := unversioned.ParseGroupVersion(groupVersionString.String()) - if err != nil { - kind.Group = gv.Group - kind.Version = gv.Version - } - - if kind.Kind == "List" { - items := objvalue.FieldByName("Items") - if items.Type().String() == "[]runtime.RawExtension" { - for i := 0; i < items.Len(); i++ { - rawObj := items.Index(i).FieldByName("RawJSON").Interface().([]byte) - scheme := api.Scheme - groupVersionKind, err := scheme.DataKind(rawObj) - if err != nil { - return err - } - decodedObj, err := scheme.DecodeToVersion(rawObj, unversioned.GroupVersion{}) - if err != nil { - return err - } - tpmeta := unversioned.TypeMeta{ - APIVersion: groupVersionKind.GroupVersion().String(), - Kind: groupVersionKind.Kind, - } - s := reflect.ValueOf(decodedObj).Elem() - s.FieldByName("TypeMeta").Set(reflect.ValueOf(tpmeta)) - p.PrintObj(decodedObj, w) + if meta.IsListType(obj) { + items, err := meta.ExtractList(obj) + if err != nil { + return err + } + if errs := runtime.DecodeList(items, p.Decoder, runtime.UnstructuredJSONScheme); len(errs) > 0 { + return utilerrors.NewAggregate(errs) + } + for _, obj := range items { + if err := p.PrintObj(obj, w); err != nil { + return err } - } else { - return errors.New("the list object contains unrecognized items.") } - } else { - name := objvalue.FieldByName("Name") - if !name.IsValid() { - name = reflect.ValueOf("") + return nil + } + + // TODO: this is wrong, runtime.Unknown and runtime.Unstructured are not handled properly here. + + name := "" + if acc, err := meta.Accessor(obj); err == nil { + if n := acc.GetName(); len(n) > 0 { + name = n } - _, resource := meta.KindToResource(kind, false) + } + + if gvk != nil { + // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper + _, resource := meta.KindToResource(*gvk, false) fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) + } else { + fmt.Fprintf(w, "/%s\n", name) } return nil @@ -1742,9 +1730,8 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) { // PrintObj formats the obj with the JSONPath Template. func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { - var queryObj interface{} - switch obj.(type) { - case *v1.List, *api.List: + var queryObj interface{} = obj + if meta.IsListType(obj) { data, err := json.Marshal(obj) if err != nil { return err @@ -1753,8 +1740,6 @@ func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { if err := json.Unmarshal(data, &queryObj); err != nil { return err } - default: - queryObj = obj } if err := j.JSONPath.Execute(w, queryObj); err != nil { diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index 1f71995a2649..3dc43870b75a 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -27,12 +27,14 @@ import ( "time" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/extensions" kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing" "k8s.io/kubernetes/pkg/runtime" + yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/sets" @@ -121,17 +123,17 @@ func TestPrinter(t *testing.T) { {"test jsonpath", "jsonpath", "{.metadata.name}", podTest, "foo"}, {"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, "foo bar"}, {"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, ""}, - {"test name", "name", "", podTest, "/foo\n"}, + {"test name", "name", "", podTest, "pod/foo\n"}, {"emits versioned objects", "template", "{{.kind}}", testapi, "Pod"}, } for _, test := range printerTests { buf := bytes.NewBuffer([]byte{}) printer, found, err := GetPrinter(test.Format, test.FormatArgument) if err != nil || !found { - t.Errorf("unexpected error: %#v", err) + t.Errorf("in %s, unexpected error: %#v", test.Name, err) } if err := printer.PrintObj(test.Input, buf); err != nil { - t.Errorf("unexpected error: %#v", err) + t.Errorf("in %s, unexpected error: %#v", test.Name, err) } if buf.String() != test.Expect { t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String()) @@ -175,8 +177,8 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data } // Use real decode function to undo the versioning process. poutput = kubectltesting.TestStruct{} - err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &poutput) - if err != nil { + s := yamlserializer.NewDecodingSerializer(testapi.Default.Codec()) + if err := runtime.DecodeInto(s, buf.Bytes(), &poutput); err != nil { t.Fatal(err) } if !reflect.DeepEqual(testData, poutput) { @@ -196,8 +198,7 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data } // Use real decode function to undo the versioning process. objOut = api.Pod{} - err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &objOut) - if err != nil { + if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil { t.Fatal(err) } if !reflect.DeepEqual(obj, &objOut) { @@ -463,7 +464,10 @@ func TestPrinters(t *testing.T) { "template": templatePrinter, "template2": templatePrinter2, "jsonpath": jsonpathPrinter, - "name": &NamePrinter{}, + "name": &NamePrinter{ + Typer: runtime.ObjectTyperToTyper(api.Scheme), + Decoder: latest.Codecs.UniversalDecoder(), + }, } objects := map[string]runtime.Object{ "pod": &api.Pod{ObjectMeta: om("pod")}, diff --git a/pkg/kubectl/sorting_printer.go b/pkg/kubectl/sorting_printer.go index c1f3c54ae45d..49e36b882e9b 100644 --- a/pkg/kubectl/sorting_printer.go +++ b/pkg/kubectl/sorting_printer.go @@ -22,7 +22,6 @@ import ( "reflect" "sort" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -36,6 +35,7 @@ import ( type SortingPrinter struct { SortField string Delegate ResourcePrinter + Decoder runtime.Decoder } func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error { @@ -63,7 +63,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error { return nil } - sorter, err := SortObjects(objs, s.SortField) + sorter, err := SortObjects(s.Decoder, objs, s.SortField) if err != nil { return err } @@ -80,7 +80,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error { return meta.SetList(obj, objs) } -func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error) { +func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) { parser := jsonpath.New("sorting") field, err := massageJSONPath(fieldInput) @@ -97,7 +97,7 @@ func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error) switch u := item.(type) { case *runtime.Unknown: var err error - if objs[ix], err = api.Codec.Decode(u.RawJSON); err != nil { + if objs[ix], _, err = decoder.Decode(u.RawJSON, nil, nil); err != nil { return nil, err } } diff --git a/pkg/kubectl/sorting_printer_test.go b/pkg/kubectl/sorting_printer_test.go index 8aed8a5b0af0..d62acacf07c0 100644 --- a/pkg/kubectl/sorting_printer_test.go +++ b/pkg/kubectl/sorting_printer_test.go @@ -20,12 +20,13 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/api/latest" api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" ) func encodeOrDie(obj runtime.Object) []byte { - data, err := api.Codec.Encode(obj) + data, err := runtime.Encode(latest.Codecs.LegacyCodec(api.SchemeGroupVersion), obj) if err != nil { panic(err.Error()) } @@ -223,7 +224,7 @@ func TestSortingPrinter(t *testing.T) { }, } for _, test := range tests { - sort := &SortingPrinter{SortField: test.field} + sort := &SortingPrinter{SortField: test.field, Decoder: latest.Codecs.UniversalDecoder()} if err := sort.sortObj(test.obj); err != nil { t.Errorf("unexpected error: %v (%s)", err, test.name) continue From 181dc7c64c30f2ea05f75619662699992cbd67f2 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:36:03 -0500 Subject: [PATCH 13/17] Update thirdpartyresourcecodec with new Decoder --- pkg/registry/thirdpartyresourcedata/codec.go | 133 ++++++++++-------- .../thirdpartyresourcedata/codec_test.go | 7 +- 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/pkg/registry/thirdpartyresourcedata/codec.go b/pkg/registry/thirdpartyresourcedata/codec.go index cfedc0565e04..9293af0f2cdd 100644 --- a/pkg/registry/thirdpartyresourcedata/codec.go +++ b/pkg/registry/thirdpartyresourcedata/codec.go @@ -21,11 +21,11 @@ import ( "encoding/json" "fmt" "io" - "net/url" "strings" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/registered" "k8s.io/kubernetes/pkg/api/unversioned" apiutil "k8s.io/kubernetes/pkg/api/util" "k8s.io/kubernetes/pkg/apimachinery/registered" @@ -116,7 +116,6 @@ func (t *thirdPartyResourceDataMapper) RESTMapping(gk unversioned.GroupKind, ver if err != nil { return nil, err } - mapping.Codec = NewCodec(mapping.Codec, t.kind) return mapping, nil } @@ -141,6 +140,37 @@ func NewMapper(mapper meta.RESTMapper, kind, version, group string) meta.RESTMap } } +type thirdPartyResourceDataCodecFactory struct { + runtime.NegotiatedSerializer + kind string + encodeGV unversioned.GroupVersion + decodeGV unversioned.GroupVersion +} + +func NewNegotiatedSerializer(s runtime.NegotiatedSerializer, kind string, encodeGV, decodeGV unversioned.GroupVersion) runtime.NegotiatedSerializer { + return &thirdPartyResourceDataCodecFactory{ + NegotiatedSerializer: s, + + kind: kind, + encodeGV: encodeGV, + decodeGV: decodeGV, + } +} + +func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder { + return NewCodec(runtime.NewCodec( + t.NegotiatedSerializer.EncoderForVersion(s, gv), + t.NegotiatedSerializer.DecoderToVersion(s, t.decodeGV), + ), t.kind) +} + +func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder { + return NewCodec(runtime.NewCodec( + t.NegotiatedSerializer.EncoderForVersion(s, t.encodeGV), + t.NegotiatedSerializer.DecoderToVersion(s, gv), + ), t.kind) +} + type thirdPartyResourceDataCodec struct { delegate runtime.Codec kind string @@ -190,84 +220,77 @@ func (t *thirdPartyResourceDataCodec) populateFromObject(objIn *extensions.Third return nil } -func (t *thirdPartyResourceDataCodec) Decode(data []byte) (runtime.Object, error) { - result := &extensions.ThirdPartyResourceData{} - if err := t.populate(result, data); err != nil { - return nil, err - } - return result, nil -} - -func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (runtime.Object, error) { - // TODO: this is hacky, there must be a better way... - obj, err := runtime.Decode(t, data) - if err != nil { - return nil, err - } - objData, err := t.delegate.Encode(obj) - if err != nil { - return nil, err - } - return t.delegate.DecodeToVersion(objData, gv) -} - -func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object) error { - thirdParty, ok := obj.(*extensions.ThirdPartyResourceData) - if !ok { - return fmt.Errorf("unexpected object: %#v", obj) +func (t *thirdPartyResourceDataCodec) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + if into == nil { + obj := &extensions.ThirdPartyResourceData{} + if err := t.populate(obj, data); err != nil { + return nil, nil, err + } + objData, err := runtime.Encode(t.delegate, obj) + if err != nil { + return nil, nil, err + } + return t.delegate.Decode(objData, gvk, into) } - return t.populate(thirdParty, data) -} -func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, gvk unversioned.GroupVersionKind) error { - thirdParty, ok := obj.(*extensions.ThirdPartyResourceData) + thirdParty, ok := into.(*extensions.ThirdPartyResourceData) if !ok { - return fmt.Errorf("unexpected object: %#v", obj) - } - - if gvk.Kind != "ThirdPartyResourceData" { - return fmt.Errorf("unexpeceted kind: %s", gvk.Kind) + return nil, nil, fmt.Errorf("unexpected object: %#v", into) } var dataObj interface{} if err := json.Unmarshal(data, &dataObj); err != nil { - return err + return nil, nil, err } mapObj, ok := dataObj.(map[string]interface{}) if !ok { - return fmt.Errorf("unexpcted object: %#v", dataObj) + return nil, nil, fmt.Errorf("unexpcted object: %#v", dataObj) } + /*if gvk.Kind != "ThirdPartyResourceData" { + return nil, nil, fmt.Errorf("unexpected kind: %s", gvk.Kind) + }*/ + actual := &unversioned.GroupVersionKind{} if kindObj, found := mapObj["kind"]; !found { + if gvk == nil { + return nil, nil, runtime.NewMissingKindErr(string(data)) + } mapObj["kind"] = gvk.Kind + actual.Kind = gvk.Kind } else { kindStr, ok := kindObj.(string) if !ok { - return fmt.Errorf("unexpected object for 'kind': %v", kindObj) + return nil, nil, fmt.Errorf("unexpected object for 'kind': %v", kindObj) } if kindStr != t.kind { - return fmt.Errorf("kind doesn't match, expecting: %s, got %s", gvk.Kind, kindStr) + return nil, nil, fmt.Errorf("kind doesn't match, expecting: %s, got %s", gvk.Kind, kindStr) } + actual.Kind = t.kind } if versionObj, found := mapObj["apiVersion"]; !found { + if gvk == nil { + return nil, nil, runtime.NewMissingVersionErr(string(data)) + } mapObj["apiVersion"] = gvk.GroupVersion().String() + actual.Group, actual.Version = gvk.Group, gvk.Version } else { versionStr, ok := versionObj.(string) if !ok { - return fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj) + return nil, nil, fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj) + } + if gvk != nil && versionStr != gvk.GroupVersion().String() { + return nil, nil, fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr) } - if versionStr != gvk.GroupVersion().String() { - return fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr) + gv, err := unversioned.ParseGroupVersion(versionStr) + if err != nil { + return nil, nil, err } + actual.Group, actual.Version = gv.Group, gv.Version } if err := t.populate(thirdParty, data); err != nil { - return err + return nil, actual, err } - return nil -} - -func (t *thirdPartyResourceDataCodec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error { - return t.delegate.DecodeParametersInto(parameters, obj) + return thirdParty, actual, nil } const template = `{ @@ -289,15 +312,7 @@ func encodeToJSON(obj *extensions.ThirdPartyResourceData, stream io.Writer) erro return encoder.Encode(objMap) } -func (t *thirdPartyResourceDataCodec) Encode(obj runtime.Object) ([]byte, error) { - buff := &bytes.Buffer{} - if err := t.EncodeToStream(obj, buff); err != nil { - return nil, err - } - return buff.Bytes(), nil -} - -func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream io.Writer) (err error) { +func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream io.Writer, overrides ...unversioned.GroupVersion) (err error) { switch obj := obj.(type) { case *extensions.ThirdPartyResourceData: return encodeToJSON(obj, stream) @@ -315,7 +330,7 @@ func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream fmt.Fprintf(stream, template, t.kind+"List", strings.Join(dataStrings, ",")) return nil case *unversioned.Status: - return t.delegate.EncodeToStream(obj, stream) + return t.delegate.EncodeToStream(obj, stream, overrides...) default: return fmt.Errorf("unexpected object to encode: %#v", obj) } diff --git a/pkg/registry/thirdpartyresourcedata/codec_test.go b/pkg/registry/thirdpartyresourcedata/codec_test.go index 5af08ce82e3f..2fdd4e9c4b91 100644 --- a/pkg/registry/thirdpartyresourcedata/codec_test.go +++ b/pkg/registry/thirdpartyresourcedata/codec_test.go @@ -23,6 +23,7 @@ import ( "time" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/extensions" @@ -86,13 +87,13 @@ func TestCodec(t *testing.T) { }, } for _, test := range tests { - codec := thirdPartyResourceDataCodec{kind: "Foo"} + codec := &thirdPartyResourceDataCodec{kind: "Foo", delegate: testapi.Extensions.Codec()} data, err := json.Marshal(test.obj) if err != nil { t.Errorf("[%s] unexpected error: %v", test.name, err) continue } - obj, err := runtime.Decode(&codec, data) + obj, err := runtime.Decode(codec, data) if err != nil && !test.expectErr { t.Errorf("[%s] unexpected error: %v", test.name, err) continue @@ -120,7 +121,7 @@ func TestCodec(t *testing.T) { t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, test.obj, &output) } - data, err = runtime.Encode(&codec, rsrcObj) + data, err = runtime.Encode(codec, rsrcObj) if err != nil { t.Errorf("[%s] unexpected error: %v", test.name, err) } From 2fd38a7dc022c28f05403355061d521d8f124da3 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:37:49 -0500 Subject: [PATCH 14/17] Break kubectl from assuming details of codecs Most of the logic related to type and kind retrieval belongs in the codec, not in the various classes. Make it explicit that the codec should handle these details. Factory now returns a universal Decoder and a JSONEncoder to assist code in kubectl that needs to specifically deal with JSON serialization (apply, merge, patch, edit, jsonpath). Add comments to indicate the serialization is explicit in those places. These methods decode to internal and encode to the preferred API version as previous, although in the future they may be changed. React to removing Codec from version interfaces and RESTMapping by passing it in to all the places that it is needed. --- pkg/kubectl/apply.go | 25 ++++--- pkg/kubectl/cmd/annotate.go | 4 +- pkg/kubectl/cmd/apply.go | 11 +-- pkg/kubectl/cmd/autoscale.go | 11 ++- pkg/kubectl/cmd/clusterinfo.go | 8 +-- pkg/kubectl/cmd/cmd_test.go | 28 +++++--- pkg/kubectl/cmd/convert.go | 10 ++- pkg/kubectl/cmd/create.go | 14 ++-- pkg/kubectl/cmd/delete.go | 2 +- pkg/kubectl/cmd/describe.go | 4 +- pkg/kubectl/cmd/drain.go | 3 +- pkg/kubectl/cmd/edit.go | 24 ++++--- pkg/kubectl/cmd/expose.go | 15 ++-- pkg/kubectl/cmd/get.go | 9 +-- pkg/kubectl/cmd/get_test.go | 4 +- pkg/kubectl/cmd/label.go | 4 +- pkg/kubectl/cmd/logs.go | 6 +- pkg/kubectl/cmd/patch.go | 4 +- pkg/kubectl/cmd/replace.go | 10 +-- pkg/kubectl/cmd/rollingupdate.go | 12 ++-- pkg/kubectl/cmd/run.go | 16 +++-- pkg/kubectl/cmd/scale.go | 2 +- pkg/kubectl/cmd/stop.go | 2 +- pkg/kubectl/cmd/util/factory.go | 39 ++++++----- pkg/kubectl/cmd/util/factory_test.go | 2 +- pkg/kubectl/cmd/util/helpers.go | 44 ++---------- pkg/kubectl/cmd/util/helpers_test.go | 2 +- pkg/kubectl/custom_column_printer.go | 4 +- pkg/kubectl/resource/builder.go | 4 +- pkg/kubectl/resource/builder_test.go | 74 +++++++++++--------- pkg/kubectl/resource/helper.go | 5 +- pkg/kubectl/resource/helper_test.go | 2 - pkg/kubectl/resource/interfaces.go | 2 +- pkg/kubectl/resource/mapper.go | 46 ++++++------ pkg/kubectl/resource/result.go | 10 +-- pkg/kubectl/resource/visitor.go | 2 +- pkg/kubectl/resource_printer.go | 3 +- pkg/kubectl/resource_printer_test.go | 3 +- pkg/kubectl/sorting_printer_test.go | 6 +- pkg/registry/thirdpartyresourcedata/codec.go | 1 - 40 files changed, 245 insertions(+), 232 deletions(-) diff --git a/pkg/kubectl/apply.go b/pkg/kubectl/apply.go index 9edbd6fa7377..3149bcb29daa 100644 --- a/pkg/kubectl/apply.go +++ b/pkg/kubectl/apply.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" ) type debugError interface { @@ -80,7 +81,7 @@ func SetOriginalConfiguration(info *resource.Info, original []byte) error { // If annotate is true, it embeds the result as an anotation in the modified // configuration. If an object was read from the command input, it will use that // version of the object. Otherwise, it will use the version from the server. -func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error) { +func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.Encoder) ([]byte, error) { // First serialize the object without the annotation to prevent recursion, // then add that serialization to it as the annotation and serialize it again. var modified []byte @@ -100,6 +101,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error original := annotations[LastAppliedConfigAnnotation] delete(annotations, LastAppliedConfigAnnotation) accessor.SetAnnotations(annotations) + // TODO: this needs to be abstracted - there should be no assumption that versioned object + // can be marshalled to JSON. modified, err = json.Marshal(info.VersionedObject) if err != nil { return nil, err @@ -108,6 +111,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error if annotate { annotations[LastAppliedConfigAnnotation] = string(modified) accessor.SetAnnotations(annotations) + // TODO: this needs to be abstracted - there should be no assumption that versioned object + // can be marshalled to JSON. modified, err = json.Marshal(info.VersionedObject) if err != nil { return nil, err @@ -136,7 +141,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error return nil, err } - modified, err = info.Mapping.Codec.Encode(info.Object) + modified, err = runtime.Encode(codec, info.Object) if err != nil { return nil, err } @@ -147,7 +152,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error return nil, err } - modified, err = info.Mapping.Codec.Encode(info.Object) + modified, err = runtime.Encode(codec, info.Object) if err != nil { return nil, err } @@ -165,17 +170,17 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error // UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied // configuration annotation is already present. Otherwise, it does nothing. -func UpdateApplyAnnotation(info *resource.Info) error { +func UpdateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error { if original, err := GetOriginalConfiguration(info); err != nil || len(original) <= 0 { return err } - return CreateApplyAnnotation(info) + return CreateApplyAnnotation(info, codec) } // CreateApplyAnnotation gets the modified configuration of the object, // without embedding it again, and then sets it on the object as the annotation. -func CreateApplyAnnotation(info *resource.Info) error { - modified, err := GetModifiedConfiguration(info, false) +func CreateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error { + modified, err := GetModifiedConfiguration(info, false, codec) if err != nil { return err } @@ -184,9 +189,9 @@ func CreateApplyAnnotation(info *resource.Info) error { // Create the annotation used by kubectl apply only when createAnnotation is true // Otherwise, only update the annotation when it already exists -func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info) error { +func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info, codec runtime.Encoder) error { if createAnnotation { - return CreateApplyAnnotation(info) + return CreateApplyAnnotation(info, codec) } - return UpdateApplyAnnotation(info) + return UpdateApplyAnnotation(info, codec) } diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 3f82db4ff527..95869a3d5342 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -151,7 +151,7 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra } mapper, typer := f.Object() - o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + o.builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, o.filenames...). @@ -211,7 +211,7 @@ func (o AnnotateOptions) RunAnnotate() error { } mapping := info.ResourceMapping() - client, err := o.f.RESTClient(mapping) + client, err := o.f.ClientForMapping(mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index a13165fc823a..ef63b040092a 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/strategicpatch" ) @@ -92,7 +93,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -104,6 +105,8 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap return err } + encoder := f.JSONEncoder() + count := 0 err = r.Visit(func(info *resource.Info, err error) error { // In this method, info.Object contains the object retrieved from the server @@ -115,7 +118,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap // Get the modified configuration of the object. Embed the result // as an annotation in the modified configuration, so that it will appear // in the patch sent to the server. - modified, err := kubectl.GetModifiedConfiguration(info, true) + modified, err := kubectl.GetModifiedConfiguration(info, true, encoder) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err) } @@ -126,7 +129,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap } // Create the resource if it doesn't exist // First, update the annotation used by kubectl apply - if err := kubectl.CreateApplyAnnotation(info); err != nil { + if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } // Then create the resource and skip the three-way merge @@ -139,7 +142,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap } // Serialize the current configuration of the object from the server. - current, err := info.Mapping.Codec.Encode(info.Object) + current, err := runtime.Encode(encoder, info.Object) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", info), info.Source, err) } diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index b8a14827faac..6647db011808 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -79,7 +79,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, filenames...). @@ -129,7 +129,12 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), + } hpa, err := resourceMapper.InfoForObject(object) if err != nil { return err @@ -139,7 +144,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return f.PrintObject(cmd, object, out) } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, f.JSONEncoder()); err != nil { return err } diff --git a/pkg/kubectl/cmd/clusterinfo.go b/pkg/kubectl/cmd/clusterinfo.go index a5adb2d33da5..c76e28f7c63f 100644 --- a/pkg/kubectl/cmd/clusterinfo.go +++ b/pkg/kubectl/cmd/clusterinfo.go @@ -45,25 +45,25 @@ func NewCmdClusterInfo(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func RunClusterInfo(factory *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { +func RunClusterInfo(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { if len(os.Args) > 1 && os.Args[1] == "clusterinfo" { printDeprecationWarning("cluster-info", "clusterinfo") } - client, err := factory.ClientConfig() + client, err := f.ClientConfig() if err != nil { return err } printService(out, "Kubernetes master", client.Host) - mapper, typer := factory.Object() + mapper, typer := f.Object() cmdNamespace := cmdutil.GetFlagString(cmd, "namespace") if cmdNamespace == "" { cmdNamespace = api.NamespaceSystem } // TODO use generalized labels once they are implemented (#341) - b := resource.NewBuilder(mapper, typer, factory.ClientMapperForCommand()). + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). SelectorParam("kubernetes.io/cluster-service=true"). ResourceTypeOrNameArgs(false, []string{"services"}...). diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 2fff13e9b918..4db40989c67a 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -38,6 +38,7 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/util" ) @@ -100,22 +101,21 @@ func versionErrIfFalse(b bool) error { } var validVersion = testapi.Default.GroupVersion().Version -var internalGV = unversioned.GroupVersion{Group: "apitest", Version: ""} +var internalGV = unversioned.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal} var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"} var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion} func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) { scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{}) scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{}) //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name. scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{}) - codec := runtime.CodecFor(scheme, unlikelyGV) + codecs := serializer.NewCodecFactory(scheme) + codec := codecs.LegacyCodec(unlikelyGV) mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { return &meta.VersionInterfaces{ - Codec: runtime.CodecFor(scheme, version), ObjectConvertor: scheme, MetadataAccessor: meta.NewAccessor(), }, versionErrIfFalse(version == validVersionGV || version == unlikelyGV) @@ -183,9 +183,15 @@ func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { Object: func() (meta.RESTMapper, runtime.ObjectTyper) { return t.Mapper, t.Typer }, - RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) { + ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) { return t.Client, t.Err }, + Decoder: func(bool) runtime.Decoder { + return codec + }, + JSONEncoder: func() runtime.Encoder { + return codec + }, Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, @@ -209,7 +215,7 @@ func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFact f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { return meta.MultiRESTMapper{t.Mapper, testapi.Default.RESTMapper()}, runtime.MultiObjectTyper{t.Typer, api.Scheme} } - f.RESTClient = func(m *meta.RESTMapping) (resource.RESTClient, error) { + f.ClientForMapping = func(m *meta.RESTMapping) (resource.RESTClient, error) { if m.ObjectConvertor == api.Scheme { return apiClient, t.Err } @@ -235,9 +241,15 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { c.ExtensionsClient.Client = fakeClient.Client return c, t.Err }, - RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) { + ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) { return t.Client, t.Err }, + Decoder: func(bool) runtime.Decoder { + return testapi.Default.Codec() + }, + JSONEncoder: func() runtime.Encoder { + return testapi.Default.Codec() + }, Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, @@ -303,7 +315,7 @@ func stringBody(body string) io.ReadCloser { // mapping := &meta.RESTMapping{ // APIVersion: version, // } -// c, err := f.RESTClient(mapping) +// c, err := f.ClientForMapping(mapping) // if err != nil { // t.Errorf("unexpected error: %v", err) // } diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index 694a005cd959..e7e04d5b99df 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "github.com/spf13/cobra" ) @@ -87,6 +88,7 @@ type ConvertOptions struct { filenames []string local bool + encoder runtime.Encoder out io.Writer printer kubectl.ResourcePrinter @@ -105,11 +107,12 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra. // build the builder mapper, typer := f.Object() + clientMapper := resource.ClientMapperFunc(f.ClientForMapping) if o.local { fmt.Fprintln(out, "running in local mode...") - o.builder = resource.NewBuilder(mapper, typer, f.NilClientMapperForCommand()) + o.builder = resource.NewBuilder(mapper, typer, resource.DisabledClientForMapping{clientMapper}, f.Decoder(true)) } else { - o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()) + o.builder = resource.NewBuilder(mapper, typer, clientMapper, f.Decoder(true)) schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) if err != nil { return err @@ -136,6 +139,7 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra. outputFormat = "template" } } + o.encoder = f.JSONEncoder() o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile) if err != nil { return err @@ -151,7 +155,7 @@ func (o *ConvertOptions) RunConvert() error { return err } - objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String()) + objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String(), o.encoder) if err != nil { return err } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 6b887a076272..10b30ba4ef31 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -99,7 +99,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -116,7 +116,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C if err != nil { return err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } @@ -218,16 +218,20 @@ func RunCreateSubcommand(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, if err != nil { return err } - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return err } - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + } info, err := resourceMapper.InfoForObject(obj) if err != nil { return err } - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.UpdateApplyAnnotation(info, f.JSONEncoder()); err != nil { return err } if !options.DryRun { diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 6bac41cdec61..94973d6e584b 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -108,7 +108,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } deleteAll := cmdutil.GetFlagBool(cmd, "all") mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 7fa4dedf196c..3ea83ecdc4fa 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -106,7 +106,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -147,7 +147,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s } func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(namespace).DefaultNamespace(). ResourceTypeOrNameArgs(true, rsrc). SingleResourceType(). diff --git a/pkg/kubectl/cmd/drain.go b/pkg/kubectl/cmd/drain.go index db09620e1e1f..8c07a18281a0 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -209,8 +209,7 @@ func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) { if found { // Now verify that the specified creator actually exists. var sr api.SerializedReference - err := api.Scheme.DecodeInto([]byte(creatorRef), &sr) - if err != nil { + if err := runtime.DecodeInto(o.factory.Decoder(true), []byte(creatorRef), &sr); err != nil { return pods, err } if sr.Reference.Kind == "ReplicationController" { diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index 350362060437..e98407bf8461 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -23,7 +23,7 @@ import ( "fmt" "io" "os" - "runtime" + gruntime "runtime" "strings" "k8s.io/kubernetes/pkg/api" @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" "k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/strategicpatch" "k8s.io/kubernetes/pkg/util/yaml" @@ -94,7 +95,7 @@ func NewCmdEdit(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") - cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") + cmd.Flags().Bool("windows-line-endings", gruntime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -119,13 +120,14 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin } mapper, typer := f.Object() - rmap := &resource.Mapper{ + resourceMapper := &resource.Mapper{ ObjectTyper: typer, RESTMapper: mapper, - ClientMapper: f.ClientMapperForCommand(), + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), } - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, filenames...). ResourceTypeOrNameArgs(true, args...). @@ -147,6 +149,8 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin return err } + encoder := f.JSONEncoder() + windowsLineEndings := cmdutil.GetFlagBool(cmd, "windows-line-endings") edit := editor.NewDefaultEditor(f.EditorEnvs()) defaultVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) @@ -155,7 +159,7 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin } results := editResults{} for { - objs, err := resource.AsVersionedObjects(infos, defaultVersion.String()) + objs, err := resource.AsVersionedObjects(infos, defaultVersion.String(), encoder) if err != nil { return preservedFile(err, results.file, out) } @@ -219,21 +223,21 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin } // parse the edited file - updates, err := rmap.InfoForData(edited, "edited-file") + updates, err := resourceMapper.InfoForData(edited, "edited-file") if err != nil { return fmt.Errorf("The edited file had a syntax error: %v", err) } // put configuration annotation in "updates" - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates, encoder); err != nil { return preservedFile(err, file, out) } // encode updates back to "edited" since we'll only generate patch from "edited" - if edited, err = updates.Mapping.Codec.Encode(updates.Object); err != nil { + if edited, err = runtime.Encode(encoder, updates.Object); err != nil { return preservedFile(err, file, out) } - visitor := resource.NewFlattenListVisitor(updates, rmap) + visitor := resource.NewFlattenListVisitor(updates, resourceMapper) // need to make sure the original namespace wasn't changed while editing if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil { diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 382a6c098e46..054697131649 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation" ) @@ -104,7 +105,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -192,13 +193,19 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 { - object, err = cmdutil.Merge(object, inline, mapping.GroupVersionKind.Kind) + codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) + object, err = cmdutil.Merge(codec, object, inline, mapping.GroupVersionKind.Kind) if err != nil { return err } } - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), + } info, err = resourceMapper.InfoForObject(object) if err != nil { return err @@ -207,7 +214,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str if cmdutil.GetFlagBool(cmd, "dry-run") { return f.PrintObject(cmd, object, out) } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return err } diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index e4c3f009b2e4..284e3555052c 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -137,7 +137,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string // handle watch separately since we cannot watch multiple resource types isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") if isWatch || isWatchOnly { - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). @@ -192,7 +192,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string return nil } - b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). @@ -224,7 +224,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string if err != nil { return err } - obj, err := resource.AsVersionedObject(infos, !singular, version.String()) + obj, err := resource.AsVersionedObject(infos, !singular, version.String(), f.JSONEncoder()) if err != nil { return err } @@ -244,7 +244,8 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string sorting, err := cmd.Flags().GetString("sort-by") var sorter *kubectl.RuntimeSort if err == nil && len(sorting) > 0 { - if sorter, err = kubectl.SortObjects(objs, sorting); err != nil { + // TODO: questionable + if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil { return err } } diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 7cedaf51786d..d8ba37e2b91c 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -177,7 +177,7 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) { }, } for k, test := range testCases { - apiCodec := runtime.CodecFor(api.Scheme, *testapi.Default.GroupVersion()) + apiCodec := testapi.Default.Codec() regularClient := &fake.RESTClient{ Codec: apiCodec, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { @@ -482,7 +482,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 { + if errs := runtime.DecodeList(list, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := meta.SetList(out, list); err != nil { diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index d2952d201fa0..23f7b605644d 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -201,7 +201,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return cmdutil.UsageError(cmd, err.Error()) } mapper, typer := f.Object() - b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -264,7 +264,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } mapping := info.ResourceMapping() - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/logs.go b/pkg/kubectl/cmd/logs.go index 5f1b47c6a526..369d41d8a96b 100644 --- a/pkg/kubectl/cmd/logs.go +++ b/pkg/kubectl/cmd/logs.go @@ -59,6 +59,7 @@ type LogsOptions struct { Mapper meta.RESTMapper Typer runtime.ObjectTyper ClientMapper resource.ClientMapper + Decoder runtime.Decoder LogsForObject func(object, options runtime.Object) (*client.Request, error) @@ -151,7 +152,8 @@ func (o *LogsOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Com o.Options = logOptions o.Mapper, o.Typer = f.Object() - o.ClientMapper = f.ClientMapperForCommand() + o.Decoder = f.Decoder(true) + o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping) o.LogsForObject = f.LogsForObject o.Out = out @@ -176,7 +178,7 @@ func (o LogsOptions) Validate() error { // RunLogs retrieves a pod log func (o LogsOptions) RunLogs() (int64, error) { - infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper). + infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, o.Decoder). NamespaceParam(o.Namespace).DefaultNamespace(). ResourceNames("pods", o.ResourceArg). SingleResourceType(). diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index 9a971d729781..b537adaa186c 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -92,7 +92,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -114,7 +114,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri info := infos[0] name, namespace := info.Name, info.Namespace mapping := info.ResourceMapping() - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index a076e91f9abe..226e5c434d0b 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -112,7 +112,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -129,7 +129,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st return err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return cmdutil.AddSourceToErr("replacing", info.Source, err) } @@ -174,7 +174,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -198,7 +198,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - r = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -216,7 +216,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return err } diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index fb11b9a3cf7f..bf4015b6e624 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -29,7 +29,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -198,7 +197,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg return err } - request := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + request := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, filename). @@ -385,12 +384,9 @@ func isReplicasDefaulted(info *resource.Info) bool { // was unable to recover versioned info return false } - - switch info.Mapping.GroupVersionKind.GroupVersion() { - case unversioned.GroupVersion{Version: "v1"}: - if rc, ok := info.VersionedObject.(*v1.ReplicationController); ok { - return rc.Spec.Replicas == nil - } + switch t := info.VersionedObject.(type) { + case *v1.ReplicationController: + return t.Spec.Replicas == nil } return false } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index a87efcee0d6d..8affcbda870f 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -236,7 +236,7 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob return err } _, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). ResourceNames(mapping.Resource, name). @@ -409,7 +409,8 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub } if len(overrides) > 0 { - obj, err = cmdutil.Merge(obj, overrides, groupVersionKind.Kind) + codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) + obj, err = cmdutil.Merge(codec, obj, overrides, groupVersionKind.Kind) if err != nil { return nil, "", nil, nil, err } @@ -419,20 +420,25 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub if err != nil { return nil, "", nil, nil, err } - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return nil, "", nil, nil, err } // TODO: extract this flag to a central location, when such a location exists. if !cmdutil.GetFlagBool(cmd, "dry-run") { - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), + } info, err := resourceMapper.InfoForObject(obj) if err != nil { return nil, "", nil, nil, err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return nil, "", nil, nil, err } diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index 18e9fedbffdc..2637fa2b0af0 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -105,7 +105,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index c6673fa481ee..6069866171af 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -85,7 +85,7 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Write } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, args...). diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 66f9ebfab120..3d52e44eba12 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -45,6 +45,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" "k8s.io/kubernetes/pkg/util" ) @@ -63,13 +64,19 @@ type Factory struct { // Returns interfaces for dealing with arbitrary runtime.Objects. Object func() (meta.RESTMapper, runtime.ObjectTyper) + // Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted + // into their internal form (if possible). Eventually the internal form will be removed as an option, + // and only versioned objects will be returned. + Decoder func(toInternal bool) runtime.Decoder + // Returns an encoder capable of encoding a provided object into JSON in the default desired version. + JSONEncoder func() runtime.Encoder // Returns a client for accessing Kubernetes resources or an error. Client func() (*client.Client, error) // Returns a client.Config for accessing the Kubernetes server. ClientConfig func() (*client.Config, error) // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. - RESTClient func(mapping *meta.RESTMapping) (resource.RESTClient, error) + ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) // Returns a Describer for displaying the specified RESTMapping type or an error. Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error) // Returns a Printer for formatting objects of the given type or an error. @@ -185,7 +192,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { ClientConfig: func() (*client.Config, error) { return clients.ClientConfigForVersion(nil) }, - RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { + ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { @@ -210,6 +217,15 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, + Decoder: func(toInternal bool) runtime.Decoder { + if toInternal { + return api.Codecs.UniversalDecoder() + } + return api.Codecs.UniversalDeserializer() + }, + JSONEncoder: func() runtime.Encoder { + return api.Codecs.LegacyCodec(registered.EnabledVersions()...) + }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil }, @@ -531,7 +547,7 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac } func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { - gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) + gvk, err := json.DefaultMetaFactory.Interpret(data) if err != nil { return err } @@ -663,24 +679,9 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin return printer, nil } -// ClientMapperForCommand returns a ClientMapper for the factory. -func (f *Factory) ClientMapperForCommand() resource.ClientMapper { - return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return f.RESTClient(mapping) - }) -} - -// NilClientMapperForCommand returns a ClientMapper which always returns nil. -// When command is running locally and client isn't needed, this mapper can be parsed to NewBuilder. -func (f *Factory) NilClientMapperForCommand() resource.ClientMapper { - return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return nil, nil - }) -} - // One stop shopping for a Builder func (f *Factory) NewBuilder() *resource.Builder { mapper, typer := f.Object() - return resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()) + return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)) } diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index 7818e7b9049b..be9484fae3c1 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -245,7 +245,7 @@ func TestValidateCachesSchema(t *testing.T) { os.RemoveAll(dir) obj := &api.Pod{} - data, err := testapi.Default.Codec().Encode(obj) + data, err := runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("unexpected error: %v", err) t.FailNow() diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 67e3b01c018b..72327493460e 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -18,7 +18,6 @@ package util import ( "bytes" - "encoding/json" "fmt" "io" "io/ioutil" @@ -28,10 +27,8 @@ import ( "strings" "time" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" @@ -371,38 +368,11 @@ func ReadConfigDataFromLocation(location string) ([]byte, error) { } } -func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) { - // Ok, this is a little hairy, we'd rather not force the user to specify a kind for their JSON - // So we pull it into a map, add the Kind field, and then reserialize. - // We also pull the apiVersion for proper parsing - var intermediate interface{} - if err := json.Unmarshal([]byte(fragment), &intermediate); err != nil { - return nil, err - } - dataMap, ok := intermediate.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("Expected a map, found something else: %s", fragment) - } - version, found := dataMap["apiVersion"] - if !found { - return nil, fmt.Errorf("Inline JSON requires an apiVersion field") - } - versionString, ok := version.(string) - if !ok { - return nil, fmt.Errorf("apiVersion must be a string") - } - groupVersion, err := unversioned.ParseGroupVersion(versionString) - if err != nil { - return nil, err - } - - i, err := registered.GroupOrDie(api.GroupName).InterfacesFor(groupVersion) - if err != nil { - return nil, err - } - +// Merge requires JSON serialization +// TODO: merge assumes JSON serialization, and does not properly abstract API retrieval +func Merge(codec runtime.Codec, dst runtime.Object, fragment, kind string) (runtime.Object, error) { // encode dst into versioned json and apply fragment directly too it - target, err := i.Codec.Encode(dst) + target, err := runtime.Encode(codec, dst) if err != nil { return nil, err } @@ -410,7 +380,7 @@ func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) { if err != nil { return nil, err } - out, err := i.Codec.Decode(patched) + out, err := runtime.Decode(codec, patched) if err != nil { return nil, err } @@ -443,7 +413,7 @@ func DumpReaderToFile(reader io.Reader, filename string) error { } // UpdateObject updates resource object with updateFn -func UpdateObject(info *resource.Info, updateFn func(runtime.Object) error) (runtime.Object, error) { +func UpdateObject(info *resource.Info, codec runtime.Codec, updateFn func(runtime.Object) error) (runtime.Object, error) { helper := resource.NewHelper(info.Client, info.Mapping) if err := updateFn(info.Object); err != nil { @@ -451,7 +421,7 @@ func UpdateObject(info *resource.Info, updateFn func(runtime.Object) error) (run } // Update the annotation used by kubectl apply - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.UpdateApplyAnnotation(info, codec); err != nil { return nil, err } diff --git a/pkg/kubectl/cmd/util/helpers_test.go b/pkg/kubectl/cmd/util/helpers_test.go index 3c878b4a01c7..598c3e961e2b 100644 --- a/pkg/kubectl/cmd/util/helpers_test.go +++ b/pkg/kubectl/cmd/util/helpers_test.go @@ -183,7 +183,7 @@ func TestMerge(t *testing.T) { } for i, test := range tests { - out, err := Merge(test.obj, test.fragment, test.kind) + out, err := Merge(testapi.Default.Codec(), test.obj, test.fragment, test.kind) if !test.expectErr { if err != nil { t.Errorf("testcase[%d], unexpected error: %v", i, err) diff --git a/pkg/kubectl/custom_column_printer.go b/pkg/kubectl/custom_column_printer.go index 7d91691f734a..431a114c7489 100644 --- a/pkg/kubectl/custom_column_printer.go +++ b/pkg/kubectl/custom_column_printer.go @@ -26,7 +26,6 @@ import ( "strings" "text/tabwriter" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/jsonpath" @@ -152,6 +151,7 @@ type Column struct { // of data from templates specified in the `Columns` array type CustomColumnsPrinter struct { Columns []Column + Decoder runtime.Decoder } func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error { @@ -192,7 +192,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso switch u := obj.(type) { case *runtime.Unknown: var err error - if obj, err = api.Codec.Decode(u.RawJSON); err != nil { + if obj, _, err = s.Decoder.Decode(u.RawJSON, nil, nil); err != nil { return err } } diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index a9dd31267e29..61b0f1131847 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -80,9 +80,9 @@ type resourceTuple struct { } // NewBuilder creates a builder that operates on generic objects. -func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper) *Builder { +func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder { return &Builder{ - mapper: &Mapper{typer, mapper, clientMapper}, + mapper: &Mapper{typer, mapper, clientMapper, decoder}, requireObject: true, } } diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index 2ee364b05c54..1fad662b63fe 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/api/testapi" apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -177,9 +178,9 @@ func (v *testVisitor) Objects() []runtime.Object { return objects } -func TestPathBuilder(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml") +func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) { + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). + FilenameParam(false, "../../../docs/user-guide/update-demo/kitten-rc.yaml") test := &testVisitor{} singular := false @@ -190,9 +191,14 @@ func TestPathBuilder(t *testing.T) { } info := test.Infos[0] - if info.Name != "redis-master" || info.Namespace != "" || info.Object == nil { + if info.Name != "update-demo-kitten" || info.Namespace != "" || info.Object == nil { t.Errorf("unexpected info: %#v", info) } + version, ok := info.VersionedObject.(*v1.ReplicationController) + // versioned object does not have defaulting applied + if info.VersionedObject == nil || !ok || version.Spec.Replicas != nil { + t.Errorf("unexpected versioned object: %#v", info.VersionedObject) + } } func TestNodeBuilder(t *testing.T) { @@ -212,7 +218,7 @@ func TestNodeBuilder(t *testing.T) { w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), node))) }() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN") test := &testVisitor{} @@ -228,7 +234,7 @@ func TestNodeBuilder(t *testing.T) { } func TestPathBuilderWithMultiple(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). FilenameParam(false, "../../../examples/pod"). NamespaceParam("test").DefaultNamespace() @@ -252,7 +258,7 @@ func TestPathBuilderWithMultiple(t *testing.T) { } func TestDirectoryBuilder(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, "../../../examples/guestbook"). NamespaceParam("test").DefaultNamespace() @@ -283,7 +289,7 @@ func TestNamespaceOverride(t *testing.T) { // TODO: Uncomment when fix #19254 // defer s.Close() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). NamespaceParam("test") @@ -294,7 +300,7 @@ func TestNamespaceOverride(t *testing.T) { t.Fatalf("unexpected response: %v %#v", err, test.Infos) } - b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(true, s.URL). NamespaceParam("test") @@ -314,7 +320,7 @@ func TestURLBuilder(t *testing.T) { // TODO: Uncomment when fix #19254 // defer s.Close() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). NamespaceParam("test") @@ -339,7 +345,7 @@ func TestURLBuilderRequireNamespace(t *testing.T) { // TODO: Uncomment when fix #19254 // defer s.Close() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). NamespaceParam("test").RequireNamespace() @@ -356,7 +362,7 @@ func TestResourceByName(t *testing.T) { pods, _ := testData() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -392,7 +398,7 @@ func TestMultipleResourceByTheSameName(t *testing.T) { "/namespaces/test/pods/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[1]), "/namespaces/test/services/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -422,7 +428,7 @@ func TestResourceNames(t *testing.T) { b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -446,7 +452,7 @@ func TestResourceNames(t *testing.T) { } func TestResourceByNameWithoutRequireObject(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{})). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{}), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -482,7 +488,7 @@ func TestResourceByNameAndEmptySelector(t *testing.T) { pods, _ := testData() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test"). SelectorParam(""). ResourceTypeOrNameArgs(true, "pods", "foo") @@ -511,7 +517,7 @@ func TestSelector(t *testing.T) { b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), - })). + }), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). Flatten() @@ -539,7 +545,7 @@ func TestSelector(t *testing.T) { } func TestSelectorRequiresKnownTypes(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). ResourceTypes("unknown") @@ -550,7 +556,7 @@ func TestSelectorRequiresKnownTypes(t *testing.T) { } func TestSingleResourceType(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). SelectorParam("a=b"). SingleResourceType(). ResourceTypeOrNameArgs(true, "pods,services") @@ -620,7 +626,7 @@ func TestResourceTuple(t *testing.T) { } } - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests)). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). ResourceTypeOrNameArgs(true, testCase.args...).RequireObject(requireObject) @@ -651,7 +657,7 @@ func TestResourceTuple(t *testing.T) { func TestStream(t *testing.T) { r, pods, rc := streamTestData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN").Flatten() test := &testVisitor{} @@ -668,7 +674,7 @@ func TestStream(t *testing.T) { func TestYAMLStream(t *testing.T) { r, pods, rc := streamYAMLTestData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN").Flatten() test := &testVisitor{} @@ -685,7 +691,7 @@ func TestYAMLStream(t *testing.T) { func TestMultipleObject(t *testing.T) { r, pods, svc := streamTestData() - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN").Flatten(). Do().Object() @@ -707,7 +713,7 @@ func TestMultipleObject(t *testing.T) { func TestContinueOnErrorVisitor(t *testing.T) { r, _, _ := streamTestData() - req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). ContinueOnError(). NamespaceParam("test").Stream(r, "STDIN").Flatten(). Do() @@ -736,7 +742,7 @@ func TestContinueOnErrorVisitor(t *testing.T) { } func TestSingularObject(t *testing.T) { - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). Flatten(). @@ -756,7 +762,7 @@ func TestSingularObject(t *testing.T) { } func TestSingularObjectNoExtension(t *testing.T) { - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/pod"). Flatten(). @@ -778,7 +784,7 @@ func TestSingularObjectNoExtension(t *testing.T) { func TestSingularRootScopedObject(t *testing.T) { node := &api.Node{ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.NodeSpec{ExternalID: "test"}} r := streamTestObject(node) - infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). Stream(r, "STDIN"). Flatten(). @@ -805,7 +811,7 @@ func TestListObject(t *testing.T) { labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), - })). + }), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). ResourceTypeOrNameArgs(true, "pods"). @@ -839,7 +845,7 @@ func TestListObjectWithDifferentVersions(t *testing.T) { obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), - })). + }), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). ResourceTypeOrNameArgs(true, "pods,services"). @@ -867,7 +873,7 @@ func TestWatch(t *testing.T) { Type: watch.Added, Object: &svc.Items[0], }), - })). + }), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten(). Do().Watch("12") @@ -894,7 +900,7 @@ func TestWatch(t *testing.T) { } func TestWatchMultipleError(t *testing.T) { - _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). @@ -921,7 +927,7 @@ func TestLatest(t *testing.T) { "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), newPod), "/namespaces/test/pods/bar": runtime.EncodeOrDie(testapi.Default.Codec(), newPod2), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), newSvc), - })). + }), testapi.Default.Codec()). NamespaceParam("other").Stream(r, "STDIN").Flatten().Latest() test := &testVisitor{} @@ -953,7 +959,7 @@ func TestReceiveMultipleErrors(t *testing.T) { w2.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]))) }() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). Stream(r, "1").Stream(r2, "2"). ContinueOnError() @@ -997,7 +1003,7 @@ func TestReplaceAliases(t *testing.T) { }, } - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()) + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()) for _, test := range tests { replaced := b.replaceAliases(test.arg) diff --git a/pkg/kubectl/resource/helper.go b/pkg/kubectl/resource/helper.go index 85f7bc1946ca..fcce5b0cae6e 100644 --- a/pkg/kubectl/resource/helper.go +++ b/pkg/kubectl/resource/helper.go @@ -33,8 +33,6 @@ type Helper struct { Resource string // A RESTClient capable of mutating this resource. RESTClient RESTClient - // A codec for decoding and encoding objects of this resource type. - Codec runtime.Codec // An interface for reading or writing the resource version of this // type. Versioner runtime.ResourceVersioner @@ -45,9 +43,8 @@ type Helper struct { // NewHelper creates a Helper from a ResourceMapping func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper { return &Helper{ - RESTClient: client, Resource: mapping.Resource, - Codec: mapping.Codec, + RESTClient: client, Versioner: mapping.MetadataAccessor, NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace, } diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 2bdc977b0ffa..541daf880f45 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -189,7 +189,6 @@ func TestHelperCreate(t *testing.T) { } modifier := &Helper{ RESTClient: client, - Codec: testapi.Default.Codec(), Versioner: testapi.Default.MetadataAccessor(), NamespaceScoped: true, } @@ -441,7 +440,6 @@ func TestHelperReplace(t *testing.T) { } modifier := &Helper{ RESTClient: client, - Codec: testapi.Default.Codec(), Versioner: testapi.Default.MetadataAccessor(), NamespaceScoped: true, } diff --git a/pkg/kubectl/resource/interfaces.go b/pkg/kubectl/resource/interfaces.go index 3d64609984a6..54d20dfbfffb 100644 --- a/pkg/kubectl/resource/interfaces.go +++ b/pkg/kubectl/resource/interfaces.go @@ -32,7 +32,7 @@ type RESTClient interface { Put() *client.Request } -// ClientMapper retrieves a client object for a given mapping +// ClientMapper abstracts retrieving a Client for mapped objects. type ClientMapper interface { ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) } diff --git a/pkg/kubectl/resource/mapper.go b/pkg/kubectl/resource/mapper.go index 2c61325bddc3..0839ca4a8ea1 100644 --- a/pkg/kubectl/resource/mapper.go +++ b/pkg/kubectl/resource/mapper.go @@ -20,69 +20,63 @@ import ( "fmt" "reflect" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/yaml" ) +// DisabledClientForMapping allows callers to avoid allowing remote calls when handling +// resources. +type DisabledClientForMapping struct { + ClientMapper +} + +func (f DisabledClientForMapping) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) { + return nil, nil +} + // Mapper is a convenience struct for holding references to the three interfaces // needed to create Info for arbitrary objects. type Mapper struct { runtime.ObjectTyper meta.RESTMapper ClientMapper + runtime.Decoder } // InfoForData creates an Info object for the given data. An error is returned // if any of the decoding or client lookup steps fail. Name and namespace will be // set into Info if the mapping's MetadataAccessor can retrieve them. func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { - json, err := yaml.ToJSON(data) - if err != nil { - return nil, fmt.Errorf("unable to parse %q: %v", source, err) - } - data = json - gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) + versions := &runtime.VersionedObjects{} + _, gvk, err := m.Decode(data, nil, versions) if err != nil { - return nil, fmt.Errorf("unable to get type info from %q: %v", source, err) - } - if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok { - return nil, fmt.Errorf("API version %q in %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), source, registered.EnabledVersions()) - } - if gvk.Kind == "" { - return nil, fmt.Errorf("kind not set in %q", source) + return nil, fmt.Errorf("unable to decode %q: %v", source, err) } mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version) if err != nil { return nil, fmt.Errorf("unable to recognize %q: %v", source, err) } - obj, err := mapping.Codec.Decode(data) - if err != nil { - return nil, fmt.Errorf("unable to load %q: %v", source, err) - } + client, err := m.ClientForMapping(mapping) if err != nil { return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err) } + // TODO: decoding the version object is convenient, but questionable. This is used by apply + // and rolling-update today, but both of those cases should probably be requesting the raw + // object and performing their own decoding. + obj, versioned := versions.Last(), versions.First() name, _ := mapping.MetadataAccessor.Name(obj) namespace, _ := mapping.MetadataAccessor.Namespace(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) - var versionedObject interface{} - - if vo, _, err := api.Scheme.Raw().DecodeToVersionedObject(data); err == nil { - versionedObject = vo - } return &Info{ Mapping: mapping, Client: client, Namespace: namespace, Name: name, Source: source, - VersionedObject: versionedObject, + VersionedObject: versioned, Object: obj, ResourceVersion: resourceVersion, }, nil diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index be6b4d5e051c..8d726ab7b732 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -210,8 +210,8 @@ func (r *Result) Watch(resourceVersion string) (watch.Interface, error) { // the objects as children, or if only a single Object is present, as that object. The provided // version will be preferred as the conversion target, but the Object's mapping version will be // used if that version is not present. -func AsVersionedObject(infos []*Info, forceList bool, version string) (runtime.Object, error) { - objects, err := AsVersionedObjects(infos, version) +func AsVersionedObject(infos []*Info, forceList bool, version string, encoder runtime.Encoder) (runtime.Object, error) { + objects, err := AsVersionedObjects(infos, version, encoder) if err != nil { return nil, err } @@ -233,19 +233,21 @@ func AsVersionedObject(infos []*Info, forceList bool, version string) (runtime.O // AsVersionedObjects converts a list of infos into versioned objects. The provided // version will be preferred as the conversion target, but the Object's mapping version will be // used if that version is not present. -func AsVersionedObjects(infos []*Info, version string) ([]runtime.Object, error) { +func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder) ([]runtime.Object, error) { objects := []runtime.Object{} for _, info := range infos { if info.Object == nil { continue } + // TODO: use info.VersionedObject as the value? + // objects that are not part of api.Scheme must be converted to JSON // TODO: convert to map[string]interface{}, attach to runtime.Unknown? if len(version) > 0 { if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) { // TODO: ideally this would encode to version, but we don't expose multiple codecs here. - data, err := info.Mapping.Codec.Encode(info.Object) + data, err := runtime.Encode(encoder, info.Object) if err != nil { return nil, err } diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index 5054a7238528..a187919a4142 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -345,7 +345,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error { if errs := runtime.DecodeList(items, struct { runtime.ObjectTyper runtime.Decoder - }{v.Mapper, info.Mapping.Codec}); len(errs) > 0 { + }{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 { return utilerrors.NewAggregate(errs) } for i := range items { diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 637fc1d7ba70..974cff0dfc42 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -33,7 +33,6 @@ import ( "github.com/ghodss/yaml" "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" @@ -68,7 +67,7 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) { case "name": printer = &NamePrinter{ Typer: runtime.ObjectTyperToTyper(api.Scheme), - Decoder: latest.Codecs.UniversalDecoder(), + Decoder: api.Codecs.UniversalDecoder(), } case "template", "go-template": if len(formatArgument) == 0 { diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index 3dc43870b75a..a60eae592923 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -27,7 +27,6 @@ import ( "time" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" @@ -466,7 +465,7 @@ func TestPrinters(t *testing.T) { "jsonpath": jsonpathPrinter, "name": &NamePrinter{ Typer: runtime.ObjectTyperToTyper(api.Scheme), - Decoder: latest.Codecs.UniversalDecoder(), + Decoder: api.Codecs.UniversalDecoder(), }, } objects := map[string]runtime.Object{ diff --git a/pkg/kubectl/sorting_printer_test.go b/pkg/kubectl/sorting_printer_test.go index d62acacf07c0..f1d1d0c65cbd 100644 --- a/pkg/kubectl/sorting_printer_test.go +++ b/pkg/kubectl/sorting_printer_test.go @@ -20,13 +20,13 @@ import ( "reflect" "testing" - "k8s.io/kubernetes/pkg/api/latest" + internal "k8s.io/kubernetes/pkg/api" api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" ) func encodeOrDie(obj runtime.Object) []byte { - data, err := runtime.Encode(latest.Codecs.LegacyCodec(api.SchemeGroupVersion), obj) + data, err := runtime.Encode(internal.Codecs.LegacyCodec(api.SchemeGroupVersion), obj) if err != nil { panic(err.Error()) } @@ -224,7 +224,7 @@ func TestSortingPrinter(t *testing.T) { }, } for _, test := range tests { - sort := &SortingPrinter{SortField: test.field, Decoder: latest.Codecs.UniversalDecoder()} + sort := &SortingPrinter{SortField: test.field, Decoder: internal.Codecs.UniversalDecoder()} if err := sort.sortObj(test.obj); err != nil { t.Errorf("unexpected error: %v (%s)", err, test.name) continue diff --git a/pkg/registry/thirdpartyresourcedata/codec.go b/pkg/registry/thirdpartyresourcedata/codec.go index 9293af0f2cdd..33ee1094520c 100644 --- a/pkg/registry/thirdpartyresourcedata/codec.go +++ b/pkg/registry/thirdpartyresourcedata/codec.go @@ -25,7 +25,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/registered" "k8s.io/kubernetes/pkg/api/unversioned" apiutil "k8s.io/kubernetes/pkg/api/util" "k8s.io/kubernetes/pkg/apimachinery/registered" From 33085c0cf28245fa6bf5a00e0de47ff6a302a6a6 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 22 Jan 2016 00:06:52 -0500 Subject: [PATCH 15/17] Update tests to handle codec changes --- contrib/mesos/pkg/executor/executor_test.go | 4 +- examples/examples_test.go | 6 +- pkg/api/testapi/testapi_test.go | 63 +++++++++++++++++++ .../testing/compat/compatibility_tester.go | 1 + .../registered/registered_test.go | 62 ------------------ .../componentconfig/install/install_test.go | 1 + pkg/apiserver/resthandler_test.go | 1 - pkg/client/unversioned/client_test.go | 2 - .../unversioned/helper_blackbox_test.go | 2 +- pkg/controller/controller_utils_test.go | 2 +- ...tentvolume_claim_binder_controller_test.go | 12 ++-- pkg/conversion/unversioned_test.go | 1 + pkg/kubectl/cmd/drain_test.go | 2 +- pkg/kubelet/config/common_test.go | 28 ++++----- pkg/kubelet/config/file_test.go | 2 +- pkg/kubelet/config/http_test.go | 2 +- pkg/kubelet/dockertools/labels_test.go | 5 +- pkg/kubelet/dockertools/manager_test.go | 3 +- pkg/storage/etcd/etcd_helper_test.go | 35 +++++++---- pkg/storage/etcd/etcd_watcher_test.go | 4 ++ pkg/util/jsonpath/jsonpath_test.go | 4 +- pkg/util/yaml/decoder_test.go | 2 +- pkg/volume/aws_ebs/aws_ebs_test.go | 7 +-- pkg/volume/fc/fc_test.go | 7 +-- pkg/volume/gce_pd/gce_pd_test.go | 7 +-- pkg/volume/glusterfs/glusterfs_test.go | 8 +-- pkg/volume/host_path/host_path_test.go | 7 +-- pkg/volume/iscsi/iscsi_test.go | 7 +-- pkg/volume/nfs/nfs_test.go | 7 +-- .../persistent_claim/persistent_claim_test.go | 12 +--- pkg/volume/rbd/rbd_test.go | 7 +-- pkg/watch/json/decoder_test.go | 2 +- .../defaults/compatibility_test.go | 7 +-- plugin/pkg/scheduler/factory/factory_test.go | 6 +- 34 files changed, 147 insertions(+), 181 deletions(-) diff --git a/contrib/mesos/pkg/executor/executor_test.go b/contrib/mesos/pkg/executor/executor_test.go index 139042a7a49e..9e07281531c8 100644 --- a/contrib/mesos/pkg/executor/executor_test.go +++ b/contrib/mesos/pkg/executor/executor_test.go @@ -211,7 +211,7 @@ func TestExecutorLaunchAndKillTask(t *testing.T) { taskInfo, err := podTask.BuildTaskInfo() assert.Equal(t, nil, err, "must be able to build task info") - data, err := testapi.Default.Codec().Encode(pod) + data, err := runtime.Encode(testapi.Default.Codec(), pod) assert.Equal(t, nil, err, "must be able to encode a pod's spec data") taskInfo.Data = data @@ -410,7 +410,7 @@ func TestExecutorFrameworkMessage(t *testing.T) { taskInfo, err := podTask.BuildTaskInfo() assert.Equal(t, nil, err, "must be able to build task info") - data, _ := testapi.Default.Codec().Encode(pod) + data, _ := runtime.Encode(testapi.Default.Codec(), pod) taskInfo.Data = data mockDriver.On( diff --git a/examples/examples_test.go b/examples/examples_test.go index 6ec81b83f126..f6920945d1b5 100644 --- a/examples/examples_test.go +++ b/examples/examples_test.go @@ -421,7 +421,7 @@ func TestExampleObjectSchemas(t *testing.T) { return } if strings.Contains(name, "scheduler-policy-config") { - if err := schedulerapilatest.Codec.DecodeInto(data, expectedType); err != nil { + if err := runtime.DecodeInto(schedulerapilatest.Codec, data, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) return } @@ -516,14 +516,14 @@ func TestReadme(t *testing.T) { if err != nil { t.Errorf("%s could not be converted to JSON: %v\n%s", path, err, string(content)) } - if err := testapi.Default.Codec().DecodeInto(json, expectedType); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), json, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content)) continue } if errors := validateObject(expectedType); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } - _, err = testapi.Default.Codec().Encode(expectedType) + _, err = runtime.Encode(testapi.Default.Codec(), expectedType) if err != nil { t.Errorf("Could not encode object: %v", err) continue diff --git a/pkg/api/testapi/testapi_test.go b/pkg/api/testapi/testapi_test.go index b90ea579ab6a..7b8bb02ba36f 100644 --- a/pkg/api/testapi/testapi_test.go +++ b/pkg/api/testapi/testapi_test.go @@ -17,7 +17,12 @@ limitations under the License. package testapi import ( + "encoding/json" + "reflect" "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" ) // TODO these tests don't add much value for testing things that have groups @@ -61,3 +66,61 @@ func TestResourcePath(t *testing.T) { } } } + +var status = &unversioned.Status{ + Status: unversioned.StatusFailure, + Code: 200, + Reason: unversioned.StatusReasonUnknown, + Message: "", +} + +func TestV1EncodeDecodeStatus(t *testing.T) { + v1Codec := 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) { + extensionCodec := Extensions.Codec() + encoded, err := runtime.Encode(extensionCodec, 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(extensionCodec, encoded) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(status, decoded) { + t.Errorf("expected: %v, got: %v", status, decoded) + } +} diff --git a/pkg/api/testing/compat/compatibility_tester.go b/pkg/api/testing/compat/compatibility_tester.go index 17db89ad2cb5..82f165a339de 100644 --- a/pkg/api/testing/compat/compatibility_tester.go +++ b/pkg/api/testing/compat/compatibility_tester.go @@ -26,6 +26,7 @@ import ( "strings" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/runtime" diff --git a/pkg/apimachinery/registered/registered_test.go b/pkg/apimachinery/registered/registered_test.go index de40d2e2ac03..e7466fcc2b8a 100644 --- a/pkg/apimachinery/registered/registered_test.go +++ b/pkg/apimachinery/registered/registered_test.go @@ -17,14 +17,10 @@ limitations under the License. package registered import ( - "encoding/json" - "reflect" "testing" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery" - "k8s.io/kubernetes/pkg/runtime" ) func TestAllPreferredGroupVersions(t *testing.T) { @@ -70,61 +66,3 @@ func TestAllPreferredGroupVersions(t *testing.T) { reset() } } - -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) { - extensionCodec := testapi.Extensions.Codec() - encoded, err := runtime.Encode(extensionCodec, 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(extensionCodec, encoded) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(status, decoded) { - t.Errorf("expected: %v, got: %v", status, decoded) - } -} diff --git a/pkg/apis/componentconfig/install/install_test.go b/pkg/apis/componentconfig/install/install_test.go index 20536d57928e..940e17567387 100644 --- a/pkg/apis/componentconfig/install/install_test.go +++ b/pkg/apis/componentconfig/install/install_test.go @@ -20,6 +20,7 @@ import ( "encoding/json" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/componentconfig" diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index 0ddf0056b727..50477fc89ad9 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -30,7 +30,6 @@ import ( apierrors "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/strategicpatch" diff --git a/pkg/client/unversioned/client_test.go b/pkg/client/unversioned/client_test.go index 9ee779a0d2ce..8fdfe4ecd243 100644 --- a/pkg/client/unversioned/client_test.go +++ b/pkg/client/unversioned/client_test.go @@ -30,8 +30,6 @@ import ( "k8s.io/kubernetes/pkg/version" ) -const nameRequiredError = "resource name may not be empty" - func TestGetServerVersion(t *testing.T) { expect := version.Info{ Major: "foo", diff --git a/pkg/client/unversioned/helper_blackbox_test.go b/pkg/client/unversioned/helper_blackbox_test.go index de2df14fbb14..0c961e251e1a 100644 --- a/pkg/client/unversioned/helper_blackbox_test.go +++ b/pkg/client/unversioned/helper_blackbox_test.go @@ -71,7 +71,7 @@ func TestNegotiateVersion(t *testing.T) { { name: "explicit version supported", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, - serverVersions: []string{"/ersion1", testapi.Default.GroupVersion().String()}, + serverVersions: []string{"/version1", testapi.Default.GroupVersion().String()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectedVersion: testapi.Default.GroupVersion(), }, diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go index 12689300a063..17f054249ee8 100644 --- a/pkg/controller/controller_utils_test.go +++ b/pkg/controller/controller_utils_test.go @@ -212,7 +212,7 @@ func TestCreatePods(t *testing.T) { Spec: controllerSpec.Spec.Template.Spec, } fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath("pods", api.NamespaceDefault, ""), "POST", nil) - actualPod, err := client.Codec.Decode([]byte(fakeHandler.RequestBody)) + actualPod, err := runtime.Decode(client.Codec, []byte(fakeHandler.RequestBody)) if err != nil { t.Errorf("Unexpected error: %#v", err) } diff --git a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go index 13c3d30b6236..32a0bc709368 100644 --- a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go @@ -275,8 +275,9 @@ func TestExampleObjects(t *testing.T) { } for name, scenario := range scenarios { - o := testclient.NewObjects(api.Scheme, api.Scheme) - if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/"+name, o, api.Scheme); err != nil { + codec := api.Codecs.UniversalDecoder() + o := testclient.NewObjects(api.Scheme, codec) + if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/"+name, o, codec); err != nil { t.Fatal(err) } @@ -332,11 +333,12 @@ func TestExampleObjects(t *testing.T) { } func TestBindingWithExamples(t *testing.T) { - o := testclient.NewObjects(api.Scheme, api.Scheme) - if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil { + codec := api.Codecs.UniversalDecoder() + o := testclient.NewObjects(api.Scheme, codec) + if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, codec); err != nil { t.Fatal(err) } - if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil { + if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, codec); err != nil { t.Fatal(err) } diff --git a/pkg/conversion/unversioned_test.go b/pkg/conversion/unversioned_test.go index ae6b89c7613d..5a798be95eb0 100644 --- a/pkg/conversion/unversioned_test.go +++ b/pkg/conversion/unversioned_test.go @@ -23,6 +23,7 @@ import ( // 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" diff --git a/pkg/kubectl/cmd/drain_test.go b/pkg/kubectl/cmd/drain_test.go index e4daed8ae91e..10d2ef268fc9 100644 --- a/pkg/kubectl/cmd/drain_test.go +++ b/pkg/kubectl/cmd/drain_test.go @@ -441,7 +441,7 @@ func refJson(t *testing.T, o runtime.Object) string { } _, _, codec := NewAPIFactory() - json, err := codec.Encode(&api.SerializedReference{Reference: *ref}) + json, err := runtime.Encode(codec, &api.SerializedReference{Reference: *ref}) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/kubelet/config/common_test.go b/pkg/kubelet/config/common_test.go index acdad1b68382..5265fca36750 100644 --- a/pkg/kubelet/config/common_test.go +++ b/pkg/kubelet/config/common_test.go @@ -24,9 +24,10 @@ import ( "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/securitycontext" - "github.com/ghodss/yaml" + //"github.com/ghodss/yaml" ) func noDefault(*api.Pod) error { return nil } @@ -56,7 +57,7 @@ func TestDecodeSinglePod(t *testing.T) { SecurityContext: &api.PodSecurityContext{}, }, } - json, err := testapi.Default.Codec().Encode(pod) + json, err := runtime.Encode(testapi.Default.Codec(), pod) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -72,15 +73,12 @@ func TestDecodeSinglePod(t *testing.T) { } for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) { - externalPod, err := testapi.Default.Converter().ConvertToVersion(pod, gv.String()) + s, _ := api.Codecs.SerializerForFileExtension("yaml") + encoder := api.Codecs.EncoderForVersion(s, gv) + yaml, err := runtime.Encode(encoder, pod) if err != nil { t.Errorf("unexpected error: %v", err) } - yaml, err := yaml.Marshal(externalPod) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - parsed, podOut, err = tryDecodeSinglePod(yaml, noDefault) if !parsed { t.Errorf("expected to have parsed file: (%s)", string(yaml)) @@ -122,7 +120,7 @@ func TestDecodePodList(t *testing.T) { podList := &api.PodList{ Items: []api.Pod{*pod}, } - json, err := testapi.Default.Codec().Encode(podList) + json, err := runtime.Encode(testapi.Default.Codec(), podList) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -138,21 +136,21 @@ func TestDecodePodList(t *testing.T) { } for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) { - externalPodList, err := testapi.Default.Converter().ConvertToVersion(podList, gv.String()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - yaml, err := yaml.Marshal(externalPodList) + s, _ := api.Codecs.SerializerForFileExtension("yaml") + encoder := api.Codecs.EncoderForVersion(s, gv) + yaml, err := runtime.Encode(encoder, podList) if err != nil { t.Errorf("unexpected error: %v", err) } parsed, podListOut, err = tryDecodePodList(yaml, noDefault) if !parsed { - t.Errorf("expected to have parsed file: (%s)", string(yaml)) + t.Errorf("expected to have parsed file: (%s): %v", string(yaml), err) + continue } if err != nil { t.Errorf("unexpected error: %v (%s)", err, string(yaml)) + continue } if !reflect.DeepEqual(podList, &podListOut) { t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", pod, &podListOut, string(yaml)) diff --git a/pkg/kubelet/config/file_test.go b/pkg/kubelet/config/file_test.go index 268985b2506d..1b41f0729b74 100644 --- a/pkg/kubelet/config/file_test.go +++ b/pkg/kubelet/config/file_test.go @@ -132,7 +132,7 @@ func TestReadPodsFromFile(t *testing.T) { if err != nil { t.Fatalf("%s: error in versioning the pod: %v", testCase.desc, err) } - fileContents, err := testapi.Default.Codec().Encode(versionedPod) + fileContents, err := runtime.Encode(testapi.Default.Codec(), versionedPod) if err != nil { t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err) } diff --git a/pkg/kubelet/config/http_test.go b/pkg/kubelet/config/http_test.go index aa63f248775a..14dab53176ec 100644 --- a/pkg/kubelet/config/http_test.go +++ b/pkg/kubelet/config/http_test.go @@ -281,7 +281,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { if err != nil { t.Fatalf("%s: error in versioning the pods: %s", testCase.desc, err) } - data, err := testapi.Default.Codec().Encode(versionedPods) + data, err := runtime.Encode(testapi.Default.Codec(), versionedPods) if err != nil { t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err) } diff --git a/pkg/kubelet/dockertools/labels_test.go b/pkg/kubelet/dockertools/labels_test.go index 1dfe24c4f83f..41e8a4b1056d 100644 --- a/pkg/kubelet/dockertools/labels_test.go +++ b/pkg/kubelet/dockertools/labels_test.go @@ -22,9 +22,10 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/api/testapi" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/util/format" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" ) @@ -107,7 +108,7 @@ func TestLabels(t *testing.T) { pod.DeletionGracePeriodSeconds = &deletionGracePeriod pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriod container.Lifecycle = lifecycle - data, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) + data, err := runtime.Encode(testapi.Default.Codec(), pod) if err != nil { t.Fatalf("Failed to encode pod %q into string: %v", format.Pod(pod), err) } diff --git a/pkg/kubelet/dockertools/manager_test.go b/pkg/kubelet/dockertools/manager_test.go index 4b8735a9b0d6..ede3251da178 100644 --- a/pkg/kubelet/dockertools/manager_test.go +++ b/pkg/kubelet/dockertools/manager_test.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/network" proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" uexec "k8s.io/kubernetes/pkg/util/exec" @@ -448,7 +449,7 @@ func TestKillContainerInPodWithPreStop(t *testing.T) { }, {Name: "bar"}}}, } - podString, err := testapi.Default.Codec().Encode(pod) + podString, err := runtime.Encode(testapi.Default.Codec(), pod) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/storage/etcd/etcd_helper_test.go b/pkg/storage/etcd/etcd_helper_test.go index 469e07695144..253f121d429f 100644 --- a/pkg/storage/etcd/etcd_helper_test.go +++ b/pkg/storage/etcd/etcd_helper_test.go @@ -21,6 +21,7 @@ import ( "reflect" "sync" "testing" + "time" etcd "github.com/coreos/etcd/client" "github.com/stretchr/testify/assert" @@ -30,6 +31,7 @@ import ( apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing" @@ -39,20 +41,25 @@ import ( const validEtcdVersion = "etcd 2.0.9" -var scheme *runtime.Scheme -var codec runtime.Codec - -func init() { - scheme = runtime.NewScheme() - scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &storagetesting.TestResource{}) +func testScheme(t *testing.T) (*runtime.Scheme, runtime.Codec) { + scheme := runtime.NewScheme() + scheme.Log(t) scheme.AddKnownTypes(*testapi.Default.GroupVersion(), &storagetesting.TestResource{}) - codec = runtime.CodecFor(scheme, *testapi.Default.GroupVersion()) - scheme.AddConversionFuncs( + scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &storagetesting.TestResource{}) + if err := scheme.AddConversionFuncs( func(in *storagetesting.TestResource, out *storagetesting.TestResource, s conversion.Scope) error { *out = *in return nil }, - ) + func(in, out *time.Time, s conversion.Scope) error { + *out = *in + return nil + }, + ); err != nil { + panic(err) + } + codec := serializer.NewCodecFactory(scheme).LegacyCodec(*testapi.Default.GroupVersion()) + return scheme, codec } func newEtcdHelper(client etcd.Client, codec runtime.Codec, prefix string) etcdHelper { @@ -61,7 +68,7 @@ func newEtcdHelper(client etcd.Client, codec runtime.Codec, prefix string) etcdH // Returns an encoded version of api.Pod with the given name. func getEncodedPod(name string) string { - pod, _ := testapi.Default.Codec().Encode(&api.Pod{ + pod, _ := runtime.Encode(testapi.Default.Codec(), &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name}, }) return string(pod) @@ -258,7 +265,7 @@ func TestCreate(t *testing.T) { if err != nil { t.Errorf("Unexpected error %#v", err) } - _, err = testapi.Default.Codec().Encode(obj) + _, err = runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -266,7 +273,7 @@ func TestCreate(t *testing.T) { if err != nil { t.Errorf("Unexpected error %#v", err) } - _, err = testapi.Default.Codec().Encode(returnedObj) + _, err = runtime.Encode(testapi.Default.Codec(), returnedObj) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -374,6 +381,7 @@ func TestSetNilOutParam(t *testing.T) { } func TestGuaranteedUpdate(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") @@ -418,6 +426,7 @@ func TestGuaranteedUpdate(t *testing.T) { } func TestGuaranteedUpdateNoChange(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") @@ -447,6 +456,7 @@ func TestGuaranteedUpdateNoChange(t *testing.T) { } func TestGuaranteedUpdateKeyNotFound(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") @@ -473,6 +483,7 @@ func TestGuaranteedUpdateKeyNotFound(t *testing.T) { } func TestGuaranteedUpdate_CreateCollision(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") diff --git a/pkg/storage/etcd/etcd_watcher_test.go b/pkg/storage/etcd/etcd_watcher_test.go index 65ba1bcb7d07..372968cee58a 100644 --- a/pkg/storage/etcd/etcd_watcher_test.go +++ b/pkg/storage/etcd/etcd_watcher_test.go @@ -169,6 +169,7 @@ func TestWatchInterpretations(t *testing.T) { } func TestWatchInterpretation_ResponseNotSet(t *testing.T) { + _, codec := testScheme(t) w := newEtcdWatcher(false, nil, storage.Everything, codec, versioner, nil, &fakeEtcdCache{}) w.emit = func(e watch.Event) { t.Errorf("Unexpected emit: %v", e) @@ -181,6 +182,7 @@ func TestWatchInterpretation_ResponseNotSet(t *testing.T) { } func TestWatchInterpretation_ResponseNoNode(t *testing.T) { + _, codec := testScheme(t) actions := []string{"create", "set", "compareAndSwap", "delete"} for _, action := range actions { w := newEtcdWatcher(false, nil, storage.Everything, codec, versioner, nil, &fakeEtcdCache{}) @@ -195,6 +197,7 @@ func TestWatchInterpretation_ResponseNoNode(t *testing.T) { } func TestWatchInterpretation_ResponseBadData(t *testing.T) { + _, codec := testScheme(t) actions := []string{"create", "set", "compareAndSwap", "delete"} for _, action := range actions { w := newEtcdWatcher(false, nil, storage.Everything, codec, versioner, nil, &fakeEtcdCache{}) @@ -448,6 +451,7 @@ func TestWatchListIgnoresRootKey(t *testing.T) { } func TestWatchPurposefulShutdown(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := "/some/key" diff --git a/pkg/util/jsonpath/jsonpath_test.go b/pkg/util/jsonpath/jsonpath_test.go index 0817dcf7198c..01f07c908bb7 100644 --- a/pkg/util/jsonpath/jsonpath_test.go +++ b/pkg/util/jsonpath/jsonpath_test.go @@ -93,7 +93,7 @@ func testFailJSONPath(tests []jsonpathTest, t *testing.T) { out = err.Error() } if out != test.expect { - t.Errorf("in %s, expect to get error %s, got %s", test.name, test.expect, out) + t.Errorf("in %s, expect to get error %q, got %q", test.name, test.expect, out) } } } @@ -160,7 +160,7 @@ func TestStructInput(t *testing.T) { testJSONPath(storeTests, t) failStoreTests := []jsonpathTest{ - {"invalid identfier", "{hello}", storeData, "unrecongnized identifier hello"}, + {"invalid identfier", "{hello}", storeData, "unrecognized identifier hello"}, {"nonexistent field", "{.hello}", storeData, "hello is not found"}, {"invalid array", "{.Labels[0]}", storeData, "map[string]int is not array or slice"}, {"invalid filter operator", "{.Book[?(@.Price<>10)]}", storeData, "unrecognized filter operator <>"}, diff --git a/pkg/util/yaml/decoder_test.go b/pkg/util/yaml/decoder_test.go index c4658a34008e..e1e3f66095d5 100644 --- a/pkg/util/yaml/decoder_test.go +++ b/pkg/util/yaml/decoder_test.go @@ -62,7 +62,7 @@ func TestSplitYAMLDocument(t *testing.T) { } func TestGuessJSON(t *testing.T) { - if r, isJSON := guessJSONStream(bytes.NewReader([]byte(" \n{}")), 100); !isJSON { + if r, isJSON := GuessJSONStream(bytes.NewReader([]byte(" \n{}")), 100); !isJSON { t.Fatalf("expected stream to be JSON") } else { b := make([]byte, 30) diff --git a/pkg/volume/aws_ebs/aws_ebs_test.go b/pkg/volume/aws_ebs/aws_ebs_test.go index c2b2921db62b..736fd8e0d990 100644 --- a/pkg/volume/aws_ebs/aws_ebs_test.go +++ b/pkg/volume/aws_ebs/aws_ebs_test.go @@ -25,7 +25,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -259,11 +258,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) tmpDir, err := ioutil.TempDir(os.TempDir(), "awsebsTest") if err != nil { diff --git a/pkg/volume/fc/fc_test.go b/pkg/volume/fc/fc_test.go index 96531fb74565..21214f30034f 100644 --- a/pkg/volume/fc/fc_test.go +++ b/pkg/volume/fc/fc_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -231,11 +230,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/gce_pd/gce_pd_test.go b/pkg/volume/gce_pd/gce_pd_test.go index 4e5651e393b4..881ad3a3bd77 100644 --- a/pkg/volume/gce_pd/gce_pd_test.go +++ b/pkg/volume/gce_pd/gce_pd_test.go @@ -25,7 +25,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -274,11 +273,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) tmpDir, err := ioutil.TempDir(os.TempDir(), "gcepdTest") if err != nil { diff --git a/pkg/volume/glusterfs/glusterfs_test.go b/pkg/volume/glusterfs/glusterfs_test.go index f074e6690097..bedcfb9a91d4 100644 --- a/pkg/volume/glusterfs/glusterfs_test.go +++ b/pkg/volume/glusterfs/glusterfs_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/exec" @@ -193,12 +192,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }}, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - o.Add(ep) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim, ep) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index 4188fb05d6c6..88623d2636c9 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -26,7 +26,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" @@ -256,11 +255,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/iscsi/iscsi_test.go b/pkg/volume/iscsi/iscsi_test.go index aec79aa3460f..bc99b65922b9 100644 --- a/pkg/volume/iscsi/iscsi_test.go +++ b/pkg/volume/iscsi/iscsi_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -231,11 +230,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/nfs/nfs_test.go b/pkg/volume/nfs/nfs_test.go index 8dd1414c44c2..b9ebc48ac995 100644 --- a/pkg/volume/nfs/nfs_test.go +++ b/pkg/volume/nfs/nfs_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -233,11 +232,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/persistent_claim/persistent_claim_test.go b/pkg/volume/persistent_claim/persistent_claim_test.go index ea1c3cc6d6ba..2a1c45cc3a91 100644 --- a/pkg/volume/persistent_claim/persistent_claim_test.go +++ b/pkg/volume/persistent_claim/persistent_claim_test.go @@ -234,11 +234,7 @@ func TestNewBuilder(t *testing.T) { } for _, item := range tests { - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(item.pv) - o.Add(item.claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, api.RESTMapper)) + client := testclient.NewSimpleFake(item.pv, item.claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client)) @@ -289,11 +285,7 @@ func TestNewBuilderClaimNotBound(t *testing.T) { ClaimName: "claimC", }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, api.RESTMapper)) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client)) diff --git a/pkg/volume/rbd/rbd_test.go b/pkg/volume/rbd/rbd_test.go index f9a6096ae19d..d0c2b8997f49 100644 --- a/pkg/volume/rbd/rbd_test.go +++ b/pkg/volume/rbd/rbd_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -188,11 +187,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/watch/json/decoder_test.go b/pkg/watch/json/decoder_test.go index 95312acb2e3a..39bb892dfae8 100644 --- a/pkg/watch/json/decoder_test.go +++ b/pkg/watch/json/decoder_test.go @@ -39,7 +39,7 @@ func TestDecoder(t *testing.T) { expect := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} encoder := json.NewEncoder(in) go func() { - data, err := testapi.Default.Codec().Encode(expect) + data, err := runtime.Encode(testapi.Default.Codec(), expect) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go b/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go index c6b39f29297a..009244fd75b5 100644 --- a/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go +++ b/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/runtime" schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api" latestschedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api/latest" "k8s.io/kubernetes/plugin/pkg/scheduler/factory" @@ -92,16 +93,14 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { for v, tc := range schedulerFiles { policy := schedulerapi.Policy{} - err := latestschedulerapi.Codec.DecodeInto([]byte(tc.JSON), &policy) - if err != nil { + if err := runtime.DecodeInto(latestschedulerapi.Codec, []byte(tc.JSON), &policy); err != nil { t.Errorf("%s: Error decoding: %v", v, err) continue } if !reflect.DeepEqual(policy, tc.ExpectedPolicy) { t.Errorf("%s: Expected:\n\t%#v\nGot:\n\t%#v", v, tc.ExpectedPolicy, policy) } - _, err = factory.NewConfigFactory(nil, "some-scheduler-name").CreateFromConfig(policy) - if err != nil { + if _, err := factory.NewConfigFactory(nil, "some-scheduler-name").CreateFromConfig(policy); err != nil { t.Errorf("%s: Error constructing: %v", v, err) continue } diff --git a/plugin/pkg/scheduler/factory/factory_test.go b/plugin/pkg/scheduler/factory/factory_test.go index 3f7654a4075e..d98f60d03ea0 100644 --- a/plugin/pkg/scheduler/factory/factory_test.go +++ b/plugin/pkg/scheduler/factory/factory_test.go @@ -87,8 +87,7 @@ func TestCreateFromConfig(t *testing.T) { {"name" : "PriorityOne", "weight" : 2}, {"name" : "PriorityTwo", "weight" : 1} ] }`) - err := latestschedulerapi.Codec.DecodeInto(configData, &policy) - if err != nil { + if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil { t.Errorf("Invalid configuration: %v", err) } @@ -111,8 +110,7 @@ func TestCreateFromEmptyConfig(t *testing.T) { factory := NewConfigFactory(client, api.DefaultSchedulerName) configData = []byte(`{}`) - err := latestschedulerapi.Codec.DecodeInto(configData, &policy) - if err != nil { + if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil { t.Errorf("Invalid configuration: %v", err) } From 4a6935b31fcc4d1498c977d90387e02b6b93288f Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 22 Jan 2016 00:11:30 -0500 Subject: [PATCH 16/17] Remaining codec change refactors --- cmd/integration/integration.go | 7 ++--- contrib/mesos/pkg/executor/executor.go | 3 ++- contrib/mesos/pkg/podutil/gzip.go | 5 ++-- contrib/mesos/pkg/podutil/io.go | 5 ++-- .../pkg/scheduler/components/binder/binder.go | 4 ++- examples/https-nginx/make_secret.go | 2 +- examples/sharing-clusters/make_secret.go | 2 +- pkg/controller/controller_utils.go | 10 +++++-- pkg/kubelet/config/common.go | 11 +++----- pkg/kubelet/dockertools/labels.go | 5 ++-- pkg/kubelet/dockertools/manager.go | 13 +++++----- pkg/kubelet/server/server.go | 20 +++++++------- pkg/storage/etcd/etcd_helper.go | 26 ++++++++++++------- pkg/storage/etcd/etcd_watcher.go | 21 ++++++++------- pkg/util/httpstream/spdy/roundtripper.go | 3 ++- pkg/util/io/io.go | 7 +++-- pkg/util/io/io_test.go | 6 +++-- pkg/watch/json/decoder.go | 2 +- pkg/watch/json/encoder.go | 4 +-- pkg/watch/json/types.go | 4 +-- plugin/cmd/kube-scheduler/app/server.go | 18 ++++++------- test/e2e/serviceloadbalancers.go | 5 ++-- test/soak/serve_hostnames/serve_hostnames.go | 3 ++- 23 files changed, 104 insertions(+), 82 deletions(-) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index f7d1cd809952..eb9d174dde51 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -26,7 +26,7 @@ import ( "net/http/httptest" "os" "reflect" - "runtime" + gruntime "runtime" "strconv" "strings" "sync" @@ -52,6 +52,7 @@ import ( kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" utilnet "k8s.io/kubernetes/pkg/util/net" "k8s.io/kubernetes/pkg/util/sets" @@ -441,7 +442,7 @@ func runReplicationControllerTest(c *client.Client) { glog.Fatalf("Unexpected error: %v", err) } var controller api.ReplicationController - if err := api.Scheme.DecodeInto(data, &controller); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &controller); err != nil { glog.Fatalf("Unexpected error: %v", err) } @@ -952,7 +953,7 @@ func addFlags(fs *pflag.FlagSet) { } func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) + gruntime.GOMAXPROCS(gruntime.NumCPU()) addFlags(pflag.CommandLine) util.InitFlags() diff --git a/contrib/mesos/pkg/executor/executor.go b/contrib/mesos/pkg/executor/executor.go index 5270a4ee29a1..3edb92697e01 100644 --- a/contrib/mesos/pkg/executor/executor.go +++ b/contrib/mesos/pkg/executor/executor.go @@ -42,6 +42,7 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/dockertools" + kruntime "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" ) @@ -343,7 +344,7 @@ func (k *Executor) LaunchTask(driver bindings.ExecutorDriver, taskInfo *mesos.Ta return } - obj, err := api.Codec.Decode(taskInfo.GetData()) + obj, err := kruntime.Decode(api.Codecs.UniversalDecoder(), taskInfo.GetData()) if err != nil { log.Errorf("failed to extract yaml data from the taskInfo.data %v", err) k.sendStatus(driver, newStatus(taskInfo.GetTaskId(), mesos.TaskState_TASK_FAILED, diff --git a/contrib/mesos/pkg/podutil/gzip.go b/contrib/mesos/pkg/podutil/gzip.go index 8778326d092b..ba0f1cd1b725 100644 --- a/contrib/mesos/pkg/podutil/gzip.go +++ b/contrib/mesos/pkg/podutil/gzip.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/api" _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" ) func Gzip(pods <-chan *api.Pod) ([]byte, error) { @@ -32,7 +33,7 @@ func Gzip(pods <-chan *api.Pod) ([]byte, error) { } func gzipList(list *api.PodList) ([]byte, error) { - raw, err := v1.Codec.Encode(list) + raw, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), list) if err != nil { return nil, err } @@ -68,7 +69,7 @@ func gunzipList(gzipped []byte) (*api.PodList, error) { return nil, err } - obj, err := api.Scheme.Decode(raw) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), raw) if err != nil { return nil, err } diff --git a/contrib/mesos/pkg/podutil/io.go b/contrib/mesos/pkg/podutil/io.go index e21c22f7ab70..4eb563a2f9c5 100644 --- a/contrib/mesos/pkg/podutil/io.go +++ b/contrib/mesos/pkg/podutil/io.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/runtime" utilyaml "k8s.io/kubernetes/pkg/util/yaml" ) @@ -41,7 +42,7 @@ func WriteToDir(pods <-chan *api.Pod, destDir string) error { log.Warningf("skipping static pod %s/%s that had no filename", p.Namespace, p.Name) continue } - raw, err := v1.Codec.Encode(p) + raw, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), p) if err != nil { log.Errorf("failed to encode static pod as v1 object: %v", err) continue @@ -105,7 +106,7 @@ func tryDecodeSinglePod(data []byte) (parsed bool, pod *api.Pod, err error) { if err != nil { return false, nil, err } - obj, err := api.Scheme.Decode(json) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), json) if err != nil { return false, pod, err } diff --git a/contrib/mesos/pkg/scheduler/components/binder/binder.go b/contrib/mesos/pkg/scheduler/components/binder/binder.go index 55425316376f..4cb39fa529c7 100644 --- a/contrib/mesos/pkg/scheduler/components/binder/binder.go +++ b/contrib/mesos/pkg/scheduler/components/binder/binder.go @@ -26,6 +26,8 @@ import ( annotation "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/meta" "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" ) type Binder interface { @@ -150,7 +152,7 @@ func (b *binder) prepareTaskForLaunch(ctx api.Context, machine string, task *pod // the kubelet-executor uses this to instantiate the pod log.V(3).Infof("prepared pod spec: %+v", pod) - data, err := api.Codec.Encode(&pod) + data, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), &pod) if err != nil { log.V(2).Infof("Failed to marshal the pod spec: %v", err) return err diff --git a/examples/https-nginx/make_secret.go b/examples/https-nginx/make_secret.go index ebefa958664c..ae3947fcaacc 100644 --- a/examples/https-nginx/make_secret.go +++ b/examples/https-nginx/make_secret.go @@ -66,5 +66,5 @@ func main() { "nginx.key": nginxKey, }, } - fmt.Printf(runtime.EncodeOrDie(registered.GroupOrDie(api.GroupName).Codec, secret)) + fmt.Printf(runtime.EncodeOrDie(api.Codecs.LegacyCodec(registered.EnabledVersions()...), secret)) } diff --git a/examples/sharing-clusters/make_secret.go b/examples/sharing-clusters/make_secret.go index 6219948c3e64..20b35b513d80 100644 --- a/examples/sharing-clusters/make_secret.go +++ b/examples/sharing-clusters/make_secret.go @@ -59,5 +59,5 @@ func main() { "config": cfg, }, } - fmt.Printf(runtime.EncodeOrDie(registered.GroupOrDie(api.GroupName).Codec, secret)) + fmt.Printf(runtime.EncodeOrDie(api.Codecs.LegacyCodec(registered.EnabledVersions()...), secret)) } diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index b7481d76316f..ef1bca9df803 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -24,8 +24,8 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/record" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -259,7 +259,13 @@ func getPodsAnnotationSet(template *api.PodTemplateSpec, object runtime.Object) if err != nil { return desiredAnnotations, fmt.Errorf("unable to get controller reference: %v", err) } - createdByRefJson, err := registered.GroupOrDie(api.GroupName).Codec.Encode(&api.SerializedReference{ + + // TODO: this code was not safe previously - as soon as new code came along that switched to v2, old clients + // would be broken upon reading it. This is explicitly hardcoded to v1 to guarantee predictable deployment. + // We need to consistently handle this case of annotation versioning. + codec := api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}) + + createdByRefJson, err := runtime.Encode(codec, &api.SerializedReference{ Reference: *createdByRef, }) if err != nil { diff --git a/pkg/kubelet/config/common.go b/pkg/kubelet/config/common.go index 86318deb883d..0838699ec262 100644 --- a/pkg/kubelet/config/common.go +++ b/pkg/kubelet/config/common.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/hash" utilyaml "k8s.io/kubernetes/pkg/util/yaml" @@ -93,7 +94,7 @@ func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *a if err != nil { return false, nil, err } - obj, err := api.Scheme.Decode(json) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), json) if err != nil { return false, pod, err } @@ -115,17 +116,13 @@ func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *a } func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods api.PodList, err error) { - json, err := utilyaml.ToJSON(data) - if err != nil { - return false, api.PodList{}, err - } - obj, err := api.Scheme.Decode(json) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) if err != nil { return false, pods, err } // Check whether the object could be converted to list of pods. if _, ok := obj.(*api.PodList); !ok { - err = fmt.Errorf("invalid pods list: %+v", obj) + err = fmt.Errorf("invalid pods list: %#v", obj) return false, pods, err } newPods := obj.(*api.PodList) diff --git a/pkg/kubelet/dockertools/labels.go b/pkg/kubelet/dockertools/labels.go index 15535d975229..fd7ad7c89853 100644 --- a/pkg/kubelet/dockertools/labels.go +++ b/pkg/kubelet/dockertools/labels.go @@ -22,9 +22,9 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery/registered" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/util/format" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" ) @@ -187,8 +187,7 @@ func supplyContainerInfoWithOldLabel(labels map[string]string, containerInfo *la return } pod = &api.Pod{} - err := registered.GroupOrDie(api.GroupName).Codec.DecodeInto([]byte(data), pod) - if err != nil { + if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), []byte(data), pod); err != nil { // If the pod label can't be parsed, we should report an error logError(containerInfo, kubernetesPodLabel, err) return diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index f19a65667974..973f78facef0 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -37,7 +37,6 @@ import ( cadvisorapi "github.com/google/cadvisor/info/v1" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/record" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/lifecycle" @@ -48,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/qos" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/util/format" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/securitycontext" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" @@ -655,11 +655,12 @@ func (dm *DockerManager) runContainer( // TODO(random-liu): Remove this when we start to use new labels for KillContainerInPod if container.Lifecycle != nil && container.Lifecycle.PreStop != nil { // TODO: This is kind of hacky, we should really just encode the bits we need. - data, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) - if err != nil { - glog.Errorf("Failed to encode pod: %s for prestop hook", pod.Name) - } else { + // TODO: This is hacky because the Kubelet should be parameterized to encode a specific version + // and needs to be able to migrate this whenever we deprecate v1. Should be a member of DockerManager. + if data, err := runtime.Encode(api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}), pod); err == nil { labels[kubernetesPodLabel] = string(data) + } else { + glog.Errorf("Failed to encode pod: %s for prestop hook", pod.Name) } } memoryLimit := container.Resources.Limits.Memory().Value() @@ -1432,7 +1433,7 @@ func containerAndPodFromLabels(inspect *docker.Container) (pod *api.Pod, contain // the pod data may not be set if body, found := labels[kubernetesPodLabel]; found { pod = &api.Pod{} - if err = registered.GroupOrDie(api.GroupName).Codec.DecodeInto([]byte(body), pod); err == nil { + if err = runtime.DecodeInto(api.Codecs.UniversalDecoder(), []byte(body), pod); err == nil { name := labels[kubernetesContainerNameLabel] for ix := range pod.Spec.Containers { if pod.Spec.Containers[ix].Name == name { diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go index 37c7f563d66c..0f61d40b426c 100644 --- a/pkg/kubelet/server/server.go +++ b/pkg/kubelet/server/server.go @@ -39,7 +39,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/auth/authenticator" "k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/client/unversioned/remotecommand" @@ -48,6 +47,7 @@ import ( kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/server/portforward" "k8s.io/kubernetes/pkg/kubelet/server/stats" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/flushwriter" @@ -402,18 +402,12 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re delete(query, "tailLines") } } - // container logs on the kubelet are locked to v1 - versioned := &v1.PodLogOptions{} - if err := api.Scheme.Convert(&query, versioned); err != nil { + // container logs on the kubelet are locked to the v1 API version of PodLogOptions + logOptions := &api.PodLogOptions{} + if err := api.ParameterCodec.DecodeParameters(query, v1.SchemeGroupVersion, logOptions); err != nil { response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to decode query."}`)) return } - out, err := api.Scheme.ConvertToVersion(versioned, "") - if err != nil { - response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to convert request query."}`)) - return - } - logOptions := out.(*api.PodLogOptions) logOptions.TypeMeta = unversioned.TypeMeta{} if errs := validation.ValidatePodLogOptions(logOptions); len(errs) > 0 { response.WriteError(apierrs.StatusUnprocessableEntity, fmt.Errorf(`{"message": "Invalid request."}`)) @@ -462,7 +456,11 @@ func encodePods(pods []*api.Pod) (data []byte, err error) { for _, pod := range pods { podList.Items = append(podList.Items, *pod) } - return registered.GroupOrDie(api.GroupName).Codec.Encode(podList) + // TODO: this needs to be parameterized to the kubelet, not hardcoded. Depends on Kubelet + // as API server refactor. + // TODO: Locked to v1, needs to be made generic + codec := api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}) + return runtime.Encode(codec, podList) } // getPods returns a list of pods bound to the Kubelet and their spec. diff --git a/pkg/storage/etcd/etcd_helper.go b/pkg/storage/etcd/etcd_helper.go index 7e4fcac21f05..960c1b712070 100644 --- a/pkg/storage/etcd/etcd_helper.go +++ b/pkg/storage/etcd/etcd_helper.go @@ -148,7 +148,7 @@ func (h *etcdHelper) Create(ctx context.Context, key string, obj, out runtime.Ob glog.Errorf("Context is nil") } key = h.prefixEtcdKey(key) - data, err := h.codec.Encode(obj) + data, err := runtime.Encode(h.codec, obj) if err != nil { return err } @@ -183,7 +183,7 @@ func (h *etcdHelper) Set(ctx context.Context, key string, obj, out runtime.Objec glog.Errorf("Context is nil") } var response *etcd.Response - data, err := h.codec.Encode(obj) + data, err := runtime.Encode(h.codec, obj) if err != nil { return err } @@ -333,7 +333,13 @@ func (h *etcdHelper) extractObj(response *etcd.Response, inErr error, objPtr run return "", nil, fmt.Errorf("unable to locate a value on the response: %#v", response) } body = node.Value - err = h.codec.DecodeInto([]byte(body), objPtr) + out, gvk, err := h.codec.Decode([]byte(body), nil, objPtr) + if err != nil { + return body, nil, err + } + if out != objPtr { + return body, nil, fmt.Errorf("unable to decode object %s into %v", gvk.String(), reflect.TypeOf(objPtr)) + } if h.versioner != nil { _ = h.versioner.UpdateObject(objPtr, node.Expiration, node.ModifiedIndex) // being unable to set the version does not prevent the object from being extracted @@ -403,19 +409,19 @@ func (h *etcdHelper) decodeNodeList(nodes []*etcd.Node, filter storage.FilterFun v.Set(reflect.Append(v, reflect.ValueOf(obj).Elem())) } } else { - obj := reflect.New(v.Type().Elem()) - if err := h.codec.DecodeInto([]byte(node.Value), obj.Interface().(runtime.Object)); err != nil { + obj, _, err := h.codec.Decode([]byte(node.Value), nil, reflect.New(v.Type().Elem()).Interface().(runtime.Object)) + if err != nil { return err } if h.versioner != nil { // being unable to set the version does not prevent the object from being extracted - _ = h.versioner.UpdateObject(obj.Interface().(runtime.Object), node.Expiration, node.ModifiedIndex) + _ = h.versioner.UpdateObject(obj, node.Expiration, node.ModifiedIndex) } - if filter(obj.Interface().(runtime.Object)) { - v.Set(reflect.Append(v, obj.Elem())) + if filter(obj) { + v.Set(reflect.Append(v, reflect.ValueOf(obj).Elem())) } if node.ModifiedIndex != 0 { - h.addToCache(node.ModifiedIndex, obj.Interface().(runtime.Object)) + h.addToCache(node.ModifiedIndex, obj) } } } @@ -532,7 +538,7 @@ func (h *etcdHelper) GuaranteedUpdate(ctx context.Context, key string, ptrToType ttl = *newTTL } - data, err := h.codec.Encode(ret) + data, err := runtime.Encode(h.codec, ret) if err != nil { return err } diff --git a/pkg/storage/etcd/etcd_watcher.go b/pkg/storage/etcd/etcd_watcher.go index 4c807dac3560..0411afe8e336 100644 --- a/pkg/storage/etcd/etcd_watcher.go +++ b/pkg/storage/etcd/etcd_watcher.go @@ -17,6 +17,7 @@ limitations under the License. package etcd import ( + "fmt" "net/http" "sync" "sync/atomic" @@ -210,7 +211,7 @@ func etcdGetInitialWatchState(ctx context.Context, client etcd.KeysAPI, key stri resp, err := client.Get(ctx, key, &opts) if err != nil { if !etcdutil.IsEtcdNotFound(err) { - glog.Errorf("watch was unable to retrieve the current index for the provided key (%q): %v", key, err) + util.HandleError(fmt.Errorf("watch was unable to retrieve the current index for the provided key (%q): %v", key, err)) return resourceVersion, err } if etcdError, ok := err.(etcd.Error); ok { @@ -300,7 +301,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { return obj, nil } - obj, err := w.encoding.Decode([]byte(node.Value)) + obj, err := runtime.Decode(w.encoding, []byte(node.Value)) if err != nil { return nil, err } @@ -308,7 +309,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { // ensure resource version is set on the object we load from etcd if w.versioner != nil { if err := w.versioner.UpdateObject(obj, node.Expiration, node.ModifiedIndex); err != nil { - glog.Errorf("failure to version api object (%d) %#v: %v", node.ModifiedIndex, obj, err) + util.HandleError(fmt.Errorf("failure to version api object (%d) %#v: %v", node.ModifiedIndex, obj, err)) } } @@ -316,7 +317,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { if w.transform != nil { obj, err = w.transform(obj) if err != nil { - glog.Errorf("failure to transform api object %#v: %v", obj, err) + util.HandleError(fmt.Errorf("failure to transform api object %#v: %v", obj, err)) return nil, err } } @@ -329,7 +330,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { func (w *etcdWatcher) sendAdd(res *etcd.Response) { if res.Node == nil { - glog.Errorf("unexpected nil node: %#v", res) + util.HandleError(fmt.Errorf("unexpected nil node: %#v", res)) return } if w.include != nil && !w.include(res.Node.Key) { @@ -337,7 +338,7 @@ func (w *etcdWatcher) sendAdd(res *etcd.Response) { } obj, err := w.decodeObject(res.Node) if err != nil { - glog.Errorf("failure to decode api object: '%v' from %#v %#v", string(res.Node.Value), res, res.Node) + util.HandleError(fmt.Errorf("failure to decode api object: %v\n'%v' from %#v %#v", err, string(res.Node.Value), res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. @@ -366,7 +367,7 @@ func (w *etcdWatcher) sendModify(res *etcd.Response) { } curObj, err := w.decodeObject(res.Node) if err != nil { - glog.Errorf("failure to decode api object: '%v' from %#v %#v", string(res.Node.Value), res, res.Node) + util.HandleError(fmt.Errorf("failure to decode api object: %v\n'%v' from %#v %#v", err, string(res.Node.Value), res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. @@ -406,7 +407,7 @@ func (w *etcdWatcher) sendModify(res *etcd.Response) { func (w *etcdWatcher) sendDelete(res *etcd.Response) { if res.PrevNode == nil { - glog.Errorf("unexpected nil prev node: %#v", res) + util.HandleError(fmt.Errorf("unexpected nil prev node: %#v", res)) return } if w.include != nil && !w.include(res.PrevNode.Key) { @@ -421,7 +422,7 @@ func (w *etcdWatcher) sendDelete(res *etcd.Response) { } obj, err := w.decodeObject(&node) if err != nil { - glog.Errorf("failure to decode api object: '%v' from %#v %#v", string(res.PrevNode.Value), res, res.PrevNode) + util.HandleError(fmt.Errorf("failure to decode api object: %v\nfrom %#v %#v", err, res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. @@ -445,7 +446,7 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) { case EtcdDelete, EtcdExpire: w.sendDelete(res) default: - glog.Errorf("unknown action: %v", res.Action) + util.HandleError(fmt.Errorf("unknown action: %v", res.Action)) } } diff --git a/pkg/util/httpstream/spdy/roundtripper.go b/pkg/util/httpstream/spdy/roundtripper.go index 47b67307735d..a054d30c233e 100644 --- a/pkg/util/httpstream/spdy/roundtripper.go +++ b/pkg/util/httpstream/spdy/roundtripper.go @@ -212,7 +212,8 @@ func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connec if err != nil { responseError = "unable to read error from server response" } else { - if obj, err := api.Scheme.Decode(responseErrorBytes); err == nil { + // TODO: I don't belong here, I should be abstracted from this class + if obj, _, err := api.Codecs.UniversalDecoder().Decode(responseErrorBytes, nil, &unversioned.Status{}); err == nil { if status, ok := obj.(*unversioned.Status); ok { return nil, &apierrors.StatusError{ErrStatus: *status} } diff --git a/pkg/util/io/io.go b/pkg/util/io/io.go index 64bc074ba5f1..d600c08c191b 100644 --- a/pkg/util/io/io.go +++ b/pkg/util/io/io.go @@ -23,6 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" ) // LoadPodFromFile will read, decode, and return a Pod from a file. @@ -39,7 +40,8 @@ func LoadPodFromFile(filePath string) (*api.Pod, error) { } pod := &api.Pod{} - if err := registered.GroupOrDie(api.GroupName).Codec.DecodeInto(podDef, pod); err != nil { + codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) + if err := runtime.DecodeInto(codec, podDef, pod); err != nil { return nil, fmt.Errorf("failed decoding file: %v", err) } return pod, nil @@ -50,7 +52,8 @@ func SavePodToFile(pod *api.Pod, filePath string, perm os.FileMode) error { if filePath == "" { return fmt.Errorf("file path not specified") } - data, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) + codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) + data, err := runtime.Encode(codec, pod) if err != nil { return fmt.Errorf("failed encoding pod: %v", err) } diff --git a/pkg/util/io/io_test.go b/pkg/util/io/io_test.go index 993ff5fec576..466f436b8cb8 100644 --- a/pkg/util/io/io_test.go +++ b/pkg/util/io/io_test.go @@ -24,6 +24,7 @@ import ( "github.com/pborman/uuid" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/volume" ) @@ -32,8 +33,9 @@ func TestSavePodToFile(t *testing.T) { pod := volume.NewPersistentVolumeRecyclerPodTemplate() // sets all default values on a pod for equality comparison after decoding from file - encoded, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) - registered.GroupOrDie(api.GroupName).Codec.DecodeInto(encoded, pod) + codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) + encoded, err := runtime.Encode(codec, pod) + runtime.DecodeInto(codec, encoded, pod) path := fmt.Sprintf("/tmp/kube-io-test-%s", uuid.New()) defer os.Remove(path) diff --git a/pkg/watch/json/decoder.go b/pkg/watch/json/decoder.go index f6c0924dd40e..d1632336f672 100644 --- a/pkg/watch/json/decoder.go +++ b/pkg/watch/json/decoder.go @@ -56,7 +56,7 @@ func (d *Decoder) Decode() (watch.EventType, runtime.Object, error) { return "", nil, fmt.Errorf("got invalid watch event type: %v", got.Type) } - obj, err := d.codec.Decode(got.Object.RawJSON) + obj, err := runtime.Decode(d.codec, got.Object.RawJSON) if err != nil { return "", nil, fmt.Errorf("unable to decode watch event: %v", err) } diff --git a/pkg/watch/json/encoder.go b/pkg/watch/json/encoder.go index 1110e9293fa5..1eaf6a8a09cb 100644 --- a/pkg/watch/json/encoder.go +++ b/pkg/watch/json/encoder.go @@ -30,11 +30,11 @@ import ( type Encoder struct { w io.Writer encoder *json.Encoder - codec runtime.Codec + codec runtime.Encoder } // NewEncoder creates an Encoder for the given writer and codec -func NewEncoder(w io.Writer, codec runtime.Codec) *Encoder { +func NewEncoder(w io.Writer, codec runtime.Encoder) *Encoder { return &Encoder{ w: w, encoder: json.NewEncoder(w), diff --git a/pkg/watch/json/types.go b/pkg/watch/json/types.go index f8fbd397aa88..28ee0dec75f3 100644 --- a/pkg/watch/json/types.go +++ b/pkg/watch/json/types.go @@ -40,12 +40,12 @@ type WatchEvent struct { } // Object converts a watch.Event into an appropriately serializable JSON object -func Object(codec runtime.Codec, event *watch.Event) (interface{}, error) { +func Object(encoder runtime.Encoder, event *watch.Event) (interface{}, error) { obj, ok := event.Object.(runtime.Object) if !ok { return nil, fmt.Errorf("the event object cannot be safely converted to JSON: %v", reflect.TypeOf(event.Object).Name()) } - data, err := runtime.Encode(codec, obj) + data, err := runtime.Encode(encoder, obj) if err != nil { return nil, err } diff --git a/plugin/cmd/kube-scheduler/app/server.go b/plugin/cmd/kube-scheduler/app/server.go index fde563c0f06d..07d85c60118f 100644 --- a/plugin/cmd/kube-scheduler/app/server.go +++ b/plugin/cmd/kube-scheduler/app/server.go @@ -32,6 +32,7 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/healthz" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/plugin/pkg/scheduler" _ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider" @@ -152,19 +153,18 @@ func Run(s *options.SchedulerServer) error { } func createConfig(s *options.SchedulerServer, configFactory *factory.ConfigFactory) (*scheduler.Config, error) { - var policy schedulerapi.Policy - var configData []byte - if _, err := os.Stat(s.PolicyConfigFile); err == nil { - configData, err = ioutil.ReadFile(s.PolicyConfigFile) + var ( + policy schedulerapi.Policy + configData []byte + ) + configData, err := ioutil.ReadFile(s.PolicyConfigFile) if err != nil { - return nil, fmt.Errorf("Unable to read policy config: %v", err) + return nil, fmt.Errorf("unable to read policy config: %v", err) } - err = latestschedulerapi.Codec.DecodeInto(configData, &policy) - if err != nil { - return nil, fmt.Errorf("Invalid configuration: %v", err) + if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil { + return nil, fmt.Errorf("invalid configuration: %v", err) } - return configFactory.CreateFromConfig(policy) } diff --git a/test/e2e/serviceloadbalancers.go b/test/e2e/serviceloadbalancers.go index 82748cb695b3..b8376e2cf3aa 100644 --- a/test/e2e/serviceloadbalancers.go +++ b/test/e2e/serviceloadbalancers.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/wait" utilyaml "k8s.io/kubernetes/pkg/util/yaml" @@ -274,7 +275,7 @@ func rcFromManifest(fileName string) *api.ReplicationController { json, err := utilyaml.ToJSON(data) Expect(err).NotTo(HaveOccurred()) - Expect(api.Scheme.DecodeInto(json, &controller)).NotTo(HaveOccurred()) + Expect(runtime.DecodeInto(api.Codecs.UniversalDecoder(), json, &controller)).NotTo(HaveOccurred()) return &controller } @@ -288,6 +289,6 @@ func svcFromManifest(fileName string) *api.Service { json, err := utilyaml.ToJSON(data) Expect(err).NotTo(HaveOccurred()) - Expect(api.Scheme.DecodeInto(json, &svc)).NotTo(HaveOccurred()) + Expect(runtime.DecodeInto(api.Codecs.UniversalDecoder(), json, &svc)).NotTo(HaveOccurred()) return &svc } diff --git a/test/soak/serve_hostnames/serve_hostnames.go b/test/soak/serve_hostnames/serve_hostnames.go index 57b066610977..f19d42eb67fc 100644 --- a/test/soak/serve_hostnames/serve_hostnames.go +++ b/test/soak/serve_hostnames/serve_hostnames.go @@ -35,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" ) @@ -264,7 +265,7 @@ func main() { continue } var r unversioned.Status - if err := api.Scheme.DecodeInto(hostname, &r); err != nil { + if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), hostname, &r); err != nil { break } if r.Status == unversioned.StatusFailure { From 3262d8efd8ddbd90b0f938c0ebf4dd03d470a1fb Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Mon, 21 Dec 2015 00:50:36 -0500 Subject: [PATCH 17/17] GENERATED: all --- api/swagger-spec/api.json | 6 +- api/swagger-spec/apis.json | 6 +- api/swagger-spec/extensions.json | 6 +- api/swagger-spec/v1.json | 369 ++-- api/swagger-spec/v1beta1.json | 120 +- .../extensions/v1beta1/operations.html | 122 +- docs/api-reference/v1/operations.html | 369 ++++ pkg/api/deep_copy_generated.go | 5 + pkg/api/v1/conversion_generated.go | 275 +-- pkg/api/v1/deep_copy_generated.go | 7 + pkg/api/v1/types.generated.go | 2 +- .../componentconfig/deep_copy_generated.go | 45 +- pkg/apis/extensions/deep_copy_generated.go | 1863 +---------------- pkg/apis/metrics/deep_copy_generated.go | 32 +- .../unversioned/extensions_client.go | 3 +- .../legacy/unversioned/legacy_client.go | 3 +- 16 files changed, 868 insertions(+), 2365 deletions(-) diff --git a/api/swagger-spec/api.json b/api/swagger-spec/api.json index 2da21dda5fc9..b21e436b15e2 100644 --- a/api/swagger-spec/api.json +++ b/api/swagger-spec/api.json @@ -15,10 +15,12 @@ "nickname": "getAPIVersions", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/apis.json b/api/swagger-spec/apis.json index 0602e2207586..07d061d49b3f 100644 --- a/api/swagger-spec/apis.json +++ b/api/swagger-spec/apis.json @@ -15,10 +15,12 @@ "nickname": "getAPIVersions", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/extensions.json b/api/swagger-spec/extensions.json index 3b4acb17fbf2..7bdf667192d0 100644 --- a/api/swagger-spec/extensions.json +++ b/api/swagger-spec/extensions.json @@ -15,10 +15,12 @@ "nickname": "getAPIGroup", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index cbc65ba3b869..bedb828a8542 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -47,7 +47,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -122,7 +123,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -165,7 +167,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -248,7 +251,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -293,7 +297,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -370,7 +375,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -520,7 +526,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -573,7 +580,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -626,7 +634,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -681,7 +690,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -847,7 +857,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1005,7 +1016,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1050,7 +1062,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1127,7 +1140,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1277,7 +1291,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1330,7 +1345,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1383,7 +1399,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -1438,7 +1455,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1604,7 +1622,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1762,7 +1781,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1807,7 +1827,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1884,7 +1905,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2034,7 +2056,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2087,7 +2110,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2140,7 +2164,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2195,7 +2220,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2361,7 +2387,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2511,7 +2538,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2548,7 +2576,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2617,7 +2646,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2751,7 +2781,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2796,7 +2827,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2841,7 +2873,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2888,7 +2921,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3022,7 +3056,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3073,7 +3108,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3148,7 +3184,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3185,7 +3222,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3254,7 +3292,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3388,7 +3427,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3433,7 +3473,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3478,7 +3519,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -3525,7 +3567,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3983,7 +4026,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4066,7 +4110,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4111,7 +4156,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4188,7 +4234,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4338,7 +4385,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4391,7 +4439,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4444,7 +4493,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -4499,7 +4549,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4665,7 +4716,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4799,7 +4851,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4874,7 +4927,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4911,7 +4965,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4980,7 +5035,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5114,7 +5170,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5159,7 +5216,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5204,7 +5262,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -5251,7 +5310,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5385,7 +5445,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5468,7 +5529,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5513,7 +5575,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5590,7 +5653,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5740,7 +5804,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5793,7 +5858,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5846,7 +5912,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -5901,7 +5968,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -6487,7 +6555,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -6767,7 +6836,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7044,7 +7114,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7685,7 +7756,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7768,7 +7840,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7813,7 +7886,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7890,7 +7964,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8040,7 +8115,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8093,7 +8169,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8146,7 +8223,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -8201,7 +8279,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8367,7 +8446,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8525,7 +8605,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8570,7 +8651,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8647,7 +8729,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8797,7 +8880,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8850,7 +8934,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8903,7 +8988,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -8958,7 +9044,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9124,7 +9211,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9258,7 +9346,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9341,7 +9430,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9386,7 +9476,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9463,7 +9554,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9613,7 +9705,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9666,7 +9759,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9719,7 +9813,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -9774,7 +9869,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9940,7 +10036,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10074,7 +10171,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10157,7 +10255,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10202,7 +10301,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10279,7 +10379,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10429,7 +10530,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10482,7 +10584,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10535,7 +10638,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -10590,7 +10694,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10756,7 +10861,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10914,7 +11020,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10959,7 +11066,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11036,7 +11144,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11186,7 +11295,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11239,7 +11349,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11292,7 +11403,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -11347,7 +11459,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11513,7 +11626,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11671,7 +11785,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11716,7 +11831,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11850,7 +11966,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11903,7 +12020,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11956,7 +12074,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -12003,7 +12122,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -12589,7 +12709,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -12683,10 +12804,12 @@ "nickname": "getAPIResources", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/v1beta1.json b/api/swagger-spec/v1beta1.json index c6869483de2d..ce8b4a539649 100644 --- a/api/swagger-spec/v1beta1.json +++ b/api/swagger-spec/v1beta1.json @@ -79,7 +79,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -124,7 +125,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -201,7 +203,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -351,7 +354,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -404,7 +408,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -457,7 +462,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -512,7 +518,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -678,7 +685,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -836,7 +844,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -881,7 +890,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -958,7 +968,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1108,7 +1119,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1161,7 +1173,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1214,7 +1227,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -1269,7 +1283,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1435,7 +1450,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1569,7 +1585,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1652,7 +1669,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1697,7 +1715,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1774,7 +1793,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1924,7 +1944,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1977,7 +1998,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2030,7 +2052,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2085,7 +2108,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2251,7 +2275,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2385,7 +2410,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2468,7 +2494,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2513,7 +2540,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2590,7 +2618,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2740,7 +2769,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2793,7 +2823,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2846,7 +2877,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2901,7 +2933,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3067,7 +3100,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3201,7 +3235,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3252,7 +3287,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3305,7 +3341,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3358,7 +3395,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -3379,10 +3417,12 @@ "nickname": "getAPIResources", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/docs/api-reference/extensions/v1beta1/operations.html b/docs/api-reference/extensions/v1beta1/operations.html index f50ffd3cb8e9..f2edc2e3936d 100755 --- a/docs/api-reference/extensions/v1beta1/operations.html +++ b/docs/api-reference/extensions/v1beta1/operations.html @@ -406,6 +406,9 @@

Consumes

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -416,6 +419,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -553,6 +559,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -690,6 +699,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -827,6 +839,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -964,6 +979,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1109,6 +1127,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1254,6 +1275,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1367,6 +1391,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1496,6 +1523,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1617,6 +1647,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1738,6 +1771,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1865,6 +1901,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2010,6 +2049,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2155,6 +2197,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2268,6 +2313,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2397,6 +2445,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2518,6 +2569,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2639,6 +2693,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2766,6 +2823,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2887,6 +2947,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3032,6 +3095,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3177,6 +3243,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3290,6 +3359,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3419,6 +3491,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3540,6 +3615,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3661,6 +3739,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3788,6 +3869,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3909,6 +3993,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4054,6 +4141,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4199,6 +4289,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4312,6 +4405,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4441,6 +4537,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4562,6 +4661,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4683,6 +4785,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4810,6 +4915,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4931,6 +5039,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5044,6 +5155,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5165,6 +5279,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5292,6 +5409,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -7051,7 +7171,7 @@

    Tags

    diff --git a/docs/api-reference/v1/operations.html b/docs/api-reference/v1/operations.html index 532f1f224ab3..a8bc0828e791 100755 --- a/docs/api-reference/v1/operations.html +++ b/docs/api-reference/v1/operations.html @@ -406,6 +406,9 @@

    Consumes

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -416,6 +419,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -553,6 +559,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -658,6 +667,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -795,6 +807,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -932,6 +947,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1069,6 +1087,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1206,6 +1227,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1343,6 +1367,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1448,6 +1475,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1561,6 +1591,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1706,6 +1739,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1851,6 +1887,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1964,6 +2003,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2093,6 +2135,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2214,6 +2259,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2335,6 +2383,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2462,6 +2513,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2607,6 +2661,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2752,6 +2809,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2865,6 +2925,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2994,6 +3057,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3115,6 +3181,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3236,6 +3305,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3363,6 +3435,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3508,6 +3583,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3653,6 +3731,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3766,6 +3847,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3895,6 +3979,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4016,6 +4103,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4137,6 +4227,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4264,6 +4357,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4409,6 +4505,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4554,6 +4653,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4667,6 +4769,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4796,6 +4901,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4917,6 +5025,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5038,6 +5149,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5165,6 +5279,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5286,6 +5403,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5431,6 +5551,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5576,6 +5699,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5689,6 +5815,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5818,6 +5947,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5939,6 +6071,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -6060,6 +6195,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -6187,6 +6325,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -6598,6 +6739,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -7081,6 +7225,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8348,6 +8495,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8493,6 +8643,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8638,6 +8791,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8751,6 +8907,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8880,6 +9039,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9001,6 +9163,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9122,6 +9287,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9249,6 +9417,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9394,6 +9565,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9539,6 +9713,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9652,6 +9829,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9781,6 +9961,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9902,6 +10085,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10023,6 +10209,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10150,6 +10339,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10271,6 +10463,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10416,6 +10611,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10561,6 +10759,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10674,6 +10875,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10803,6 +11007,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10924,6 +11131,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11045,6 +11255,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11172,6 +11385,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11293,6 +11509,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11438,6 +11657,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11583,6 +11805,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11696,6 +11921,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11825,6 +12053,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11946,6 +12177,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12067,6 +12301,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12194,6 +12431,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12339,6 +12579,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12484,6 +12727,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12597,6 +12843,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12726,6 +12975,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12847,6 +13099,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12968,6 +13223,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13095,6 +13353,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13240,6 +13501,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13353,6 +13617,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13466,6 +13733,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13587,6 +13857,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13700,6 +13973,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13827,6 +14103,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13948,6 +14227,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14061,6 +14343,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14174,6 +14459,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14293,6 +14581,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14406,6 +14697,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14519,6 +14813,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14656,6 +14953,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14793,6 +15093,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14898,6 +15201,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15019,6 +15325,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15132,6 +15441,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15245,6 +15557,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15364,6 +15679,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15477,6 +15795,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15614,6 +15935,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15751,6 +16075,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15888,6 +16215,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15993,6 +16323,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16114,6 +16447,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16227,6 +16563,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16340,6 +16679,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16459,6 +16801,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16572,6 +16917,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16709,6 +17057,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16846,6 +17197,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19535,6 +19889,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19672,6 +20029,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19809,6 +20169,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19946,6 +20309,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -20083,6 +20449,9 @@

    Produces

  • application/json

  • +
  • +

    application/yaml

    +
  • diff --git a/pkg/api/deep_copy_generated.go b/pkg/api/deep_copy_generated.go index c3d8273e0fd0..4bd65710dd3d 100644 --- a/pkg/api/deep_copy_generated.go +++ b/pkg/api/deep_copy_generated.go @@ -2778,6 +2778,11 @@ func deepCopy_runtime_RawExtension(in runtime.RawExtension, out *runtime.RawExte } else { out.RawJSON = nil } + if newVal, err := c.DeepCopy(in.Object); err != nil { + return err + } else { + out.Object = newVal.(runtime.Object) + } return nil } diff --git a/pkg/api/v1/conversion_generated.go b/pkg/api/v1/conversion_generated.go index c3d6674c82f7..b7f2d780287d 100644 --- a/pkg/api/v1/conversion_generated.go +++ b/pkg/api/v1/conversion_generated.go @@ -25,6 +25,7 @@ import ( resource "k8s.io/kubernetes/pkg/api/resource" unversioned "k8s.io/kubernetes/pkg/api/unversioned" conversion "k8s.io/kubernetes/pkg/conversion" + runtime "k8s.io/kubernetes/pkg/runtime" ) func autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in *api.AWSElasticBlockStoreVolumeSource, out *AWSElasticBlockStoreVolumeSource, s conversion.Scope) error { @@ -46,9 +47,6 @@ func autoConvert_api_Binding_To_v1_Binding(in *api.Binding, out *Binding, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Binding))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -153,9 +151,6 @@ func autoConvert_api_ComponentStatus_To_v1_ComponentStatus(in *api.ComponentStat if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ComponentStatus))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -180,9 +175,6 @@ func autoConvert_api_ComponentStatusList_To_v1_ComponentStatusList(in *api.Compo if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ComponentStatusList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -484,9 +476,6 @@ func autoConvert_api_DeleteOptions_To_v1_DeleteOptions(in *api.DeleteOptions, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.DeleteOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if in.GracePeriodSeconds != nil { out.GracePeriodSeconds = new(int64) *out.GracePeriodSeconds = *in.GracePeriodSeconds @@ -628,9 +617,6 @@ func autoConvert_api_Endpoints_To_v1_Endpoints(in *api.Endpoints, out *Endpoints if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Endpoints))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -655,9 +641,6 @@ func autoConvert_api_EndpointsList_To_v1_EndpointsList(in *api.EndpointsList, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.EndpointsList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -742,9 +725,6 @@ func autoConvert_api_Event_To_v1_Event(in *api.Event, out *Event, s conversion.S if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Event))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -775,9 +755,6 @@ func autoConvert_api_EventList_To_v1_EventList(in *api.EventList, out *EventList if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.EventList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1060,9 +1037,6 @@ func autoConvert_api_LimitRange_To_v1_LimitRange(in *api.LimitRange, out *LimitR if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.LimitRange))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1152,9 +1126,6 @@ func autoConvert_api_LimitRangeList_To_v1_LimitRangeList(in *api.LimitRangeList, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.LimitRangeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1200,14 +1171,18 @@ func autoConvert_api_List_To_v1_List(in *api.List, out *List, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.List))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } - if err := s.Convert(&in.Items, &out.Items, 0); err != nil { - return err + if in.Items != nil { + out.Items = make([]runtime.RawExtension, len(in.Items)) + for i := range in.Items { + if err := s.Convert(&in.Items[i], &out.Items[i], 0); err != nil { + return err + } + } + } else { + out.Items = nil } return nil } @@ -1220,9 +1195,6 @@ func autoConvert_api_ListOptions_To_v1_ListOptions(in *api.ListOptions, out *Lis if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ListOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_labels_Selector_To_string(&in.LabelSelector, &out.LabelSelector, s); err != nil { return err } @@ -1308,9 +1280,6 @@ func autoConvert_api_Namespace_To_v1_Namespace(in *api.Namespace, out *Namespace if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Namespace))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1331,9 +1300,6 @@ func autoConvert_api_NamespaceList_To_v1_NamespaceList(in *api.NamespaceList, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.NamespaceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1389,9 +1355,6 @@ func autoConvert_api_Node_To_v1_Node(in *api.Node, out *Node, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Node))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1460,9 +1423,6 @@ func autoConvert_api_NodeList_To_v1_NodeList(in *api.NodeList, out *NodeList, s if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.NodeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1676,9 +1636,6 @@ func autoConvert_api_PersistentVolume_To_v1_PersistentVolume(in *api.PersistentV if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolume))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1699,9 +1656,6 @@ func autoConvert_api_PersistentVolumeClaim_To_v1_PersistentVolumeClaim(in *api.P if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolumeClaim))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1722,9 +1676,6 @@ func autoConvert_api_PersistentVolumeClaimList_To_v1_PersistentVolumeClaimList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolumeClaimList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1817,9 +1768,6 @@ func autoConvert_api_PersistentVolumeList_To_v1_PersistentVolumeList(in *api.Per if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolumeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2021,9 +1969,6 @@ func autoConvert_api_Pod_To_v1_Pod(in *api.Pod, out *Pod, s conversion.Scope) er if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Pod))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2040,9 +1985,6 @@ func autoConvert_api_PodAttachOptions_To_v1_PodAttachOptions(in *api.PodAttachOp if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodAttachOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -2080,9 +2022,6 @@ func autoConvert_api_PodExecOptions_To_v1_PodExecOptions(in *api.PodExecOptions, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodExecOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -2107,9 +2046,6 @@ func autoConvert_api_PodList_To_v1_PodList(in *api.PodList, out *PodList, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2134,9 +2070,6 @@ func autoConvert_api_PodLogOptions_To_v1_PodLogOptions(in *api.PodLogOptions, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodLogOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Container = in.Container out.Follow = in.Follow out.Previous = in.Previous @@ -2179,9 +2112,6 @@ func autoConvert_api_PodProxyOptions_To_v1_PodProxyOptions(in *api.PodProxyOptio if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodProxyOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Path = in.Path return nil } @@ -2308,9 +2238,6 @@ func autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResu if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodStatusResult))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2328,9 +2255,6 @@ func autoConvert_api_PodTemplate_To_v1_PodTemplate(in *api.PodTemplate, out *Pod if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodTemplate))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2348,9 +2272,6 @@ func autoConvert_api_PodTemplateList_To_v1_PodTemplateList(in *api.PodTemplateLi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodTemplateList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2445,9 +2366,6 @@ func autoConvert_api_RangeAllocation_To_v1_RangeAllocation(in *api.RangeAllocati if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.RangeAllocation))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2466,9 +2384,6 @@ func autoConvert_api_ReplicationController_To_v1_ReplicationController(in *api.R if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ReplicationController))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2489,9 +2404,6 @@ func autoConvert_api_ReplicationControllerList_To_v1_ReplicationControllerList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ReplicationControllerList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2556,9 +2468,6 @@ func autoConvert_api_ResourceQuota_To_v1_ResourceQuota(in *api.ResourceQuota, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ResourceQuota))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2579,9 +2488,6 @@ func autoConvert_api_ResourceQuotaList_To_v1_ResourceQuotaList(in *api.ResourceQ if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ResourceQuotaList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2714,9 +2620,6 @@ func autoConvert_api_Secret_To_v1_Secret(in *api.Secret, out *Secret, s conversi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Secret))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2759,9 +2662,6 @@ func autoConvert_api_SecretList_To_v1_SecretList(in *api.SecretList, out *Secret if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.SecretList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2845,9 +2745,6 @@ func autoConvert_api_SerializedReference_To_v1_SerializedReference(in *api.Seria if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.SerializedReference))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectReference_To_v1_ObjectReference(&in.Reference, &out.Reference, s); err != nil { return err } @@ -2862,9 +2759,6 @@ func autoConvert_api_Service_To_v1_Service(in *api.Service, out *Service, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Service))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2885,9 +2779,6 @@ func autoConvert_api_ServiceAccount_To_v1_ServiceAccount(in *api.ServiceAccount, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ServiceAccount))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2922,9 +2813,6 @@ func autoConvert_api_ServiceAccountList_To_v1_ServiceAccountList(in *api.Service if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ServiceAccountList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2949,9 +2837,6 @@ func autoConvert_api_ServiceList_To_v1_ServiceList(in *api.ServiceList, out *Ser if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ServiceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -3252,9 +3137,6 @@ func autoConvert_unversioned_ExportOptions_To_v1_ExportOptions(in *unversioned.E if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*unversioned.ExportOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Export = in.Export out.Exact = in.Exact return nil @@ -3283,9 +3165,6 @@ func autoConvert_v1_Binding_To_api_Binding(in *Binding, out *api.Binding, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Binding))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -3390,9 +3269,6 @@ func autoConvert_v1_ComponentStatus_To_api_ComponentStatus(in *ComponentStatus, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ComponentStatus))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -3417,9 +3293,6 @@ func autoConvert_v1_ComponentStatusList_To_api_ComponentStatusList(in *Component if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ComponentStatusList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -3721,9 +3594,6 @@ func autoConvert_v1_DeleteOptions_To_api_DeleteOptions(in *DeleteOptions, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*DeleteOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if in.GracePeriodSeconds != nil { out.GracePeriodSeconds = new(int64) *out.GracePeriodSeconds = *in.GracePeriodSeconds @@ -3865,9 +3735,6 @@ func autoConvert_v1_Endpoints_To_api_Endpoints(in *Endpoints, out *api.Endpoints if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Endpoints))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -3892,9 +3759,6 @@ func autoConvert_v1_EndpointsList_To_api_EndpointsList(in *EndpointsList, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*EndpointsList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -3979,9 +3843,6 @@ func autoConvert_v1_Event_To_api_Event(in *Event, out *api.Event, s conversion.S if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Event))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4012,9 +3873,6 @@ func autoConvert_v1_EventList_To_api_EventList(in *EventList, out *api.EventList if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*EventList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4071,9 +3929,6 @@ func autoConvert_v1_ExportOptions_To_unversioned_ExportOptions(in *ExportOptions if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ExportOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Export = in.Export out.Exact = in.Exact return nil @@ -4313,9 +4168,6 @@ func autoConvert_v1_LimitRange_To_api_LimitRange(in *LimitRange, out *api.LimitR if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*LimitRange))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4360,9 +4212,6 @@ func autoConvert_v1_LimitRangeList_To_api_LimitRangeList(in *LimitRangeList, out if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*LimitRangeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4408,14 +4257,18 @@ func autoConvert_v1_List_To_api_List(in *List, out *api.List, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*List))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } - if err := s.Convert(&in.Items, &out.Items, 0); err != nil { - return err + if in.Items != nil { + out.Items = make([]runtime.Object, len(in.Items)) + for i := range in.Items { + if err := s.Convert(&in.Items[i], &out.Items[i], 0); err != nil { + return err + } + } + } else { + out.Items = nil } return nil } @@ -4428,9 +4281,6 @@ func autoConvert_v1_ListOptions_To_api_ListOptions(in *ListOptions, out *api.Lis if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ListOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_string_To_labels_Selector(&in.LabelSelector, &out.LabelSelector, s); err != nil { return err } @@ -4516,9 +4366,6 @@ func autoConvert_v1_Namespace_To_api_Namespace(in *Namespace, out *api.Namespace if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Namespace))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4539,9 +4386,6 @@ func autoConvert_v1_NamespaceList_To_api_NamespaceList(in *NamespaceList, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*NamespaceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4597,9 +4441,6 @@ func autoConvert_v1_Node_To_api_Node(in *Node, out *api.Node, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Node))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4668,9 +4509,6 @@ func autoConvert_v1_NodeList_To_api_NodeList(in *NodeList, out *api.NodeList, s if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*NodeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4866,9 +4704,6 @@ func autoConvert_v1_PersistentVolume_To_api_PersistentVolume(in *PersistentVolum if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolume))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4889,9 +4724,6 @@ func autoConvert_v1_PersistentVolumeClaim_To_api_PersistentVolumeClaim(in *Persi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolumeClaim))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4912,9 +4744,6 @@ func autoConvert_v1_PersistentVolumeClaimList_To_api_PersistentVolumeClaimList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolumeClaimList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4998,9 +4827,6 @@ func autoConvert_v1_PersistentVolumeList_To_api_PersistentVolumeList(in *Persist if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolumeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5193,9 +5019,6 @@ func autoConvert_v1_Pod_To_api_Pod(in *Pod, out *api.Pod, s conversion.Scope) er if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Pod))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5212,9 +5035,6 @@ func autoConvert_v1_PodAttachOptions_To_api_PodAttachOptions(in *PodAttachOption if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodAttachOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -5252,9 +5072,6 @@ func autoConvert_v1_PodExecOptions_To_api_PodExecOptions(in *PodExecOptions, out if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodExecOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -5279,9 +5096,6 @@ func autoConvert_v1_PodList_To_api_PodList(in *PodList, out *api.PodList, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5306,9 +5120,6 @@ func autoConvert_v1_PodLogOptions_To_api_PodLogOptions(in *PodLogOptions, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodLogOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Container = in.Container out.Follow = in.Follow out.Previous = in.Previous @@ -5351,9 +5162,6 @@ func autoConvert_v1_PodProxyOptions_To_api_PodProxyOptions(in *PodProxyOptions, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodProxyOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Path = in.Path return nil } @@ -5484,9 +5292,6 @@ func autoConvert_v1_PodStatusResult_To_api_PodStatusResult(in *PodStatusResult, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodStatusResult))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5504,9 +5309,6 @@ func autoConvert_v1_PodTemplate_To_api_PodTemplate(in *PodTemplate, out *api.Pod if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodTemplate))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5524,9 +5326,6 @@ func autoConvert_v1_PodTemplateList_To_api_PodTemplateList(in *PodTemplateList, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodTemplateList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5621,9 +5420,6 @@ func autoConvert_v1_RangeAllocation_To_api_RangeAllocation(in *RangeAllocation, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*RangeAllocation))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5642,9 +5438,6 @@ func autoConvert_v1_ReplicationController_To_api_ReplicationController(in *Repli if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ReplicationController))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5665,9 +5458,6 @@ func autoConvert_v1_ReplicationControllerList_To_api_ReplicationControllerList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ReplicationControllerList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5730,9 +5520,6 @@ func autoConvert_v1_ResourceQuota_To_api_ResourceQuota(in *ResourceQuota, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ResourceQuota))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5753,9 +5540,6 @@ func autoConvert_v1_ResourceQuotaList_To_api_ResourceQuotaList(in *ResourceQuota if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ResourceQuotaList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5843,9 +5627,6 @@ func autoConvert_v1_Secret_To_api_Secret(in *Secret, out *api.Secret, s conversi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Secret))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5888,9 +5669,6 @@ func autoConvert_v1_SecretList_To_api_SecretList(in *SecretList, out *api.Secret if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*SecretList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5974,9 +5752,6 @@ func autoConvert_v1_SerializedReference_To_api_SerializedReference(in *Serialize if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*SerializedReference))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectReference_To_api_ObjectReference(&in.Reference, &out.Reference, s); err != nil { return err } @@ -5991,9 +5766,6 @@ func autoConvert_v1_Service_To_api_Service(in *Service, out *api.Service, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Service))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -6014,9 +5786,6 @@ func autoConvert_v1_ServiceAccount_To_api_ServiceAccount(in *ServiceAccount, out if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ServiceAccount))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -6051,9 +5820,6 @@ func autoConvert_v1_ServiceAccountList_To_api_ServiceAccountList(in *ServiceAcco if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ServiceAccountList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -6078,9 +5844,6 @@ func autoConvert_v1_ServiceList_To_api_ServiceList(in *ServiceList, out *api.Ser if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ServiceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } diff --git a/pkg/api/v1/deep_copy_generated.go b/pkg/api/v1/deep_copy_generated.go index 2bc06f6daa66..581e006ef94c 100644 --- a/pkg/api/v1/deep_copy_generated.go +++ b/pkg/api/v1/deep_copy_generated.go @@ -2471,6 +2471,13 @@ func deepCopy_runtime_RawExtension(in runtime.RawExtension, out *runtime.RawExte } else { out.RawJSON = nil } + if newVal, err := c.DeepCopy(in.Object); err != nil { + return err + } else if newVal == nil { + out.Object = nil + } else { + out.Object = newVal.(runtime.Object) + } return nil } diff --git a/pkg/api/v1/types.generated.go b/pkg/api/v1/types.generated.go index a013450798ac..81ff09a6cb2a 100644 --- a/pkg/api/v1/types.generated.go +++ b/pkg/api/v1/types.generated.go @@ -48803,7 +48803,7 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten yyrg3828 := len(yyv3828) > 0 yyv23828 := yyv3828 - yyrl3828, yyrt3828 = z.DecInferLen(yyl3828, z.DecBasicHandle().MaxInitLen, 24) + yyrl3828, yyrt3828 = z.DecInferLen(yyl3828, z.DecBasicHandle().MaxInitLen, 40) if yyrt3828 { if yyrl3828 <= cap(yyv3828) { yyv3828 = yyv3828[:yyrl3828] diff --git a/pkg/apis/componentconfig/deep_copy_generated.go b/pkg/apis/componentconfig/deep_copy_generated.go index 431013d314c8..c9a7b31e2f01 100644 --- a/pkg/apis/componentconfig/deep_copy_generated.go +++ b/pkg/apis/componentconfig/deep_copy_generated.go @@ -18,51 +18,10 @@ limitations under the License. package componentconfig -import ( - api "k8s.io/kubernetes/pkg/api" - unversioned "k8s.io/kubernetes/pkg/api/unversioned" - conversion "k8s.io/kubernetes/pkg/conversion" -) - -func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { - out.Kind = in.Kind - out.APIVersion = in.APIVersion - return nil -} - -func deepCopy_componentconfig_KubeProxyConfiguration(in KubeProxyConfiguration, out *KubeProxyConfiguration, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - out.BindAddress = in.BindAddress - out.CleanupIPTables = in.CleanupIPTables - out.HealthzBindAddress = in.HealthzBindAddress - out.HealthzPort = in.HealthzPort - out.HostnameOverride = in.HostnameOverride - out.IPTablesSyncePeriodSeconds = in.IPTablesSyncePeriodSeconds - out.KubeAPIBurst = in.KubeAPIBurst - out.KubeAPIQPS = in.KubeAPIQPS - out.KubeconfigPath = in.KubeconfigPath - out.MasqueradeAll = in.MasqueradeAll - out.Master = in.Master - if in.OOMScoreAdj != nil { - out.OOMScoreAdj = new(int) - *out.OOMScoreAdj = *in.OOMScoreAdj - } else { - out.OOMScoreAdj = nil - } - out.Mode = in.Mode - out.PortRange = in.PortRange - out.ResourceContainer = in.ResourceContainer - out.UDPTimeoutMilliseconds = in.UDPTimeoutMilliseconds - return nil -} +import api "k8s.io/kubernetes/pkg/api" func init() { - err := api.Scheme.AddGeneratedDeepCopyFuncs( - deepCopy_unversioned_TypeMeta, - deepCopy_componentconfig_KubeProxyConfiguration, - ) + err := api.Scheme.AddGeneratedDeepCopyFuncs() if err != nil { // if one of the deep copy functions is malformed, detect it immediately. panic(err) diff --git a/pkg/apis/extensions/deep_copy_generated.go b/pkg/apis/extensions/deep_copy_generated.go index aa29f5272ba1..ff5898cfe0d9 100644 --- a/pkg/apis/extensions/deep_copy_generated.go +++ b/pkg/apis/extensions/deep_copy_generated.go @@ -18,1869 +18,10 @@ limitations under the License. package extensions -import ( - time "time" - - api "k8s.io/kubernetes/pkg/api" - resource "k8s.io/kubernetes/pkg/api/resource" - unversioned "k8s.io/kubernetes/pkg/api/unversioned" - conversion "k8s.io/kubernetes/pkg/conversion" - intstr "k8s.io/kubernetes/pkg/util/intstr" - inf "speter.net/go/exp/math/dec/inf" -) - -func deepCopy_api_AWSElasticBlockStoreVolumeSource(in api.AWSElasticBlockStoreVolumeSource, out *api.AWSElasticBlockStoreVolumeSource, c *conversion.Cloner) error { - out.VolumeID = in.VolumeID - out.FSType = in.FSType - out.Partition = in.Partition - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_Capabilities(in api.Capabilities, out *api.Capabilities, c *conversion.Cloner) error { - if in.Add != nil { - out.Add = make([]api.Capability, len(in.Add)) - for i := range in.Add { - out.Add[i] = in.Add[i] - } - } else { - out.Add = nil - } - if in.Drop != nil { - out.Drop = make([]api.Capability, len(in.Drop)) - for i := range in.Drop { - out.Drop[i] = in.Drop[i] - } - } else { - out.Drop = nil - } - return nil -} - -func deepCopy_api_CephFSVolumeSource(in api.CephFSVolumeSource, out *api.CephFSVolumeSource, c *conversion.Cloner) error { - if in.Monitors != nil { - out.Monitors = make([]string, len(in.Monitors)) - for i := range in.Monitors { - out.Monitors[i] = in.Monitors[i] - } - } else { - out.Monitors = nil - } - out.User = in.User - out.SecretFile = in.SecretFile - if in.SecretRef != nil { - out.SecretRef = new(api.LocalObjectReference) - if err := deepCopy_api_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { - return err - } - } else { - out.SecretRef = nil - } - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_CinderVolumeSource(in api.CinderVolumeSource, out *api.CinderVolumeSource, c *conversion.Cloner) error { - out.VolumeID = in.VolumeID - out.FSType = in.FSType - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_ConfigMapKeySelector(in api.ConfigMapKeySelector, out *api.ConfigMapKeySelector, c *conversion.Cloner) error { - if err := deepCopy_api_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { - return err - } - out.Key = in.Key - return nil -} - -func deepCopy_api_Container(in api.Container, out *api.Container, c *conversion.Cloner) error { - out.Name = in.Name - out.Image = in.Image - if in.Command != nil { - out.Command = make([]string, len(in.Command)) - for i := range in.Command { - out.Command[i] = in.Command[i] - } - } else { - out.Command = nil - } - if in.Args != nil { - out.Args = make([]string, len(in.Args)) - for i := range in.Args { - out.Args[i] = in.Args[i] - } - } else { - out.Args = nil - } - out.WorkingDir = in.WorkingDir - if in.Ports != nil { - out.Ports = make([]api.ContainerPort, len(in.Ports)) - for i := range in.Ports { - if err := deepCopy_api_ContainerPort(in.Ports[i], &out.Ports[i], c); err != nil { - return err - } - } - } else { - out.Ports = nil - } - if in.Env != nil { - out.Env = make([]api.EnvVar, len(in.Env)) - for i := range in.Env { - if err := deepCopy_api_EnvVar(in.Env[i], &out.Env[i], c); err != nil { - return err - } - } - } else { - out.Env = nil - } - if err := deepCopy_api_ResourceRequirements(in.Resources, &out.Resources, c); err != nil { - return err - } - if in.VolumeMounts != nil { - out.VolumeMounts = make([]api.VolumeMount, len(in.VolumeMounts)) - for i := range in.VolumeMounts { - if err := deepCopy_api_VolumeMount(in.VolumeMounts[i], &out.VolumeMounts[i], c); err != nil { - return err - } - } - } else { - out.VolumeMounts = nil - } - if in.LivenessProbe != nil { - out.LivenessProbe = new(api.Probe) - if err := deepCopy_api_Probe(*in.LivenessProbe, out.LivenessProbe, c); err != nil { - return err - } - } else { - out.LivenessProbe = nil - } - if in.ReadinessProbe != nil { - out.ReadinessProbe = new(api.Probe) - if err := deepCopy_api_Probe(*in.ReadinessProbe, out.ReadinessProbe, c); err != nil { - return err - } - } else { - out.ReadinessProbe = nil - } - if in.Lifecycle != nil { - out.Lifecycle = new(api.Lifecycle) - if err := deepCopy_api_Lifecycle(*in.Lifecycle, out.Lifecycle, c); err != nil { - return err - } - } else { - out.Lifecycle = nil - } - out.TerminationMessagePath = in.TerminationMessagePath - out.ImagePullPolicy = in.ImagePullPolicy - if in.SecurityContext != nil { - out.SecurityContext = new(api.SecurityContext) - if err := deepCopy_api_SecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { - return err - } - } else { - out.SecurityContext = nil - } - out.Stdin = in.Stdin - out.StdinOnce = in.StdinOnce - out.TTY = in.TTY - return nil -} - -func deepCopy_api_ContainerPort(in api.ContainerPort, out *api.ContainerPort, c *conversion.Cloner) error { - out.Name = in.Name - out.HostPort = in.HostPort - out.ContainerPort = in.ContainerPort - out.Protocol = in.Protocol - out.HostIP = in.HostIP - return nil -} - -func deepCopy_api_DownwardAPIVolumeFile(in api.DownwardAPIVolumeFile, out *api.DownwardAPIVolumeFile, c *conversion.Cloner) error { - out.Path = in.Path - if err := deepCopy_api_ObjectFieldSelector(in.FieldRef, &out.FieldRef, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_DownwardAPIVolumeSource(in api.DownwardAPIVolumeSource, out *api.DownwardAPIVolumeSource, c *conversion.Cloner) error { - if in.Items != nil { - out.Items = make([]api.DownwardAPIVolumeFile, len(in.Items)) - for i := range in.Items { - if err := deepCopy_api_DownwardAPIVolumeFile(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_api_EmptyDirVolumeSource(in api.EmptyDirVolumeSource, out *api.EmptyDirVolumeSource, c *conversion.Cloner) error { - out.Medium = in.Medium - return nil -} - -func deepCopy_api_EnvVar(in api.EnvVar, out *api.EnvVar, c *conversion.Cloner) error { - out.Name = in.Name - out.Value = in.Value - if in.ValueFrom != nil { - out.ValueFrom = new(api.EnvVarSource) - if err := deepCopy_api_EnvVarSource(*in.ValueFrom, out.ValueFrom, c); err != nil { - return err - } - } else { - out.ValueFrom = nil - } - return nil -} - -func deepCopy_api_EnvVarSource(in api.EnvVarSource, out *api.EnvVarSource, c *conversion.Cloner) error { - if in.FieldRef != nil { - out.FieldRef = new(api.ObjectFieldSelector) - if err := deepCopy_api_ObjectFieldSelector(*in.FieldRef, out.FieldRef, c); err != nil { - return err - } - } else { - out.FieldRef = nil - } - if in.ConfigMapKeyRef != nil { - out.ConfigMapKeyRef = new(api.ConfigMapKeySelector) - if err := deepCopy_api_ConfigMapKeySelector(*in.ConfigMapKeyRef, out.ConfigMapKeyRef, c); err != nil { - return err - } - } else { - out.ConfigMapKeyRef = nil - } - if in.SecretKeyRef != nil { - out.SecretKeyRef = new(api.SecretKeySelector) - if err := deepCopy_api_SecretKeySelector(*in.SecretKeyRef, out.SecretKeyRef, c); err != nil { - return err - } - } else { - out.SecretKeyRef = nil - } - return nil -} - -func deepCopy_api_ExecAction(in api.ExecAction, out *api.ExecAction, c *conversion.Cloner) error { - if in.Command != nil { - out.Command = make([]string, len(in.Command)) - for i := range in.Command { - out.Command[i] = in.Command[i] - } - } else { - out.Command = nil - } - return nil -} - -func deepCopy_api_FCVolumeSource(in api.FCVolumeSource, out *api.FCVolumeSource, c *conversion.Cloner) error { - if in.TargetWWNs != nil { - out.TargetWWNs = make([]string, len(in.TargetWWNs)) - for i := range in.TargetWWNs { - out.TargetWWNs[i] = in.TargetWWNs[i] - } - } else { - out.TargetWWNs = nil - } - if in.Lun != nil { - out.Lun = new(int) - *out.Lun = *in.Lun - } else { - out.Lun = nil - } - out.FSType = in.FSType - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_FlexVolumeSource(in api.FlexVolumeSource, out *api.FlexVolumeSource, c *conversion.Cloner) error { - out.Driver = in.Driver - out.FSType = in.FSType - if in.SecretRef != nil { - out.SecretRef = new(api.LocalObjectReference) - if err := deepCopy_api_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { - return err - } - } else { - out.SecretRef = nil - } - out.ReadOnly = in.ReadOnly - if in.Options != nil { - out.Options = make(map[string]string) - for key, val := range in.Options { - out.Options[key] = val - } - } else { - out.Options = nil - } - return nil -} - -func deepCopy_api_FlockerVolumeSource(in api.FlockerVolumeSource, out *api.FlockerVolumeSource, c *conversion.Cloner) error { - out.DatasetName = in.DatasetName - return nil -} - -func deepCopy_api_GCEPersistentDiskVolumeSource(in api.GCEPersistentDiskVolumeSource, out *api.GCEPersistentDiskVolumeSource, c *conversion.Cloner) error { - out.PDName = in.PDName - out.FSType = in.FSType - out.Partition = in.Partition - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_GitRepoVolumeSource(in api.GitRepoVolumeSource, out *api.GitRepoVolumeSource, c *conversion.Cloner) error { - out.Repository = in.Repository - out.Revision = in.Revision - out.Directory = in.Directory - return nil -} - -func deepCopy_api_GlusterfsVolumeSource(in api.GlusterfsVolumeSource, out *api.GlusterfsVolumeSource, c *conversion.Cloner) error { - out.EndpointsName = in.EndpointsName - out.Path = in.Path - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_HTTPGetAction(in api.HTTPGetAction, out *api.HTTPGetAction, c *conversion.Cloner) error { - out.Path = in.Path - if err := deepCopy_intstr_IntOrString(in.Port, &out.Port, c); err != nil { - return err - } - out.Host = in.Host - out.Scheme = in.Scheme - return nil -} - -func deepCopy_api_Handler(in api.Handler, out *api.Handler, c *conversion.Cloner) error { - if in.Exec != nil { - out.Exec = new(api.ExecAction) - if err := deepCopy_api_ExecAction(*in.Exec, out.Exec, c); err != nil { - return err - } - } else { - out.Exec = nil - } - if in.HTTPGet != nil { - out.HTTPGet = new(api.HTTPGetAction) - if err := deepCopy_api_HTTPGetAction(*in.HTTPGet, out.HTTPGet, c); err != nil { - return err - } - } else { - out.HTTPGet = nil - } - if in.TCPSocket != nil { - out.TCPSocket = new(api.TCPSocketAction) - if err := deepCopy_api_TCPSocketAction(*in.TCPSocket, out.TCPSocket, c); err != nil { - return err - } - } else { - out.TCPSocket = nil - } - return nil -} - -func deepCopy_api_HostPathVolumeSource(in api.HostPathVolumeSource, out *api.HostPathVolumeSource, c *conversion.Cloner) error { - out.Path = in.Path - return nil -} - -func deepCopy_api_ISCSIVolumeSource(in api.ISCSIVolumeSource, out *api.ISCSIVolumeSource, c *conversion.Cloner) error { - out.TargetPortal = in.TargetPortal - out.IQN = in.IQN - out.Lun = in.Lun - out.ISCSIInterface = in.ISCSIInterface - out.FSType = in.FSType - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_Lifecycle(in api.Lifecycle, out *api.Lifecycle, c *conversion.Cloner) error { - if in.PostStart != nil { - out.PostStart = new(api.Handler) - if err := deepCopy_api_Handler(*in.PostStart, out.PostStart, c); err != nil { - return err - } - } else { - out.PostStart = nil - } - if in.PreStop != nil { - out.PreStop = new(api.Handler) - if err := deepCopy_api_Handler(*in.PreStop, out.PreStop, c); err != nil { - return err - } - } else { - out.PreStop = nil - } - return nil -} - -func deepCopy_api_LoadBalancerIngress(in api.LoadBalancerIngress, out *api.LoadBalancerIngress, c *conversion.Cloner) error { - out.IP = in.IP - out.Hostname = in.Hostname - return nil -} - -func deepCopy_api_LoadBalancerStatus(in api.LoadBalancerStatus, out *api.LoadBalancerStatus, c *conversion.Cloner) error { - if in.Ingress != nil { - out.Ingress = make([]api.LoadBalancerIngress, len(in.Ingress)) - for i := range in.Ingress { - if err := deepCopy_api_LoadBalancerIngress(in.Ingress[i], &out.Ingress[i], c); err != nil { - return err - } - } - } else { - out.Ingress = nil - } - return nil -} - -func deepCopy_api_LocalObjectReference(in api.LocalObjectReference, out *api.LocalObjectReference, c *conversion.Cloner) error { - out.Name = in.Name - return nil -} - -func deepCopy_api_NFSVolumeSource(in api.NFSVolumeSource, out *api.NFSVolumeSource, c *conversion.Cloner) error { - out.Server = in.Server - out.Path = in.Path - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_ObjectFieldSelector(in api.ObjectFieldSelector, out *api.ObjectFieldSelector, c *conversion.Cloner) error { - out.APIVersion = in.APIVersion - out.FieldPath = in.FieldPath - return nil -} - -func deepCopy_api_ObjectMeta(in api.ObjectMeta, out *api.ObjectMeta, c *conversion.Cloner) error { - out.Name = in.Name - out.GenerateName = in.GenerateName - out.Namespace = in.Namespace - out.SelfLink = in.SelfLink - out.UID = in.UID - out.ResourceVersion = in.ResourceVersion - out.Generation = in.Generation - if err := deepCopy_unversioned_Time(in.CreationTimestamp, &out.CreationTimestamp, c); err != nil { - return err - } - if in.DeletionTimestamp != nil { - out.DeletionTimestamp = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.DeletionTimestamp, out.DeletionTimestamp, c); err != nil { - return err - } - } else { - out.DeletionTimestamp = nil - } - if in.DeletionGracePeriodSeconds != nil { - out.DeletionGracePeriodSeconds = new(int64) - *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds - } else { - out.DeletionGracePeriodSeconds = nil - } - if in.Labels != nil { - out.Labels = make(map[string]string) - for key, val := range in.Labels { - out.Labels[key] = val - } - } else { - out.Labels = nil - } - if in.Annotations != nil { - out.Annotations = make(map[string]string) - for key, val := range in.Annotations { - out.Annotations[key] = val - } - } else { - out.Annotations = nil - } - return nil -} - -func deepCopy_api_PersistentVolumeClaimVolumeSource(in api.PersistentVolumeClaimVolumeSource, out *api.PersistentVolumeClaimVolumeSource, c *conversion.Cloner) error { - out.ClaimName = in.ClaimName - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecurityContext, c *conversion.Cloner) error { - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC - if in.SELinuxOptions != nil { - out.SELinuxOptions = new(api.SELinuxOptions) - if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { - return err - } - } else { - out.SELinuxOptions = nil - } - if in.RunAsUser != nil { - out.RunAsUser = new(int64) - *out.RunAsUser = *in.RunAsUser - } else { - out.RunAsUser = nil - } - if in.RunAsNonRoot != nil { - out.RunAsNonRoot = new(bool) - *out.RunAsNonRoot = *in.RunAsNonRoot - } else { - out.RunAsNonRoot = nil - } - if in.SupplementalGroups != nil { - out.SupplementalGroups = make([]int64, len(in.SupplementalGroups)) - for i := range in.SupplementalGroups { - out.SupplementalGroups[i] = in.SupplementalGroups[i] - } - } else { - out.SupplementalGroups = nil - } - if in.FSGroup != nil { - out.FSGroup = new(int64) - *out.FSGroup = *in.FSGroup - } else { - out.FSGroup = nil - } - return nil -} - -func deepCopy_api_PodSpec(in api.PodSpec, out *api.PodSpec, c *conversion.Cloner) error { - if in.Volumes != nil { - out.Volumes = make([]api.Volume, len(in.Volumes)) - for i := range in.Volumes { - if err := deepCopy_api_Volume(in.Volumes[i], &out.Volumes[i], c); err != nil { - return err - } - } - } else { - out.Volumes = nil - } - if in.Containers != nil { - out.Containers = make([]api.Container, len(in.Containers)) - for i := range in.Containers { - if err := deepCopy_api_Container(in.Containers[i], &out.Containers[i], c); err != nil { - return err - } - } - } else { - out.Containers = nil - } - out.RestartPolicy = in.RestartPolicy - if in.TerminationGracePeriodSeconds != nil { - out.TerminationGracePeriodSeconds = new(int64) - *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds - } else { - out.TerminationGracePeriodSeconds = nil - } - if in.ActiveDeadlineSeconds != nil { - out.ActiveDeadlineSeconds = new(int64) - *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds - } else { - out.ActiveDeadlineSeconds = nil - } - out.DNSPolicy = in.DNSPolicy - if in.NodeSelector != nil { - out.NodeSelector = make(map[string]string) - for key, val := range in.NodeSelector { - out.NodeSelector[key] = val - } - } else { - out.NodeSelector = nil - } - out.ServiceAccountName = in.ServiceAccountName - out.NodeName = in.NodeName - if in.SecurityContext != nil { - out.SecurityContext = new(api.PodSecurityContext) - if err := deepCopy_api_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { - return err - } - } else { - out.SecurityContext = nil - } - if in.ImagePullSecrets != nil { - out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) - for i := range in.ImagePullSecrets { - if err := deepCopy_api_LocalObjectReference(in.ImagePullSecrets[i], &out.ImagePullSecrets[i], c); err != nil { - return err - } - } - } else { - out.ImagePullSecrets = nil - } - return nil -} - -func deepCopy_api_PodTemplateSpec(in api.PodTemplateSpec, out *api.PodTemplateSpec, c *conversion.Cloner) error { - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_api_PodSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_Probe(in api.Probe, out *api.Probe, c *conversion.Cloner) error { - if err := deepCopy_api_Handler(in.Handler, &out.Handler, c); err != nil { - return err - } - out.InitialDelaySeconds = in.InitialDelaySeconds - out.TimeoutSeconds = in.TimeoutSeconds - out.PeriodSeconds = in.PeriodSeconds - out.SuccessThreshold = in.SuccessThreshold - out.FailureThreshold = in.FailureThreshold - return nil -} - -func deepCopy_api_RBDVolumeSource(in api.RBDVolumeSource, out *api.RBDVolumeSource, c *conversion.Cloner) error { - if in.CephMonitors != nil { - out.CephMonitors = make([]string, len(in.CephMonitors)) - for i := range in.CephMonitors { - out.CephMonitors[i] = in.CephMonitors[i] - } - } else { - out.CephMonitors = nil - } - out.RBDImage = in.RBDImage - out.FSType = in.FSType - out.RBDPool = in.RBDPool - out.RadosUser = in.RadosUser - out.Keyring = in.Keyring - if in.SecretRef != nil { - out.SecretRef = new(api.LocalObjectReference) - if err := deepCopy_api_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { - return err - } - } else { - out.SecretRef = nil - } - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_ResourceRequirements(in api.ResourceRequirements, out *api.ResourceRequirements, c *conversion.Cloner) error { - if in.Limits != nil { - out.Limits = make(api.ResourceList) - for key, val := range in.Limits { - newVal := new(resource.Quantity) - if err := deepCopy_resource_Quantity(val, newVal, c); err != nil { - return err - } - out.Limits[key] = *newVal - } - } else { - out.Limits = nil - } - if in.Requests != nil { - out.Requests = make(api.ResourceList) - for key, val := range in.Requests { - newVal := new(resource.Quantity) - if err := deepCopy_resource_Quantity(val, newVal, c); err != nil { - return err - } - out.Requests[key] = *newVal - } - } else { - out.Requests = nil - } - return nil -} - -func deepCopy_api_SELinuxOptions(in api.SELinuxOptions, out *api.SELinuxOptions, c *conversion.Cloner) error { - out.User = in.User - out.Role = in.Role - out.Type = in.Type - out.Level = in.Level - return nil -} - -func deepCopy_api_SecretKeySelector(in api.SecretKeySelector, out *api.SecretKeySelector, c *conversion.Cloner) error { - if err := deepCopy_api_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { - return err - } - out.Key = in.Key - return nil -} - -func deepCopy_api_SecretVolumeSource(in api.SecretVolumeSource, out *api.SecretVolumeSource, c *conversion.Cloner) error { - out.SecretName = in.SecretName - return nil -} - -func deepCopy_api_SecurityContext(in api.SecurityContext, out *api.SecurityContext, c *conversion.Cloner) error { - if in.Capabilities != nil { - out.Capabilities = new(api.Capabilities) - if err := deepCopy_api_Capabilities(*in.Capabilities, out.Capabilities, c); err != nil { - return err - } - } else { - out.Capabilities = nil - } - if in.Privileged != nil { - out.Privileged = new(bool) - *out.Privileged = *in.Privileged - } else { - out.Privileged = nil - } - if in.SELinuxOptions != nil { - out.SELinuxOptions = new(api.SELinuxOptions) - if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { - return err - } - } else { - out.SELinuxOptions = nil - } - if in.RunAsUser != nil { - out.RunAsUser = new(int64) - *out.RunAsUser = *in.RunAsUser - } else { - out.RunAsUser = nil - } - if in.RunAsNonRoot != nil { - out.RunAsNonRoot = new(bool) - *out.RunAsNonRoot = *in.RunAsNonRoot - } else { - out.RunAsNonRoot = nil - } - return nil -} - -func deepCopy_api_TCPSocketAction(in api.TCPSocketAction, out *api.TCPSocketAction, c *conversion.Cloner) error { - if err := deepCopy_intstr_IntOrString(in.Port, &out.Port, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_Volume(in api.Volume, out *api.Volume, c *conversion.Cloner) error { - out.Name = in.Name - if err := deepCopy_api_VolumeSource(in.VolumeSource, &out.VolumeSource, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_VolumeMount(in api.VolumeMount, out *api.VolumeMount, c *conversion.Cloner) error { - out.Name = in.Name - out.ReadOnly = in.ReadOnly - out.MountPath = in.MountPath - return nil -} - -func deepCopy_api_VolumeSource(in api.VolumeSource, out *api.VolumeSource, c *conversion.Cloner) error { - if in.HostPath != nil { - out.HostPath = new(api.HostPathVolumeSource) - if err := deepCopy_api_HostPathVolumeSource(*in.HostPath, out.HostPath, c); err != nil { - return err - } - } else { - out.HostPath = nil - } - if in.EmptyDir != nil { - out.EmptyDir = new(api.EmptyDirVolumeSource) - if err := deepCopy_api_EmptyDirVolumeSource(*in.EmptyDir, out.EmptyDir, c); err != nil { - return err - } - } else { - out.EmptyDir = nil - } - if in.GCEPersistentDisk != nil { - out.GCEPersistentDisk = new(api.GCEPersistentDiskVolumeSource) - if err := deepCopy_api_GCEPersistentDiskVolumeSource(*in.GCEPersistentDisk, out.GCEPersistentDisk, c); err != nil { - return err - } - } else { - out.GCEPersistentDisk = nil - } - if in.AWSElasticBlockStore != nil { - out.AWSElasticBlockStore = new(api.AWSElasticBlockStoreVolumeSource) - if err := deepCopy_api_AWSElasticBlockStoreVolumeSource(*in.AWSElasticBlockStore, out.AWSElasticBlockStore, c); err != nil { - return err - } - } else { - out.AWSElasticBlockStore = nil - } - if in.GitRepo != nil { - out.GitRepo = new(api.GitRepoVolumeSource) - if err := deepCopy_api_GitRepoVolumeSource(*in.GitRepo, out.GitRepo, c); err != nil { - return err - } - } else { - out.GitRepo = nil - } - if in.Secret != nil { - out.Secret = new(api.SecretVolumeSource) - if err := deepCopy_api_SecretVolumeSource(*in.Secret, out.Secret, c); err != nil { - return err - } - } else { - out.Secret = nil - } - if in.NFS != nil { - out.NFS = new(api.NFSVolumeSource) - if err := deepCopy_api_NFSVolumeSource(*in.NFS, out.NFS, c); err != nil { - return err - } - } else { - out.NFS = nil - } - if in.ISCSI != nil { - out.ISCSI = new(api.ISCSIVolumeSource) - if err := deepCopy_api_ISCSIVolumeSource(*in.ISCSI, out.ISCSI, c); err != nil { - return err - } - } else { - out.ISCSI = nil - } - if in.Glusterfs != nil { - out.Glusterfs = new(api.GlusterfsVolumeSource) - if err := deepCopy_api_GlusterfsVolumeSource(*in.Glusterfs, out.Glusterfs, c); err != nil { - return err - } - } else { - out.Glusterfs = nil - } - if in.PersistentVolumeClaim != nil { - out.PersistentVolumeClaim = new(api.PersistentVolumeClaimVolumeSource) - if err := deepCopy_api_PersistentVolumeClaimVolumeSource(*in.PersistentVolumeClaim, out.PersistentVolumeClaim, c); err != nil { - return err - } - } else { - out.PersistentVolumeClaim = nil - } - if in.RBD != nil { - out.RBD = new(api.RBDVolumeSource) - if err := deepCopy_api_RBDVolumeSource(*in.RBD, out.RBD, c); err != nil { - return err - } - } else { - out.RBD = nil - } - if in.FlexVolume != nil { - out.FlexVolume = new(api.FlexVolumeSource) - if err := deepCopy_api_FlexVolumeSource(*in.FlexVolume, out.FlexVolume, c); err != nil { - return err - } - } else { - out.FlexVolume = nil - } - if in.Cinder != nil { - out.Cinder = new(api.CinderVolumeSource) - if err := deepCopy_api_CinderVolumeSource(*in.Cinder, out.Cinder, c); err != nil { - return err - } - } else { - out.Cinder = nil - } - if in.CephFS != nil { - out.CephFS = new(api.CephFSVolumeSource) - if err := deepCopy_api_CephFSVolumeSource(*in.CephFS, out.CephFS, c); err != nil { - return err - } - } else { - out.CephFS = nil - } - if in.Flocker != nil { - out.Flocker = new(api.FlockerVolumeSource) - if err := deepCopy_api_FlockerVolumeSource(*in.Flocker, out.Flocker, c); err != nil { - return err - } - } else { - out.Flocker = nil - } - if in.DownwardAPI != nil { - out.DownwardAPI = new(api.DownwardAPIVolumeSource) - if err := deepCopy_api_DownwardAPIVolumeSource(*in.DownwardAPI, out.DownwardAPI, c); err != nil { - return err - } - } else { - out.DownwardAPI = nil - } - if in.FC != nil { - out.FC = new(api.FCVolumeSource) - if err := deepCopy_api_FCVolumeSource(*in.FC, out.FC, c); err != nil { - return err - } - } else { - out.FC = nil - } - return nil -} - -func deepCopy_resource_Quantity(in resource.Quantity, out *resource.Quantity, c *conversion.Cloner) error { - if in.Amount != nil { - if newVal, err := c.DeepCopy(in.Amount); err != nil { - return err - } else { - out.Amount = newVal.(*inf.Dec) - } - } else { - out.Amount = nil - } - out.Format = in.Format - return nil -} - -func deepCopy_unversioned_ListMeta(in unversioned.ListMeta, out *unversioned.ListMeta, c *conversion.Cloner) error { - out.SelfLink = in.SelfLink - out.ResourceVersion = in.ResourceVersion - return nil -} - -func deepCopy_unversioned_Time(in unversioned.Time, out *unversioned.Time, c *conversion.Cloner) error { - if newVal, err := c.DeepCopy(in.Time); err != nil { - return err - } else { - out.Time = newVal.(time.Time) - } - return nil -} - -func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { - out.Kind = in.Kind - out.APIVersion = in.APIVersion - return nil -} - -func deepCopy_extensions_APIVersion(in APIVersion, out *APIVersion, c *conversion.Cloner) error { - out.Name = in.Name - out.APIGroup = in.APIGroup - return nil -} - -func deepCopy_extensions_CPUTargetUtilization(in CPUTargetUtilization, out *CPUTargetUtilization, c *conversion.Cloner) error { - out.TargetPercentage = in.TargetPercentage - return nil -} - -func deepCopy_extensions_ClusterAutoscaler(in ClusterAutoscaler, out *ClusterAutoscaler, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_ClusterAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_ClusterAutoscalerList(in ClusterAutoscalerList, out *ClusterAutoscalerList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ClusterAutoscaler, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ClusterAutoscaler(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_ClusterAutoscalerSpec(in ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, c *conversion.Cloner) error { - out.MinNodes = in.MinNodes - out.MaxNodes = in.MaxNodes - if in.TargetUtilization != nil { - out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) - for i := range in.TargetUtilization { - if err := deepCopy_extensions_NodeUtilization(in.TargetUtilization[i], &out.TargetUtilization[i], c); err != nil { - return err - } - } - } else { - out.TargetUtilization = nil - } - return nil -} - -func deepCopy_extensions_ConfigMap(in ConfigMap, out *ConfigMap, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if in.Data != nil { - out.Data = make(map[string]string) - for key, val := range in.Data { - out.Data[key] = val - } - } else { - out.Data = nil - } - return nil -} - -func deepCopy_extensions_ConfigMapList(in ConfigMapList, out *ConfigMapList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ConfigMap, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ConfigMap(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_DaemonSet(in DaemonSet, out *DaemonSet, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_DaemonSetSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_DaemonSetStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_DaemonSetList(in DaemonSetList, out *DaemonSetList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]DaemonSet, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_DaemonSet(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_DaemonSetSpec(in DaemonSetSpec, out *DaemonSetSpec, c *conversion.Cloner) error { - if in.Selector != nil { - out.Selector = new(LabelSelector) - if err := deepCopy_extensions_LabelSelector(*in.Selector, out.Selector, c); err != nil { - return err - } - } else { - out.Selector = nil - } - if in.Template != nil { - out.Template = new(api.PodTemplateSpec) - if err := deepCopy_api_PodTemplateSpec(*in.Template, out.Template, c); err != nil { - return err - } - } else { - out.Template = nil - } - if err := deepCopy_extensions_DaemonSetUpdateStrategy(in.UpdateStrategy, &out.UpdateStrategy, c); err != nil { - return err - } - out.UniqueLabelKey = in.UniqueLabelKey - return nil -} - -func deepCopy_extensions_DaemonSetStatus(in DaemonSetStatus, out *DaemonSetStatus, c *conversion.Cloner) error { - out.CurrentNumberScheduled = in.CurrentNumberScheduled - out.NumberMisscheduled = in.NumberMisscheduled - out.DesiredNumberScheduled = in.DesiredNumberScheduled - return nil -} - -func deepCopy_extensions_DaemonSetUpdateStrategy(in DaemonSetUpdateStrategy, out *DaemonSetUpdateStrategy, c *conversion.Cloner) error { - out.Type = in.Type - if in.RollingUpdate != nil { - out.RollingUpdate = new(RollingUpdateDaemonSet) - if err := deepCopy_extensions_RollingUpdateDaemonSet(*in.RollingUpdate, out.RollingUpdate, c); err != nil { - return err - } - } else { - out.RollingUpdate = nil - } - return nil -} - -func deepCopy_extensions_Deployment(in Deployment, out *Deployment, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_DeploymentSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_DeploymentStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_DeploymentList(in DeploymentList, out *DeploymentList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]Deployment, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_Deployment(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_DeploymentSpec(in DeploymentSpec, out *DeploymentSpec, c *conversion.Cloner) error { - out.Replicas = in.Replicas - if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val - } - } else { - out.Selector = nil - } - if err := deepCopy_api_PodTemplateSpec(in.Template, &out.Template, c); err != nil { - return err - } - if err := deepCopy_extensions_DeploymentStrategy(in.Strategy, &out.Strategy, c); err != nil { - return err - } - out.UniqueLabelKey = in.UniqueLabelKey - return nil -} - -func deepCopy_extensions_DeploymentStatus(in DeploymentStatus, out *DeploymentStatus, c *conversion.Cloner) error { - out.Replicas = in.Replicas - out.UpdatedReplicas = in.UpdatedReplicas - return nil -} - -func deepCopy_extensions_DeploymentStrategy(in DeploymentStrategy, out *DeploymentStrategy, c *conversion.Cloner) error { - out.Type = in.Type - if in.RollingUpdate != nil { - out.RollingUpdate = new(RollingUpdateDeployment) - if err := deepCopy_extensions_RollingUpdateDeployment(*in.RollingUpdate, out.RollingUpdate, c); err != nil { - return err - } - } else { - out.RollingUpdate = nil - } - return nil -} - -func deepCopy_extensions_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error { - out.Path = in.Path - if err := deepCopy_extensions_IngressBackend(in.Backend, &out.Backend, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_HTTPIngressRuleValue(in HTTPIngressRuleValue, out *HTTPIngressRuleValue, c *conversion.Cloner) error { - if in.Paths != nil { - out.Paths = make([]HTTPIngressPath, len(in.Paths)) - for i := range in.Paths { - if err := deepCopy_extensions_HTTPIngressPath(in.Paths[i], &out.Paths[i], c); err != nil { - return err - } - } - } else { - out.Paths = nil - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_HorizontalPodAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_HorizontalPodAutoscalerStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscalerList(in HorizontalPodAutoscalerList, out *HorizontalPodAutoscalerList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]HorizontalPodAutoscaler, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_HorizontalPodAutoscaler(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscalerSpec(in HorizontalPodAutoscalerSpec, out *HorizontalPodAutoscalerSpec, c *conversion.Cloner) error { - if err := deepCopy_extensions_SubresourceReference(in.ScaleRef, &out.ScaleRef, c); err != nil { - return err - } - if in.MinReplicas != nil { - out.MinReplicas = new(int) - *out.MinReplicas = *in.MinReplicas - } else { - out.MinReplicas = nil - } - out.MaxReplicas = in.MaxReplicas - if in.CPUUtilization != nil { - out.CPUUtilization = new(CPUTargetUtilization) - if err := deepCopy_extensions_CPUTargetUtilization(*in.CPUUtilization, out.CPUUtilization, c); err != nil { - return err - } - } else { - out.CPUUtilization = nil - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscalerStatus(in HorizontalPodAutoscalerStatus, out *HorizontalPodAutoscalerStatus, c *conversion.Cloner) error { - if in.ObservedGeneration != nil { - out.ObservedGeneration = new(int64) - *out.ObservedGeneration = *in.ObservedGeneration - } else { - out.ObservedGeneration = nil - } - if in.LastScaleTime != nil { - out.LastScaleTime = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.LastScaleTime, out.LastScaleTime, c); err != nil { - return err - } - } else { - out.LastScaleTime = nil - } - out.CurrentReplicas = in.CurrentReplicas - out.DesiredReplicas = in.DesiredReplicas - if in.CurrentCPUUtilizationPercentage != nil { - out.CurrentCPUUtilizationPercentage = new(int) - *out.CurrentCPUUtilizationPercentage = *in.CurrentCPUUtilizationPercentage - } else { - out.CurrentCPUUtilizationPercentage = nil - } - return nil -} - -func deepCopy_extensions_Ingress(in Ingress, out *Ingress, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_IngressSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_IngressStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_IngressBackend(in IngressBackend, out *IngressBackend, c *conversion.Cloner) error { - out.ServiceName = in.ServiceName - if err := deepCopy_intstr_IntOrString(in.ServicePort, &out.ServicePort, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_IngressList(in IngressList, out *IngressList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]Ingress, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_Ingress(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_IngressRule(in IngressRule, out *IngressRule, c *conversion.Cloner) error { - out.Host = in.Host - if err := deepCopy_extensions_IngressRuleValue(in.IngressRuleValue, &out.IngressRuleValue, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_IngressRuleValue(in IngressRuleValue, out *IngressRuleValue, c *conversion.Cloner) error { - if in.HTTP != nil { - out.HTTP = new(HTTPIngressRuleValue) - if err := deepCopy_extensions_HTTPIngressRuleValue(*in.HTTP, out.HTTP, c); err != nil { - return err - } - } else { - out.HTTP = nil - } - return nil -} - -func deepCopy_extensions_IngressSpec(in IngressSpec, out *IngressSpec, c *conversion.Cloner) error { - if in.Backend != nil { - out.Backend = new(IngressBackend) - if err := deepCopy_extensions_IngressBackend(*in.Backend, out.Backend, c); err != nil { - return err - } - } else { - out.Backend = nil - } - if in.Rules != nil { - out.Rules = make([]IngressRule, len(in.Rules)) - for i := range in.Rules { - if err := deepCopy_extensions_IngressRule(in.Rules[i], &out.Rules[i], c); err != nil { - return err - } - } - } else { - out.Rules = nil - } - return nil -} - -func deepCopy_extensions_IngressStatus(in IngressStatus, out *IngressStatus, c *conversion.Cloner) error { - if err := deepCopy_api_LoadBalancerStatus(in.LoadBalancer, &out.LoadBalancer, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_Job(in Job, out *Job, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_JobSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_JobStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_JobCondition(in JobCondition, out *JobCondition, c *conversion.Cloner) error { - out.Type = in.Type - out.Status = in.Status - if err := deepCopy_unversioned_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil { - return err - } - if err := deepCopy_unversioned_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil { - return err - } - out.Reason = in.Reason - out.Message = in.Message - return nil -} - -func deepCopy_extensions_JobList(in JobList, out *JobList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]Job, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_Job(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) error { - if in.Parallelism != nil { - out.Parallelism = new(int) - *out.Parallelism = *in.Parallelism - } else { - out.Parallelism = nil - } - if in.Completions != nil { - out.Completions = new(int) - *out.Completions = *in.Completions - } else { - out.Completions = nil - } - if in.ActiveDeadlineSeconds != nil { - out.ActiveDeadlineSeconds = new(int64) - *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds - } else { - out.ActiveDeadlineSeconds = nil - } - if in.Selector != nil { - out.Selector = new(LabelSelector) - if err := deepCopy_extensions_LabelSelector(*in.Selector, out.Selector, c); err != nil { - return err - } - } else { - out.Selector = nil - } - if err := deepCopy_api_PodTemplateSpec(in.Template, &out.Template, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_JobStatus(in JobStatus, out *JobStatus, c *conversion.Cloner) error { - if in.Conditions != nil { - out.Conditions = make([]JobCondition, len(in.Conditions)) - for i := range in.Conditions { - if err := deepCopy_extensions_JobCondition(in.Conditions[i], &out.Conditions[i], c); err != nil { - return err - } - } - } else { - out.Conditions = nil - } - if in.StartTime != nil { - out.StartTime = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.StartTime, out.StartTime, c); err != nil { - return err - } - } else { - out.StartTime = nil - } - if in.CompletionTime != nil { - out.CompletionTime = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.CompletionTime, out.CompletionTime, c); err != nil { - return err - } - } else { - out.CompletionTime = nil - } - out.Active = in.Active - out.Succeeded = in.Succeeded - out.Failed = in.Failed - return nil -} - -func deepCopy_extensions_LabelSelector(in LabelSelector, out *LabelSelector, c *conversion.Cloner) error { - if in.MatchLabels != nil { - out.MatchLabels = make(map[string]string) - for key, val := range in.MatchLabels { - out.MatchLabels[key] = val - } - } else { - out.MatchLabels = nil - } - if in.MatchExpressions != nil { - out.MatchExpressions = make([]LabelSelectorRequirement, len(in.MatchExpressions)) - for i := range in.MatchExpressions { - if err := deepCopy_extensions_LabelSelectorRequirement(in.MatchExpressions[i], &out.MatchExpressions[i], c); err != nil { - return err - } - } - } else { - out.MatchExpressions = nil - } - return nil -} - -func deepCopy_extensions_LabelSelectorRequirement(in LabelSelectorRequirement, out *LabelSelectorRequirement, c *conversion.Cloner) error { - out.Key = in.Key - out.Operator = in.Operator - if in.Values != nil { - out.Values = make([]string, len(in.Values)) - for i := range in.Values { - out.Values[i] = in.Values[i] - } - } else { - out.Values = nil - } - return nil -} - -func deepCopy_extensions_NodeUtilization(in NodeUtilization, out *NodeUtilization, c *conversion.Cloner) error { - out.Resource = in.Resource - out.Value = in.Value - return nil -} - -func deepCopy_extensions_ReplicaSet(in ReplicaSet, out *ReplicaSet, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_ReplicaSetSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_ReplicaSetStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_ReplicaSetList(in ReplicaSetList, out *ReplicaSetList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ReplicaSet, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ReplicaSet(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_ReplicaSetSpec(in ReplicaSetSpec, out *ReplicaSetSpec, c *conversion.Cloner) error { - out.Replicas = in.Replicas - if in.Selector != nil { - out.Selector = new(LabelSelector) - if err := deepCopy_extensions_LabelSelector(*in.Selector, out.Selector, c); err != nil { - return err - } - } else { - out.Selector = nil - } - if in.Template != nil { - out.Template = new(api.PodTemplateSpec) - if err := deepCopy_api_PodTemplateSpec(*in.Template, out.Template, c); err != nil { - return err - } - } else { - out.Template = nil - } - return nil -} - -func deepCopy_extensions_ReplicaSetStatus(in ReplicaSetStatus, out *ReplicaSetStatus, c *conversion.Cloner) error { - out.Replicas = in.Replicas - out.ObservedGeneration = in.ObservedGeneration - return nil -} - -func deepCopy_extensions_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_RollingUpdateDaemonSet(in RollingUpdateDaemonSet, out *RollingUpdateDaemonSet, c *conversion.Cloner) error { - if err := deepCopy_intstr_IntOrString(in.MaxUnavailable, &out.MaxUnavailable, c); err != nil { - return err - } - out.MinReadySeconds = in.MinReadySeconds - return nil -} - -func deepCopy_extensions_RollingUpdateDeployment(in RollingUpdateDeployment, out *RollingUpdateDeployment, c *conversion.Cloner) error { - if err := deepCopy_intstr_IntOrString(in.MaxUnavailable, &out.MaxUnavailable, c); err != nil { - return err - } - if err := deepCopy_intstr_IntOrString(in.MaxSurge, &out.MaxSurge, c); err != nil { - return err - } - out.MinReadySeconds = in.MinReadySeconds - return nil -} - -func deepCopy_extensions_Scale(in Scale, out *Scale, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_ScaleSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_ScaleStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_ScaleSpec(in ScaleSpec, out *ScaleSpec, c *conversion.Cloner) error { - out.Replicas = in.Replicas - return nil -} - -func deepCopy_extensions_ScaleStatus(in ScaleStatus, out *ScaleStatus, c *conversion.Cloner) error { - out.Replicas = in.Replicas - if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val - } - } else { - out.Selector = nil - } - return nil -} - -func deepCopy_extensions_SubresourceReference(in SubresourceReference, out *SubresourceReference, c *conversion.Cloner) error { - out.Kind = in.Kind - out.Name = in.Name - out.APIVersion = in.APIVersion - out.Subresource = in.Subresource - return nil -} - -func deepCopy_extensions_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - out.Description = in.Description - if in.Versions != nil { - out.Versions = make([]APIVersion, len(in.Versions)) - for i := range in.Versions { - if err := deepCopy_extensions_APIVersion(in.Versions[i], &out.Versions[i], c); err != nil { - return err - } - } - } else { - out.Versions = nil - } - return nil -} - -func deepCopy_extensions_ThirdPartyResourceData(in ThirdPartyResourceData, out *ThirdPartyResourceData, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if in.Data != nil { - out.Data = make([]uint8, len(in.Data)) - for i := range in.Data { - out.Data[i] = in.Data[i] - } - } else { - out.Data = nil - } - return nil -} - -func deepCopy_extensions_ThirdPartyResourceDataList(in ThirdPartyResourceDataList, out *ThirdPartyResourceDataList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ThirdPartyResourceData, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ThirdPartyResourceData(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_ThirdPartyResourceList(in ThirdPartyResourceList, out *ThirdPartyResourceList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ThirdPartyResource, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ThirdPartyResource(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_intstr_IntOrString(in intstr.IntOrString, out *intstr.IntOrString, c *conversion.Cloner) error { - out.Type = in.Type - out.IntVal = in.IntVal - out.StrVal = in.StrVal - return nil -} +import api "k8s.io/kubernetes/pkg/api" func init() { - err := api.Scheme.AddGeneratedDeepCopyFuncs( - deepCopy_api_AWSElasticBlockStoreVolumeSource, - deepCopy_api_Capabilities, - deepCopy_api_CephFSVolumeSource, - deepCopy_api_CinderVolumeSource, - deepCopy_api_ConfigMapKeySelector, - deepCopy_api_Container, - deepCopy_api_ContainerPort, - deepCopy_api_DownwardAPIVolumeFile, - deepCopy_api_DownwardAPIVolumeSource, - deepCopy_api_EmptyDirVolumeSource, - deepCopy_api_EnvVar, - deepCopy_api_EnvVarSource, - deepCopy_api_ExecAction, - deepCopy_api_FCVolumeSource, - deepCopy_api_FlexVolumeSource, - deepCopy_api_FlockerVolumeSource, - deepCopy_api_GCEPersistentDiskVolumeSource, - deepCopy_api_GitRepoVolumeSource, - deepCopy_api_GlusterfsVolumeSource, - deepCopy_api_HTTPGetAction, - deepCopy_api_Handler, - deepCopy_api_HostPathVolumeSource, - deepCopy_api_ISCSIVolumeSource, - deepCopy_api_Lifecycle, - deepCopy_api_LoadBalancerIngress, - deepCopy_api_LoadBalancerStatus, - deepCopy_api_LocalObjectReference, - deepCopy_api_NFSVolumeSource, - deepCopy_api_ObjectFieldSelector, - deepCopy_api_ObjectMeta, - deepCopy_api_PersistentVolumeClaimVolumeSource, - deepCopy_api_PodSecurityContext, - deepCopy_api_PodSpec, - deepCopy_api_PodTemplateSpec, - deepCopy_api_Probe, - deepCopy_api_RBDVolumeSource, - deepCopy_api_ResourceRequirements, - deepCopy_api_SELinuxOptions, - deepCopy_api_SecretKeySelector, - deepCopy_api_SecretVolumeSource, - deepCopy_api_SecurityContext, - deepCopy_api_TCPSocketAction, - deepCopy_api_Volume, - deepCopy_api_VolumeMount, - deepCopy_api_VolumeSource, - deepCopy_resource_Quantity, - deepCopy_unversioned_ListMeta, - deepCopy_unversioned_Time, - deepCopy_unversioned_TypeMeta, - deepCopy_extensions_APIVersion, - deepCopy_extensions_CPUTargetUtilization, - deepCopy_extensions_ClusterAutoscaler, - deepCopy_extensions_ClusterAutoscalerList, - deepCopy_extensions_ClusterAutoscalerSpec, - deepCopy_extensions_ConfigMap, - deepCopy_extensions_ConfigMapList, - deepCopy_extensions_DaemonSet, - deepCopy_extensions_DaemonSetList, - deepCopy_extensions_DaemonSetSpec, - deepCopy_extensions_DaemonSetStatus, - deepCopy_extensions_DaemonSetUpdateStrategy, - deepCopy_extensions_Deployment, - deepCopy_extensions_DeploymentList, - deepCopy_extensions_DeploymentSpec, - deepCopy_extensions_DeploymentStatus, - deepCopy_extensions_DeploymentStrategy, - deepCopy_extensions_HTTPIngressPath, - deepCopy_extensions_HTTPIngressRuleValue, - deepCopy_extensions_HorizontalPodAutoscaler, - deepCopy_extensions_HorizontalPodAutoscalerList, - deepCopy_extensions_HorizontalPodAutoscalerSpec, - deepCopy_extensions_HorizontalPodAutoscalerStatus, - deepCopy_extensions_Ingress, - deepCopy_extensions_IngressBackend, - deepCopy_extensions_IngressList, - deepCopy_extensions_IngressRule, - deepCopy_extensions_IngressRuleValue, - deepCopy_extensions_IngressSpec, - deepCopy_extensions_IngressStatus, - deepCopy_extensions_Job, - deepCopy_extensions_JobCondition, - deepCopy_extensions_JobList, - deepCopy_extensions_JobSpec, - deepCopy_extensions_JobStatus, - deepCopy_extensions_LabelSelector, - deepCopy_extensions_LabelSelectorRequirement, - deepCopy_extensions_NodeUtilization, - deepCopy_extensions_ReplicaSet, - deepCopy_extensions_ReplicaSetList, - deepCopy_extensions_ReplicaSetSpec, - deepCopy_extensions_ReplicaSetStatus, - deepCopy_extensions_ReplicationControllerDummy, - deepCopy_extensions_RollingUpdateDaemonSet, - deepCopy_extensions_RollingUpdateDeployment, - deepCopy_extensions_Scale, - deepCopy_extensions_ScaleSpec, - deepCopy_extensions_ScaleStatus, - deepCopy_extensions_SubresourceReference, - deepCopy_extensions_ThirdPartyResource, - deepCopy_extensions_ThirdPartyResourceData, - deepCopy_extensions_ThirdPartyResourceDataList, - deepCopy_extensions_ThirdPartyResourceList, - deepCopy_intstr_IntOrString, - ) + err := api.Scheme.AddGeneratedDeepCopyFuncs() if err != nil { // if one of the deep copy functions is malformed, detect it immediately. panic(err) diff --git a/pkg/apis/metrics/deep_copy_generated.go b/pkg/apis/metrics/deep_copy_generated.go index 1de6b1d4c0b5..e7562de5221e 100644 --- a/pkg/apis/metrics/deep_copy_generated.go +++ b/pkg/apis/metrics/deep_copy_generated.go @@ -18,38 +18,10 @@ limitations under the License. package metrics -import ( - api "k8s.io/kubernetes/pkg/api" - unversioned "k8s.io/kubernetes/pkg/api/unversioned" - conversion "k8s.io/kubernetes/pkg/conversion" -) - -func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { - out.Kind = in.Kind - out.APIVersion = in.APIVersion - return nil -} - -func deepCopy_metrics_RawNode(in RawNode, out *RawNode, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - return nil -} - -func deepCopy_metrics_RawPod(in RawPod, out *RawPod, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - return nil -} +import api "k8s.io/kubernetes/pkg/api" func init() { - err := api.Scheme.AddGeneratedDeepCopyFuncs( - deepCopy_unversioned_TypeMeta, - deepCopy_metrics_RawNode, - deepCopy_metrics_RawPod, - ) + err := api.Scheme.AddGeneratedDeepCopyFuncs() if err != nil { // if one of the deep copy functions is malformed, detect it immediately. panic(err) diff --git a/pkg/client/typed/generated/extensions/unversioned/extensions_client.go b/pkg/client/typed/generated/extensions/unversioned/extensions_client.go index e4db05c0f529..9836bf0c6fa3 100644 --- a/pkg/client/typed/generated/extensions/unversioned/extensions_client.go +++ b/pkg/client/typed/generated/extensions/unversioned/extensions_client.go @@ -17,8 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" - + api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) diff --git a/pkg/client/typed/generated/legacy/unversioned/legacy_client.go b/pkg/client/typed/generated/legacy/unversioned/legacy_client.go index 6c4cd5a93d1a..0f5b12a6f927 100644 --- a/pkg/client/typed/generated/legacy/unversioned/legacy_client.go +++ b/pkg/client/typed/generated/legacy/unversioned/legacy_client.go @@ -17,8 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" - + api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" )