From d488e238dd90080724f3735dcd2d61c824feff59 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Thu, 9 Oct 2014 18:32:46 -0400 Subject: [PATCH] Genericize MetaInsertionFactory into a simpler interface The common path code for MIF goes through a conversion cycle - it can also be done through reflection. This simplifies the Create/Update methods into Interpret (return version) and Update (through reflection). In addition it uses only one MetaFactory implementation across all of our packages which reduces a bit of duplication. --- pkg/conversion/meta.go | 119 +++++++++++++++++ pkg/conversion/meta_test.go | 244 ++++++++++++++++++++++++++++++++++ pkg/conversion/scheme.go | 71 ++-------- pkg/conversion/scheme_test.go | 174 +++--------------------- pkg/runtime/scheme.go | 27 +--- 5 files changed, 391 insertions(+), 244 deletions(-) create mode 100644 pkg/conversion/meta.go create mode 100644 pkg/conversion/meta_test.go diff --git a/pkg/conversion/meta.go b/pkg/conversion/meta.go new file mode 100644 index 00000000000..d82968bae8d --- /dev/null +++ b/pkg/conversion/meta.go @@ -0,0 +1,119 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package conversion + +import ( + "fmt" + "reflect" + + "gopkg.in/v1/yaml" +) + +// 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 version and kind of the wire-format of + // the object. + Interpret(data []byte) (version, kind string, err error) +} + +// DefaultMetaFactory is a default factory for versioning objects in JSON/YAML. 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/YAML +// 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 APIVersion and Kind of the JSON/YAML wire-format +// encoding of an object, or an error. +func (SimpleMetaFactory) Interpret(data []byte) (version, kind string, err error) { + findKind := struct { + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,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 "", "", fmt.Errorf("couldn't get version/kind: %v", err) + } + return findKind.APIVersion, 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. +func UpdateVersionAndKind(baseFields []string, versionField, version, kindField, kind string, obj interface{}) error { + v, err := EnforcePtr(obj) + if err != nil { + return err + } + 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() { + 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 { + return reflect.Value{}, fmt.Errorf("expected pointer, but got %v", v.Type().Name()) + } + return v.Elem(), nil +} diff --git a/pkg/conversion/meta_test.go b/pkg/conversion/meta_test.go new file mode 100644 index 00000000000..b4c6c0771b2 --- /dev/null +++ b/pkg/conversion/meta_test.go @@ -0,0 +1,244 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package conversion + +import ( + "reflect" + "testing" +) + +func TestSimpleMetaFactoryInterpret(t *testing.T) { + factory := SimpleMetaFactory{} + version, kind, err := factory.Interpret([]byte(`{"apiVersion":"1","kind":"object"}`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if version != "1" || kind != "object" { + t.Errorf("unexpected interpret: %s %s", version, kind) + } + + // no kind or version + version, kind, err = factory.Interpret([]byte(`{}`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if version != "" || kind != "" { + t.Errorf("unexpected interpret: %s %s", version, kind) + } + + // unparsable + version, kind, 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) + } +} + +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" yaml:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + TestString string `json:"testString" yaml:"testString"` + } + type ExternalSimple struct { + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + TestString string `json:"testString" yaml:"testString"` + } + s := NewScheme() + s.AddKnownTypeWithName("", "Simple", &InternalSimple{}) + s.AddKnownTypeWithName("externalVersion", "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 := "", scope.Meta().SrcVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + if e, a := "externalVersion", 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 := "externalVersion", scope.Meta().SrcVersion; e != a { + t.Errorf("Expected '%v', got '%v'", e, a) + } + if e, a := "", 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, "externalVersion") + 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" yaml:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + TestString string `json:"testString" yaml:"testString"` + } + type ExternalSimple struct { + Version string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + TestString string `json:"testString" yaml:"testString"` + } + s := NewScheme() + s.InternalVersion = "" + // 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", scope.Meta().SrcVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + if e, a := "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/scheme.go b/pkg/conversion/scheme.go index d5300378777..605bb8e7f35 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -19,27 +19,8 @@ package conversion import ( "fmt" "reflect" - - "gopkg.in/v1/yaml" ) -// MetaInsertionFactory is used to create an object to store and retrieve -// the version and kind information for all objects. The default uses the -// keys "version" and "kind" respectively. The object produced by this -// factory is used to clear the version and kind fields in memory, so it -// must match the layout of your actual api structs. (E.g., if you have your -// version and kind field inside an inlined struct, this must produce an -// inlined struct with the same field name.) -type MetaInsertionFactory interface { - // Create should make a new object with two fields. - // This object will be used to encode this metadata along with your - // API objects, so the tags on the fields you use shouldn't conflict. - Create(version, kind string) interface{} - // Interpret should take the same type of object that Create creates. - // It should return the version and kind information from this object. - Interpret(interface{}) (version, kind string) -} - // Scheme defines an entire encoding and decoding scheme. type Scheme struct { // versionMap allows one to figure out the go type of an object with @@ -68,19 +49,19 @@ type Scheme struct { // MetaInsertionFactory is used to create an object to store and retrieve // the version and kind information for all objects. The default uses the - // keys "version" and "kind" respectively. - MetaInsertionFactory MetaInsertionFactory + // keys "apiVersion" and "kind" respectively. + MetaFactory MetaFactory } // NewScheme manufactures a new scheme. func NewScheme() *Scheme { s := &Scheme{ - versionMap: map[string]map[string]reflect.Type{}, - typeToVersion: map[reflect.Type]string{}, - typeToKind: map[reflect.Type][]string{}, - converter: NewConverter(), - InternalVersion: "", - MetaInsertionFactory: metaInsertion{}, + versionMap: map[string]map[string]reflect.Type{}, + typeToVersion: map[reflect.Type]string{}, + typeToKind: map[reflect.Type][]string{}, + converter: NewConverter(), + InternalVersion: "", + MetaFactory: DefaultMetaFactory, } s.converter.NameFunc = s.nameFunc return s @@ -238,41 +219,10 @@ func (s *Scheme) generateConvertMeta(srcVersion, destVersion string) *Meta { } } -// metaInsertion provides a default implementation of MetaInsertionFactory. -type metaInsertion struct { - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` -} - -// Create should make a new object with two fields. -// This object will be used to encode this metadata along with your -// API objects, so the tags on the fields you use shouldn't conflict. -func (metaInsertion) Create(version, kind string) interface{} { - m := metaInsertion{} - m.Version = version - m.Kind = kind - return &m -} - -// Interpret should take the same type of object that Create creates. -// It should return the version and kind information from this object. -func (metaInsertion) Interpret(in interface{}) (version, kind string) { - m := in.(*metaInsertion) - return m.Version, m.Kind -} - // DataVersionAndKind will return the APIVersion and Kind of the given wire-format // enconding of an API Object, or an error. func (s *Scheme) DataVersionAndKind(data []byte) (version, kind string, err error) { - findKind := s.MetaInsertionFactory.Create("", "") - // 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 "", "", fmt.Errorf("couldn't get version/kind: %v", err) - } - version, kind = s.MetaInsertionFactory.Interpret(findKind) - return version, kind, nil + return s.MetaFactory.Interpret(data) } // ObjectVersionAndKind returns the API version and kind of the go object, @@ -297,8 +247,7 @@ func (s *Scheme) ObjectVersionAndKind(obj interface{}) (apiVersion, kind string, // MetaInsertionFactory). Returns an error if this isn't possible. obj // must be a pointer. func (s *Scheme) SetVersionAndKind(version, kind string, obj interface{}) error { - versionAndKind := s.MetaInsertionFactory.Create(version, kind) - return s.converter.Convert(versionAndKind, obj, SourceToDest|IgnoreMissingFields|AllowDifferentFieldTypeNames, nil) + return s.MetaFactory.Update(version, kind, obj) } // 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 16157e866a0..b299299e545 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -25,7 +25,9 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/google/gofuzz" + "gopkg.in/v1/yaml" ) var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.") @@ -129,30 +131,28 @@ func GetTestScheme() *Scheme { s.AddKnownTypeWithName("", "TestType3", &TestType1{}) s.AddKnownTypeWithName("v1", "TestType3", &ExternalTestType1{}) s.InternalVersion = "" - s.MetaInsertionFactory = testMetaInsertionFactory{} + s.MetaFactory = testMetaFactory{} return s } -type testMetaInsertionFactory struct { - MyWeirdCustomEmbeddedVersionKindField struct { +type testMetaFactory struct{} + +func (testMetaFactory) Interpret(data []byte) (version, kind string, err error) { + findKind := struct { APIVersion string `json:"myVersionKey,omitempty" yaml:"myVersionKey,omitempty"` ObjectKind string `json:"myKindKey,omitempty" yaml:"myKindKey,omitempty"` - } `json:",inline" yaml:",inline"` + }{} + // 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 "", "", fmt.Errorf("couldn't get version/kind: %v", err) + } + return findKind.APIVersion, findKind.ObjectKind, nil } -// Create returns a new testMetaInsertionFactory with the version and kind fields set. -func (testMetaInsertionFactory) Create(version, kind string) interface{} { - m := testMetaInsertionFactory{} - m.MyWeirdCustomEmbeddedVersionKindField.APIVersion = version - m.MyWeirdCustomEmbeddedVersionKindField.ObjectKind = kind - return &m -} - -// Interpret returns the version and kind information from in, which must be -// a testMetaInsertionFactory pointer object. -func (testMetaInsertionFactory) Interpret(in interface{}) (version, kind string) { - m := in.(*testMetaInsertionFactory) - return m.MyWeirdCustomEmbeddedVersionKindField.APIVersion, m.MyWeirdCustomEmbeddedVersionKindField.ObjectKind +func (testMetaFactory) Update(version, kind string, obj interface{}) error { + return UpdateVersionAndKind(nil, "APIVersion", version, "ObjectKind", kind, obj) } func objDiff(a, b interface{}) string { @@ -308,143 +308,3 @@ func TestBadJSONRejectionForSetInternalVersion(t *testing.T) { t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) } } - -func TestMetaValues(t *testing.T) { - type InternalSimple struct { - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` - TestString string `json:"testString" yaml:"testString"` - } - type ExternalSimple struct { - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` - TestString string `json:"testString" yaml:"testString"` - } - s := NewScheme() - s.InternalVersion = "" - s.AddKnownTypeWithName("", "Simple", &InternalSimple{}) - s.AddKnownTypeWithName("externalVersion", "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 { - if e, a := "", scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := "externalVersion", scope.Meta().DestVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - func(in *ExternalSimple, out *InternalSimple, scope Scope) error { - if e, a := "externalVersion", scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := "", scope.Meta().DestVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - externalToInternalCalls++ - return nil - }, - ) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - simple := &InternalSimple{ - TestString: "foo", - } - - // Test Encode, Decode, and DecodeInto - data, err := s.EncodeToVersion(simple, "externalVersion") - obj2, err2 := s.Decode(data) - obj3 := &InternalSimple{} - err3 := s.DecodeInto(data, obj3) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v' '%v'", err, err2, err3) - } - 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) - } - 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.Errorf("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:"version,omitempty" yaml:"version,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` - TestString string `json:"testString" yaml:"testString"` - } - type ExternalSimple struct { - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` - TestString string `json:"testString" yaml:"testString"` - } - s := NewScheme() - s.InternalVersion = "" - // 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", scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := "unknown", scope.Meta().DestVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - ) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - simple := &InternalSimple{TestString: "foo"} - external := &ExternalSimple{} - err = s.Convert(simple, external) - if err != nil { - t.Errorf("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/runtime/scheme.go b/pkg/runtime/scheme.go index 58291356bce..29d83c1f880 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -145,7 +145,7 @@ func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *Embedded func NewScheme() *Scheme { s := &Scheme{conversion.NewScheme()} s.raw.InternalVersion = "" - s.raw.MetaInsertionFactory = metaInsertion{} + s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"} s.raw.AddConversionFuncs( s.embeddedObjectToRawExtension, s.rawExtensionToEmbeddedObject, @@ -339,28 +339,3 @@ func (s *Scheme) CopyOrDie(obj Object) Object { } return newObj } - -// metaInsertion implements conversion.MetaInsertionFactory, which lets the conversion -// package figure out how to encode our object's types and versions. These fields are -// located in our TypeMeta. -type metaInsertion struct { - TypeMeta struct { - APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` - } `json:",inline" yaml:",inline"` -} - -// Create returns a new metaInsertion with the version and kind fields set. -func (metaInsertion) Create(version, kind string) interface{} { - m := metaInsertion{} - m.TypeMeta.APIVersion = version - m.TypeMeta.Kind = kind - return &m -} - -// Interpret returns the version and kind information from in, which must be -// a metaInsertion pointer object. -func (metaInsertion) Interpret(in interface{}) (version, kind string) { - m := in.(*metaInsertion) - return m.TypeMeta.APIVersion, m.TypeMeta.Kind -}