diff --git a/pkg/api/conversion_test.go b/pkg/api/conversion_test.go index e3449def625..ecbd3867fc9 100644 --- a/pkg/api/conversion_test.go +++ b/pkg/api/conversion_test.go @@ -41,7 +41,7 @@ func BenchmarkPodConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersion) + obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.Group].String()) if err != nil { b.Fatalf("Conversion error: %v", err) } @@ -69,7 +69,7 @@ func BenchmarkNodeConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersion) + obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.Group].String()) if err != nil { b.Fatalf("Conversion error: %v", err) } @@ -97,7 +97,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersion) + obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.Group].String()) if err != nil { b.Fatalf("Conversion error: %v", err) } diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index 18f1ba63bf1..4e76ac8938f 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -49,11 +49,15 @@ type GroupVersion struct { Version string } +func (gv GroupVersion) IsEmpty() bool { + return len(gv.Group) == 0 && len(gv.Version) == 0 +} + // String puts "group" and "version" into a single "group/version" string. For the legacy v1 // it returns "v1". func (gv GroupVersion) String() string { // special case the internal apiVersion for kube - if len(gv.Group) == 0 && len(gv.Version) == 0 { + if gv.IsEmpty() { return "" } diff --git a/pkg/apis/componentconfig/register.go b/pkg/apis/componentconfig/register.go index 8e4c95a487e..c1b6bbb5bfb 100644 --- a/pkg/apis/componentconfig/register.go +++ b/pkg/apis/componentconfig/register.go @@ -25,7 +25,8 @@ func init() { } func addKnownTypes() { - api.Scheme.AddKnownTypes("", + // TODO this will get cleaned up with the scheme types are fixed + api.Scheme.AddKnownTypes("componentconfig/", &KubeProxyConfiguration{}, ) } diff --git a/pkg/apis/extensions/register.go b/pkg/apis/extensions/register.go index 535c3c407df..cdd251171f6 100644 --- a/pkg/apis/extensions/register.go +++ b/pkg/apis/extensions/register.go @@ -27,7 +27,8 @@ func init() { // Adds the list of known types to api.Scheme. func addKnownTypes() { - api.Scheme.AddKnownTypes("", + // TODO this gets cleaned up when the types are fixed + api.Scheme.AddKnownTypes("extensions/", &ClusterAutoscaler{}, &ClusterAutoscalerList{}, &Deployment{}, diff --git a/pkg/apis/metrics/register.go b/pkg/apis/metrics/register.go index ea56e48fc68..6ff816526a6 100644 --- a/pkg/apis/metrics/register.go +++ b/pkg/apis/metrics/register.go @@ -27,7 +27,8 @@ func init() { // Adds the list of known types to api.Scheme. func addKnownTypes() { - api.Scheme.AddKnownTypes("", + // TODO this will get cleaned up with the scheme types are fixed + api.Scheme.AddKnownTypes("metrics/", &RawNode{}, &RawPod{}, ) diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 208c687799e..0b3359774af 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -375,11 +375,14 @@ 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, - APIVersion: a.group.GroupVersion.String(), + ContextFunc: ctxFn, + Creater: a.group.Creater, + Convertor: a.group.Convertor, + Codec: mapping.Codec, + APIVersion: a.group.GroupVersion.String(), + // TODO, this internal version needs to plumbed through from the caller + // this works in all the cases we have now + InternalVersion: unversioned.GroupVersion{Group: a.group.GroupVersion.Group}, ServerAPIVersion: serverGroupVersion.String(), Resource: resource, Subresource: subresource, diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index be8d56e4d26..2e2b9041905 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -58,6 +58,7 @@ 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 testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"} var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"} var prefix = "apis" @@ -136,8 +137,9 @@ func init() { // "internal" version api.Scheme.AddKnownTypes( - "", &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, + testInternalGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, &unversioned.ListOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) + api.Scheme.AddInternalGroupVersion(testInternalGroupVersion) addGrouplessTypes() addTestTypes() addNewTestTypes() @@ -1630,7 +1632,7 @@ func TestConnectWithOptions(t *testing.T) { } opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions) if !ok { - t.Errorf("Unexpected options type: %#v", connectStorage.receivedConnectOptions) + t.Fatalf("Unexpected options type: %#v", connectStorage.receivedConnectOptions) } if opts.Param1 != "value1" && opts.Param2 != "value2" { t.Errorf("Unexpected options value: %#v", opts) @@ -1677,7 +1679,7 @@ func TestConnectWithOptionsAndPath(t *testing.T) { } opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions) if !ok { - t.Errorf("Unexpected options type: %#v", connectStorage.receivedConnectOptions) + t.Fatalf("Unexpected options type: %#v", connectStorage.receivedConnectOptions) } if opts.Param1 != "value1" && opts.Param2 != "value2" { t.Errorf("Unexpected options value: %#v", opts) diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 1246bc22a19..618fbab3694 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -73,10 +73,11 @@ type RequestScope struct { Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor - Resource string - Subresource string - Kind string - APIVersion string + Resource string + Subresource string + Kind string + APIVersion string + InternalVersion unversioned.GroupVersion // The version of apiserver resources to use ServerAPIVersion string @@ -156,7 +157,7 @@ func getRequestOptions(req *restful.Request, scope RequestScope, kind string, su if err := scope.Codec.DecodeParametersInto(query, versioned); err != nil { return nil, errors.NewBadRequest(err.Error()) } - out, err := scope.Convertor.ConvertToVersion(versioned, "") + out, err := scope.Convertor.ConvertToVersion(versioned, scope.InternalVersion.String()) if err != nil { // programmer error return nil, err diff --git a/pkg/conversion/decode.go b/pkg/conversion/decode.go index 8d9801559d1..d3ed57f1202 100644 --- a/pkg/conversion/decode.go +++ b/pkg/conversion/decode.go @@ -26,18 +26,29 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" ) -func (s *Scheme) DecodeToVersionedObject(data []byte) (obj interface{}, version, kind string, err error) { - version, kind, err = s.DataVersionAndKind(data) +func (s *Scheme) DecodeToVersionedObject(data []byte) (interface{}, string, string, error) { + version, kind, err := s.DataVersionAndKind(data) if err != nil { - return + return nil, "", "", err } - if version == "" && s.InternalVersion != "" { + + gv, err := unversioned.ParseGroupVersion(version) + if err != nil { + return nil, "", "", err + } + + internalGV, exists := s.InternalVersions[gv.Group] + if !exists { + return nil, "", "", fmt.Errorf("no internalVersion specified for %v", gv) + } + + if len(gv.Version) == 0 && len(internalGV.Version) != 0 { return nil, "", "", fmt.Errorf("version not set in '%s'", string(data)) } if kind == "" { return nil, "", "", fmt.Errorf("kind not set in '%s'", string(data)) } - obj, err = s.NewObject(version, kind) + obj, err := s.NewObject(version, kind) if err != nil { return nil, "", "", err } @@ -45,7 +56,7 @@ func (s *Scheme) DecodeToVersionedObject(data []byte) (obj interface{}, version, if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil { return nil, "", "", err } - return + return obj, version, kind, nil } // Decode converts a JSON string back into a pointer to an api object. @@ -54,8 +65,7 @@ func (s *Scheme) DecodeToVersionedObject(data []byte) (obj interface{}, version, // 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) { - // TODO this is cleaned up when internal types are fixed - return s.DecodeToVersion(data, unversioned.ParseGroupVersionOrDie(s.InternalVersion)) + return s.DecodeToVersion(data, unversioned.GroupVersion{}) } // DecodeToVersion converts a JSON string back into a pointer to an api object. @@ -63,6 +73,8 @@ func (s *Scheme) Decode(data []byte) (interface{}, error) { // 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, gv unversioned.GroupVersion) (interface{}, error) { obj, sourceVersion, kind, err := s.DecodeToVersionedObject(data) if err != nil { @@ -73,8 +85,23 @@ func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (inte return nil, err } + sourceGV, err := unversioned.ParseGroupVersion(sourceVersion) + if err != nil { + return nil, err + } + + // if the gv 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 gv.IsEmpty() { + exists := false + gv, exists = s.InternalVersions[sourceGV.Group] + if !exists { + return nil, fmt.Errorf("no internalVersion specified for %v", gv) + } + } + // Convert if needed. - if gv.String() != sourceVersion { + if gv != sourceGV { objOut, err := s.NewObject(gv.String(), kind) if err != nil { return nil, err @@ -118,7 +145,7 @@ func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{} if dataKind == "" { dataKind = gvk.Kind } - if (len(gvk.GroupVersion().Group) > 0 || len(gvk.GroupVersion().Version) > 0) && (dataVersion != gvk.GroupVersion().String()) { + if (len(gvk.Group) > 0 || len(gvk.Version) > 0) && (dataVersion != gvk.GroupVersion().String()) { return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%v)", dataVersion, gvk.GroupVersion())) } if len(gvk.Kind) > 0 && (dataKind != gvk.Kind) { diff --git a/pkg/conversion/error.go b/pkg/conversion/error.go index fc62e842f23..6ddbf9b7a78 100644 --- a/pkg/conversion/error.go +++ b/pkg/conversion/error.go @@ -19,25 +19,27 @@ package conversion import ( "fmt" "reflect" + + "k8s.io/kubernetes/pkg/api/unversioned" ) type notRegisteredErr struct { - kind string - version string - t reflect.Type + gvk unversioned.GroupVersionKind + t reflect.Type } func (k *notRegisteredErr) Error() string { if k.t != nil { return fmt.Sprintf("no kind is registered for the type %v", k.t) } - if len(k.kind) == 0 { - return fmt.Sprintf("no version %q has been registered", k.version) + if len(k.gvk.Kind) == 0 { + return fmt.Sprintf("no version %q has been registered", k.gvk.GroupVersion()) } - if len(k.version) == 0 { - return fmt.Sprintf("no kind %q is registered for the default version", k.kind) + if len(k.gvk.Version) == 0 { + return fmt.Sprintf("no kind %q is registered for the default version of group %q", k.gvk.Kind, k.gvk.Group) } - return fmt.Sprintf("no kind %q is registered for version %q", k.kind, k.version) + + return fmt.Sprintf("no kind %q is registered for version %q", k.gvk.Kind, k.gvk.GroupVersion()) } // IsNotRegisteredError returns true if the error indicates the provided diff --git a/pkg/conversion/meta_test.go b/pkg/conversion/meta_test.go index 452ea148301..a05572eae94 100644 --- a/pkg/conversion/meta_test.go +++ b/pkg/conversion/meta_test.go @@ -125,9 +125,13 @@ func TestMetaValues(t *testing.T) { 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.AddKnownTypeWithName("", "Simple", &InternalSimple{}) - s.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) + s.InternalVersions[internalGV.Group] = internalGV + s.AddKnownTypeWithName(internalGV.String(), "Simple", &InternalSimple{}) + s.AddKnownTypeWithName(externalGV.String(), "Simple", &ExternalSimple{}) internalToExternalCalls := 0 externalToInternalCalls := 0 @@ -136,10 +140,10 @@ func TestMetaValues(t *testing.T) { err := s.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope Scope) error { t.Logf("internal -> external") - if e, a := "", scope.Meta().SrcVersion; e != a { + if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { t.Fatalf("Expected '%v', got '%v'", e, a) } - if e, a := "externalVersion", scope.Meta().DestVersion; 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) @@ -148,10 +152,10 @@ func TestMetaValues(t *testing.T) { }, func(in *ExternalSimple, out *InternalSimple, scope Scope) error { t.Logf("external -> internal") - if e, a := "externalVersion", scope.Meta().SrcVersion; e != a { + if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } - if e, a := "", scope.Meta().DestVersion; 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) @@ -169,7 +173,7 @@ func TestMetaValues(t *testing.T) { s.Log(t) // Test Encode, Decode, and DecodeInto - data, err := s.EncodeToVersion(simple, "externalVersion") + data, err := s.EncodeToVersion(simple, externalGV.String()) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -226,7 +230,6 @@ func TestMetaValuesUnregisteredConvert(t *testing.T) { TestString string `json:"testString"` } s := NewScheme() - s.InternalVersion = "" // We deliberately don't register the types. internalToExternalCalls := 0 diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index 2a3013474cf..a532a3b15cd 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -19,6 +19,8 @@ package conversion import ( "fmt" "reflect" + + "k8s.io/kubernetes/pkg/api/unversioned" ) // Scheme defines an entire encoding and decoding scheme. @@ -50,7 +52,9 @@ type Scheme struct { // InternalVersion is the default internal version. It is recommended that // you use "" for the internal version. - InternalVersion string + // 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 @@ -61,13 +65,19 @@ type Scheme struct { // 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(), - cloner: NewCloner(), - InternalVersion: "", - MetaFactory: DefaultMetaFactory, + versionMap: map[string]map[string]reflect.Type{}, + typeToVersion: map[reflect.Type]string{}, + typeToKind: map[reflect.Type][]string{}, + 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{ + "": unversioned.GroupVersion{}, + "componentconfig": unversioned.GroupVersion{Group: "componentconfig"}, + "extensions": unversioned.GroupVersion{Group: "extensions"}, + "metrics": unversioned.GroupVersion{Group: "metrics"}, + }, + MetaFactory: DefaultMetaFactory, } s.converter.nameFunc = s.nameFunc return s @@ -159,13 +169,21 @@ func (s *Scheme) KnownTypes(version string) map[string]reflect.Type { // NewObject returns a new object of the given version and name, // or an error if it hasn't been registered. func (s *Scheme) NewObject(gvString, kind string) (interface{}, error) { + gv, err := unversioned.ParseGroupVersion(gvString) + if err != nil { + return nil, err + } + gvk := gv.WithKind(kind) + if types, ok := s.versionMap[gvString]; ok { if t, ok := types[kind]; ok { return reflect.New(t).Interface(), nil } - return nil, ¬RegisteredErr{kind: kind, version: gvString} + + return nil, ¬RegisteredErr{gvk: gvk} } - return nil, ¬RegisteredErr{kind: kind, version: gvString} + + return nil, ¬RegisteredErr{gvk: gvk} } // AddConversionFuncs adds functions to the list of conversion functions. The given diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index 05fd320696c..07be9dbdad5 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -23,6 +23,7 @@ import ( "strings" "testing" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/util" "github.com/ghodss/yaml" @@ -118,7 +119,6 @@ func GetTestScheme() *Scheme { s.AddKnownTypeWithName("v1", "TestType2", &ExternalTestType2{}) s.AddKnownTypeWithName("", "TestType3", &TestType1{}) s.AddKnownTypeWithName("v1", "TestType3", &ExternalTestType1{}) - s.InternalVersion = "" s.MetaFactory = testMetaFactory{} return s } @@ -361,7 +361,7 @@ func TestBadJSONRejection(t *testing.T) { func TestBadJSONRejectionForSetInternalVersion(t *testing.T) { s := GetTestScheme() - s.InternalVersion = "v1" + s.InternalVersions[""] = unversioned.GroupVersion{Version: "v1"} badJSONs := [][]byte{ []byte(`{"myKindKey":"TestType1"}`), // Missing version } diff --git a/pkg/runtime/conversion_generator.go b/pkg/runtime/conversion_generator.go index 9bfab5ddc76..93ece01fc27 100644 --- a/pkg/runtime/conversion_generator.go +++ b/pkg/runtime/conversion_generator.go @@ -88,7 +88,8 @@ func (g *conversionGenerator) AddImport(pkg string) string { func (g *conversionGenerator) GenerateConversionsForType(version string, reflection reflect.Type) error { kind := reflection.Name() - internalObj, err := g.scheme.NewObject(g.scheme.InternalVersion, kind) + // TODO this is equivalent to what it did before, but it needs to be fixed for the proper group + internalObj, err := g.scheme.NewObject(g.scheme.InternalVersions[""].String(), kind) if err != nil { return fmt.Errorf("cannot create an object of type %v in internal version", kind) } diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index 0b396a18c35..ebb31846531 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -23,6 +23,7 @@ import ( "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/util" ) @@ -61,11 +62,15 @@ func (*EmbeddedTest) IsAnAPIObject() {} func (*EmbeddedTestExternal) IsAnAPIObject() {} func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { - s := runtime.NewScheme() - s.AddKnownTypes("", &ObjectTest{}) - s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{}) + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "v1test", Kind: "ObjectTest"} - obj, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`)) + s := runtime.NewScheme() + s.AddInternalGroupVersion(internalGV) + s.AddKnownTypes(internalGV.String(), &ObjectTest{}) + s.AddKnownTypeWithName(externalGVK.GroupVersion().String(), externalGVK.Kind, &ObjectTestExternal{}) + + obj, err := s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGVK.GroupVersion().String() + `","items":[{}]}`)) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -74,7 +79,7 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { t.Fatalf("unexpected object: %#v", test.Items[0]) } - obj, err = s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{"kind":"Other","apiVersion":"v1"}]}`)) + obj, err = s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGVK.GroupVersion().String() + `","items":[{"kind":"Other","apiVersion":"v1"}]}`)) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -85,11 +90,15 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { } func TestArrayOfRuntimeObject(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} + s := runtime.NewScheme() - s.AddKnownTypes("", &EmbeddedTest{}) - s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) - s.AddKnownTypes("", &ObjectTest{}) - s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{}) + s.AddInternalGroupVersion(internalGV) + s.AddKnownTypes(internalGV.String(), &EmbeddedTest{}) + s.AddKnownTypeWithName(externalGV.String(), "EmbeddedTest", &EmbeddedTestExternal{}) + s.AddKnownTypes(internalGV.String(), &ObjectTest{}) + s.AddKnownTypeWithName(externalGV.String(), "ObjectTest", &ObjectTestExternal{}) internal := &ObjectTest{ Items: []runtime.Object{ @@ -104,7 +113,7 @@ func TestArrayOfRuntimeObject(t *testing.T) { }, }, } - wire, err := s.EncodeToVersion(internal, "v1test") + wire, err := s.EncodeToVersion(internal, externalGV.String()) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -147,9 +156,14 @@ func TestArrayOfRuntimeObject(t *testing.T) { } func TestEmbeddedObject(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} + embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") + s := runtime.NewScheme() - s.AddKnownTypes("", &EmbeddedTest{}) - s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) + s.AddInternalGroupVersion(internalGV) + s.AddKnownTypes(internalGV.String(), &EmbeddedTest{}) + s.AddKnownTypeWithName(externalGV.String(), embeddedTestExternalGVK.Kind, &EmbeddedTestExternal{}) outer := &EmbeddedTest{ ID: "outer", @@ -160,7 +174,7 @@ func TestEmbeddedObject(t *testing.T) { }, } - wire, err := s.EncodeToVersion(outer, "v1test") + wire, err := s.EncodeToVersion(outer, externalGV.String()) if err != nil { t.Fatalf("Unexpected encode error '%v'", err) } @@ -206,9 +220,14 @@ func TestEmbeddedObject(t *testing.T) { // 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: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} + embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") + s := runtime.NewScheme() - s.AddKnownTypes("", &EmbeddedTest{}) - s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) + s.AddInternalGroupVersion(internalGV) + s.AddKnownTypes(internalGV.String(), &EmbeddedTest{}) + s.AddKnownTypeWithName(externalGV.String(), embeddedTestExternalGVK.Kind, &EmbeddedTestExternal{}) original := &EmbeddedTest{ ID: "outer", @@ -219,7 +238,7 @@ func TestDeepCopyOfEmbeddedObject(t *testing.T) { }, } - originalData, err := s.EncodeToVersion(original, "v1test") + originalData, err := s.EncodeToVersion(original, externalGV.String()) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -230,7 +249,7 @@ func TestDeepCopyOfEmbeddedObject(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - copiedData, err := s.EncodeToVersion(copyOfOriginal.(runtime.Object), "v1test") + copiedData, err := s.EncodeToVersion(copyOfOriginal.(runtime.Object), externalGV.String()) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index e9268802db0..49c9bd468eb 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -181,8 +181,16 @@ func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExt default: version := outVersion // if the object exists - if inVersion, _, err := scheme.ObjectVersionAndKind(src[i]); err == nil && len(inVersion) != 0 { - version = inVersion + // this code is try to set the outputVersion, but only if the object has a non-internal group version + if inGVString, _, err := scheme.ObjectVersionAndKind(src[i]); err == nil && len(inGVString) != 0 { + inGV, err := unversioned.ParseGroupVersion(inGVString) + if err != nil { + return err + } + + if self.raw.InternalVersions[inGV.Group] != inGV { + version = inGV.String() + } } data, err := scheme.EncodeToVersion(src[i], version) if err != nil { @@ -222,9 +230,13 @@ func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[] } // NewScheme creates a new Scheme. This scheme is pluggable by default. -func NewScheme() *Scheme { +func NewScheme(internalGroupVersions ...unversioned.GroupVersion) *Scheme { s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}} - s.raw.InternalVersion = "" + + 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, @@ -247,6 +259,10 @@ func NewScheme() *Scheme { return s } +func (s *Scheme) AddInternalGroupVersion(gv unversioned.GroupVersion) { + s.raw.InternalVersions[gv.Group] = gv +} + // 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(version string, types ...Object) { diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index 401c3cbd308..508eb2962a5 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -44,12 +44,15 @@ func (*InternalSimple) IsAnAPIObject() {} func (*ExternalSimple) IsAnAPIObject() {} func TestScheme(t *testing.T) { - internalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "", Kind: "Simple"} - externalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "externalVersion", Kind: "Simple"} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} + internalGVK := internalGV.WithKind("Simple") + externalGVK := externalGV.WithKind("Simple") scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGVK.GroupVersion().String(), internalGVK.Kind, &InternalSimple{}) - scheme.AddKnownTypeWithName(externalGVK.GroupVersion().String(), externalGVK.Kind, &ExternalSimple{}) + scheme.AddInternalGroupVersion(internalGV) + scheme.AddKnownTypeWithName(internalGV.String(), internalGVK.Kind, &InternalSimple{}) + scheme.AddKnownTypeWithName(externalGV.String(), externalGVK.Kind, &ExternalSimple{}) // test that scheme is an ObjectTyper var _ runtime.ObjectTyper = scheme @@ -60,10 +63,10 @@ func TestScheme(t *testing.T) { // Register functions to verify that scope.Meta() gets set correctly. err := scheme.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { - if e, a := internalGVK.GroupVersion().String(), scope.Meta().SrcVersion; e != a { + if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } - if e, a := externalGVK.GroupVersion().String(), scope.Meta().DestVersion; e != a { + if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) @@ -72,10 +75,10 @@ func TestScheme(t *testing.T) { return nil }, func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { - if e, a := externalGVK.GroupVersion().String(), scope.Meta().SrcVersion; e != a { + if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } - if e, a := internalGVK.GroupVersion().String(), scope.Meta().DestVersion; e != a { + if e, a := internalGV.String(), scope.Meta().DestVersion; e != a { t.Errorf("Expected '%v', got '%v'", e, a) } scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) @@ -93,11 +96,11 @@ func TestScheme(t *testing.T) { // Test Encode, Decode, DecodeInto, and DecodeToVersion obj := runtime.Object(simple) - data, err := scheme.EncodeToVersion(obj, "externalVersion") + data, err := scheme.EncodeToVersion(obj, externalGV.String()) obj2, err2 := scheme.Decode(data) obj3 := &InternalSimple{} err3 := scheme.DecodeInto(data, obj3) - obj4, err4 := scheme.DecodeToVersion(data, externalGVK.GroupVersion()) + 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) } @@ -202,9 +205,13 @@ func (*ExternalOptionalExtensionType) IsAnAPIObject() {} func (*InternalOptionalExtensionType) IsAnAPIObject() {} func TestExternalToInternalMapping(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} + scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName("", "OptionalExtensionType", &InternalOptionalExtensionType{}) - scheme.AddKnownTypeWithName("testExternal", "OptionalExtensionType", &ExternalOptionalExtensionType{}) + scheme.AddInternalGroupVersion(internalGV) + scheme.AddKnownTypeWithName(internalGV.String(), "OptionalExtensionType", &InternalOptionalExtensionType{}) + scheme.AddKnownTypeWithName(externalGV.String(), "OptionalExtensionType", &ExternalOptionalExtensionType{}) table := []struct { obj runtime.Object @@ -212,7 +219,7 @@ func TestExternalToInternalMapping(t *testing.T) { }{ { &InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, - `{"kind":"OptionalExtensionType","apiVersion":"testExternal"}`, + `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } @@ -234,15 +241,19 @@ func TestExternalToInternalMapping(t *testing.T) { } func TestExtensionMapping(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} + 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{}) + scheme.AddInternalGroupVersion(internalGV) + scheme.AddKnownTypeWithName(internalGV.String(), "ExtensionType", &InternalExtensionType{}) + scheme.AddKnownTypeWithName(internalGV.String(), "OptionalExtensionType", &InternalOptionalExtensionType{}) + scheme.AddKnownTypeWithName(internalGV.String(), "A", &ExtensionA{}) + scheme.AddKnownTypeWithName(internalGV.String(), "B", &ExtensionB{}) + scheme.AddKnownTypeWithName(externalGV.String(), "ExtensionType", &ExternalExtensionType{}) + scheme.AddKnownTypeWithName(externalGV.String(), "OptionalExtensionType", &ExternalOptionalExtensionType{}) + scheme.AddKnownTypeWithName(externalGV.String(), "A", &ExtensionA{}) + scheme.AddKnownTypeWithName(externalGV.String(), "B", &ExtensionB{}) table := []struct { obj runtime.Object @@ -250,21 +261,21 @@ func TestExtensionMapping(t *testing.T) { }{ { &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionA{TestString: "foo"}}}, - `{"kind":"ExtensionType","apiVersion":"testExternal","extension":{"kind":"A","testString":"foo"}} + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","testString":"foo"}} `, }, { &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionB{TestString: "bar"}}}, - `{"kind":"ExtensionType","apiVersion":"testExternal","extension":{"kind":"B","testString":"bar"}} + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","testString":"bar"}} `, }, { &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, - `{"kind":"ExtensionType","apiVersion":"testExternal","extension":null} + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null} `, }, } for _, item := range table { - gotEncoded, err := scheme.EncodeToVersion(item.obj, "testExternal") + gotEncoded, err := scheme.EncodeToVersion(item.obj, externalGV.String()) if err != nil { t.Errorf("unexpected error '%v' (%#v)", err, item.obj) } else if e, a := item.encoded, string(gotEncoded); e != a { @@ -288,10 +299,14 @@ func TestExtensionMapping(t *testing.T) { } func TestEncode(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} + scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{}) - scheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) - codec := runtime.CodecFor(scheme, "externalVersion") + scheme.AddInternalGroupVersion(internalGV) + scheme.AddKnownTypeWithName(internalGV.String(), "Simple", &InternalSimple{}) + scheme.AddKnownTypeWithName(externalGV.String(), "Simple", &ExternalSimple{}) + codec := runtime.CodecFor(scheme, externalGV.String()) test := &InternalSimple{ TestString: "I'm the same", }