mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +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)
|
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"))
|
apiShort := generator.AddImport(path.Join(pkgBase, "api"))
|
||||||
generator.AddImport(path.Join(pkgBase, "api/resource"))
|
generator.AddImport(path.Join(pkgBase, "api/resource"))
|
||||||
// TODO(wojtek-t): Change the overwrites to a flag.
|
// TODO(wojtek-t): Change the overwrites to a flag.
|
||||||
|
@ -114,7 +114,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
versionPath := pkgPath(gv.Group, gv.Version)
|
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"))
|
generator.AddImport(path.Join(pkgBase, "api"))
|
||||||
|
|
||||||
if len(*overwrites) > 0 {
|
if len(*overwrites) > 0 {
|
||||||
|
@ -35,7 +35,7 @@ func BenchmarkPodConversion(b *testing.B) {
|
|||||||
b.Fatalf("Unexpected error decoding pod: %v", err)
|
b.Fatalf("Unexpected error decoding pod: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scheme := api.Scheme.Raw()
|
scheme := api.Scheme
|
||||||
var result *api.Pod
|
var result *api.Pod
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
versionedObj, err := scheme.ConvertToVersion(&pod, testapi.Default.GroupVersion().String())
|
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)
|
b.Fatalf("Unexpected error decoding node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scheme := api.Scheme.Raw()
|
scheme := api.Scheme
|
||||||
var result *api.Node
|
var result *api.Node
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
versionedObj, err := scheme.ConvertToVersion(&node, testapi.Default.GroupVersion().String())
|
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)
|
b.Fatalf("Unexpected error decoding node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scheme := api.Scheme.Raw()
|
scheme := api.Scheme
|
||||||
var result *api.ReplicationController
|
var result *api.ReplicationController
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
versionedObj, err := scheme.ConvertToVersion(&replicationController, testapi.Default.GroupVersion().String())
|
versionedObj, err := scheme.ConvertToVersion(&replicationController, testapi.Default.GroupVersion().String())
|
||||||
|
@ -36,6 +36,10 @@ type DebugLogger interface {
|
|||||||
Logf(format string, args ...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.
|
// Converter knows how to convert one type to another.
|
||||||
type Converter struct {
|
type Converter struct {
|
||||||
// Map from the conversion pair to a function which can
|
// Map from the conversion pair to a function which can
|
||||||
@ -77,14 +81,14 @@ type Converter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewConverter creates a new Converter object.
|
// NewConverter creates a new Converter object.
|
||||||
func NewConverter() *Converter {
|
func NewConverter(nameFn NameFunc) *Converter {
|
||||||
c := &Converter{
|
c := &Converter{
|
||||||
conversionFuncs: NewConversionFuncs(),
|
conversionFuncs: NewConversionFuncs(),
|
||||||
generatedConversionFuncs: NewConversionFuncs(),
|
generatedConversionFuncs: NewConversionFuncs(),
|
||||||
ignoredConversions: make(map[typePair]struct{}),
|
ignoredConversions: make(map[typePair]struct{}),
|
||||||
defaultingFuncs: make(map[reflect.Type]reflect.Value),
|
defaultingFuncs: make(map[reflect.Type]reflect.Value),
|
||||||
defaultingInterfaces: make(map[reflect.Type]interface{}),
|
defaultingInterfaces: make(map[reflect.Type]interface{}),
|
||||||
nameFunc: func(t reflect.Type) string { return t.Name() },
|
nameFunc: nameFn,
|
||||||
structFieldDests: make(map[typeNamePair][]typeNamePair),
|
structFieldDests: make(map[typeNamePair][]typeNamePair),
|
||||||
structFieldSources: make(map[typeNamePair][]typeNamePair),
|
structFieldSources: make(map[typeNamePair][]typeNamePair),
|
||||||
|
|
||||||
@ -103,6 +107,13 @@ func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
|
|||||||
return &copied
|
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
|
// ByteSliceCopy prevents recursing into every byte
|
||||||
func ByteSliceCopy(in *[]byte, out *[]byte, s Scope) error {
|
func ByteSliceCopy(in *[]byte, out *[]byte, s Scope) error {
|
||||||
*out = make([]byte, len(*in))
|
*out = make([]byte, len(*in))
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package conversion
|
package conversion
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -24,10 +25,71 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/gofuzz"
|
"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 {
|
func testLogger(t *testing.T) DebugLogger {
|
||||||
// We don't set logger to eliminate rubbish logs in tests.
|
// We don't set logger to eliminate rubbish logs in tests.
|
||||||
// If you want to switch it, simply switch it to: "return t"
|
// 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) {
|
func TestConverter_byteSlice(t *testing.T) {
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
src := []byte{1, 2, 3}
|
src := []byte{1, 2, 3}
|
||||||
dest := []byte{}
|
dest := []byte{}
|
||||||
err := c.Convert(&src, &dest, 0, nil)
|
err := c.Convert(&src, &dest, 0, nil)
|
||||||
@ -48,7 +110,7 @@ func TestConverter_byteSlice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestConverter_MismatchedTypes(t *testing.T) {
|
func TestConverter_MismatchedTypes(t *testing.T) {
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
|
|
||||||
err := c.RegisterConversionFunc(
|
err := c.RegisterConversionFunc(
|
||||||
func(in *[]string, out *int, s Scope) error {
|
func(in *[]string, out *int, s Scope) error {
|
||||||
@ -84,7 +146,7 @@ func TestConverter_DefaultConvert(t *testing.T) {
|
|||||||
Bar string
|
Bar string
|
||||||
Baz int
|
Baz int
|
||||||
}
|
}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
c.nameFunc = func(t reflect.Type) string { return "MyType" }
|
c.nameFunc = func(t reflect.Type) string { return "MyType" }
|
||||||
|
|
||||||
@ -123,7 +185,7 @@ func TestConverter_DeepCopy(t *testing.T) {
|
|||||||
Baz interface{}
|
Baz interface{}
|
||||||
Qux map[string]string
|
Qux map[string]string
|
||||||
}
|
}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
|
|
||||||
foo, baz := "foo", "baz"
|
foo, baz := "foo", "baz"
|
||||||
@ -166,7 +228,7 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
|||||||
Baz int
|
Baz int
|
||||||
}
|
}
|
||||||
type C struct{}
|
type C struct{}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||||
out.Bar = in.Foo
|
out.Bar = in.Foo
|
||||||
@ -228,7 +290,7 @@ func TestConverter_IgnoredConversion(t *testing.T) {
|
|||||||
type B struct{}
|
type B struct{}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||||
count++
|
count++
|
||||||
return nil
|
return nil
|
||||||
@ -257,7 +319,7 @@ func TestConverter_IgnoredConversionNested(t *testing.T) {
|
|||||||
C C
|
C C
|
||||||
}
|
}
|
||||||
|
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
typed := C("")
|
typed := C("")
|
||||||
if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil {
|
if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -275,7 +337,7 @@ func TestConverter_IgnoredConversionNested(t *testing.T) {
|
|||||||
func TestConverter_GeneratedConversionOverriden(t *testing.T) {
|
func TestConverter_GeneratedConversionOverriden(t *testing.T) {
|
||||||
type A struct{}
|
type A struct{}
|
||||||
type B struct{}
|
type B struct{}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -297,7 +359,7 @@ func TestConverter_GeneratedConversionOverriden(t *testing.T) {
|
|||||||
func TestConverter_WithConversionOverriden(t *testing.T) {
|
func TestConverter_WithConversionOverriden(t *testing.T) {
|
||||||
type A struct{}
|
type A struct{}
|
||||||
type B struct{}
|
type B struct{}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||||
return fmt.Errorf("conversion function should be overriden")
|
return fmt.Errorf("conversion function should be overriden")
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -331,7 +393,7 @@ func TestConverter_MapsStringArrays(t *testing.T) {
|
|||||||
Baz int
|
Baz int
|
||||||
Other string
|
Other string
|
||||||
}
|
}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
||||||
if len(*input) == 0 {
|
if len(*input) == 0 {
|
||||||
@ -384,7 +446,7 @@ func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) {
|
|||||||
Baz int
|
Baz int
|
||||||
Other string
|
Other string
|
||||||
}
|
}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
||||||
if len(*input) == 0 {
|
if len(*input) == 0 {
|
||||||
@ -434,7 +496,7 @@ func TestConverter_fuzz(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.nameFunc = func(t reflect.Type) string {
|
c.nameFunc = func(t reflect.Type) string {
|
||||||
// Hide the fact that we don't have separate packages for these things.
|
// Hide the fact that we don't have separate packages for these things.
|
||||||
return map[reflect.Type]string{
|
return map[reflect.Type]string{
|
||||||
@ -473,7 +535,7 @@ func TestConverter_MapElemAddr(t *testing.T) {
|
|||||||
type Bar struct {
|
type Bar struct {
|
||||||
A map[string]string
|
A map[string]string
|
||||||
}
|
}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
err := c.RegisterConversionFunc(
|
err := c.RegisterConversionFunc(
|
||||||
func(in *int, out *string, s Scope) error {
|
func(in *int, out *string, s Scope) error {
|
||||||
@ -519,7 +581,7 @@ func TestConverter_tags(t *testing.T) {
|
|||||||
type Bar struct {
|
type Bar struct {
|
||||||
A string `test:"bar"`
|
A string `test:"bar"`
|
||||||
}
|
}
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
err := c.RegisterConversionFunc(
|
err := c.RegisterConversionFunc(
|
||||||
func(in *string, out *string, s Scope) error {
|
func(in *string, out *string, s Scope) error {
|
||||||
@ -544,7 +606,7 @@ func TestConverter_tags(t *testing.T) {
|
|||||||
func TestConverter_meta(t *testing.T) {
|
func TestConverter_meta(t *testing.T) {
|
||||||
type Foo struct{ A string }
|
type Foo struct{ A string }
|
||||||
type Bar struct{ A string }
|
type Bar struct{ A string }
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
checks := 0
|
checks := 0
|
||||||
err := c.RegisterConversionFunc(
|
err := c.RegisterConversionFunc(
|
||||||
@ -653,7 +715,7 @@ func TestConverter_flags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
c.Debug = testLogger(t)
|
c.Debug = testLogger(t)
|
||||||
|
|
||||||
for i, item := range table {
|
for i, item := range table {
|
||||||
@ -691,7 +753,7 @@ func TestConverter_FieldRename(t *testing.T) {
|
|||||||
NameMeta
|
NameMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
c := NewConverter()
|
c := NewConverter(DefaultNameFunc)
|
||||||
err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta")
|
err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
@ -764,131 +826,22 @@ func TestConverter_FieldRename(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMetaValues(t *testing.T) {
|
func objDiff(a, b interface{}) string {
|
||||||
type InternalSimple struct {
|
ab, err := json.Marshal(a)
|
||||||
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
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
panic("a")
|
||||||
}
|
}
|
||||||
simple := &InternalSimple{
|
bb, err := json.Marshal(b)
|
||||||
TestString: "foo",
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Log(t)
|
|
||||||
|
|
||||||
out, err := s.ConvertToVersion(simple, externalGV.String())
|
|
||||||
if err != nil {
|
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())
|
// An alternate diff attempt, in case json isn't showing you
|
||||||
if err != nil {
|
// the difference. (reflect.DeepEqual makes a distinction between
|
||||||
t.Fatalf("unexpected error: %v", err)
|
// nil and empty slices, for example.)
|
||||||
}
|
//return util.StringDiff(
|
||||||
|
// fmt.Sprintf("%#v", a),
|
||||||
if e, a := simple, internal; !reflect.DeepEqual(e, a) {
|
// fmt.Sprintf("%#v", b),
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||||
@ -181,7 +180,7 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
converted, err := p.convertor.ConvertToVersion(obj, version.String())
|
converted, err := p.convertor.ConvertToVersion(obj, version.String())
|
||||||
if conversion.IsNotRegisteredError(err) {
|
if runtime.IsNotRegisteredError(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +41,7 @@ type ConversionGenerator interface {
|
|||||||
AssumePrivateConversions()
|
AssumePrivateConversions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConversionGenerator(scheme *conversion.Scheme, targetPkg string) ConversionGenerator {
|
func NewConversionGenerator(scheme *Scheme, targetPkg string) ConversionGenerator {
|
||||||
g := &conversionGenerator{
|
g := &conversionGenerator{
|
||||||
scheme: scheme,
|
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}
|
var complexTypes []reflect.Kind = []reflect.Kind{reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct}
|
||||||
|
|
||||||
type conversionGenerator struct {
|
type conversionGenerator struct {
|
||||||
scheme *conversion.Scheme
|
scheme *Scheme
|
||||||
|
|
||||||
nameFormat string
|
nameFormat string
|
||||||
generatedNamePrefix string
|
generatedNamePrefix string
|
||||||
@ -105,7 +104,7 @@ func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVer
|
|||||||
internalVersion := gv
|
internalVersion := gv
|
||||||
internalVersion.Version = APIVersionInternal
|
internalVersion.Version = APIVersionInternal
|
||||||
|
|
||||||
internalObj, err := g.scheme.NewObject(internalVersion.WithKind(kind))
|
internalObj, err := g.scheme.New(internalVersion.WithKind(kind))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create an object of type %v in internal version", kind)
|
return fmt.Errorf("cannot create an object of type %v in internal version", kind)
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -69,7 +68,7 @@ type DeepCopyGenerator interface {
|
|||||||
OverwritePackage(pkg, overwrite string)
|
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{
|
g := &deepCopyGenerator{
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
targetPkg: targetPkg,
|
targetPkg: targetPkg,
|
||||||
@ -91,7 +90,7 @@ type pkgPathNamePair struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type deepCopyGenerator struct {
|
type deepCopyGenerator struct {
|
||||||
scheme *conversion.Scheme
|
scheme *Scheme
|
||||||
targetPkg string
|
targetPkg string
|
||||||
copyables map[reflect.Type]bool
|
copyables map[reflect.Type]bool
|
||||||
// map of package names to shortname
|
// map of package names to shortname
|
||||||
|
@ -17,31 +17,86 @@ limitations under the License.
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
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
|
// IsNotRegisteredError returns true if the error indicates the provided
|
||||||
// object or input data is not registered.
|
// object or input data is not registered.
|
||||||
func IsNotRegisteredError(err error) bool {
|
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
|
// IsMissingKind returns true if the error indicates that the provided object
|
||||||
// is missing a 'Kind' field.
|
// is missing a 'Kind' field.
|
||||||
func IsMissingKind(err error) bool {
|
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
|
// IsMissingVersion returns true if the error indicates that the provided object
|
||||||
// is missing a 'Versioj' field.
|
// 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 {
|
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 (
|
const (
|
||||||
APIVersionInternal = "__internal"
|
// APIVersionInternal may be used if you are registering a type that should not
|
||||||
APIVersionUnversioned = "__unversioned"
|
// 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.
|
// Typer retrieves information about an object's group, version, and kind.
|
||||||
|
@ -25,47 +25,115 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scheme defines methods for serializing and deserializing API objects. It
|
// Scheme defines methods for serializing and deserializing API objects, a type
|
||||||
// is an adaptation of conversion's Scheme for our API objects.
|
// 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 {
|
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
|
// Map from version and resource to the corresponding func to convert
|
||||||
// resource field labels in that version to internal version.
|
// resource field labels in that version to internal version.
|
||||||
fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc
|
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.
|
// Function to convert a field selector to internal representation.
|
||||||
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error)
|
||||||
|
|
||||||
func (self *Scheme) Raw() *conversion.Scheme {
|
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
||||||
return self.raw
|
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
|
// fromScope gets the input version, desired output version, and desired Scheme
|
||||||
// from a conversion.Scope.
|
// from a conversion.Scope.
|
||||||
func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
|
func (s *Scheme) fromScope(scope conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
|
||||||
scheme = self
|
scheme = s
|
||||||
inVersion = s.Meta().SrcVersion
|
inVersion = scope.Meta().SrcVersion
|
||||||
outVersion = s.Meta().DestVersion
|
outVersion = scope.Meta().DestVersion
|
||||||
return inVersion, outVersion, scheme
|
return inVersion, outVersion, scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScheme creates a new Scheme. This scheme is pluggable by default.
|
// Converter allows access to the converter for the scheme
|
||||||
func NewScheme() *Scheme {
|
func (s *Scheme) Converter() *conversion.Converter {
|
||||||
s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}}
|
return s.converter
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
|
// 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
|
// 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.
|
// every version with particular schemas. Resolve tihs method at that point.
|
||||||
func (s *Scheme) AddUnversionedTypes(gv unversioned.GroupVersion, types ...Object) {
|
func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) {
|
||||||
interfaces := make([]interface{}, len(types))
|
s.AddKnownTypes(version, types...)
|
||||||
for i := range types {
|
for _, obj := range types {
|
||||||
interfaces[i] = types[i]
|
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) {
|
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) {
|
||||||
interfaces := make([]interface{}, len(types))
|
if len(gv.Version) == 0 {
|
||||||
for i := range types {
|
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
|
||||||
interfaces[i] = types[i]
|
|
||||||
}
|
}
|
||||||
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
|
gvk := gv.WithKind(t.Name())
|
||||||
// this method is not invoked.
|
s.gvkToType[gvk] = t
|
||||||
func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
|
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
|
||||||
return s.raw.AddIgnoredConversionType(from, to)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
|
// 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
|
// 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) {
|
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.
|
// 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 {
|
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) {
|
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) {
|
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
|
// Recognizes returns true if the scheme is able to handle the provided group,version,kind
|
||||||
// of an object.
|
// of an object.
|
||||||
func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool {
|
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) {
|
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
|
// New returns a new API object of the given version and name, or an error if it hasn't
|
||||||
// representation) and name, or an error if it hasn't been registered.
|
// been registered. The version and kind fields must be specified.
|
||||||
func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
||||||
obj, err := s.raw.NewObject(kind)
|
if t, exists := s.gvkToType[kind]; exists {
|
||||||
if err != nil {
|
return reflect.New(t).Interface().(Object), nil
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
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
|
// Log sets a logger on the scheme. For test purposes only
|
||||||
func (s *Scheme) Log(l conversion.DebugLogger) {
|
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
|
// AddIgnoredConversionType identifies a pair of types that should be skipped by
|
||||||
// function should know how to convert between two API objects. We deduce how to call
|
// conversion (because the data inside them is explicitly dropped during
|
||||||
// it from the types of its two parameters; see the comment for
|
// conversion).
|
||||||
// Converter.RegisterConversionFunction.
|
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
|
// Note that, if you need to copy sub-objects that didn't change, you can use the
|
||||||
// Convert() inside your conversionFuncs, as long as you don't start a conversion
|
// conversion.Scope object that will be passed to your conversion function.
|
||||||
// chain that's infinitely recursive.
|
// 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
|
// 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
|
// sanely copy fields that have the same names and same type names. It's OK if the
|
||||||
// extra fields, but it must not remove any. So you only need to add a conversion
|
// destination type has extra fields, but it must not remove any. So you only need to
|
||||||
// function for things with changed/removed fields.
|
// add conversion functions for things with changed/removed fields.
|
||||||
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
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
|
// Similar to AddConversionFuncs, but registers conversion functions that were
|
||||||
// automatically generated.
|
// automatically generated.
|
||||||
func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error {
|
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.
|
// AddDeepCopyFuncs adds a function to the list of deep-copy functions.
|
||||||
// For the expected format of deep-copy function, see the comment for
|
// For the expected format of deep-copy function, see the comment for
|
||||||
// Copier.RegisterDeepCopyFunction.
|
// Copier.RegisterDeepCopyFunction.
|
||||||
func (s *Scheme) AddDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
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
|
// Similar to AddDeepCopyFuncs, but registers deep-copy functions that were
|
||||||
// automatically generated.
|
// automatically generated.
|
||||||
func (s *Scheme) AddGeneratedDeepCopyFuncs(deepCopyFuncs ...interface{}) error {
|
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
|
// 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.
|
// the comment in conversion.Converter.SetStructFieldCopy for parameter details.
|
||||||
// Call as many times as needed, even on the same fields.
|
// Call as many times as needed, even on the same fields.
|
||||||
func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
|
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.
|
// RegisterInputDefaults sets the provided field mapping function and field matching
|
||||||
// We deduce how to call it from the types of its two parameters; see the
|
// as the defaults for the provided input type. The fn may be nil, in which case no
|
||||||
// comment for Converter.RegisterDefaultingFunction.
|
// 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 {
|
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.
|
// Copy does a deep copy of an API object.
|
||||||
func (s *Scheme) Copy(src Object) (Object, error) {
|
func (s *Scheme) Copy(src Object) (Object, error) {
|
||||||
dst, err := s.raw.DeepCopy(src)
|
dst, err := s.DeepCopy(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -219,25 +421,33 @@ func (s *Scheme) Copy(src Object) (Object, error) {
|
|||||||
|
|
||||||
// Performs a deep copy of the given object.
|
// Performs a deep copy of the given object.
|
||||||
func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) {
|
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
|
// Convert will attempt to convert in into out. Both must be pointers. For easy
|
||||||
// defined in fns. The current scheme is not altered.
|
// testing of conversion functions. Returns an error if the conversion isn't
|
||||||
func (s *Scheme) WithConversions(fns *conversion.ConversionFuncs) ObjectConvertor {
|
// possible. You can call this with types that haven't been registered (for example,
|
||||||
if fns == nil {
|
// a to test conversion of types that are nested within registered types), but in
|
||||||
return s
|
// that case, the conversion.Scope object passed to your conversion functions won't
|
||||||
}
|
// have SrcVersion or DestVersion fields set correctly in Meta().
|
||||||
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.
|
|
||||||
func (s *Scheme) Convert(in, out interface{}) error {
|
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
|
// 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:
|
case *Unknown, *Unstructured:
|
||||||
old := in.GetObjectKind().GroupVersionKind()
|
old := in.GetObjectKind().GroupVersionKind()
|
||||||
defer in.GetObjectKind().SetGroupVersionKind(old)
|
defer in.GetObjectKind().SetGroupVersionKind(old)
|
||||||
setTargetVersion(in, s.raw, gv)
|
setTargetVersion(in, s, gv)
|
||||||
return in, nil
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
obj, ok := unknown.(Object)
|
|
||||||
if !ok {
|
out, err := s.New(outKind)
|
||||||
return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown)
|
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 {
|
if gv.Version == APIVersionInternal {
|
||||||
// internal is a special case
|
// internal is a special case
|
||||||
obj.GetObjectKind().SetGroupVersionKind(nil)
|
obj.GetObjectKind().SetGroupVersionKind(nil)
|
||||||
|
@ -20,6 +20,9 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/gofuzz"
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
@ -27,29 +30,16 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TypeMeta struct {
|
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
type InternalSimple struct {
|
type InternalSimple struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
TestString string `json:"testString"`
|
TestString string `json:"testString"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalSimple struct {
|
type ExternalSimple struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
TestString string `json:"testString"`
|
TestString string `json:"testString"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
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
|
// clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
|
||||||
// does not automatically clear TypeMeta anymore).
|
// 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) {
|
if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
|
||||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", 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 {
|
type ExtensionA struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
TestString string `json:"testString"`
|
TestString string `json:"testString"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtensionB struct {
|
type ExtensionB struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
TestString string `json:"testString"`
|
TestString string `json:"testString"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalExtensionType struct {
|
type ExternalExtensionType struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
Extension runtime.RawExtension `json:"extension"`
|
Extension runtime.RawExtension `json:"extension"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InternalExtensionType struct {
|
type InternalExtensionType struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
Extension runtime.Object `json:"extension"`
|
Extension runtime.Object `json:"extension"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalOptionalExtensionType struct {
|
type ExternalOptionalExtensionType struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
Extension runtime.RawExtension `json:"extension,omitempty"`
|
Extension runtime.RawExtension `json:"extension,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InternalOptionalExtensionType struct {
|
type InternalOptionalExtensionType struct {
|
||||||
TypeMeta `json:",inline"`
|
runtime.TypeMeta `json:",inline"`
|
||||||
Extension runtime.Object `json:"extension,omitempty"`
|
Extension runtime.Object `json:"extension,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
|
||||||
@ -284,28 +274,28 @@ func TestExtensionMapping(t *testing.T) {
|
|||||||
},
|
},
|
||||||
&InternalExtensionType{
|
&InternalExtensionType{
|
||||||
Extension: &runtime.Unknown{
|
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
|
// 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.NewEncodable(codec, &ExtensionB{TestString: "bar"})},
|
||||||
&InternalExtensionType{
|
&InternalExtensionType{
|
||||||
Extension: &runtime.Unknown{
|
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
|
// 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},
|
||||||
&InternalExtensionType{
|
&InternalExtensionType{
|
||||||
Extension: nil,
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
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"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"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"}`),
|
data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
|
||||||
into: &testDecodable{},
|
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"},
|
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
|
||||||
expectedObject: &testDecodable{
|
expectedObject: &testDecodable{
|
||||||
Other: "test",
|
Other: "test",
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
|
// 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 {
|
if len(unstruct.APIVersion) == 0 {
|
||||||
return nil, nil, conversion.NewMissingVersionErr(string(data))
|
return nil, nil, NewMissingVersionErr(string(data))
|
||||||
}
|
}
|
||||||
gv, err := unversioned.ParseGroupVersion(unstruct.APIVersion)
|
gv, err := unversioned.ParseGroupVersion(unstruct.APIVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -60,7 +59,7 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionK
|
|||||||
}
|
}
|
||||||
gvk := gv.WithKind(unstruct.Kind)
|
gvk := gv.WithKind(unstruct.Kind)
|
||||||
if len(unstruct.Kind) == 0 {
|
if len(unstruct.Kind) == 0 {
|
||||||
return nil, &gvk, conversion.NewMissingKindErr(string(data))
|
return nil, &gvk, NewMissingKindErr(string(data))
|
||||||
}
|
}
|
||||||
unstruct.Object = m
|
unstruct.Object = m
|
||||||
return unstruct, &gvk, nil
|
return unstruct, &gvk, nil
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conversion_test
|
package runtime_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
Loading…
Reference in New Issue
Block a user