mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Merge pull request #20127 from smarterclayton/remove_conversion_scheme
Auto commit by PR queue bot
This commit is contained in:
commit
6f66dbb9ab
@ -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))
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@ -24,10 +25,71 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
func testLogger(t *testing.T) DebugLogger {
|
||||
// We don't set logger to eliminate rubbish logs in tests.
|
||||
// If you want to switch it, simply switch it to: "return t"
|
||||
@ -35,7 +97,7 @@ func testLogger(t *testing.T) DebugLogger {
|
||||
}
|
||||
|
||||
func TestConverter_byteSlice(t *testing.T) {
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
src := []byte{1, 2, 3}
|
||||
dest := []byte{}
|
||||
err := c.Convert(&src, &dest, 0, nil)
|
||||
@ -48,7 +110,7 @@ func TestConverter_byteSlice(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConverter_MismatchedTypes(t *testing.T) {
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *[]string, out *int, s Scope) error {
|
||||
@ -84,7 +146,7 @@ func TestConverter_DefaultConvert(t *testing.T) {
|
||||
Bar string
|
||||
Baz int
|
||||
}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
c.nameFunc = func(t reflect.Type) string { return "MyType" }
|
||||
|
||||
@ -123,7 +185,7 @@ func TestConverter_DeepCopy(t *testing.T) {
|
||||
Baz interface{}
|
||||
Qux map[string]string
|
||||
}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
|
||||
foo, baz := "foo", "baz"
|
||||
@ -166,7 +228,7 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
||||
Baz int
|
||||
}
|
||||
type C struct{}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
out.Bar = in.Foo
|
||||
@ -228,7 +290,7 @@ func TestConverter_IgnoredConversion(t *testing.T) {
|
||||
type B struct{}
|
||||
|
||||
count := 0
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
count++
|
||||
return nil
|
||||
@ -257,7 +319,7 @@ func TestConverter_IgnoredConversionNested(t *testing.T) {
|
||||
C C
|
||||
}
|
||||
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
typed := C("")
|
||||
if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -275,7 +337,7 @@ func TestConverter_IgnoredConversionNested(t *testing.T) {
|
||||
func TestConverter_GeneratedConversionOverriden(t *testing.T) {
|
||||
type A struct{}
|
||||
type B struct{}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
return nil
|
||||
}); err != nil {
|
||||
@ -297,7 +359,7 @@ func TestConverter_GeneratedConversionOverriden(t *testing.T) {
|
||||
func TestConverter_WithConversionOverriden(t *testing.T) {
|
||||
type A struct{}
|
||||
type B struct{}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
return fmt.Errorf("conversion function should be overriden")
|
||||
}); err != nil {
|
||||
@ -331,7 +393,7 @@ func TestConverter_MapsStringArrays(t *testing.T) {
|
||||
Baz int
|
||||
Other string
|
||||
}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
||||
if len(*input) == 0 {
|
||||
@ -384,7 +446,7 @@ func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) {
|
||||
Baz int
|
||||
Other string
|
||||
}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
||||
if len(*input) == 0 {
|
||||
@ -434,7 +496,7 @@ func TestConverter_fuzz(t *testing.T) {
|
||||
}
|
||||
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.nameFunc = func(t reflect.Type) string {
|
||||
// Hide the fact that we don't have separate packages for these things.
|
||||
return map[reflect.Type]string{
|
||||
@ -473,7 +535,7 @@ func TestConverter_MapElemAddr(t *testing.T) {
|
||||
type Bar struct {
|
||||
A map[string]string
|
||||
}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *int, out *string, s Scope) error {
|
||||
@ -519,7 +581,7 @@ func TestConverter_tags(t *testing.T) {
|
||||
type Bar struct {
|
||||
A string `test:"bar"`
|
||||
}
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *string, out *string, s Scope) error {
|
||||
@ -544,7 +606,7 @@ func TestConverter_tags(t *testing.T) {
|
||||
func TestConverter_meta(t *testing.T) {
|
||||
type Foo struct{ A string }
|
||||
type Bar struct{ A string }
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
checks := 0
|
||||
err := c.RegisterConversionFunc(
|
||||
@ -653,7 +715,7 @@ func TestConverter_flags(t *testing.T) {
|
||||
},
|
||||
}
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
|
||||
for i, item := range table {
|
||||
@ -691,7 +753,7 @@ func TestConverter_FieldRename(t *testing.T) {
|
||||
NameMeta
|
||||
}
|
||||
|
||||
c := NewConverter()
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
@ -764,131 +826,22 @@ func TestConverter_FieldRename(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaValues(t *testing.T) {
|
||||
type InternalSimple struct {
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
type ExternalSimple struct {
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
|
||||
|
||||
s := NewScheme()
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||
|
||||
internalToExternalCalls := 0
|
||||
externalToInternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := s.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope Scope) error {
|
||||
t.Logf("internal -> external")
|
||||
if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", 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)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
func(in *ExternalSimple, out *InternalSimple, scope Scope) error {
|
||||
t.Logf("external -> internal")
|
||||
if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", 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)
|
||||
externalToInternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
func objDiff(a, b interface{}) string {
|
||||
ab, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
panic("a")
|
||||
}
|
||||
simple := &InternalSimple{
|
||||
TestString: "foo",
|
||||
}
|
||||
|
||||
s.Log(t)
|
||||
|
||||
out, err := s.ConvertToVersion(simple, externalGV.String())
|
||||
bb, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
panic("b")
|
||||
}
|
||||
return util.StringDiff(string(ab), string(bb))
|
||||
|
||||
internal, err := s.ConvertToVersion(out, internalGV.String())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if e, a := simple, internal; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
||||
}
|
||||
|
||||
if e, a := 1, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 1, externalToInternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaValuesUnregisteredConvert(t *testing.T) {
|
||||
type InternalSimple struct {
|
||||
Version string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
type ExternalSimple struct {
|
||||
Version string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
s := NewScheme()
|
||||
// We deliberately don't register the types.
|
||||
|
||||
internalToExternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := s.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope Scope) error {
|
||||
if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
simple := &InternalSimple{TestString: "foo"}
|
||||
external := &ExternalSimple{}
|
||||
err = s.Convert(simple, external)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if e, a := simple.TestString, external.TestString; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// Verify that our conversion handler got called.
|
||||
if e, a := 1, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
// 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),
|
||||
//)
|
||||
}
|
||||
|
@ -1,98 +0,0 @@
|
||||
/*
|
||||
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 ¬RegisteredErr{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
|
||||
}
|
@ -1,464 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// 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.
|
||||
gvkToType map[unversioned.GroupVersionKind]reflect.Type
|
||||
|
||||
// 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
|
||||
|
||||
// unversionedTypes are transformed without conversion in ConvertToVersion.
|
||||
unversionedTypes map[reflect.Type]unversioned.GroupVersionKind
|
||||
// unversionedKinds are the names of kinds that can be created in the context of any group
|
||||
// or version
|
||||
// TODO: resolve the status of unversioned types.
|
||||
unversionedKinds map[string]reflect.Type
|
||||
|
||||
// converter stores all registered conversion functions. It also has
|
||||
// default coverting behavior.
|
||||
converter *Converter
|
||||
|
||||
// cloner stores all registered copy functions. It also has default
|
||||
// deep copy behavior.
|
||||
cloner *Cloner
|
||||
|
||||
// Indent will cause the JSON output from Encode to be indented,
|
||||
// if and only if it is true.
|
||||
Indent bool
|
||||
}
|
||||
|
||||
// NewScheme manufactures a new scheme.
|
||||
func NewScheme() *Scheme {
|
||||
s := &Scheme{
|
||||
gvkToType: map[unversioned.GroupVersionKind]reflect.Type{},
|
||||
typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{},
|
||||
unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{},
|
||||
unversionedKinds: map[string]reflect.Type{},
|
||||
converter: NewConverter(),
|
||||
cloner: NewCloner(),
|
||||
}
|
||||
s.converter.nameFunc = s.nameFunc
|
||||
return s
|
||||
}
|
||||
|
||||
// Log sets a logger on the scheme. For test purposes only
|
||||
func (s *Scheme) Log(l DebugLogger) {
|
||||
s.converter.Debug = l
|
||||
}
|
||||
|
||||
// nameFunc returns the name of the type that we wish to use to determine when two types attempt
|
||||
// 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
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return t.Name()
|
||||
}
|
||||
|
||||
for _, gvk := range gvks {
|
||||
internalGV := gvk.GroupVersion()
|
||||
internalGV.Version = "__internal" // this is hacky and maybe should be passed in
|
||||
internalGVK := internalGV.WithKind(gvk.Kind)
|
||||
|
||||
if internalType, exists := s.gvkToType[internalGVK]; exists {
|
||||
return s.typeToGVK[internalType][0].Kind
|
||||
}
|
||||
}
|
||||
|
||||
return gvks[0].Kind
|
||||
}
|
||||
|
||||
// AddUnversionedTypes registers all types passed in 'types' as being members of version 'version',
|
||||
// and marks them as being convertible to all API versions.
|
||||
// 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) AddUnversionedTypes(version unversioned.GroupVersion, types ...interface{}) {
|
||||
s.AddKnownTypes(version, types...)
|
||||
for _, obj := range types {
|
||||
t := reflect.TypeOf(obj).Elem()
|
||||
gvk := version.WithKind(t.Name())
|
||||
s.unversionedTypes[t] = gvk
|
||||
if _, ok := s.unversionedKinds[gvk.Kind]; ok {
|
||||
panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind))
|
||||
}
|
||||
s.unversionedKinds[gvk.Kind] = t
|
||||
}
|
||||
}
|
||||
|
||||
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
|
||||
// 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(gv unversioned.GroupVersion, types ...interface{}) {
|
||||
if len(gv.Version) == 0 {
|
||||
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
|
||||
}
|
||||
for _, obj := range types {
|
||||
t := reflect.TypeOf(obj)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
|
||||
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(gvk unversioned.GroupVersionKind, obj interface{}) {
|
||||
t := reflect.TypeOf(obj)
|
||||
if len(gvk.Version) == 0 {
|
||||
panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t))
|
||||
}
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
|
||||
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(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(kind unversioned.GroupVersionKind) (interface{}, error) {
|
||||
if t, exists := s.gvkToType[kind]; exists {
|
||||
return reflect.New(t).Interface(), nil
|
||||
}
|
||||
|
||||
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
||||
return reflect.New(t).Interface(), nil
|
||||
}
|
||||
return nil, ¬RegisteredErr{gvk: kind}
|
||||
}
|
||||
|
||||
// AddConversionFuncs adds functions to the list of conversion functions. The given
|
||||
// functions should know how to convert between two of your API objects, or their
|
||||
// sub-objects. We deduce how to call these functions from the types of their two
|
||||
// parameters; see the comment for Converter.Register.
|
||||
//
|
||||
// Note that, if you need to copy sub-objects that didn't change, you can use the
|
||||
// conversion.Scope object that will be passed to your conversion function.
|
||||
// Additionally, all conversions started by Scheme will set the SrcVersion and
|
||||
// DestVersion fields on the Meta object. Example:
|
||||
//
|
||||
// s.AddConversionFuncs(
|
||||
// func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
|
||||
// // You can depend on Meta() being non-nil, and this being set to
|
||||
// // the source version, e.g., ""
|
||||
// s.Meta().SrcVersion
|
||||
// // You can depend on this being set to the destination version,
|
||||
// // e.g., "v1".
|
||||
// s.Meta().DestVersion
|
||||
// // Call scope.Convert to copy sub-fields.
|
||||
// s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
|
||||
// return nil
|
||||
// },
|
||||
// )
|
||||
//
|
||||
// (For more detail about conversion functions, see Converter.Register's comment.)
|
||||
//
|
||||
// Also note that the default behavior, if you don't add a conversion function, is to
|
||||
// sanely copy fields that have the same names and same type names. It's OK if the
|
||||
// destination type has extra fields, but it must not remove any. So you only need to
|
||||
// add conversion functions for things with changed/removed fields.
|
||||
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.RegisterConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddConversionFuncs, but registers conversion functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.RegisterGeneratedConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddIgnoredConversionType identifies a pair of types that should be skipped by
|
||||
// dynamic conversion (because the data inside them is explicitly dropped during
|
||||
// conversion).
|
||||
func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
|
||||
return s.converter.RegisterIgnoredConversion(from, to)
|
||||
}
|
||||
|
||||
// AddDeepCopyFuncs adds functions to the list of deep copy functions.
|
||||
// Note that to copy sub-objects, you can use the conversion.Cloner object that
|
||||
// will be passed to your deep-copy function.
|
||||
func (s *Scheme) AddDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
||||
for _, f := range deepCopyFuncs {
|
||||
if err := s.cloner.RegisterDeepCopyFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddDeepCopyFuncs, but registers deep copy functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
||||
for _, f := range deepCopyFuncs {
|
||||
if err := s.cloner.RegisterGeneratedDeepCopyFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddStructFieldConversion allows you to specify a mechanical copy for a moved
|
||||
// or renamed struct field without writing an entire conversion function. See
|
||||
// the comment in Converter.SetStructFieldCopy for parameter details.
|
||||
// Call as many times as needed, even on the same fields.
|
||||
func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
|
||||
return s.converter.SetStructFieldCopy(srcFieldType, srcFieldName, destFieldType, destFieldName)
|
||||
}
|
||||
|
||||
// AddDefaultingFuncs adds functions to the list of default-value functions.
|
||||
// Each of the given functions is responsible for applying default values
|
||||
// when converting an instance of a versioned API object into an internal
|
||||
// API object. These functions do not need to handle sub-objects. We deduce
|
||||
// how to call these functions from the types of their two parameters.
|
||||
//
|
||||
// s.AddDefaultingFuncs(
|
||||
// func(obj *v1.Pod) {
|
||||
// if obj.OptionalField == "" {
|
||||
// obj.OptionalField = "DefaultValue"
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
|
||||
for _, f := range defaultingFuncs {
|
||||
err := s.converter.RegisterDefaultingFunc(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recognizes returns true if the scheme is able to handle the provided group,version,kind
|
||||
// of an object.
|
||||
func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
||||
_, exists := s.gvkToType[gvk]
|
||||
return exists
|
||||
}
|
||||
|
||||
// IsUnversioned returns true if the Go object is registered as an unversioned type, or sets
|
||||
// ok to false if the provided object is not registered in the scheme.
|
||||
func (s *Scheme) IsUnversioned(obj interface{}) (unversioned bool, registered bool) {
|
||||
v, err := EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
if _, ok := s.typeToGVK[t]; !ok {
|
||||
return false, false
|
||||
}
|
||||
_, ok := s.unversionedTypes[t]
|
||||
return ok, true
|
||||
}
|
||||
|
||||
// RegisterInputDefaults sets the provided field mapping function and field matching
|
||||
// as the defaults for the provided input type. The fn may be nil, in which case no
|
||||
// mapping will happen by default. Use this method to register a mechanism for handling
|
||||
// a specific input type in conversion, such as a map[string]string to structs.
|
||||
func (s *Scheme) RegisterInputDefaults(in interface{}, fn FieldMappingFunc, defaultFlags FieldMatchingFlags) error {
|
||||
return s.converter.RegisterInputDefaults(in, fn, defaultFlags)
|
||||
}
|
||||
|
||||
// Performs a deep copy of the given object.
|
||||
func (s *Scheme) DeepCopy(in interface{}) (interface{}, error) {
|
||||
return s.cloner.DeepCopy(in)
|
||||
}
|
||||
|
||||
// Convert will attempt to convert in into out. Both must be pointers. For easy
|
||||
// testing of conversion functions. Returns an error if the conversion isn't
|
||||
// possible. You can call this with types that haven't been registered (for example,
|
||||
// a to test conversion of types that are nested within registered types), but in
|
||||
// that case, the conversion.Scope object passed to your conversion functions won't
|
||||
// have SrcVersion or DestVersion fields set correctly in Meta().
|
||||
func (s *Scheme) Convert(in, out interface{}) error {
|
||||
inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
if gvk, err := s.ObjectKind(in); err == nil {
|
||||
inVersion = gvk.GroupVersion()
|
||||
}
|
||||
if gvk, err := s.ObjectKind(out); err == nil {
|
||||
outVersion = gvk.GroupVersion()
|
||||
}
|
||||
flags, meta := s.generateConvertMeta(inVersion, outVersion, in)
|
||||
if flags == 0 {
|
||||
flags = AllowDifferentFieldTypeNames
|
||||
}
|
||||
return s.converter.Convert(in, out, flags, meta)
|
||||
}
|
||||
|
||||
// ConvertToVersion attempts to convert an input object to its matching Kind in another
|
||||
// version within this scheme. Will return an error if the provided version does not
|
||||
// contain the inKind (or a mapping by name defined with AddKnownTypeWithName).
|
||||
func (s *Scheme) ConvertToVersion(in interface{}, outGroupVersionString string) (interface{}, error) {
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("only pointer types may be converted: %v", t)
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
|
||||
}
|
||||
|
||||
outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kind unversioned.GroupVersionKind
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind = unversionedKind
|
||||
} else {
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outGroupVersionString)
|
||||
}
|
||||
kind = kinds[0]
|
||||
}
|
||||
|
||||
outKind := outVersion.WithKind(kind.Kind)
|
||||
|
||||
inKind, err := s.ObjectKind(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := s.NewObject(outKind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flags, meta := s.generateConvertMeta(inKind.GroupVersion(), outVersion, in)
|
||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Converter allows access to the converter for the scheme
|
||||
func (s *Scheme) Converter() *Converter {
|
||||
return s.converter
|
||||
}
|
||||
|
||||
// WithConversions returns a scheme with additional conversion functions
|
||||
func (s *Scheme) WithConversions(fns ConversionFuncs) *Scheme {
|
||||
c := s.converter.WithConversions(fns)
|
||||
copied := *s
|
||||
copied.converter = c
|
||||
return &copied
|
||||
}
|
||||
|
||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (FieldMatchingFlags, *Meta) {
|
||||
t := reflect.TypeOf(in)
|
||||
return s.converter.inputDefaultFlags[t], &Meta{
|
||||
SrcVersion: srcGroupVersion.String(),
|
||||
DestVersion: destGroupVersion.String(),
|
||||
KeyNameMapping: s.converter.inputFieldMappingFuncs[t],
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectKind returns the group,version,kind of the go object,
|
||||
// or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, error) {
|
||||
gvks, err := s.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return unversioned.GroupVersionKind{}, err
|
||||
}
|
||||
return gvks[0], nil
|
||||
}
|
||||
|
||||
// ObjectKinds returns all possible group,version,kind of the go object,
|
||||
// or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKinds(obj interface{}) ([]unversioned.GroupVersionKind, error) {
|
||||
v, err := EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return nil, ¬RegisteredErr{t: t}
|
||||
}
|
||||
|
||||
return gvks, nil
|
||||
}
|
||||
|
||||
// maybeCopy copies obj if it is not a pointer, to get a settable/addressable
|
||||
// object. Guaranteed to return a pointer.
|
||||
func maybeCopy(obj interface{}) interface{} {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
return obj
|
||||
}
|
||||
v2 := reflect.New(v.Type())
|
||||
v2.Elem().Set(v)
|
||||
return v2.Interface()
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
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 ¬RegisteredErr{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 NewMissingKindErr(data string) error {
|
||||
return conversion.NewMissingKindErr(data)
|
||||
}
|
||||
|
||||
func NewMissingVersionErr(data string) error {
|
||||
return conversion.NewMissingVersionErr(data)
|
||||
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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -25,47 +25,115 @@ import (
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
)
|
||||
|
||||
// Scheme defines methods for serializing and deserializing API objects. It
|
||||
// is an adaptation of conversion's Scheme for our API objects.
|
||||
// Scheme defines methods for serializing and deserializing API objects, a type
|
||||
// registry for converting group, version, and kind information to and from Go
|
||||
// schemas, and mappings between Go schemas of different versions. A scheme is the
|
||||
// foundation for a versioned API and versioned configuration over time.
|
||||
//
|
||||
// In a Scheme, a Type is a particular Go struct, a Version is a point-in-time
|
||||
// identifier for a particular representation of that Type (typically backwards
|
||||
// compatible), a Kind is the unique name for that Type within the Version, and a
|
||||
// Group identifies a set of Versions, Kinds, and Types that evolve over time. An
|
||||
// Unversioned Type is one that is not yet formally bound to a type and is promised
|
||||
// to be backwards compatible (effectively a "v1" of a Type that does not expect
|
||||
// to break in the future).
|
||||
//
|
||||
// Schemes are not expected to change at runtime and are only threadsafe after
|
||||
// registration is complete.
|
||||
type Scheme struct {
|
||||
raw *conversion.Scheme
|
||||
// versionMap allows one to figure out the go type of an object with
|
||||
// the given version and name.
|
||||
gvkToType map[unversioned.GroupVersionKind]reflect.Type
|
||||
|
||||
// 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
|
||||
|
||||
// unversionedTypes are transformed without conversion in ConvertToVersion.
|
||||
unversionedTypes map[reflect.Type]unversioned.GroupVersionKind
|
||||
|
||||
// unversionedKinds are the names of kinds that can be created in the context of any group
|
||||
// or version
|
||||
// TODO: resolve the status of unversioned types.
|
||||
unversionedKinds map[string]reflect.Type
|
||||
|
||||
// Map from version and resource to the corresponding func to convert
|
||||
// resource field labels in that version to internal version.
|
||||
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
|
||||
|
||||
// converter stores all registered conversion functions. It also has
|
||||
// default coverting behavior.
|
||||
converter *conversion.Converter
|
||||
|
||||
// cloner stores all registered copy functions. It also has default
|
||||
// deep copy behavior.
|
||||
cloner *conversion.Cloner
|
||||
}
|
||||
|
||||
// Function to convert a field selector to internal representation.
|
||||
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
||||
|
||||
func (self *Scheme) Raw() *conversion.Scheme {
|
||||
return self.raw
|
||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
||||
func NewScheme() *Scheme {
|
||||
s := &Scheme{
|
||||
gvkToType: map[unversioned.GroupVersionKind]reflect.Type{},
|
||||
typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{},
|
||||
unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{},
|
||||
unversionedKinds: map[string]reflect.Type{},
|
||||
cloner: conversion.NewCloner(),
|
||||
fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{},
|
||||
}
|
||||
s.converter = conversion.NewConverter(s.nameFunc)
|
||||
|
||||
s.AddConversionFuncs(DefaultEmbeddedConversions()...)
|
||||
|
||||
// Enable map[string][]string conversions by default
|
||||
if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// nameFunc returns the name of the type that we wish to use to determine when two types attempt
|
||||
// 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
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return t.Name()
|
||||
}
|
||||
|
||||
for _, gvk := range gvks {
|
||||
internalGV := gvk.GroupVersion()
|
||||
internalGV.Version = "__internal" // this is hacky and maybe should be passed in
|
||||
internalGVK := internalGV.WithKind(gvk.Kind)
|
||||
|
||||
if internalType, exists := s.gvkToType[internalGVK]; exists {
|
||||
return s.typeToGVK[internalType][0].Kind
|
||||
}
|
||||
}
|
||||
|
||||
return gvks[0].Kind
|
||||
}
|
||||
|
||||
// fromScope gets the input version, desired output version, and desired Scheme
|
||||
// from a conversion.Scope.
|
||||
func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
|
||||
scheme = self
|
||||
inVersion = s.Meta().SrcVersion
|
||||
outVersion = s.Meta().DestVersion
|
||||
func (s *Scheme) fromScope(scope conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
|
||||
scheme = s
|
||||
inVersion = scope.Meta().SrcVersion
|
||||
outVersion = scope.Meta().DestVersion
|
||||
return inVersion, outVersion, scheme
|
||||
}
|
||||
|
||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
||||
func NewScheme() *Scheme {
|
||||
s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}}
|
||||
s.AddConversionFuncs(DefaultEmbeddedConversions()...)
|
||||
|
||||
// Enable map[string][]string conversions by default
|
||||
if err := s.raw.AddConversionFuncs(DefaultStringConversions...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.raw.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := s.raw.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return s
|
||||
// Converter allows access to the converter for the scheme
|
||||
func (s *Scheme) Converter() *conversion.Converter {
|
||||
return s.converter
|
||||
}
|
||||
|
||||
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
|
||||
@ -75,111 +143,221 @@ func NewScheme() *Scheme {
|
||||
//
|
||||
// TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into
|
||||
// every version with particular schemas. Resolve tihs method at that point.
|
||||
func (s *Scheme) AddUnversionedTypes(gv unversioned.GroupVersion, types ...Object) {
|
||||
interfaces := make([]interface{}, len(types))
|
||||
for i := range types {
|
||||
interfaces[i] = types[i]
|
||||
func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) {
|
||||
s.AddKnownTypes(version, types...)
|
||||
for _, obj := range types {
|
||||
t := reflect.TypeOf(obj).Elem()
|
||||
gvk := version.WithKind(t.Name())
|
||||
s.unversionedTypes[t] = gvk
|
||||
if _, ok := s.unversionedKinds[gvk.Kind]; ok {
|
||||
panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind))
|
||||
}
|
||||
s.unversionedKinds[gvk.Kind] = t
|
||||
}
|
||||
s.raw.AddUnversionedTypes(gv, interfaces...)
|
||||
}
|
||||
|
||||
// AddKnownTypes registers the types of the arguments to the marshaller of the package api.
|
||||
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
|
||||
// All objects passed to types should be pointers to structs. The name that go reports for
|
||||
// the struct becomes the "kind" field when encoding. Version may not be empty - use the
|
||||
// APIVersionInternal constant if you have a type that does not have a formal version.
|
||||
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
|
||||
interfaces := make([]interface{}, len(types))
|
||||
for i := range types {
|
||||
interfaces[i] = types[i]
|
||||
if len(gv.Version) == 0 {
|
||||
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
|
||||
}
|
||||
s.raw.AddKnownTypes(gv, interfaces...)
|
||||
}
|
||||
for _, obj := range types {
|
||||
t := reflect.TypeOf(obj)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
|
||||
// AddIgnoredConversionType declares a particular conversion that should be ignored - during conversion
|
||||
// this method is not invoked.
|
||||
func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
|
||||
return s.raw.AddIgnoredConversionType(from, to)
|
||||
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.
|
||||
// your structs. Version may not be empty - use the APIVersionInternal constant if you have a
|
||||
// type that does not have a formal version.
|
||||
func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj Object) {
|
||||
s.raw.AddKnownTypeWithName(gvk, obj)
|
||||
t := reflect.TypeOf(obj)
|
||||
if len(gvk.Version) == 0 {
|
||||
panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t))
|
||||
}
|
||||
if t.Kind() != reflect.Ptr {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("All types must be pointers to structs.")
|
||||
}
|
||||
|
||||
s.gvkToType[gvk] = t
|
||||
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
|
||||
}
|
||||
|
||||
// KnownTypes returns the types known for the given version.
|
||||
// Return value must be treated as read-only.
|
||||
func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type {
|
||||
return s.raw.KnownTypes(gv)
|
||||
types := make(map[string]reflect.Type)
|
||||
for gvk, t := range s.gvkToType {
|
||||
if gv != gvk.GroupVersion() {
|
||||
continue
|
||||
}
|
||||
|
||||
types[gvk.Kind] = t
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
// ObjectKind returns the default group,version,kind of the given Object.
|
||||
// ObjectKind returns the group,version,kind of the go object,
|
||||
// or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) {
|
||||
return s.raw.ObjectKind(obj)
|
||||
gvks, err := s.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return unversioned.GroupVersionKind{}, err
|
||||
}
|
||||
return gvks[0], nil
|
||||
}
|
||||
|
||||
// ObjectKinds returns the all possible group,version,kind of the given Object.
|
||||
// ObjectKinds returns all possible group,version,kind of the go object,
|
||||
// or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, error) {
|
||||
return s.raw.ObjectKinds(obj)
|
||||
v, err := conversion.EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return nil, ¬RegisteredErr{t: t}
|
||||
}
|
||||
|
||||
return gvks, nil
|
||||
}
|
||||
|
||||
// Recognizes returns true if the scheme is able to handle the provided group,version,kind
|
||||
// of an object.
|
||||
func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
||||
return s.raw.Recognizes(gvk)
|
||||
_, exists := s.gvkToType[gvk]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
|
||||
return s.raw.IsUnversioned(obj)
|
||||
v, err := conversion.EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
if _, ok := s.typeToGVK[t]; !ok {
|
||||
return false, false
|
||||
}
|
||||
_, ok := s.unversionedTypes[t]
|
||||
return ok, true
|
||||
}
|
||||
|
||||
// New returns a new API object of the given version ("" for internal
|
||||
// representation) and name, or an error if it hasn't been registered.
|
||||
// New returns a new API object of the given version and name, or an error if it hasn't
|
||||
// been registered. The version and kind fields must be specified.
|
||||
func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
||||
obj, err := s.raw.NewObject(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if t, exists := s.gvkToType[kind]; exists {
|
||||
return reflect.New(t).Interface().(Object), nil
|
||||
}
|
||||
return obj.(Object), nil
|
||||
|
||||
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
||||
return reflect.New(t).Interface().(Object), nil
|
||||
}
|
||||
return nil, ¬RegisteredErr{gvk: kind}
|
||||
}
|
||||
|
||||
// Log sets a logger on the scheme. For test purposes only
|
||||
func (s *Scheme) Log(l conversion.DebugLogger) {
|
||||
s.raw.Log(l)
|
||||
s.converter.Debug = l
|
||||
}
|
||||
|
||||
// AddConversionFuncs adds a function to the list of conversion functions. The given
|
||||
// function should know how to convert between two API objects. We deduce how to call
|
||||
// it from the types of its two parameters; see the comment for
|
||||
// Converter.RegisterConversionFunction.
|
||||
// AddIgnoredConversionType identifies a pair of types that should be skipped by
|
||||
// conversion (because the data inside them is explicitly dropped during
|
||||
// conversion).
|
||||
func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
|
||||
return s.converter.RegisterIgnoredConversion(from, to)
|
||||
}
|
||||
|
||||
// AddConversionFuncs adds functions to the list of conversion functions. The given
|
||||
// functions should know how to convert between two of your API objects, or their
|
||||
// sub-objects. We deduce how to call these functions from the types of their two
|
||||
// parameters; see the comment for Converter.Register.
|
||||
//
|
||||
// Note that, if you need to copy sub-objects that didn't change, it's safe to call
|
||||
// Convert() inside your conversionFuncs, as long as you don't start a conversion
|
||||
// chain that's infinitely recursive.
|
||||
// Note that, if you need to copy sub-objects that didn't change, you can use the
|
||||
// conversion.Scope object that will be passed to your conversion function.
|
||||
// Additionally, all conversions started by Scheme will set the SrcVersion and
|
||||
// DestVersion fields on the Meta object. Example:
|
||||
//
|
||||
// s.AddConversionFuncs(
|
||||
// func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
|
||||
// // You can depend on Meta() being non-nil, and this being set to
|
||||
// // the source version, e.g., ""
|
||||
// s.Meta().SrcVersion
|
||||
// // You can depend on this being set to the destination version,
|
||||
// // e.g., "v1".
|
||||
// s.Meta().DestVersion
|
||||
// // Call scope.Convert to copy sub-fields.
|
||||
// s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
|
||||
// return nil
|
||||
// },
|
||||
// )
|
||||
//
|
||||
// (For more detail about conversion functions, see Converter.Register's comment.)
|
||||
//
|
||||
// Also note that the default behavior, if you don't add a conversion function, is to
|
||||
// sanely copy fields that have the same names. It's OK if the destination type has
|
||||
// extra fields, but it must not remove any. So you only need to add a conversion
|
||||
// function for things with changed/removed fields.
|
||||
// sanely copy fields that have the same names and same type names. It's OK if the
|
||||
// destination type has extra fields, but it must not remove any. So you only need to
|
||||
// add conversion functions for things with changed/removed fields.
|
||||
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
return s.raw.AddConversionFuncs(conversionFuncs...)
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.RegisterConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddConversionFuncs, but registers conversion functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
return s.raw.AddGeneratedConversionFuncs(conversionFuncs...)
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.RegisterGeneratedConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDeepCopyFuncs adds a function to the list of deep-copy functions.
|
||||
// For the expected format of deep-copy function, see the comment for
|
||||
// Copier.RegisterDeepCopyFunction.
|
||||
func (s *Scheme) AddDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
||||
return s.raw.AddDeepCopyFuncs(deepCopyFuncs...)
|
||||
for _, f := range deepCopyFuncs {
|
||||
if err := s.cloner.RegisterDeepCopyFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddDeepCopyFuncs, but registers deep-copy functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
||||
return s.raw.AddGeneratedDeepCopyFuncs(deepCopyFuncs...)
|
||||
for _, f := range deepCopyFuncs {
|
||||
if err := s.cloner.RegisterGeneratedDeepCopyFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFieldLabelConversionFunc adds a conversion function to convert field selectors
|
||||
@ -198,19 +376,43 @@ func (s *Scheme) AddFieldLabelConversionFunc(version, kind string, conversionFun
|
||||
// the comment in conversion.Converter.SetStructFieldCopy for parameter details.
|
||||
// Call as many times as needed, even on the same fields.
|
||||
func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
|
||||
return s.raw.AddStructFieldConversion(srcFieldType, srcFieldName, destFieldType, destFieldName)
|
||||
return s.converter.SetStructFieldCopy(srcFieldType, srcFieldName, destFieldType, destFieldName)
|
||||
}
|
||||
|
||||
// AddDefaultingFuncs adds a function to the list of value-defaulting functions.
|
||||
// We deduce how to call it from the types of its two parameters; see the
|
||||
// comment for Converter.RegisterDefaultingFunction.
|
||||
// RegisterInputDefaults sets the provided field mapping function and field matching
|
||||
// as the defaults for the provided input type. The fn may be nil, in which case no
|
||||
// mapping will happen by default. Use this method to register a mechanism for handling
|
||||
// a specific input type in conversion, such as a map[string]string to structs.
|
||||
func (s *Scheme) RegisterInputDefaults(in interface{}, fn conversion.FieldMappingFunc, defaultFlags conversion.FieldMatchingFlags) error {
|
||||
return s.converter.RegisterInputDefaults(in, fn, defaultFlags)
|
||||
}
|
||||
|
||||
// AddDefaultingFuncs adds functions to the list of default-value functions.
|
||||
// Each of the given functions is responsible for applying default values
|
||||
// when converting an instance of a versioned API object into an internal
|
||||
// API object. These functions do not need to handle sub-objects. We deduce
|
||||
// how to call these functions from the types of their two parameters.
|
||||
//
|
||||
// s.AddDefaultingFuncs(
|
||||
// func(obj *v1.Pod) {
|
||||
// if obj.OptionalField == "" {
|
||||
// obj.OptionalField = "DefaultValue"
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
|
||||
return s.raw.AddDefaultingFuncs(defaultingFuncs...)
|
||||
for _, f := range defaultingFuncs {
|
||||
err := s.converter.RegisterDefaultingFunc(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy does a deep copy of an API object.
|
||||
func (s *Scheme) Copy(src Object) (Object, error) {
|
||||
dst, err := s.raw.DeepCopy(src)
|
||||
dst, err := s.DeepCopy(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -219,25 +421,33 @@ func (s *Scheme) Copy(src Object) (Object, error) {
|
||||
|
||||
// Performs a deep copy of the given object.
|
||||
func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) {
|
||||
return s.raw.DeepCopy(src)
|
||||
return s.cloner.DeepCopy(src)
|
||||
}
|
||||
|
||||
// WithConversions returns an ObjectConvertor that has the additional conversion functions
|
||||
// defined in fns. The current scheme is not altered.
|
||||
func (s *Scheme) WithConversions(fns *conversion.ConversionFuncs) ObjectConvertor {
|
||||
if fns == nil {
|
||||
return s
|
||||
}
|
||||
copied := *s
|
||||
copied.raw = s.raw.WithConversions(*fns)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Convert will attempt to convert in into out. Both must be pointers.
|
||||
// For easy testing of conversion functions. Returns an error if the conversion isn't
|
||||
// possible.
|
||||
// Convert will attempt to convert in into out. Both must be pointers. For easy
|
||||
// testing of conversion functions. Returns an error if the conversion isn't
|
||||
// possible. You can call this with types that haven't been registered (for example,
|
||||
// a to test conversion of types that are nested within registered types), but in
|
||||
// that case, the conversion.Scope object passed to your conversion functions won't
|
||||
// have SrcVersion or DestVersion fields set correctly in Meta().
|
||||
func (s *Scheme) Convert(in, out interface{}) error {
|
||||
return s.raw.Convert(in, out)
|
||||
inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
if inObj, ok := in.(Object); ok {
|
||||
if gvk, err := s.ObjectKind(inObj); err == nil {
|
||||
inVersion = gvk.GroupVersion()
|
||||
}
|
||||
}
|
||||
if outObj, ok := out.(Object); ok {
|
||||
if gvk, err := s.ObjectKind(outObj); err == nil {
|
||||
outVersion = gvk.GroupVersion()
|
||||
}
|
||||
}
|
||||
flags, meta := s.generateConvertMeta(inVersion, outVersion, in)
|
||||
if flags == 0 {
|
||||
flags = conversion.AllowDifferentFieldTypeNames
|
||||
}
|
||||
return s.converter.Convert(in, out, flags, meta)
|
||||
}
|
||||
|
||||
// Converts the given field label and value for an kind field selector from
|
||||
@ -267,22 +477,60 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error)
|
||||
case *Unknown, *Unstructured:
|
||||
old := in.GetObjectKind().GroupVersionKind()
|
||||
defer in.GetObjectKind().SetGroupVersionKind(old)
|
||||
setTargetVersion(in, s.raw, gv)
|
||||
setTargetVersion(in, s, gv)
|
||||
return in, nil
|
||||
}
|
||||
unknown, err := s.raw.ConvertToVersion(in, outVersion)
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("only pointer types may be converted: %v", t)
|
||||
}
|
||||
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
|
||||
}
|
||||
|
||||
var kind unversioned.GroupVersionKind
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind = unversionedKind
|
||||
} else {
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion)
|
||||
}
|
||||
kind = kinds[0]
|
||||
}
|
||||
|
||||
outKind := gv.WithKind(kind.Kind)
|
||||
|
||||
inKind, err := s.ObjectKind(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, ok := unknown.(Object)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown)
|
||||
|
||||
out, err := s.New(outKind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setTargetVersion(obj, s.raw, gv)
|
||||
return obj, nil
|
||||
|
||||
flags, meta := s.generateConvertMeta(inKind.GroupVersion(), gv, in)
|
||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setTargetVersion(out, s, gv)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func setTargetVersion(obj Object, raw *conversion.Scheme, gv unversioned.GroupVersion) {
|
||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
flags, meta := s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||
meta.SrcVersion = srcGroupVersion.String()
|
||||
meta.DestVersion = destGroupVersion.String()
|
||||
return flags, meta
|
||||
}
|
||||
|
||||
func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
||||
if gv.Version == APIVersionInternal {
|
||||
// internal is a special case
|
||||
obj.GetObjectKind().SetGroupVersionKind(nil)
|
||||
|
@ -20,6 +20,9 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
@ -27,29 +30,16 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
type TypeMeta struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
}
|
||||
|
||||
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
|
||||
func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
||||
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||
}
|
||||
|
||||
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
|
||||
func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind {
|
||||
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||
}
|
||||
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
|
||||
|
||||
type InternalSimple struct {
|
||||
TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
type ExternalSimple struct {
|
||||
TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
@ -135,7 +125,7 @@ func TestScheme(t *testing.T) {
|
||||
}
|
||||
// clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
|
||||
// does not automatically clear TypeMeta anymore).
|
||||
simple.TypeMeta = TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
|
||||
simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
|
||||
if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
||||
}
|
||||
@ -188,33 +178,33 @@ func TestBadJSONRejection(t *testing.T) {
|
||||
}
|
||||
|
||||
type ExtensionA struct {
|
||||
TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
type ExtensionB struct {
|
||||
TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
|
||||
type ExternalExtensionType struct {
|
||||
TypeMeta `json:",inline"`
|
||||
Extension runtime.RawExtension `json:"extension"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.RawExtension `json:"extension"`
|
||||
}
|
||||
|
||||
type InternalExtensionType struct {
|
||||
TypeMeta `json:",inline"`
|
||||
Extension runtime.Object `json:"extension"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.Object `json:"extension"`
|
||||
}
|
||||
|
||||
type ExternalOptionalExtensionType struct {
|
||||
TypeMeta `json:",inline"`
|
||||
Extension runtime.RawExtension `json:"extension,omitempty"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.RawExtension `json:"extension,omitempty"`
|
||||
}
|
||||
|
||||
type InternalOptionalExtensionType struct {
|
||||
TypeMeta `json:",inline"`
|
||||
Extension runtime.Object `json:"extension,omitempty"`
|
||||
runtime.TypeMeta `json:",inline"`
|
||||
Extension runtime.Object `json:"extension,omitempty"`
|
||||
}
|
||||
|
||||
func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||
@ -284,28 +274,28 @@ func TestExtensionMapping(t *testing.T) {
|
||||
},
|
||||
&InternalExtensionType{
|
||||
Extension: &runtime.Unknown{
|
||||
RawJSON: []byte(`{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}`),
|
||||
RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`),
|
||||
},
|
||||
},
|
||||
// apiVersion is set in the serialized object for easier consumption by clients
|
||||
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}}
|
||||
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}}
|
||||
`,
|
||||
}, {
|
||||
&InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})},
|
||||
&InternalExtensionType{
|
||||
Extension: &runtime.Unknown{
|
||||
RawJSON: []byte(`{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}`),
|
||||
RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`),
|
||||
},
|
||||
},
|
||||
// apiVersion is set in the serialized object for easier consumption by clients
|
||||
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}}
|
||||
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}}
|
||||
`,
|
||||
}, {
|
||||
&InternalExtensionType{Extension: nil},
|
||||
&InternalExtensionType{
|
||||
Extension: nil,
|
||||
},
|
||||
`{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null}
|
||||
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null}
|
||||
`,
|
||||
},
|
||||
}
|
||||
@ -411,7 +401,273 @@ func TestUnversionedTypes(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(data) != `{"kind":"InternalSimple","apiVersion":"test.group/testExternal","testString":"I'm the same"}`+"\n" {
|
||||
if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" {
|
||||
t.Errorf("unexpected data: %s", data)
|
||||
}
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
|
||||
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
|
||||
}
|
||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind {
|
||||
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
|
||||
}
|
||||
|
||||
func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *TestType1) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind {
|
||||
return &obj.MyWeirdCustomEmbeddedVersionKindField
|
||||
}
|
||||
|
||||
func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
|
||||
func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind {
|
||||
return unversioned.EmptyObjectKind
|
||||
}
|
||||
|
||||
// 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() *runtime.Scheme {
|
||||
internalGV := unversioned.GroupVersion{Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Version: "v1"}
|
||||
|
||||
s := runtime.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 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 TestMetaValues(t *testing.T) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
|
||||
s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
|
||||
|
||||
internalToExternalCalls := 0
|
||||
externalToInternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := s.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
|
||||
t.Logf("internal -> external")
|
||||
if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", 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)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
|
||||
t.Logf("external -> internal")
|
||||
if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", 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)
|
||||
externalToInternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
simple := &InternalSimple{
|
||||
TestString: "foo",
|
||||
}
|
||||
|
||||
s.Log(t)
|
||||
|
||||
out, err := s.ConvertToVersion(simple, externalGV.String())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
internal, err := s.ConvertToVersion(out, internalGV.String())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if e, a := simple, internal; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
|
||||
}
|
||||
|
||||
if e, a := 1, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 1, externalToInternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaValuesUnregisteredConvert(t *testing.T) {
|
||||
type InternalSimple struct {
|
||||
Version string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
type ExternalSimple struct {
|
||||
Version string `json:"apiVersion,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
TestString string `json:"testString"`
|
||||
}
|
||||
s := runtime.NewScheme()
|
||||
// We deliberately don't register the types.
|
||||
|
||||
internalToExternalCalls := 0
|
||||
|
||||
// Register functions to verify that scope.Meta() gets set correctly.
|
||||
err := s.AddConversionFuncs(
|
||||
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
|
||||
if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a {
|
||||
t.Fatalf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
scope.Convert(&in.TestString, &out.TestString, 0)
|
||||
internalToExternalCalls++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
simple := &InternalSimple{TestString: "foo"}
|
||||
external := &ExternalSimple{}
|
||||
err = s.Convert(simple, external)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if e, a := simple.TestString, external.TestString; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// Verify that our conversion handler got called.
|
||||
if e, a := 1, internalToExternalCalls; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
Loading…
Reference in New Issue
Block a user