Merge pull request #17515 from deads2k/gv-scheme

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot
2015-11-25 10:20:35 -08:00
46 changed files with 579 additions and 339 deletions

View File

@@ -23,9 +23,9 @@ import (
"os"
"path"
"runtime"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
_ "k8s.io/kubernetes/pkg/api/v1"
_ "k8s.io/kubernetes/pkg/apis/componentconfig"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
@@ -84,25 +84,24 @@ func main() {
data := new(bytes.Buffer)
group, version := path.Split(*groupVersion)
group = strings.TrimRight(group, "/")
gv := unversioned.ParseGroupVersionOrDie(*groupVersion)
_, err := data.WriteString(fmt.Sprintf("package %v\n", version))
_, err := data.WriteString(fmt.Sprintf("package %v\n", gv.Version))
if err != nil {
glog.Fatalf("Error while writing package line: %v", err)
}
versionPath := pkgPath(group, version)
versionPath := pkgPath(gv.Group, gv.Version)
generator := kruntime.NewConversionGenerator(api.Scheme.Raw(), versionPath)
apiShort := generator.AddImport(path.Join(pkgBase, "api"))
generator.AddImport(path.Join(pkgBase, "api/resource"))
// TODO(wojtek-t): Change the overwrites to a flag.
generator.OverwritePackage(version, "")
for _, knownType := range api.Scheme.KnownTypes(*groupVersion) {
generator.OverwritePackage(gv.Version, "")
for _, knownType := range api.Scheme.KnownTypes(gv) {
if knownType.PkgPath() != versionPath {
continue
}
if err := generator.GenerateConversionsForType(version, knownType); err != nil {
if err := generator.GenerateConversionsForType(gv, knownType); err != nil {
glog.Errorf("Error while generating conversion functions for %v: %v", knownType, err)
}
}

View File

@@ -26,6 +26,7 @@ import (
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
_ "k8s.io/kubernetes/pkg/api/v1"
_ "k8s.io/kubernetes/pkg/apis/componentconfig"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
@@ -50,8 +51,8 @@ var (
)
// types inside the api package don't need to say "api.Scheme"; all others do.
func destScheme(group, version string) string {
if group == "" && version == "" {
func destScheme(gv unversioned.GroupVersion) string {
if gv == api.SchemeGroupVersion {
return "Scheme"
}
return "api.Scheme"
@@ -93,18 +94,18 @@ func main() {
data := new(bytes.Buffer)
group, version := path.Split(*groupVersion)
group = strings.TrimRight(group, "/")
registerTo := destScheme(group, version)
gv := unversioned.ParseGroupVersionOrDie(*groupVersion)
registerTo := destScheme(gv)
var pkgname string
if group == "" {
if gv.Group == "" {
// the internal version of v1 is registered in package api
pkgname = "api"
} else {
pkgname = group
pkgname = gv.Group
}
if len(version) != 0 {
pkgname = version
if len(gv.Version) != 0 {
pkgname = gv.Version
}
_, err := data.WriteString(fmt.Sprintf("package %s\n", pkgname))
@@ -112,7 +113,7 @@ func main() {
glog.Fatalf("Error while writing package line: %v", err)
}
versionPath := pkgPath(group, version)
versionPath := pkgPath(gv.Group, gv.Version)
generator := kruntime.NewDeepCopyGenerator(api.Scheme.Raw(), versionPath, sets.NewString("k8s.io/kubernetes"))
generator.AddImport(path.Join(pkgBase, "api"))
@@ -125,14 +126,8 @@ func main() {
generator.OverwritePackage(vals[0], vals[1])
}
}
var schemeVersion string
if version == "" {
// This occurs when we generate deep-copy for internal version.
schemeVersion = ""
} else {
schemeVersion = *groupVersion
}
for _, knownType := range api.Scheme.KnownTypes(schemeVersion) {
for _, knownType := range api.Scheme.KnownTypes(gv) {
if knownType.PkgPath() != versionPath {
continue
}

View File

@@ -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)
}

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util"
"github.com/google/gofuzz"
@@ -31,10 +32,10 @@ import (
func TestDeepCopyApiObjects(t *testing.T) {
for i := 0; i < *fuzzIters; i++ {
for _, version := range []string{"", testapi.Default.Version()} {
f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63()))
for kind := range api.Scheme.KnownTypes(version) {
doDeepCopyTest(t, version, kind, f)
for _, gv := range []unversioned.GroupVersion{testapi.Default.InternalGroupVersion(), *testapi.Default.GroupVersion()} {
f := apitesting.FuzzerFor(t, gv.String(), rand.NewSource(rand.Int63()))
for kind := range api.Scheme.KnownTypes(gv) {
doDeepCopyTest(t, gv.String(), kind, f)
}
}
}

View File

@@ -41,9 +41,8 @@ func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, inter
// enumerate all supported versions, get the kinds, and register with the mapper how to address
// our resources.
for _, gv := range defaultGroupVersions {
for kind, oType := range Scheme.KnownTypes(gv.String()) {
for kind, oType := range Scheme.KnownTypes(gv) {
gvk := gv.WithKind(kind)
// TODO: Remove import path prefix check.
// We check the import path prefix because we currently stuff both "api" and "extensions" objects
// into the same group within Scheme since Scheme has no notion of groups yet.

View File

@@ -189,9 +189,21 @@ func (m *DefaultRESTMapper) GroupForResource(resource string) (string, error) {
// them with group/version tuples.
// TODO this should probably become RESTMapping(GroupKind, versions ...string)
func (m *DefaultRESTMapper) RESTMapping(kind string, versions ...string) (*RESTMapping, error) {
// TODO, this looks really strange, but once this API is update, the version detection becomes clean again
// because you won't be able to request cross-group kinds
hadVersion := false
for _, gvString := range versions {
currGroupVersion, err := unversioned.ParseGroupVersion(gvString)
if err != nil {
return nil, err
}
if len(currGroupVersion.Version) != 0 {
hadVersion = true
}
}
// Pick an appropriate version
var groupVersion *unversioned.GroupVersion
hadVersion := false
for _, v := range versions {
if len(v) == 0 {
continue
@@ -202,7 +214,6 @@ func (m *DefaultRESTMapper) RESTMapping(kind string, versions ...string) (*RESTM
}
currGVK := currGroupVersion.WithKind(kind)
hadVersion = true
if _, ok := m.kindToPluralResource[currGVK]; ok {
groupVersion = &currGroupVersion
break

View File

@@ -28,6 +28,8 @@ import (
type fakeCodec struct{}
var _ runtime.Decoder = fakeCodec{}
func (fakeCodec) Encode(runtime.Object) ([]byte, error) {
return []byte{}, nil
}
@@ -40,7 +42,7 @@ func (fakeCodec) Decode([]byte) (runtime.Object, error) {
return nil, nil
}
func (fakeCodec) DecodeToVersion([]byte, string) (runtime.Object, error) {
func (fakeCodec) DecodeToVersion([]byte, unversioned.GroupVersion) (runtime.Object, error) {
return nil, nil
}
@@ -48,7 +50,7 @@ func (fakeCodec) DecodeInto([]byte, runtime.Object) error {
return nil
}
func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, string, string) error {
func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, unversioned.GroupVersionKind) error {
return nil
}

View File

@@ -24,8 +24,11 @@ import (
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
var Scheme = runtime.NewScheme()
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""}
func init() {
Scheme.AddKnownTypes("",
Scheme.AddKnownTypes(SchemeGroupVersion,
&Pod{},
&PodList{},
&PodStatusResult{},
@@ -69,12 +72,13 @@ func init() {
)
// Register Unversioned types
Scheme.AddKnownTypes("", &unversioned.ListOptions{})
Scheme.AddKnownTypes("", &unversioned.Status{})
Scheme.AddKnownTypes("", &unversioned.APIVersions{})
Scheme.AddKnownTypes("", &unversioned.APIGroupList{})
Scheme.AddKnownTypes("", &unversioned.APIGroup{})
Scheme.AddKnownTypes("", &unversioned.APIResourceList{})
// TODO this should not be done here
Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.ListOptions{})
Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.Status{})
Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIVersions{})
Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIGroupList{})
Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIGroup{})
Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIResourceList{})
}
func (*Pod) IsAnAPIObject() {}

View File

@@ -135,7 +135,7 @@ func TestRoundTripTypes(t *testing.T) {
// api.Scheme.Log(t)
// defer api.Scheme.Log(nil)
for kind := range api.Scheme.KnownTypes("") {
for kind := range api.Scheme.KnownTypes(testapi.Default.InternalGroupVersion()) {
if nonRoundTrippableTypes.Has(kind) {
continue
}

View File

@@ -24,12 +24,12 @@ import (
"k8s.io/kubernetes/pkg/api"
_ "k8s.io/kubernetes/pkg/api/install"
"k8s.io/kubernetes/pkg/api/unversioned"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
apiutil "k8s.io/kubernetes/pkg/api/util"
"k8s.io/kubernetes/pkg/runtime"
)
@@ -89,11 +89,13 @@ func (g TestGroup) GroupAndVersion() string {
}
func (g TestGroup) GroupVersion() *unversioned.GroupVersion {
gv, err := unversioned.ParseGroupVersion(g.GroupVersionUnderTest)
if err != nil {
panic(err)
}
return &gv
return &unversioned.GroupVersion{Group: g.Group, Version: g.VersionUnderTest}
}
// InternalGroupVersion returns the group,version used to identify the internal
// types for this API
func (g TestGroup) InternalGroupVersion() unversioned.GroupVersion {
return unversioned.GroupVersion{Group: g.Group}
}
// Codec returns the codec for the API version to test against, as set by the

View File

@@ -49,9 +49,19 @@ type GroupVersion struct {
Version string
}
// IsEmpty returns true if group and version are empty
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 the legacy kube types
if gv.IsEmpty() {
return ""
}
// special case of "v1" for backward compatibility
if gv.Group == "" && gv.Version == "v1" {
return gv.Version
@@ -63,6 +73,12 @@ func (gv GroupVersion) String() string {
// ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
// if it cannot parse the string.
func ParseGroupVersion(gv string) (GroupVersion, error) {
// this can be the internal version for the legacy kube types
// TODO once we've cleared the last uses as strings, this special case should be removed.
if (len(gv) == 0) || (gv == "/") {
return GroupVersion{}, nil
}
s := strings.Split(gv, "/")
// "v1" is the only special case. Otherwise GroupVersion is expected to contain
// one "/" dividing the string into two parts.
@@ -85,6 +101,7 @@ func ParseGroupVersionOrDie(gv string) GroupVersion {
return ret
}
// WithKind creates a GroupVersionKind based on the method receiver's GroupVersion and the passed Kind.
func (gv GroupVersion) WithKind(kind string) GroupVersionKind {
return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
}

View File

@@ -23,12 +23,15 @@ import (
"k8s.io/kubernetes/pkg/runtime"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"}
// Codec encodes internal objects to the v1 scheme
var Codec = runtime.CodecFor(api.Scheme, "v1")
var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion.String())
func init() {
// Check if v1 is in the list of supported API versions.
if !registered.IsRegisteredAPIGroupVersion(unversioned.GroupVersion{Group: "", Version: "v1"}) {
if !registered.IsRegisteredAPIGroupVersion(SchemeGroupVersion) {
return
}
@@ -40,7 +43,7 @@ func init() {
// Adds the list of known types to api.Scheme.
func addKnownTypes() {
api.Scheme.AddKnownTypes("v1",
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&Pod{},
&PodList{},
&PodStatusResult{},
@@ -85,7 +88,7 @@ func addKnownTypes() {
)
// Add common types
api.Scheme.AddKnownTypes("v1", &unversioned.Status{})
api.Scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.Status{})
}
func (*Pod) IsAnAPIObject() {}

View File

@@ -18,14 +18,19 @@ package componentconfig
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
)
func init() {
addKnownTypes()
}
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "componentconfig", Version: ""}
func addKnownTypes() {
api.Scheme.AddKnownTypes("",
// TODO this will get cleaned up with the scheme types are fixed
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&KubeProxyConfiguration{},
)
}

View File

@@ -18,10 +18,14 @@ package v1alpha1
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
var Codec = runtime.CodecFor(api.Scheme, "componentconfig/v1alpha1")
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "componentconfig", Version: "v1alpha1"}
var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion.String())
func init() {
addKnownTypes()
@@ -29,7 +33,7 @@ func init() {
}
func addKnownTypes() {
api.Scheme.AddKnownTypes("componentconfig/v1alpha1",
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&KubeProxyConfiguration{},
)
}

View File

@@ -18,8 +18,12 @@ package extensions
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "extensions", Version: ""}
func init() {
// Register the API.
addKnownTypes()
@@ -27,7 +31,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(SchemeGroupVersion,
&ClusterAutoscaler{},
&ClusterAutoscalerList{},
&Deployment{},

View File

@@ -18,10 +18,14 @@ package v1beta1
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
var Codec = runtime.CodecFor(api.Scheme, "extensions/v1beta1")
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "extensions", Version: "v1beta1"}
var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion.String())
func init() {
addKnownTypes()
@@ -31,7 +35,7 @@ func init() {
// Adds the list of known types to api.Scheme.
func addKnownTypes() {
api.Scheme.AddKnownTypes("extensions/v1beta1",
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&ClusterAutoscaler{},
&ClusterAutoscalerList{},
&Deployment{},

View File

@@ -18,6 +18,7 @@ package metrics
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
)
func init() {
@@ -25,9 +26,13 @@ func init() {
addKnownTypes()
}
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "metrics", Version: ""}
// 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(SchemeGroupVersion,
&RawNode{},
&RawPod{},
)

View File

@@ -18,10 +18,14 @@ package v1alpha1
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
var Codec = runtime.CodecFor(api.Scheme, "metrics/v1alpha1")
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: "metrics", Version: "v1alpha1"}
var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion.String())
func init() {
// Register the API.
@@ -30,7 +34,7 @@ func init() {
// Adds the list of known types to api.Scheme.
func addKnownTypes() {
api.Scheme.AddKnownTypes("metrics/v1alpha1",
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&RawNode{},
&RawPod{},
)

View File

@@ -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,

View File

@@ -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"
@@ -112,21 +113,21 @@ func newMapper() *meta.DefaultRESTMapper {
func addGrouplessTypes() {
api.Scheme.AddKnownTypes(
grouplessGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
grouplessGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&unversioned.ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddKnownTypes(grouplessGroupVersion.String(), &api.Pod{})
api.Scheme.AddKnownTypes(grouplessGroupVersion, &api.Pod{})
}
func addTestTypes() {
api.Scheme.AddKnownTypes(
testGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
testGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&unversioned.ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddKnownTypes(testGroupVersion.String(), &api.Pod{})
api.Scheme.AddKnownTypes(testGroupVersion, &api.Pod{})
}
func addNewTestTypes() {
api.Scheme.AddKnownTypes(
newGroupVersion.String(), &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
newGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&unversioned.ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
}
@@ -136,8 +137,9 @@ func init() {
// "internal" version
api.Scheme.AddKnownTypes(
"", &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
testInternalGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{},
&unversioned.ListOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddInternalGroupVersion(testInternalGroupVersion)
addGrouplessTypes()
addTestTypes()
addNewTestTypes()
@@ -147,9 +149,8 @@ func init() {
// enumerate all supported versions, get the kinds, and register with
// the mapper how to address our resources
for _, gv := range groupVersions {
for kind := range api.Scheme.KnownTypes(gv.String()) {
for kind := range api.Scheme.KnownTypes(gv) {
gvk := gv.WithKind(kind)
root := bool(kind == "SimpleRoot")
if root {
nsMapper.Add(gvk, meta.RESTScopeRoot, false)
@@ -1630,7 +1631,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 +1678,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)

View File

@@ -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
@@ -148,15 +149,31 @@ func getRequestOptions(req *restful.Request, scope RequestScope, kind string, su
newQuery[subpathKey] = []string{req.PathParameter("path")}
query = newQuery
}
versioned, err := scope.Creater.New(scope.ServerAPIVersion, kind)
// TODO Options a mess. Basically the intent is:
// 1. try to decode using the expected external GroupVersion
// 2. if that fails, fall back to the old external serialization being used before, which was
// "v1" and decode into the unversioned/legacykube group
gvString := scope.APIVersion
internalGVString := scope.InternalVersion.String()
versioned, err := scope.Creater.New(gvString, kind)
if err != nil {
// programmer error
return nil, err
gvString = "v1"
internalGVString = ""
var secondErr error
versioned, secondErr = scope.Creater.New(gvString, kind)
// if we have an error, return the original failure
if secondErr != nil {
return nil, err
}
}
if err := scope.Codec.DecodeParametersInto(query, versioned); err != nil {
return nil, errors.NewBadRequest(err.Error())
}
out, err := scope.Convertor.ConvertToVersion(versioned, "")
out, err := scope.Convertor.ConvertToVersion(versioned, internalGVString)
if err != nil {
// programmer error
return nil, err
@@ -343,7 +360,8 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
}
obj := r.New()
if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.APIVersion, scope.Kind); err != nil {
// TODO this cleans up with proper typing
if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, unversioned.ParseGroupVersionOrDie(scope.APIVersion).WithKind(scope.Kind)); err != nil {
err = transformDecodeError(typer, err, obj, body)
errorJSON(err, scope.Codec, w)
return
@@ -576,7 +594,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
}
obj := r.New()
if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.APIVersion, scope.Kind); err != nil {
if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, unversioned.ParseGroupVersionOrDie(scope.APIVersion).WithKind(scope.Kind)); err != nil {
err = transformDecodeError(typer, err, obj, body)
errorJSON(err, scope.Codec, w)
return

View File

@@ -17,14 +17,19 @@ limitations under the License.
package api
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
var Scheme = runtime.NewScheme()
// SchemeGroupVersion is group version used to register these objects
// TODO this should be in the "kubeconfig" group
var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""}
func init() {
Scheme.AddKnownTypes("",
Scheme.AddKnownTypes(SchemeGroupVersion,
&Config{},
)
}

View File

@@ -17,15 +17,20 @@ limitations under the License.
package v1
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/runtime"
)
// SchemeGroupVersion is group version used to register these objects
// TODO this should be in the "kubeconfig" group
var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"}
// Codec encodes internal objects to the v1 scheme
var Codec = runtime.CodecFor(api.Scheme, "v1")
var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion.String())
func init() {
api.Scheme.AddKnownTypes("v1",
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&Config{},
)
}

View File

@@ -38,7 +38,7 @@ import (
type ObjectRetriever interface {
// Kind should return a resource or a list of resources (depending on the provided kind and
// name). It should return an error if the caller should communicate an error to the server.
Kind(kind, name string) (runtime.Object, error)
Kind(gvk unversioned.GroupVersionKind, name string) (runtime.Object, error)
// Add adds a runtime object for test purposes into this object.
Add(runtime.Object) error
}
@@ -59,23 +59,29 @@ type ObjectScheme interface {
func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc {
return func(action Action) (bool, runtime.Object, error) {
_, kind, err := mapper.VersionAndKindForResource(action.GetResource())
gvString, kind, err := mapper.VersionAndKindForResource(action.GetResource())
if err != nil {
return false, nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err)
}
gv, err := unversioned.ParseGroupVersion(gvString)
if err != nil {
return false, nil, err
}
gvk := gv.WithKind(kind)
// TODO: have mapper return a Kind for a subresource?
switch castAction := action.(type) {
case ListAction:
resource, err := o.Kind(kind+"List", "")
gvk.Kind += "List"
resource, err := o.Kind(gvk, "")
return true, resource, err
case GetAction:
resource, err := o.Kind(kind, castAction.GetName())
resource, err := o.Kind(gvk, castAction.GetName())
return true, resource, err
case DeleteAction:
resource, err := o.Kind(kind, castAction.GetName())
resource, err := o.Kind(gvk, castAction.GetName())
return true, resource, err
case CreateAction:
@@ -83,7 +89,7 @@ func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc {
if err != nil {
return true, nil, err
}
resource, err := o.Kind(kind, meta.Name)
resource, err := o.Kind(gvk, meta.Name)
return true, resource, err
case UpdateAction:
@@ -91,7 +97,7 @@ func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc {
if err != nil {
return true, nil, err
}
resource, err := o.Kind(kind, meta.Name)
resource, err := o.Kind(gvk, meta.Name)
return true, resource, err
default:
@@ -151,19 +157,24 @@ func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetrie
}
}
func (o objects) Kind(kind, name string) (runtime.Object, error) {
empty, _ := o.scheme.New("", kind)
func (o objects) Kind(gvk unversioned.GroupVersionKind, name string) (runtime.Object, error) {
// TODO our test clients deal in internal versions. We need to plumb that knowledge down here
// we might do this via an extra function to the scheme to allow getting internal group versions
// I'm punting for now
gvk.Version = ""
empty, _ := o.scheme.New(gvk.GroupVersion().String(), gvk.Kind)
nilValue := reflect.Zero(reflect.TypeOf(empty)).Interface().(runtime.Object)
arr, ok := o.types[kind]
arr, ok := o.types[gvk.Kind]
if !ok {
if strings.HasSuffix(kind, "List") {
itemKind := kind[:len(kind)-4]
if strings.HasSuffix(gvk.Kind, "List") {
itemKind := gvk.Kind[:len(gvk.Kind)-4]
arr, ok := o.types[itemKind]
if !ok {
return empty, nil
}
out, err := o.scheme.New("", kind)
out, err := o.scheme.New(gvk.GroupVersion().String(), gvk.Kind)
if err != nil {
return nilValue, err
}
@@ -175,25 +186,25 @@ func (o objects) Kind(kind, name string) (runtime.Object, error) {
}
return out, nil
}
return nilValue, errors.NewNotFound(kind, name)
return nilValue, errors.NewNotFound(gvk.Kind, name)
}
index := o.last[kind]
index := o.last[gvk.Kind]
if index >= len(arr) {
index = len(arr) - 1
}
if index < 0 {
return nilValue, errors.NewNotFound(kind, name)
return nilValue, errors.NewNotFound(gvk.Kind, name)
}
out, err := o.scheme.Copy(arr[index])
if err != nil {
return nilValue, err
}
o.last[kind] = index + 1
o.last[gvk.Kind] = index + 1
if status, ok := out.(*unversioned.Status); ok {
if status.Details != nil {
status.Details.Kind = kind
status.Details.Kind = gvk.Kind
}
if status.Status != unversioned.StatusSuccess {
return nilValue, &errors.StatusError{ErrStatus: *status}

View File

@@ -22,20 +22,33 @@ import (
"net/url"
"github.com/ugorji/go/codec"
"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
}
@@ -43,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.
@@ -52,7 +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) {
return s.DecodeToVersion(data, s.InternalVersion)
return s.DecodeToVersion(data, unversioned.GroupVersion{})
}
// DecodeToVersion converts a JSON string back into a pointer to an api object.
@@ -60,7 +73,9 @@ 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 "".
func (s *Scheme) DecodeToVersion(data []byte, version string) (interface{}, error) {
// 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 {
return nil, err
@@ -70,13 +85,28 @@ func (s *Scheme) DecodeToVersion(data []byte, version string) (interface{}, erro
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 version != sourceVersion {
objOut, err := s.NewObject(version, kind)
if gv != sourceGV {
objOut, err := s.NewObject(gv.String(), kind)
if err != nil {
return nil, err
}
flags, meta := s.generateConvertMeta(sourceVersion, version, obj)
flags, meta := s.generateConvertMeta(sourceVersion, gv.String(), obj)
if err := s.converter.Convert(obj, objOut, flags, meta); err != nil {
return nil, err
}
@@ -91,17 +121,17 @@ func (s *Scheme) DecodeToVersion(data []byte, version string) (interface{}, erro
// If obj's version doesn't match that in data, an attempt will be made to convert
// data into obj's version.
func (s *Scheme) DecodeInto(data []byte, obj interface{}) error {
return s.DecodeIntoWithSpecifiedVersionKind(data, obj, "", "")
return s.DecodeIntoWithSpecifiedVersionKind(data, obj, unversioned.GroupVersionKind{})
}
// DecodeIntoWithSpecifiedVersionKind compares the passed in specifiedVersion and
// specifiedKind with data.Version and data.Kind, defaulting data.Version and
// DecodeIntoWithSpecifiedVersionKind compares the passed in requestGroupVersionKind
// with data.Version and data.Kind, defaulting data.Version and
// data.Kind to the specified value if they are empty, or generating an error if
// data.Version and data.Kind are not empty and differ from the specified value.
// The function then implements the functionality of DecodeInto.
// If specifiedVersion and specifiedKind are empty, the function degenerates to
// DecodeInto.
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, specifiedVersion, specifiedKind string) error {
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, requestedGVK unversioned.GroupVersionKind) error {
if len(data) == 0 {
return errors.New("empty input")
}
@@ -110,16 +140,16 @@ func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}
return err
}
if dataVersion == "" {
dataVersion = specifiedVersion
dataVersion = requestedGVK.GroupVersion().String()
}
if dataKind == "" {
dataKind = specifiedKind
dataKind = requestedGVK.Kind
}
if len(specifiedVersion) > 0 && (dataVersion != specifiedVersion) {
return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%s)", dataVersion, specifiedVersion))
if (len(requestedGVK.Group) > 0 || len(requestedGVK.Version) > 0) && (dataVersion != requestedGVK.GroupVersion().String()) {
return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%v)", dataVersion, requestedGVK.GroupVersion()))
}
if len(specifiedKind) > 0 && (dataKind != specifiedKind) {
return errors.New(fmt.Sprintf("The kind in the data (%s) does not match the specified kind(%s)", dataKind, specifiedKind))
if len(requestedGVK.Kind) > 0 && (dataKind != requestedGVK.Kind) {
return errors.New(fmt.Sprintf("The kind in the data (%s) does not match the specified kind(%v)", dataKind, requestedGVK))
}
objVersion, objKind, err := s.ObjectVersionAndKind(obj)

View File

@@ -77,7 +77,7 @@ func (s *Scheme) EncodeToVersionStream(obj interface{}, destVersion string, stre
return err
}
if _, registered := s.typeToVersion[v.Type()]; !registered {
if _, registered := s.typeToGVK[v.Type()]; !registered {
return fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destVersion)
}

View File

@@ -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

View File

@@ -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.WithKind("Simple"), &InternalSimple{})
s.AddKnownTypeWithName(externalGV.WithKind("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

View File

@@ -19,22 +19,19 @@ package conversion
import (
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// Scheme defines an entire encoding and decoding scheme.
type Scheme struct {
// versionMap allows one to figure out the go type of an object with
// the given version and name.
versionMap map[string]map[string]reflect.Type
gvkToType map[unversioned.GroupVersionKind]reflect.Type
// typeToVersion allows one to figure out the version for a given go object.
// The reflect.Type we index by should *not* be a pointer. If the same type
// is registered for multiple versions, the last one wins.
typeToVersion map[reflect.Type]string
// typeToKind allows one to figure out the desired "kind" field for a given
// go object. Requirements and caveats are the same as typeToVersion.
typeToKind map[reflect.Type][]string
// typeToGroupVersion allows one to find metadata for a given go object.
// The reflect.Type we index by should *not* be a pointer.
typeToGVK map[reflect.Type][]unversioned.GroupVersionKind
// converter stores all registered conversion functions. It also has
// default coverting behavior.
@@ -50,7 +47,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 +60,18 @@ 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,
gvkToType: map[unversioned.GroupVersionKind]reflect.Type{},
typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{},
converter: NewConverter(),
cloner: NewCloner(),
// TODO remove this hard coded list. As step one, hardcode it here so this pull doesn't become even bigger
InternalVersions: map[string]unversioned.GroupVersion{
"": {},
"componentconfig": {Group: "componentconfig"},
"extensions": {Group: "extensions"},
"metrics": {Group: "metrics"},
},
MetaFactory: DefaultMetaFactory,
}
s.converter.nameFunc = s.nameFunc
return s
@@ -82,30 +86,32 @@ func (s *Scheme) Log(l DebugLogger) {
// a conversion. Defaults to the go name of the type if the type is not registered.
func (s *Scheme) nameFunc(t reflect.Type) string {
// find the preferred names for this type
names, ok := s.typeToKind[t]
gvks, ok := s.typeToGVK[t]
if !ok {
return t.Name()
}
if internal, ok := s.versionMap[""]; ok {
for _, name := range names {
if t, ok := internal[name]; ok {
return s.typeToKind[t][0]
}
for _, gvk := range gvks {
internalGV, exists := s.InternalVersions[gvk.Group]
if !exists {
internalGV := gvk.GroupVersion()
internalGV.Version = ""
}
internalGVK := internalGV.WithKind(gvk.Kind)
if internalType, exists := s.gvkToType[internalGVK]; exists {
return s.typeToGVK[internalType][0].Kind
}
}
return names[0]
return gvks[0].Kind
}
// AddKnownTypes registers all types passed in 'types' as being members of version 'version.
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
// Encode() will refuse objects unless their type has been registered with AddKnownTypes.
// All objects passed to types should be pointers to structs. The name that go reports for
// the struct becomes the "kind" field when encoding.
func (s *Scheme) AddKnownTypes(version string, types ...interface{}) {
knownTypes, found := s.versionMap[version]
if !found {
knownTypes = map[string]reflect.Type{}
s.versionMap[version] = knownTypes
}
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...interface{}) {
for _, obj := range types {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Ptr {
@@ -115,21 +121,17 @@ func (s *Scheme) AddKnownTypes(version string, types ...interface{}) {
if t.Kind() != reflect.Struct {
panic("All types must be pointers to structs.")
}
knownTypes[t.Name()] = t
s.typeToVersion[t] = version
s.typeToKind[t] = append(s.typeToKind[t], t.Name())
gvk := gv.WithKind(t.Name())
s.gvkToType[gvk] = t
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
}
}
// AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
// be encoded as. Useful for testing when you don't want to make multiple packages to define
// your structs.
func (s *Scheme) AddKnownTypeWithName(version, kind string, obj interface{}) {
knownTypes, found := s.versionMap[version]
if !found {
knownTypes = map[string]reflect.Type{}
s.versionMap[version] = knownTypes
}
func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj interface{}) {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.")
@@ -138,34 +140,41 @@ func (s *Scheme) AddKnownTypeWithName(version, kind string, obj interface{}) {
if t.Kind() != reflect.Struct {
panic("All types must be pointers to structs.")
}
knownTypes[kind] = t
s.typeToVersion[t] = version
s.typeToKind[t] = append(s.typeToKind[t], kind)
s.gvkToType[gvk] = t
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
}
// KnownTypes returns an array of the types that are known for a particular version.
func (s *Scheme) KnownTypes(version string) map[string]reflect.Type {
all, ok := s.versionMap[version]
if !ok {
return map[string]reflect.Type{}
}
types := make(map[string]reflect.Type)
for k, v := range all {
types[k] = v
func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type {
types := map[string]reflect.Type{}
for gvk, t := range s.gvkToType {
if gv != gvk.GroupVersion() {
continue
}
types[gvk.Kind] = t
}
return types
}
// NewObject returns a new object of the given version and name,
// or an error if it hasn't been registered.
func (s *Scheme) NewObject(versionName, kind string) (interface{}, error) {
if types, ok := s.versionMap[versionName]; ok {
if t, ok := types[kind]; ok {
return reflect.New(t).Interface(), nil
}
return nil, &notRegisteredErr{kind: kind, version: versionName}
func (s *Scheme) NewObject(gvString, kind string) (interface{}, error) {
gv, err := unversioned.ParseGroupVersion(gvString)
if err != nil {
return nil, err
}
return nil, &notRegisteredErr{kind: kind, version: versionName}
gvk := gv.WithKind(kind)
if t, exists := s.gvkToType[gvk]; exists {
return reflect.New(t).Interface(), nil
}
return nil, &notRegisteredErr{gvk: gvk}
}
// AddConversionFuncs adds functions to the list of conversion functions. The given
@@ -274,13 +283,15 @@ func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
// Recognizes returns true if the scheme is able to handle the provided version and kind
// of an object.
func (s *Scheme) Recognizes(version, kind string) bool {
m, ok := s.versionMap[version]
if !ok {
func (s *Scheme) Recognizes(gvString, kind string) bool {
gv, err := unversioned.ParseGroupVersion(gvString)
if err != nil {
return false
}
_, ok = m[kind]
return ok
gvk := gv.WithKind(kind)
_, exists := s.gvkToType[gvk]
return exists
}
// RegisterInputDefaults sets the provided field mapping function and field matching
@@ -331,18 +342,18 @@ func (s *Scheme) ConvertToVersion(in interface{}, outVersion string) (interface{
return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
}
kinds, ok := s.typeToKind[t]
gvks, ok := s.typeToGVK[t]
if !ok {
return nil, fmt.Errorf("%v cannot be converted into version %q", t, outVersion)
}
outKind := kinds[0]
outKind := gvks[0]
inVersion, _, err := s.ObjectVersionAndKind(in)
if err != nil {
return nil, err
}
out, err := s.NewObject(outVersion, outKind)
out, err := s.NewObject(outVersion, outKind.Kind)
if err != nil {
return nil, err
}
@@ -352,7 +363,7 @@ func (s *Scheme) ConvertToVersion(in interface{}, outVersion string) (interface{
return nil, err
}
if err := s.SetVersionAndKind(outVersion, outKind, out); err != nil {
if err := s.SetVersionAndKind(outVersion, outKind.Kind, out); err != nil {
return nil, err
}
@@ -388,13 +399,13 @@ func (s *Scheme) ObjectVersionAndKind(obj interface{}) (apiVersion, kind string,
return "", "", err
}
t := v.Type()
version, vOK := s.typeToVersion[t]
kinds, kOK := s.typeToKind[t]
if !vOK || !kOK {
gvks, ok := s.typeToGVK[t]
if !ok {
return "", "", &notRegisteredErr{t: t}
}
apiVersion = version
kind = kinds[0]
apiVersion = gvks[0].GroupVersion().String()
kind = gvks[0].Kind
return
}

View File

@@ -23,6 +23,7 @@ import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util"
"github.com/ghodss/yaml"
@@ -108,17 +109,19 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
// Returns a new Scheme set up with the test objects.
func GetTestScheme() *Scheme {
internalGV := unversioned.GroupVersion{}
externalGV := unversioned.GroupVersion{Version: "v1"}
s := NewScheme()
// Ordinarily, we wouldn't add TestType2, but because this is a test and
// both types are from the same package, we need to get it into the system
// so that converter will match it with ExternalType2.
s.AddKnownTypes("", &TestType1{}, &TestType2{}, &ExternalInternalSame{})
s.AddKnownTypes("v1", &ExternalInternalSame{})
s.AddKnownTypeWithName("v1", "TestType1", &ExternalTestType1{})
s.AddKnownTypeWithName("v1", "TestType2", &ExternalTestType2{})
s.AddKnownTypeWithName("", "TestType3", &TestType1{})
s.AddKnownTypeWithName("v1", "TestType3", &ExternalTestType1{})
s.InternalVersion = ""
s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{})
s.AddKnownTypes(externalGV, &ExternalInternalSame{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
s.MetaFactory = testMetaFactory{}
return s
}
@@ -229,13 +232,16 @@ func TestMultipleNames(t *testing.T) {
}
func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) {
internalGV := unversioned.GroupVersion{}
externalGV := unversioned.GroupVersion{Version: "v1"}
s := NewScheme()
// create two names internally, with TestType1 being preferred
s.AddKnownTypeWithName("", "TestType1", &TestType1{})
s.AddKnownTypeWithName("", "OtherType1", &TestType1{})
s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{})
s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{})
// create two names externally, with TestType1 being preferred
s.AddKnownTypeWithName("v1", "TestType1", &ExternalTestType1{})
s.AddKnownTypeWithName("v1", "OtherType1", &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{})
s.MetaFactory = testMetaFactory{}
ext := &ExternalTestType1{}
@@ -267,11 +273,11 @@ func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) {
func TestKnownTypes(t *testing.T) {
s := GetTestScheme()
if len(s.KnownTypes("v2")) != 0 {
if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 {
t.Errorf("should have no known types for v2")
}
types := s.KnownTypes("v1")
types := s.KnownTypes(unversioned.GroupVersion{Version: "v1"})
for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} {
if _, ok := types[s]; !ok {
t.Errorf("missing type %q", s)
@@ -361,7 +367,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
}

View File

@@ -84,10 +84,11 @@ var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVe
func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(internalGV.Version, "Type", &internalType{})
scheme.AddKnownTypeWithName(unlikelyGV.String(), "Type", &externalType{})
scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{})
scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{})
//This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
scheme.AddKnownTypeWithName(validVersionGV.String(), "Type", &ExternalType2{})
scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{})
codec := runtime.CodecFor(scheme, unlikelyGV.String())
mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version string) (*meta.VersionInterfaces, error) {
@@ -98,7 +99,7 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
}, versionErrIfFalse(version == validVersionGV.String() || version == unlikelyGV.String())
})
for _, gv := range []unversioned.GroupVersion{unlikelyGV, validVersionGV} {
for kind := range scheme.KnownTypes(gv.String()) {
for kind := range scheme.KnownTypes(gv) {
gvk := gv.WithKind(kind)
mixedCase := false

View File

@@ -51,21 +51,12 @@ type OutputVersionMapper struct {
// RESTMapping implements meta.RESTMapper by prepending the output version to the preferred version list.
func (m OutputVersionMapper) RESTMapping(kind string, versions ...string) (*meta.RESTMapping, error) {
preferred := []string{m.OutputVersion}
for _, version := range versions {
if len(version) > 0 {
preferred = append(preferred, version)
}
mapping, err := m.RESTMapper.RESTMapping(kind, m.OutputVersion)
if err == nil {
return mapping, nil
}
// if the caller wants to use the default version list, try with the preferred version, and on
// error, use the default behavior.
if len(preferred) == 1 {
if m, err := m.RESTMapper.RESTMapping(kind, preferred...); err == nil {
return m, nil
}
preferred = nil
}
return m.RESTMapper.RESTMapping(kind, preferred...)
return m.RESTMapper.RESTMapping(kind, versions...)
}
// ShortcutExpander is a RESTMapper that can be used for Kubernetes

View File

@@ -217,7 +217,7 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
if err != nil {
return err
}
decodedObj, err := scheme.DecodeToVersion(rawObj, "")
decodedObj, err := scheme.DecodeToVersion(rawObj, unversioned.GroupVersion{})
if err != nil {
return err
}

View File

@@ -40,8 +40,8 @@ import (
)
func init() {
api.Scheme.AddKnownTypes("", &kubectltesting.TestStruct{})
api.Scheme.AddKnownTypes(testapi.Default.Version(), &kubectltesting.TestStruct{})
api.Scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &kubectltesting.TestStruct{})
api.Scheme.AddKnownTypes(*testapi.Default.GroupVersion(), &kubectltesting.TestStruct{})
}
var testData = kubectltesting.TestStruct{

View File

@@ -154,7 +154,7 @@ func (t *thirdPartyResourceDataCodec) Decode(data []byte) (runtime.Object, error
return result, nil
}
func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, version string) (runtime.Object, error) {
func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (runtime.Object, error) {
// TODO: this is hacky, there must be a better way...
obj, err := t.Decode(data)
if err != nil {
@@ -164,7 +164,7 @@ func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, version strin
if err != nil {
return nil, err
}
return t.delegate.DecodeToVersion(objData, version)
return t.delegate.DecodeToVersion(objData, gv)
}
func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object) error {
@@ -175,14 +175,14 @@ func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object
return t.populate(thirdParty, data)
}
func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, version, kind string) error {
func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, gvk unversioned.GroupVersionKind) error {
thirdParty, ok := obj.(*extensions.ThirdPartyResourceData)
if !ok {
return fmt.Errorf("unexpected object: %#v", obj)
}
if kind != "ThirdPartyResourceData" {
return fmt.Errorf("unexpeceted kind: %s", kind)
if gvk.Kind != "ThirdPartyResourceData" {
return fmt.Errorf("unexpeceted kind: %s", gvk.Kind)
}
var dataObj interface{}
@@ -194,25 +194,25 @@ func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []
return fmt.Errorf("unexpcted object: %#v", dataObj)
}
if kindObj, found := mapObj["kind"]; !found {
mapObj["kind"] = kind
mapObj["kind"] = gvk.Kind
} else {
kindStr, ok := kindObj.(string)
if !ok {
return fmt.Errorf("unexpected object for 'kind': %v", kindObj)
}
if kindStr != t.kind {
return fmt.Errorf("kind doesn't match, expecting: %s, got %s", kind, kindStr)
return fmt.Errorf("kind doesn't match, expecting: %s, got %s", gvk.Kind, kindStr)
}
}
if versionObj, found := mapObj["apiVersion"]; !found {
mapObj["apiVersion"] = version
mapObj["apiVersion"] = gvk.GroupVersion().String()
} else {
versionStr, ok := versionObj.(string)
if !ok {
return fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj)
}
if versionStr != version {
return fmt.Errorf("version doesn't match, expecting: %s, got %s", version, versionStr)
if versionStr != gvk.GroupVersion().String() {
return fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr)
}
}

View File

@@ -35,6 +35,7 @@ type yamlCodec struct {
// yamlCodec implements Codec
var _ Codec = yamlCodec{}
var _ Decoder = yamlCodec{}
// YAMLDecoder adds YAML decoding support to a codec that supports JSON.
func YAMLDecoder(codec Codec) Codec {
@@ -75,6 +76,9 @@ type codecWrapper struct {
version string
}
// codecWrapper implements Decoder
var _ Decoder = &codecWrapper{}
// Encode implements Codec
func (c *codecWrapper) Encode(obj Object) ([]byte, error) {
return c.EncodeToVersion(obj, c.version)

View File

@@ -24,12 +24,13 @@ import (
"sort"
"strings"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/util/sets"
)
type ConversionGenerator interface {
GenerateConversionsForType(version string, reflection reflect.Type) error
GenerateConversionsForType(groupVersion unversioned.GroupVersion, reflection reflect.Type) error
WriteConversionFunctions(w io.Writer) error
RegisterConversionFunctions(w io.Writer, pkg string) error
AddImport(pkg string) string
@@ -86,9 +87,15 @@ func (g *conversionGenerator) AddImport(pkg string) string {
return g.addImportByPath(pkg)
}
func (g *conversionGenerator) GenerateConversionsForType(version string, reflection reflect.Type) error {
func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVersion, 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
internalGV, exists := g.scheme.InternalVersions[gv.Group]
if !exists {
return fmt.Errorf("no internal version for %v", gv)
}
internalObj, err := g.scheme.NewObject(internalGV.String(), kind)
if err != nil {
return fmt.Errorf("cannot create an object of type %v in internal version", kind)
}

View File

@@ -20,6 +20,7 @@ import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
@@ -45,10 +46,14 @@ func (*InternalComplex) IsAnAPIObject() {}
func (*ExternalComplex) IsAnAPIObject() {}
func TestStringMapConversion(t *testing.T) {
internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""}
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"}
scheme := runtime.NewScheme()
scheme.Log(t)
scheme.AddKnownTypeWithName("", "Complex", &InternalComplex{})
scheme.AddKnownTypeWithName("external", "Complex", &ExternalComplex{})
scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{})
testCases := map[string]struct {
input map[string][]string

View File

@@ -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,16 @@ 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: ""}
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"}
externalGVK := externalGV.WithKind("ObjectTest")
obj, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`))
s := runtime.NewScheme()
s.AddInternalGroupVersion(internalGV)
s.AddKnownTypes(internalGV, &ObjectTest{})
s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{})
obj, err := s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{}]}`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -74,7 +80,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":"` + externalGV.String() + `","items":[{"kind":"Other","apiVersion":"v1"}]}`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -85,11 +91,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, &EmbeddedTest{})
s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
s.AddKnownTypes(internalGV, &ObjectTest{})
s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})
internal := &ObjectTest{
Items: []runtime.Object{
@@ -104,7 +114,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 +157,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, &EmbeddedTest{})
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
outer := &EmbeddedTest{
ID: "outer",
@@ -160,7 +175,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 +221,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, &EmbeddedTest{})
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
original := &EmbeddedTest{
ID: "outer",
@@ -219,7 +239,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 +250,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)
}

View File

@@ -19,6 +19,8 @@ package runtime
import (
"io"
"net/url"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// Codec defines methods for serializing and deserializing API objects.
@@ -31,10 +33,10 @@ type Codec interface {
type Decoder interface {
Decode(data []byte) (Object, error)
// TODO: Remove this method?
DecodeToVersion(data []byte, version string) (Object, error)
DecodeToVersion(data []byte, groupVersion unversioned.GroupVersion) (Object, error)
DecodeInto(data []byte, obj Object) error
// TODO: Remove this method?
DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, kind, version string) error
DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, groupVersionKind unversioned.GroupVersionKind) error
DecodeParametersInto(parameters url.Values, obj Object) error
}

View File

@@ -23,6 +23,7 @@ import (
"net/url"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
)
@@ -35,6 +36,8 @@ type Scheme struct {
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
}
var _ Decoder = &Scheme{}
// Function to convert a field selector to internal representation.
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
@@ -178,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 {
@@ -219,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,
@@ -244,27 +259,33 @@ func NewScheme() *Scheme {
return s
}
// AddInternalGroupVersion registers an internal GroupVersion with the scheme. This can later be
// used to lookup the internal GroupVersion for a given Group
func (s *Scheme) AddInternalGroupVersion(gv unversioned.GroupVersion) {
s.raw.InternalVersions[gv.Group] = gv
}
// 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) {
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
interfaces := make([]interface{}, len(types))
for i := range types {
interfaces[i] = types[i]
}
s.raw.AddKnownTypes(version, interfaces...)
s.raw.AddKnownTypes(gv, interfaces...)
}
// AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
// be encoded as. Useful for testing when you don't want to make multiple packages to define
// your structs.
func (s *Scheme) AddKnownTypeWithName(version, kind string, obj Object) {
s.raw.AddKnownTypeWithName(version, kind, obj)
func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj Object) {
s.raw.AddKnownTypeWithName(gvk, obj)
}
// KnownTypes returns the types known for the given version.
// Return value must be treated as read-only.
func (s *Scheme) KnownTypes(version string) map[string]reflect.Type {
return s.raw.KnownTypes(version)
func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type {
return s.raw.KnownTypes(gv)
}
// DataVersionAndKind will return the APIVersion and Kind of the given wire-format
@@ -456,8 +477,8 @@ func (s *Scheme) Decode(data []byte) (Object, error) {
// are set by Encode. Only versioned objects (APIVersion != "") are
// accepted. The object will be converted into the in-memory versioned type
// requested before being returned.
func (s *Scheme) DecodeToVersion(data []byte, version string) (Object, error) {
obj, err := s.raw.DecodeToVersion(data, version)
func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) {
obj, err := s.raw.DecodeToVersion(data, gv)
if err != nil {
return nil, err
}
@@ -476,8 +497,10 @@ func (s *Scheme) DecodeInto(data []byte, obj Object) error {
return s.raw.DecodeInto(data, obj)
}
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, version, kind string) error {
return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, version, kind)
// DecodeIntoWithSpecifiedVersionKind coerces the data into the obj, assuming that the data is
// of type GroupVersionKind
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error {
return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, gvk)
}
func (s *Scheme) DecodeParametersInto(parameters url.Values, obj Object) error {

View File

@@ -20,6 +20,7 @@ import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
)
@@ -43,9 +44,13 @@ func (*InternalSimple) IsAnAPIObject() {}
func (*ExternalSimple) IsAnAPIObject() {}
func TestScheme(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{})
scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
// test that scheme is an ObjectTyper
var _ runtime.ObjectTyper = scheme
@@ -56,10 +61,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 := "", 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 := "externalVersion", 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)
@@ -68,10 +73,10 @@ func TestScheme(t *testing.T) {
return nil
},
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
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.Errorf("Expected '%v', got '%v'", e, a)
}
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
@@ -89,11 +94,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, "externalVersion")
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)
}
@@ -131,8 +136,10 @@ func TestScheme(t *testing.T) {
}
func TestInvalidObjectValueKind(t *testing.T) {
internalGV := unversioned.GroupVersion{Group: "", Version: ""}
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName("", "Simple", &InternalSimple{})
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
embedded := &runtime.EmbeddedObject{}
switch obj := embedded.Object.(type) {
@@ -198,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.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
table := []struct {
obj runtime.Object
@@ -208,7 +219,7 @@ func TestExternalToInternalMapping(t *testing.T) {
}{
{
&InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}},
`{"kind":"OptionalExtensionType","apiVersion":"testExternal"}`,
`{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
},
}
@@ -230,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.WithKind("ExtensionType"), &InternalExtensionType{})
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{})
scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{})
scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{})
scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{})
table := []struct {
obj runtime.Object
@@ -246,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 {
@@ -284,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.WithKind("Simple"), &InternalSimple{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
codec := runtime.CodecFor(scheme, externalGV.String())
test := &InternalSimple{
TestString: "I'm the same",
}

View File

@@ -22,6 +22,7 @@ import (
"net/url"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
)
@@ -31,6 +32,8 @@ var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{}
type unstructuredJSONScheme struct{}
var _ Decoder = unstructuredJSONScheme{}
// Recognizes returns true for any version or kind that is specified (internal
// versions are specifically excluded).
func (unstructuredJSONScheme) Recognizes(version, kind string) bool {
@@ -75,11 +78,11 @@ func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error {
return nil
}
func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, kind, version string) error {
func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error {
return nil
}
func (unstructuredJSONScheme) DecodeToVersion(data []byte, version string) (Object, error) {
func (unstructuredJSONScheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) {
return nil, nil
}

View File

@@ -47,8 +47,8 @@ var codec runtime.Codec
func init() {
scheme = runtime.NewScheme()
scheme.AddKnownTypes("", &storagetesting.TestResource{})
scheme.AddKnownTypes(testapi.Default.Version(), &storagetesting.TestResource{})
scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &storagetesting.TestResource{})
scheme.AddKnownTypes(*testapi.Default.GroupVersion(), &storagetesting.TestResource{})
codec = runtime.CodecFor(scheme, testapi.Default.Version())
scheme.AddConversionFuncs(
func(in *storagetesting.TestResource, out *storagetesting.TestResource, s conversion.Scope) error {

View File

@@ -17,14 +17,19 @@ limitations under the License.
package api
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
)
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
var Scheme = runtime.NewScheme()
// SchemeGroupVersion is group version used to register these objects
// TODO this should be in the "scheduler" group
var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""}
func init() {
Scheme.AddKnownTypes("",
Scheme.AddKnownTypes(SchemeGroupVersion,
&Policy{},
)
}

View File

@@ -17,15 +17,20 @@ limitations under the License.
package v1
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/plugin/pkg/scheduler/api"
)
// SchemeGroupVersion is group version used to register these objects
// TODO this should be in the "scheduler" group
var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"}
// Codec encodes internal objects to the v1 scheme
var Codec = runtime.CodecFor(api.Scheme, "v1")
var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion.String())
func init() {
api.Scheme.AddKnownTypes("v1",
api.Scheme.AddKnownTypes(SchemeGroupVersion,
&Policy{},
)
}