Commit 60b66665 authored by Clayton Coleman's avatar Clayton Coleman

Move conversion.Scheme to runtime

There is only one type registry to rule them all
parent 0e889a1a
......@@ -92,7 +92,7 @@ func main() {
}
versionPath := pkgPath(gv.Group, gv.Version)
generator := kruntime.NewConversionGenerator(api.Scheme.Raw(), versionPath)
generator := kruntime.NewConversionGenerator(api.Scheme, versionPath)
apiShort := generator.AddImport(path.Join(pkgBase, "api"))
generator.AddImport(path.Join(pkgBase, "api/resource"))
// TODO(wojtek-t): Change the overwrites to a flag.
......
......@@ -114,7 +114,7 @@ func main() {
}
versionPath := pkgPath(gv.Group, gv.Version)
generator := kruntime.NewDeepCopyGenerator(api.Scheme.Raw(), versionPath, sets.NewString("k8s.io/kubernetes"))
generator := kruntime.NewDeepCopyGenerator(api.Scheme, versionPath, sets.NewString("k8s.io/kubernetes"))
generator.AddImport(path.Join(pkgBase, "api"))
if len(*overwrites) > 0 {
......
......@@ -35,7 +35,7 @@ func BenchmarkPodConversion(b *testing.B) {
b.Fatalf("Unexpected error decoding pod: %v", err)
}
scheme := api.Scheme.Raw()
scheme := api.Scheme
var result *api.Pod
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.ConvertToVersion(&pod, testapi.Default.GroupVersion().String())
......@@ -63,7 +63,7 @@ func BenchmarkNodeConversion(b *testing.B) {
b.Fatalf("Unexpected error decoding node: %v", err)
}
scheme := api.Scheme.Raw()
scheme := api.Scheme
var result *api.Node
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.ConvertToVersion(&node, testapi.Default.GroupVersion().String())
......@@ -91,7 +91,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) {
b.Fatalf("Unexpected error decoding node: %v", err)
}
scheme := api.Scheme.Raw()
scheme := api.Scheme
var result *api.ReplicationController
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.ConvertToVersion(&replicationController, testapi.Default.GroupVersion().String())
......
......@@ -36,6 +36,10 @@ type DebugLogger interface {
Logf(format string, args ...interface{})
}
type NameFunc func(t reflect.Type) string
var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
// Converter knows how to convert one type to another.
type Converter struct {
// Map from the conversion pair to a function which can
......@@ -77,14 +81,14 @@ type Converter struct {
}
// NewConverter creates a new Converter object.
func NewConverter() *Converter {
func NewConverter(nameFn NameFunc) *Converter {
c := &Converter{
conversionFuncs: NewConversionFuncs(),
generatedConversionFuncs: NewConversionFuncs(),
ignoredConversions: make(map[typePair]struct{}),
defaultingFuncs: make(map[reflect.Type]reflect.Value),
defaultingInterfaces: make(map[reflect.Type]interface{}),
nameFunc: func(t reflect.Type) string { return t.Name() },
nameFunc: nameFn,
structFieldDests: make(map[typeNamePair][]typeNamePair),
structFieldSources: make(map[typeNamePair][]typeNamePair),
......@@ -103,6 +107,13 @@ func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
return &copied
}
// DefaultMeta returns the conversion FieldMappingFunc and meta for a given type.
func (c *Converter) DefaultMeta(t reflect.Type) (FieldMatchingFlags, *Meta) {
return c.inputDefaultFlags[t], &Meta{
KeyNameMapping: c.inputFieldMappingFuncs[t],
}
}
// ByteSliceCopy prevents recursing into every byte
func ByteSliceCopy(in *[]byte, out *[]byte, s Scope) error {
*out = make([]byte, len(*in))
......
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package conversion
import (
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
)
type notRegisteredErr struct {
gvk unversioned.GroupVersionKind
t reflect.Type
}
// NewNotRegisteredErr is exposed for testing.
func NewNotRegisteredErr(gvk unversioned.GroupVersionKind, t reflect.Type) error {
return &notRegisteredErr{gvk: gvk, t: t}
}
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.gvk.Kind) == 0 {
return fmt.Sprintf("no version %q has been registered", k.gvk.GroupVersion())
}
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.gvk.Kind, k.gvk.GroupVersion())
}
// IsNotRegisteredError returns true if the error indicates the provided
// object or input data is not registered.
func IsNotRegisteredError(err error) bool {
if err == nil {
return false
}
_, ok := err.(*notRegisteredErr)
return ok
}
type missingKindErr struct {
data string
}
func NewMissingKindErr(data string) error {
return &missingKindErr{data}
}
func (k *missingKindErr) Error() string {
return fmt.Sprintf("Object 'Kind' is missing in '%s'", k.data)
}
func IsMissingKind(err error) bool {
if err == nil {
return false
}
_, ok := err.(*missingKindErr)
return ok
}
type missingVersionErr struct {
data string
}
func NewMissingVersionErr(data string) error {
return &missingVersionErr{data}
}
func (k *missingVersionErr) Error() string {
return fmt.Sprintf("Object 'apiVersion' is missing in '%s'", k.data)
}
func IsMissingVersion(err error) bool {
if err == nil {
return false
}
_, ok := err.(*missingVersionErr)
return ok
}
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package conversion
import (
"encoding/json"
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util"
"github.com/google/gofuzz"
flag "github.com/spf13/pflag"
)
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
// Test a weird version/kind embedding format.
type MyWeirdCustomEmbeddedVersionKindField struct {
ID string `json:"ID,omitempty"`
APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty"`
Z string `json:"Z,omitempty"`
Y uint64 `json:"Y,omitempty"`
}
type TestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]TestType2 `json:"N,omitempty"`
O *TestType2 `json:"O,omitempty"`
P []TestType2 `json:"Q,omitempty"`
}
type TestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]ExternalTestType2 `json:"N,omitempty"`
O *ExternalTestType2 `json:"O,omitempty"`
P []ExternalTestType2 `json:"Q,omitempty"`
}
type ExternalInternalSame struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A TestType2 `json:"A,omitempty"`
}
// TestObjectFuzzer can randomly populate all the above objects.
var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
// We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their
// APIVersion and Kind must remain blank in memory.
j.APIVersion = ""
j.ObjectKind = ""
j.ID = c.RandString()
},
)
// Returns a new Scheme set up with the test objects.
func GetTestScheme() *Scheme {
internalGV := unversioned.GroupVersion{Version: "__internal"}
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(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{})
return s
}
func objDiff(a, b interface{}) string {
ab, err := json.Marshal(a)
if err != nil {
panic("a")
}
bb, err := json.Marshal(b)
if err != nil {
panic("b")
}
return util.StringDiff(string(ab), string(bb))
// An alternate diff attempt, in case json isn't showing you
// the difference. (reflect.DeepEqual makes a distinction between
// nil and empty slices, for example.)
//return util.StringDiff(
// fmt.Sprintf("%#v", a),
// fmt.Sprintf("%#v", b),
//)
}
func TestKnownTypes(t *testing.T) {
s := GetTestScheme()
if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 {
t.Errorf("should have no known types for v2")
}
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)
}
}
}
func TestConvertToVersion(t *testing.T) {
s := GetTestScheme()
tt := &TestType1{A: "I'm not a pointer object"}
other, err := s.ConvertToVersion(tt, "v1")
if err != nil {
t.Fatalf("Failure: %v", err)
}
converted, ok := other.(*ExternalTestType1)
if !ok {
t.Fatalf("Got wrong type")
}
if tt.A != converted.A {
t.Fatalf("Failed to convert object correctly: %#v", converted)
}
}
func TestConvertToVersionErr(t *testing.T) {
s := GetTestScheme()
tt := TestType1{A: "I'm not a pointer object"}
_, err := s.ConvertToVersion(tt, "v1")
if err == nil {
t.Fatalf("unexpected non-error")
}
}
......@@ -36,7 +36,6 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
......@@ -181,7 +180,7 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
continue
}
converted, err := p.convertor.ConvertToVersion(obj, version.String())
if conversion.IsNotRegisteredError(err) {
if runtime.IsNotRegisteredError(err) {
continue
}
if err != nil {
......
......@@ -27,7 +27,6 @@ import (
"strings"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/util/sets"
)
......@@ -42,7 +41,7 @@ type ConversionGenerator interface {
AssumePrivateConversions()
}
func NewConversionGenerator(scheme *conversion.Scheme, targetPkg string) ConversionGenerator {
func NewConversionGenerator(scheme *Scheme, targetPkg string) ConversionGenerator {
g := &conversionGenerator{
scheme: scheme,
......@@ -66,7 +65,7 @@ func NewConversionGenerator(scheme *conversion.Scheme, targetPkg string) Convers
var complexTypes []reflect.Kind = []reflect.Kind{reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct}
type conversionGenerator struct {
scheme *conversion.Scheme
scheme *Scheme
nameFormat string
generatedNamePrefix string
......@@ -105,7 +104,7 @@ func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVer
internalVersion := gv
internalVersion.Version = APIVersionInternal
internalObj, err := g.scheme.NewObject(internalVersion.WithKind(kind))
internalObj, err := g.scheme.New(internalVersion.WithKind(kind))
if err != nil {
return fmt.Errorf("cannot create an object of type %v in internal version", kind)
}
......
......@@ -24,7 +24,6 @@ import (
"sort"
"strings"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/util/sets"
)
......@@ -69,7 +68,7 @@ type DeepCopyGenerator interface {
OverwritePackage(pkg, overwrite string)
}
func NewDeepCopyGenerator(scheme *conversion.Scheme, targetPkg string, include sets.String) DeepCopyGenerator {
func NewDeepCopyGenerator(scheme *Scheme, targetPkg string, include sets.String) DeepCopyGenerator {
g := &deepCopyGenerator{
scheme: scheme,
targetPkg: targetPkg,
......@@ -91,7 +90,7 @@ type pkgPathNamePair struct {
}
type deepCopyGenerator struct {
scheme *conversion.Scheme
scheme *Scheme
targetPkg string
copyables map[reflect.Type]bool
// map of package names to shortname
......
......@@ -17,31 +17,86 @@ limitations under the License.
package runtime
import (
"k8s.io/kubernetes/pkg/conversion"
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
)
type notRegisteredErr struct {
gvk unversioned.GroupVersionKind
t reflect.Type
}
// NewNotRegisteredErr is exposed for testing.
func NewNotRegisteredErr(gvk unversioned.GroupVersionKind, t reflect.Type) error {
return &notRegisteredErr{gvk: gvk, t: t}
}
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.gvk.Kind) == 0 {
return fmt.Sprintf("no version %q has been registered", k.gvk.GroupVersion())
}
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.gvk.Kind, k.gvk.GroupVersion())
}
// IsNotRegisteredError returns true if the error indicates the provided
// object or input data is not registered.
func IsNotRegisteredError(err error) bool {
return conversion.IsNotRegisteredError(err)
if err == nil {
return false
}
_, ok := err.(*notRegisteredErr)
return ok
}
type missingKindErr struct {
data string
}
func NewMissingKindErr(data string) error {
return &missingKindErr{data}
}
func (k *missingKindErr) Error() string {
return fmt.Sprintf("Object 'Kind' is missing in '%s'", k.data)
}
// IsMissingKind returns true if the error indicates that the provided object
// is missing a 'Kind' field.
func IsMissingKind(err error) bool {
return conversion.IsMissingKind(err)
if err == nil {
return false
}
_, ok := err.(*missingKindErr)
return ok
}
type missingVersionErr struct {
data string
}
// IsMissingVersion returns true if the error indicates that the provided object
// is missing a 'Versioj' field.
func IsMissingVersion(err error) bool {
return conversion.IsMissingVersion(err)
func NewMissingVersionErr(data string) error {
return &missingVersionErr{data}
}
func NewMissingKindErr(data string) error {
return conversion.NewMissingKindErr(data)
func (k *missingVersionErr) Error() string {
return fmt.Sprintf("Object 'apiVersion' is missing in '%s'", k.data)
}
func NewMissingVersionErr(data string) error {
return conversion.NewMissingVersionErr(data)
func IsMissingVersion(err error) bool {
if err == nil {
return false
}
_, ok := err.(*missingVersionErr)
return ok
}
......@@ -24,8 +24,10 @@ import (
)
const (
APIVersionInternal = "__internal"
APIVersionUnversioned = "__unversioned"
// APIVersionInternal may be used if you are registering a type that should not
// be considered stable or serialized - it is a convention only and has no
// special behavior in this package.
APIVersionInternal = "__internal"
)
// Typer retrieves information about an object's group, version, and kind.
......
......@@ -23,7 +23,6 @@ import (
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/serializer/json"
"k8s.io/kubernetes/pkg/util"
......@@ -141,7 +140,7 @@ func TestDecode(t *testing.T) {
{
data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
into: &testDecodable{},
typer: &mockTyper{err: conversion.NewNotRegisteredErr(unversioned.GroupVersionKind{}, nil)},
typer: &mockTyper{err: runtime.NewNotRegisteredErr(unversioned.GroupVersionKind{}, nil)},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Other: "test",
......
......@@ -21,7 +21,6 @@ import (
"io"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
)
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
......@@ -52,7 +51,7 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionK
}
if len(unstruct.APIVersion) == 0 {
return nil, nil, conversion.NewMissingVersionErr(string(data))
return nil, nil, NewMissingVersionErr(string(data))
}
gv, err := unversioned.ParseGroupVersion(unstruct.APIVersion)
if err != nil {
......@@ -60,7 +59,7 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionK
}
gvk := gv.WithKind(unstruct.Kind)
if len(unstruct.Kind) == 0 {
return nil, &gvk, conversion.NewMissingKindErr(string(data))
return nil, &gvk, NewMissingKindErr(string(data))
}
unstruct.Object = m
return unstruct, &gvk, nil
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package conversion_test
package runtime_test
import (
"encoding/json"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment