diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 447ad523f6c..08160a48520 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -95,7 +95,7 @@ func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExt // 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) == 4 && string(in.RawJSON) == "null" { + if len(in.RawJSON) == 0 || (len(in.RawJSON) == 4 && string(in.RawJSON) == "null") { out.Object = nil return nil } diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index d68c99d0aba..4185b3e5ca4 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -176,17 +176,63 @@ type InternalExtensionType struct { Extension runtime.EmbeddedObject `json:"extension"` } -func (*ExtensionA) IsAnAPIObject() {} -func (*ExtensionB) IsAnAPIObject() {} -func (*ExternalExtensionType) IsAnAPIObject() {} -func (*InternalExtensionType) IsAnAPIObject() {} +type ExternalOptionalExtensionType struct { + TypeMeta `json:",inline"` + Extension runtime.RawExtension `json:"extension,omitempty"` +} + +type InternalOptionalExtensionType struct { + TypeMeta `json:",inline"` + Extension runtime.EmbeddedObject `json:"extension,omitempty"` +} + +func (*ExtensionA) IsAnAPIObject() {} +func (*ExtensionB) IsAnAPIObject() {} +func (*ExternalExtensionType) IsAnAPIObject() {} +func (*InternalExtensionType) IsAnAPIObject() {} +func (*ExternalOptionalExtensionType) IsAnAPIObject() {} +func (*InternalOptionalExtensionType) IsAnAPIObject() {} + +func TestExternalToInternalMapping(t *testing.T) { + scheme := runtime.NewScheme() + scheme.AddKnownTypeWithName("", "OptionalExtensionType", &InternalOptionalExtensionType{}) + scheme.AddKnownTypeWithName("testExternal", "OptionalExtensionType", &ExternalOptionalExtensionType{}) + + table := []struct { + obj runtime.Object + encoded string + }{ + { + &InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{nil}}, + `{"kind":"OptionalExtensionType","apiVersion":"testExternal"}`, + }, + } + + for _, item := range table { + gotDecoded, err := scheme.Decode([]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) + } + } +} func TestExtensionMapping(t *testing.T) { scheme := runtime.NewScheme() scheme.AddKnownTypeWithName("", "ExtensionType", &InternalExtensionType{}) + scheme.AddKnownTypeWithName("", "OptionalExtensionType", &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName("", "A", &ExtensionA{}) scheme.AddKnownTypeWithName("", "B", &ExtensionB{}) scheme.AddKnownTypeWithName("testExternal", "ExtensionType", &ExternalExtensionType{}) + scheme.AddKnownTypeWithName("testExternal", "OptionalExtensionType", &ExternalOptionalExtensionType{}) scheme.AddKnownTypeWithName("testExternal", "A", &ExtensionA{}) scheme.AddKnownTypeWithName("testExternal", "B", &ExtensionB{})