internal versions

This commit is contained in:
deads2k 2015-11-17 13:21:32 -05:00
parent 6231404682
commit 68b0572974
17 changed files with 222 additions and 108 deletions

View File

@ -41,7 +41,7 @@ func BenchmarkPodConversion(b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("Conversion error: %v", err) 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 { if err != nil {
b.Fatalf("Conversion error: %v", err) b.Fatalf("Conversion error: %v", err)
} }
@ -69,7 +69,7 @@ func BenchmarkNodeConversion(b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("Conversion error: %v", err) 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 { if err != nil {
b.Fatalf("Conversion error: %v", err) b.Fatalf("Conversion error: %v", err)
} }
@ -97,7 +97,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("Conversion error: %v", err) 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 { if err != nil {
b.Fatalf("Conversion error: %v", err) b.Fatalf("Conversion error: %v", err)
} }

View File

@ -49,11 +49,15 @@ type GroupVersion struct {
Version string 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 // String puts "group" and "version" into a single "group/version" string. For the legacy v1
// it returns "v1". // it returns "v1".
func (gv GroupVersion) String() string { func (gv GroupVersion) String() string {
// special case the internal apiVersion for kube // special case the internal apiVersion for kube
if len(gv.Group) == 0 && len(gv.Version) == 0 { if gv.IsEmpty() {
return "" return ""
} }

View File

@ -25,7 +25,8 @@ func init() {
} }
func addKnownTypes() { func addKnownTypes() {
api.Scheme.AddKnownTypes("", // TODO this will get cleaned up with the scheme types are fixed
api.Scheme.AddKnownTypes("componentconfig/",
&KubeProxyConfiguration{}, &KubeProxyConfiguration{},
) )
} }

View File

@ -27,7 +27,8 @@ func init() {
// Adds the list of known types to api.Scheme. // Adds the list of known types to api.Scheme.
func addKnownTypes() { func addKnownTypes() {
api.Scheme.AddKnownTypes("", // TODO this gets cleaned up when the types are fixed
api.Scheme.AddKnownTypes("extensions/",
&ClusterAutoscaler{}, &ClusterAutoscaler{},
&ClusterAutoscalerList{}, &ClusterAutoscalerList{},
&Deployment{}, &Deployment{},

View File

@ -27,7 +27,8 @@ func init() {
// Adds the list of known types to api.Scheme. // Adds the list of known types to api.Scheme.
func addKnownTypes() { func addKnownTypes() {
api.Scheme.AddKnownTypes("", // TODO this will get cleaned up with the scheme types are fixed
api.Scheme.AddKnownTypes("metrics/",
&RawNode{}, &RawNode{},
&RawPod{}, &RawPod{},
) )

View File

@ -380,6 +380,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Convertor: a.group.Convertor, Convertor: a.group.Convertor,
Codec: mapping.Codec, Codec: mapping.Codec,
APIVersion: a.group.GroupVersion.String(), 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(), ServerAPIVersion: serverGroupVersion.String(),
Resource: resource, Resource: resource,
Subresource: subresource, Subresource: subresource,

View File

@ -58,6 +58,7 @@ func convert(obj runtime.Object) (runtime.Object, error) {
// This creates fake API versions, similar to api/latest.go. // This creates fake API versions, similar to api/latest.go.
var testAPIGroup = "test.group" var testAPIGroup = "test.group"
var testInternalGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: ""}
var testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"} var testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"}
var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"} var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"}
var prefix = "apis" var prefix = "apis"
@ -136,8 +137,9 @@ func init() {
// "internal" version // "internal" version
api.Scheme.AddKnownTypes( api.Scheme.AddKnownTypes(
"", &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, testInternalGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&unversioned.ListOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) &unversioned.ListOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddInternalGroupVersion(testInternalGroupVersion)
addGrouplessTypes() addGrouplessTypes()
addTestTypes() addTestTypes()
addNewTestTypes() addNewTestTypes()
@ -1630,7 +1632,7 @@ func TestConnectWithOptions(t *testing.T) {
} }
opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions) opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions)
if !ok { 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" { if opts.Param1 != "value1" && opts.Param2 != "value2" {
t.Errorf("Unexpected options value: %#v", opts) t.Errorf("Unexpected options value: %#v", opts)
@ -1677,7 +1679,7 @@ func TestConnectWithOptionsAndPath(t *testing.T) {
} }
opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions) opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions)
if !ok { 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" { if opts.Param1 != "value1" && opts.Param2 != "value2" {
t.Errorf("Unexpected options value: %#v", opts) t.Errorf("Unexpected options value: %#v", opts)

View File

@ -77,6 +77,7 @@ type RequestScope struct {
Subresource string Subresource string
Kind string Kind string
APIVersion string APIVersion string
InternalVersion unversioned.GroupVersion
// The version of apiserver resources to use // The version of apiserver resources to use
ServerAPIVersion string 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 { if err := scope.Codec.DecodeParametersInto(query, versioned); err != nil {
return nil, errors.NewBadRequest(err.Error()) return nil, errors.NewBadRequest(err.Error())
} }
out, err := scope.Convertor.ConvertToVersion(versioned, "") out, err := scope.Convertor.ConvertToVersion(versioned, scope.InternalVersion.String())
if err != nil { if err != nil {
// programmer error // programmer error
return nil, err return nil, err

View File

@ -26,18 +26,29 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
) )
func (s *Scheme) DecodeToVersionedObject(data []byte) (obj interface{}, version, kind string, err error) { func (s *Scheme) DecodeToVersionedObject(data []byte) (interface{}, string, string, error) {
version, kind, err = s.DataVersionAndKind(data) version, kind, err := s.DataVersionAndKind(data)
if err != nil { 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)) return nil, "", "", fmt.Errorf("version not set in '%s'", string(data))
} }
if kind == "" { if kind == "" {
return nil, "", "", fmt.Errorf("kind not set in '%s'", string(data)) 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 { if err != nil {
return nil, "", "", err 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 { if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil {
return nil, "", "", err return nil, "", "", err
} }
return return obj, version, kind, nil
} }
// Decode converts a JSON string back into a pointer to an api object. // 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 // s.InternalVersion type before being returned. Decode will not decode
// objects without version set unless InternalVersion is also "". // objects without version set unless InternalVersion is also "".
func (s *Scheme) Decode(data []byte) (interface{}, error) { func (s *Scheme) Decode(data []byte) (interface{}, error) {
// TODO this is cleaned up when internal types are fixed return s.DecodeToVersion(data, unversioned.GroupVersion{})
return s.DecodeToVersion(data, unversioned.ParseGroupVersionOrDie(s.InternalVersion))
} }
// DecodeToVersion converts a JSON string back into a pointer to an api object. // 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 // technique. The object will be converted, if necessary, into the versioned
// type before being returned. Decode will not decode objects without version // type before being returned. Decode will not decode objects without version
// set unless version is also "". // 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) { func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (interface{}, error) {
obj, sourceVersion, kind, err := s.DecodeToVersionedObject(data) obj, sourceVersion, kind, err := s.DecodeToVersionedObject(data)
if err != nil { if err != nil {
@ -73,8 +85,23 @@ func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (inte
return nil, err 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. // Convert if needed.
if gv.String() != sourceVersion { if gv != sourceGV {
objOut, err := s.NewObject(gv.String(), kind) objOut, err := s.NewObject(gv.String(), kind)
if err != nil { if err != nil {
return nil, err return nil, err
@ -118,7 +145,7 @@ func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}
if dataKind == "" { if dataKind == "" {
dataKind = gvk.Kind 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())) 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) { if len(gvk.Kind) > 0 && (dataKind != gvk.Kind) {

View File

@ -19,11 +19,12 @@ package conversion
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
) )
type notRegisteredErr struct { type notRegisteredErr struct {
kind string gvk unversioned.GroupVersionKind
version string
t reflect.Type t reflect.Type
} }
@ -31,13 +32,14 @@ func (k *notRegisteredErr) Error() string {
if k.t != nil { if k.t != nil {
return fmt.Sprintf("no kind is registered for the type %v", k.t) return fmt.Sprintf("no kind is registered for the type %v", k.t)
} }
if len(k.kind) == 0 { if len(k.gvk.Kind) == 0 {
return fmt.Sprintf("no version %q has been registered", k.version) return fmt.Sprintf("no version %q has been registered", k.gvk.GroupVersion())
} }
if len(k.version) == 0 { if len(k.gvk.Version) == 0 {
return fmt.Sprintf("no kind %q is registered for the default version", k.kind) 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 // IsNotRegisteredError returns true if the error indicates the provided

View File

@ -125,9 +125,13 @@ func TestMetaValues(t *testing.T) {
Kind string `json:"kind,omitempty"` Kind string `json:"kind,omitempty"`
TestString string `json:"testString"` TestString string `json:"testString"`
} }
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
s := NewScheme() s := NewScheme()
s.AddKnownTypeWithName("", "Simple", &InternalSimple{}) s.InternalVersions[internalGV.Group] = internalGV
s.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) s.AddKnownTypeWithName(internalGV.String(), "Simple", &InternalSimple{})
s.AddKnownTypeWithName(externalGV.String(), "Simple", &ExternalSimple{})
internalToExternalCalls := 0 internalToExternalCalls := 0
externalToInternalCalls := 0 externalToInternalCalls := 0
@ -136,10 +140,10 @@ func TestMetaValues(t *testing.T) {
err := s.AddConversionFuncs( err := s.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope Scope) error { func(in *InternalSimple, out *ExternalSimple, scope Scope) error {
t.Logf("internal -> external") 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) 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) t.Fatalf("Expected '%v', got '%v'", e, a)
} }
scope.Convert(&in.TestString, &out.TestString, 0) scope.Convert(&in.TestString, &out.TestString, 0)
@ -148,10 +152,10 @@ func TestMetaValues(t *testing.T) {
}, },
func(in *ExternalSimple, out *InternalSimple, scope Scope) error { func(in *ExternalSimple, out *InternalSimple, scope Scope) error {
t.Logf("external -> internal") 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) 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) t.Fatalf("Expected '%v', got '%v'", e, a)
} }
scope.Convert(&in.TestString, &out.TestString, 0) scope.Convert(&in.TestString, &out.TestString, 0)
@ -169,7 +173,7 @@ func TestMetaValues(t *testing.T) {
s.Log(t) s.Log(t)
// Test Encode, Decode, and DecodeInto // Test Encode, Decode, and DecodeInto
data, err := s.EncodeToVersion(simple, "externalVersion") data, err := s.EncodeToVersion(simple, externalGV.String())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@ -226,7 +230,6 @@ func TestMetaValuesUnregisteredConvert(t *testing.T) {
TestString string `json:"testString"` TestString string `json:"testString"`
} }
s := NewScheme() s := NewScheme()
s.InternalVersion = ""
// We deliberately don't register the types. // We deliberately don't register the types.
internalToExternalCalls := 0 internalToExternalCalls := 0

View File

@ -19,6 +19,8 @@ package conversion
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
) )
// Scheme defines an entire encoding and decoding scheme. // 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 // InternalVersion is the default internal version. It is recommended that
// you use "" for the internal version. // 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 // MetaInsertionFactory is used to create an object to store and retrieve
// the version and kind information for all objects. The default uses the // the version and kind information for all objects. The default uses the
@ -66,7 +70,13 @@ func NewScheme() *Scheme {
typeToKind: map[reflect.Type][]string{}, typeToKind: map[reflect.Type][]string{},
converter: NewConverter(), converter: NewConverter(),
cloner: NewCloner(), cloner: NewCloner(),
InternalVersion: "", // 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, MetaFactory: DefaultMetaFactory,
} }
s.converter.nameFunc = s.nameFunc s.converter.nameFunc = s.nameFunc
@ -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, // NewObject returns a new object of the given version and name,
// or an error if it hasn't been registered. // or an error if it hasn't been registered.
func (s *Scheme) NewObject(gvString, kind string) (interface{}, error) { 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 types, ok := s.versionMap[gvString]; ok {
if t, ok := types[kind]; ok { if t, ok := types[kind]; ok {
return reflect.New(t).Interface(), nil return reflect.New(t).Interface(), nil
} }
return nil, &notRegisteredErr{kind: kind, version: gvString}
return nil, &notRegisteredErr{gvk: gvk}
} }
return nil, &notRegisteredErr{kind: kind, version: gvString}
return nil, &notRegisteredErr{gvk: gvk}
} }
// AddConversionFuncs adds functions to the list of conversion functions. The given // AddConversionFuncs adds functions to the list of conversion functions. The given

View File

@ -23,6 +23,7 @@ import (
"strings" "strings"
"testing" "testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
@ -118,7 +119,6 @@ func GetTestScheme() *Scheme {
s.AddKnownTypeWithName("v1", "TestType2", &ExternalTestType2{}) s.AddKnownTypeWithName("v1", "TestType2", &ExternalTestType2{})
s.AddKnownTypeWithName("", "TestType3", &TestType1{}) s.AddKnownTypeWithName("", "TestType3", &TestType1{})
s.AddKnownTypeWithName("v1", "TestType3", &ExternalTestType1{}) s.AddKnownTypeWithName("v1", "TestType3", &ExternalTestType1{})
s.InternalVersion = ""
s.MetaFactory = testMetaFactory{} s.MetaFactory = testMetaFactory{}
return s return s
} }
@ -361,7 +361,7 @@ func TestBadJSONRejection(t *testing.T) {
func TestBadJSONRejectionForSetInternalVersion(t *testing.T) { func TestBadJSONRejectionForSetInternalVersion(t *testing.T) {
s := GetTestScheme() s := GetTestScheme()
s.InternalVersion = "v1" s.InternalVersions[""] = unversioned.GroupVersion{Version: "v1"}
badJSONs := [][]byte{ badJSONs := [][]byte{
[]byte(`{"myKindKey":"TestType1"}`), // Missing version []byte(`{"myKindKey":"TestType1"}`), // Missing version
} }

View File

@ -88,7 +88,8 @@ func (g *conversionGenerator) AddImport(pkg string) string {
func (g *conversionGenerator) GenerateConversionsForType(version string, reflection reflect.Type) error { func (g *conversionGenerator) GenerateConversionsForType(version string, reflection reflect.Type) error {
kind := reflection.Name() 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 { if err != nil {
return fmt.Errorf("cannot create an object of type %v in internal version", kind) return fmt.Errorf("cannot create an object of type %v in internal version", kind)
} }

View File

@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
) )
@ -61,11 +62,15 @@ func (*EmbeddedTest) IsAnAPIObject() {}
func (*EmbeddedTestExternal) IsAnAPIObject() {} func (*EmbeddedTestExternal) IsAnAPIObject() {}
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
s := runtime.NewScheme() internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
s.AddKnownTypes("", &ObjectTest{}) externalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "v1test", Kind: "ObjectTest"}
s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
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 { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@ -74,7 +79,7 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
t.Fatalf("unexpected object: %#v", test.Items[0]) 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 { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@ -85,11 +90,15 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
} }
func TestArrayOfRuntimeObject(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 := runtime.NewScheme()
s.AddKnownTypes("", &EmbeddedTest{}) s.AddInternalGroupVersion(internalGV)
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) s.AddKnownTypes(internalGV.String(), &EmbeddedTest{})
s.AddKnownTypes("", &ObjectTest{}) s.AddKnownTypeWithName(externalGV.String(), "EmbeddedTest", &EmbeddedTestExternal{})
s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{}) s.AddKnownTypes(internalGV.String(), &ObjectTest{})
s.AddKnownTypeWithName(externalGV.String(), "ObjectTest", &ObjectTestExternal{})
internal := &ObjectTest{ internal := &ObjectTest{
Items: []runtime.Object{ 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 { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@ -147,9 +156,14 @@ func TestArrayOfRuntimeObject(t *testing.T) {
} }
func TestEmbeddedObject(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 := runtime.NewScheme()
s.AddKnownTypes("", &EmbeddedTest{}) s.AddInternalGroupVersion(internalGV)
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) s.AddKnownTypes(internalGV.String(), &EmbeddedTest{})
s.AddKnownTypeWithName(externalGV.String(), embeddedTestExternalGVK.Kind, &EmbeddedTestExternal{})
outer := &EmbeddedTest{ outer := &EmbeddedTest{
ID: "outer", 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 { if err != nil {
t.Fatalf("Unexpected encode error '%v'", err) 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 // TestDeepCopyOfEmbeddedObject checks to make sure that EmbeddedObject's can be passed through DeepCopy with fidelity
func TestDeepCopyOfEmbeddedObject(t *testing.T) { 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 := runtime.NewScheme()
s.AddKnownTypes("", &EmbeddedTest{}) s.AddInternalGroupVersion(internalGV)
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) s.AddKnownTypes(internalGV.String(), &EmbeddedTest{})
s.AddKnownTypeWithName(externalGV.String(), embeddedTestExternalGVK.Kind, &EmbeddedTestExternal{})
original := &EmbeddedTest{ original := &EmbeddedTest{
ID: "outer", 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 { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -230,7 +249,7 @@ func TestDeepCopyOfEmbeddedObject(t *testing.T) {
t.Fatalf("unexpected error: %v", err) 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 { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }

View File

@ -181,8 +181,16 @@ func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExt
default: default:
version := outVersion version := outVersion
// if the object exists // if the object exists
if inVersion, _, err := scheme.ObjectVersionAndKind(src[i]); err == nil && len(inVersion) != 0 { // this code is try to set the outputVersion, but only if the object has a non-internal group version
version = inVersion 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) data, err := scheme.EncodeToVersion(src[i], version)
if err != nil { 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. // 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 := &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"} s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"}
if err := s.raw.AddConversionFuncs( if err := s.raw.AddConversionFuncs(
s.embeddedObjectToRawExtension, s.embeddedObjectToRawExtension,
@ -247,6 +259,10 @@ func NewScheme() *Scheme {
return s 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. // 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. // Encode() refuses the object unless its type is registered with AddKnownTypes.
func (s *Scheme) AddKnownTypes(version string, types ...Object) { func (s *Scheme) AddKnownTypes(version string, types ...Object) {

View File

@ -44,12 +44,15 @@ func (*InternalSimple) IsAnAPIObject() {}
func (*ExternalSimple) IsAnAPIObject() {} func (*ExternalSimple) IsAnAPIObject() {}
func TestScheme(t *testing.T) { func TestScheme(t *testing.T) {
internalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "", Kind: "Simple"} internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
externalGVK := unversioned.GroupVersionKind{Group: "test.group", Version: "externalVersion", Kind: "Simple"} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
internalGVK := internalGV.WithKind("Simple")
externalGVK := externalGV.WithKind("Simple")
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(internalGVK.GroupVersion().String(), internalGVK.Kind, &InternalSimple{}) scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName(externalGVK.GroupVersion().String(), externalGVK.Kind, &ExternalSimple{}) scheme.AddKnownTypeWithName(internalGV.String(), internalGVK.Kind, &InternalSimple{})
scheme.AddKnownTypeWithName(externalGV.String(), externalGVK.Kind, &ExternalSimple{})
// test that scheme is an ObjectTyper // test that scheme is an ObjectTyper
var _ runtime.ObjectTyper = scheme var _ runtime.ObjectTyper = scheme
@ -60,10 +63,10 @@ func TestScheme(t *testing.T) {
// Register functions to verify that scope.Meta() gets set correctly. // Register functions to verify that scope.Meta() gets set correctly.
err := scheme.AddConversionFuncs( err := scheme.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { 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) 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) t.Errorf("Expected '%v', got '%v'", e, a)
} }
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
@ -72,10 +75,10 @@ func TestScheme(t *testing.T) {
return nil return nil
}, },
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { 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) 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) t.Errorf("Expected '%v', got '%v'", e, a)
} }
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
@ -93,11 +96,11 @@ func TestScheme(t *testing.T) {
// Test Encode, Decode, DecodeInto, and DecodeToVersion // Test Encode, Decode, DecodeInto, and DecodeToVersion
obj := runtime.Object(simple) obj := runtime.Object(simple)
data, err := scheme.EncodeToVersion(obj, "externalVersion") data, err := scheme.EncodeToVersion(obj, externalGV.String())
obj2, err2 := scheme.Decode(data) obj2, err2 := scheme.Decode(data)
obj3 := &InternalSimple{} obj3 := &InternalSimple{}
err3 := scheme.DecodeInto(data, obj3) 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 { if err != nil || err2 != nil || err3 != nil || err4 != nil {
t.Fatalf("Failure: '%v' '%v' '%v' '%v'", err, err2, err3, err4) t.Fatalf("Failure: '%v' '%v' '%v' '%v'", err, err2, err3, err4)
} }
@ -202,9 +205,13 @@ func (*ExternalOptionalExtensionType) IsAnAPIObject() {}
func (*InternalOptionalExtensionType) IsAnAPIObject() {} func (*InternalOptionalExtensionType) IsAnAPIObject() {}
func TestExternalToInternalMapping(t *testing.T) { func TestExternalToInternalMapping(t *testing.T) {
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "OptionalExtensionType", &InternalOptionalExtensionType{}) scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName("testExternal", "OptionalExtensionType", &ExternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.String(), "OptionalExtensionType", &InternalOptionalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.String(), "OptionalExtensionType", &ExternalOptionalExtensionType{})
table := []struct { table := []struct {
obj runtime.Object obj runtime.Object
@ -212,7 +219,7 @@ func TestExternalToInternalMapping(t *testing.T) {
}{ }{
{ {
&InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, &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) { func TestExtensionMapping(t *testing.T) {
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"}
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "ExtensionType", &InternalExtensionType{}) scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName("", "OptionalExtensionType", &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.String(), "ExtensionType", &InternalExtensionType{})
scheme.AddKnownTypeWithName("", "A", &ExtensionA{}) scheme.AddKnownTypeWithName(internalGV.String(), "OptionalExtensionType", &InternalOptionalExtensionType{})
scheme.AddKnownTypeWithName("", "B", &ExtensionB{}) scheme.AddKnownTypeWithName(internalGV.String(), "A", &ExtensionA{})
scheme.AddKnownTypeWithName("testExternal", "ExtensionType", &ExternalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.String(), "B", &ExtensionB{})
scheme.AddKnownTypeWithName("testExternal", "OptionalExtensionType", &ExternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.String(), "ExtensionType", &ExternalExtensionType{})
scheme.AddKnownTypeWithName("testExternal", "A", &ExtensionA{}) scheme.AddKnownTypeWithName(externalGV.String(), "OptionalExtensionType", &ExternalOptionalExtensionType{})
scheme.AddKnownTypeWithName("testExternal", "B", &ExtensionB{}) scheme.AddKnownTypeWithName(externalGV.String(), "A", &ExtensionA{})
scheme.AddKnownTypeWithName(externalGV.String(), "B", &ExtensionB{})
table := []struct { table := []struct {
obj runtime.Object obj runtime.Object
@ -250,21 +261,21 @@ func TestExtensionMapping(t *testing.T) {
}{ }{
{ {
&InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionA{TestString: "foo"}}}, &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"}}}, &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}}, &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}},
`{"kind":"ExtensionType","apiVersion":"testExternal","extension":null} `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null}
`, `,
}, },
} }
for _, item := range table { for _, item := range table {
gotEncoded, err := scheme.EncodeToVersion(item.obj, "testExternal") gotEncoded, err := scheme.EncodeToVersion(item.obj, externalGV.String())
if err != nil { if err != nil {
t.Errorf("unexpected error '%v' (%#v)", err, item.obj) t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
} else if e, a := item.encoded, string(gotEncoded); e != a { } else if e, a := item.encoded, string(gotEncoded); e != a {
@ -288,10 +299,14 @@ func TestExtensionMapping(t *testing.T) {
} }
func TestEncode(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 := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{}) scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName("externalVersion", "Simple", &ExternalSimple{}) scheme.AddKnownTypeWithName(internalGV.String(), "Simple", &InternalSimple{})
codec := runtime.CodecFor(scheme, "externalVersion") scheme.AddKnownTypeWithName(externalGV.String(), "Simple", &ExternalSimple{})
codec := runtime.CodecFor(scheme, externalGV.String())
test := &InternalSimple{ test := &InternalSimple{
TestString: "I'm the same", TestString: "I'm the same",
} }