Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
60b66665
Commit
60b66665
authored
Jan 25, 2016
by
Clayton Coleman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move conversion.Scheme to runtime
There is only one type registry to rule them all
parent
0e889a1a
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
827 additions
and
1050 deletions
+827
-1050
conversion.go
cmd/genconversion/conversion.go
+1
-1
deep_copy.go
cmd/gendeepcopy/deep_copy.go
+1
-1
conversion_test.go
pkg/api/conversion_test.go
+3
-3
converter.go
pkg/conversion/converter.go
+13
-2
converter_test.go
pkg/conversion/converter_test.go
+93
-140
error.go
pkg/conversion/error.go
+0
-98
scheme.go
pkg/conversion/scheme.go
+0
-464
scheme_test.go
pkg/conversion/scheme_test.go
+0
-181
resource_printer.go
pkg/kubectl/resource_printer.go
+1
-2
conversion_generator.go
pkg/runtime/conversion_generator.go
+3
-4
deep_copy_generator.go
pkg/runtime/deep_copy_generator.go
+2
-3
error.go
pkg/runtime/error.go
+64
-9
interfaces.go
pkg/runtime/interfaces.go
+4
-2
scheme.go
pkg/runtime/scheme.go
+345
-97
scheme_test.go
pkg/runtime/scheme_test.go
+293
-37
json_test.go
pkg/runtime/serializer/json/json_test.go
+1
-2
unstructured.go
pkg/runtime/unstructured.go
+2
-3
unversioned_test.go
pkg/runtime/unversioned_test.go
+1
-1
No files found.
cmd/genconversion/conversion.go
View file @
60b66665
...
...
@@ -92,7 +92,7 @@ func main() {
}
versionPath
:=
pkgPath
(
gv
.
Group
,
gv
.
Version
)
generator
:=
kruntime
.
NewConversionGenerator
(
api
.
Scheme
.
Raw
()
,
versionPath
)
generator
:=
kruntime
.
NewConversionGenerator
(
api
.
Scheme
,
versionPath
)
apiShort
:=
generator
.
AddImport
(
path
.
Join
(
pkgBase
,
"api"
))
generator
.
AddImport
(
path
.
Join
(
pkgBase
,
"api/resource"
))
// TODO(wojtek-t): Change the overwrites to a flag.
...
...
cmd/gendeepcopy/deep_copy.go
View file @
60b66665
...
...
@@ -114,7 +114,7 @@ func main() {
}
versionPath
:=
pkgPath
(
gv
.
Group
,
gv
.
Version
)
generator
:=
kruntime
.
NewDeepCopyGenerator
(
api
.
Scheme
.
Raw
()
,
versionPath
,
sets
.
NewString
(
"k8s.io/kubernetes"
))
generator
:=
kruntime
.
NewDeepCopyGenerator
(
api
.
Scheme
,
versionPath
,
sets
.
NewString
(
"k8s.io/kubernetes"
))
generator
.
AddImport
(
path
.
Join
(
pkgBase
,
"api"
))
if
len
(
*
overwrites
)
>
0
{
...
...
pkg/api/conversion_test.go
View file @
60b66665
...
...
@@ -35,7 +35,7 @@ func BenchmarkPodConversion(b *testing.B) {
b
.
Fatalf
(
"Unexpected error decoding pod: %v"
,
err
)
}
scheme
:=
api
.
Scheme
.
Raw
()
scheme
:=
api
.
Scheme
var
result
*
api
.
Pod
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
versionedObj
,
err
:=
scheme
.
ConvertToVersion
(
&
pod
,
testapi
.
Default
.
GroupVersion
()
.
String
())
...
...
@@ -63,7 +63,7 @@ func BenchmarkNodeConversion(b *testing.B) {
b
.
Fatalf
(
"Unexpected error decoding node: %v"
,
err
)
}
scheme
:=
api
.
Scheme
.
Raw
()
scheme
:=
api
.
Scheme
var
result
*
api
.
Node
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
versionedObj
,
err
:=
scheme
.
ConvertToVersion
(
&
node
,
testapi
.
Default
.
GroupVersion
()
.
String
())
...
...
@@ -91,7 +91,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) {
b
.
Fatalf
(
"Unexpected error decoding node: %v"
,
err
)
}
scheme
:=
api
.
Scheme
.
Raw
()
scheme
:=
api
.
Scheme
var
result
*
api
.
ReplicationController
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
versionedObj
,
err
:=
scheme
.
ConvertToVersion
(
&
replicationController
,
testapi
.
Default
.
GroupVersion
()
.
String
())
...
...
pkg/conversion/converter.go
View file @
60b66665
...
...
@@ -36,6 +36,10 @@ type DebugLogger interface {
Logf
(
format
string
,
args
...
interface
{})
}
type
NameFunc
func
(
t
reflect
.
Type
)
string
var
DefaultNameFunc
=
func
(
t
reflect
.
Type
)
string
{
return
t
.
Name
()
}
// Converter knows how to convert one type to another.
type
Converter
struct
{
// Map from the conversion pair to a function which can
...
...
@@ -77,14 +81,14 @@ type Converter struct {
}
// NewConverter creates a new Converter object.
func
NewConverter
()
*
Converter
{
func
NewConverter
(
nameFn
NameFunc
)
*
Converter
{
c
:=
&
Converter
{
conversionFuncs
:
NewConversionFuncs
(),
generatedConversionFuncs
:
NewConversionFuncs
(),
ignoredConversions
:
make
(
map
[
typePair
]
struct
{}),
defaultingFuncs
:
make
(
map
[
reflect
.
Type
]
reflect
.
Value
),
defaultingInterfaces
:
make
(
map
[
reflect
.
Type
]
interface
{}),
nameFunc
:
func
(
t
reflect
.
Type
)
string
{
return
t
.
Name
()
}
,
nameFunc
:
nameFn
,
structFieldDests
:
make
(
map
[
typeNamePair
][]
typeNamePair
),
structFieldSources
:
make
(
map
[
typeNamePair
][]
typeNamePair
),
...
...
@@ -103,6 +107,13 @@ func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
return
&
copied
}
// DefaultMeta returns the conversion FieldMappingFunc and meta for a given type.
func
(
c
*
Converter
)
DefaultMeta
(
t
reflect
.
Type
)
(
FieldMatchingFlags
,
*
Meta
)
{
return
c
.
inputDefaultFlags
[
t
],
&
Meta
{
KeyNameMapping
:
c
.
inputFieldMappingFuncs
[
t
],
}
}
// ByteSliceCopy prevents recursing into every byte
func
ByteSliceCopy
(
in
*
[]
byte
,
out
*
[]
byte
,
s
Scope
)
error
{
*
out
=
make
([]
byte
,
len
(
*
in
))
...
...
pkg/conversion/converter_test.go
View file @
60b66665
...
...
@@ -17,6 +17,7 @@ limitations under the License.
package
conversion
import
(
"encoding/json"
"fmt"
"reflect"
"strconv"
...
...
@@ -24,10 +25,71 @@ import (
"testing"
"github.com/google/gofuzz"
flag
"github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/
api/unversioned
"
"k8s.io/kubernetes/pkg/
util
"
)
var
fuzzIters
=
flag
.
Int
(
"fuzz-iters"
,
50
,
"How many fuzzing iterations to do."
)
// Test a weird version/kind embedding format.
type
MyWeirdCustomEmbeddedVersionKindField
struct
{
ID
string
`json:"ID,omitempty"`
APIVersion
string
`json:"myVersionKey,omitempty"`
ObjectKind
string
`json:"myKindKey,omitempty"`
Z
string
`json:"Z,omitempty"`
Y
uint64
`json:"Y,omitempty"`
}
type
TestType1
struct
{
MyWeirdCustomEmbeddedVersionKindField
`json:",inline"`
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
C
int8
`json:"C,omitempty"`
D
int16
`json:"D,omitempty"`
E
int32
`json:"E,omitempty"`
F
int64
`json:"F,omitempty"`
G
uint
`json:"G,omitempty"`
H
uint8
`json:"H,omitempty"`
I
uint16
`json:"I,omitempty"`
J
uint32
`json:"J,omitempty"`
K
uint64
`json:"K,omitempty"`
L
bool
`json:"L,omitempty"`
M
map
[
string
]
int
`json:"M,omitempty"`
N
map
[
string
]
TestType2
`json:"N,omitempty"`
O
*
TestType2
`json:"O,omitempty"`
P
[]
TestType2
`json:"Q,omitempty"`
}
type
TestType2
struct
{
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
}
type
ExternalTestType2
struct
{
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
}
type
ExternalTestType1
struct
{
MyWeirdCustomEmbeddedVersionKindField
`json:",inline"`
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
C
int8
`json:"C,omitempty"`
D
int16
`json:"D,omitempty"`
E
int32
`json:"E,omitempty"`
F
int64
`json:"F,omitempty"`
G
uint
`json:"G,omitempty"`
H
uint8
`json:"H,omitempty"`
I
uint16
`json:"I,omitempty"`
J
uint32
`json:"J,omitempty"`
K
uint64
`json:"K,omitempty"`
L
bool
`json:"L,omitempty"`
M
map
[
string
]
int
`json:"M,omitempty"`
N
map
[
string
]
ExternalTestType2
`json:"N,omitempty"`
O
*
ExternalTestType2
`json:"O,omitempty"`
P
[]
ExternalTestType2
`json:"Q,omitempty"`
}
func
testLogger
(
t
*
testing
.
T
)
DebugLogger
{
// We don't set logger to eliminate rubbish logs in tests.
// If you want to switch it, simply switch it to: "return t"
...
...
@@ -35,7 +97,7 @@ func testLogger(t *testing.T) DebugLogger {
}
func
TestConverter_byteSlice
(
t
*
testing
.
T
)
{
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
src
:=
[]
byte
{
1
,
2
,
3
}
dest
:=
[]
byte
{}
err
:=
c
.
Convert
(
&
src
,
&
dest
,
0
,
nil
)
...
...
@@ -48,7 +110,7 @@ func TestConverter_byteSlice(t *testing.T) {
}
func
TestConverter_MismatchedTypes
(
t
*
testing
.
T
)
{
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
[]
string
,
out
*
int
,
s
Scope
)
error
{
...
...
@@ -84,7 +146,7 @@ func TestConverter_DefaultConvert(t *testing.T) {
Bar
string
Baz
int
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
c
.
nameFunc
=
func
(
t
reflect
.
Type
)
string
{
return
"MyType"
}
...
...
@@ -123,7 +185,7 @@ func TestConverter_DeepCopy(t *testing.T) {
Baz
interface
{}
Qux
map
[
string
]
string
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
foo
,
baz
:=
"foo"
,
"baz"
...
...
@@ -166,7 +228,7 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
Baz
int
}
type
C
struct
{}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
A
,
out
*
B
,
s
Scope
)
error
{
out
.
Bar
=
in
.
Foo
...
...
@@ -228,7 +290,7 @@ func TestConverter_IgnoredConversion(t *testing.T) {
type
B
struct
{}
count
:=
0
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
if
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
A
,
out
*
B
,
s
Scope
)
error
{
count
++
return
nil
...
...
@@ -257,7 +319,7 @@ func TestConverter_IgnoredConversionNested(t *testing.T) {
C
C
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
typed
:=
C
(
""
)
if
err
:=
c
.
RegisterIgnoredConversion
(
&
typed
,
&
typed
);
err
!=
nil
{
t
.
Fatal
(
err
)
...
...
@@ -275,7 +337,7 @@ func TestConverter_IgnoredConversionNested(t *testing.T) {
func
TestConverter_GeneratedConversionOverriden
(
t
*
testing
.
T
)
{
type
A
struct
{}
type
B
struct
{}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
if
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
A
,
out
*
B
,
s
Scope
)
error
{
return
nil
});
err
!=
nil
{
...
...
@@ -297,7 +359,7 @@ func TestConverter_GeneratedConversionOverriden(t *testing.T) {
func
TestConverter_WithConversionOverriden
(
t
*
testing
.
T
)
{
type
A
struct
{}
type
B
struct
{}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
if
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
A
,
out
*
B
,
s
Scope
)
error
{
return
fmt
.
Errorf
(
"conversion function should be overriden"
)
});
err
!=
nil
{
...
...
@@ -331,7 +393,7 @@ func TestConverter_MapsStringArrays(t *testing.T) {
Baz
int
Other
string
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
if
err
:=
c
.
RegisterConversionFunc
(
func
(
input
*
[]
string
,
out
*
string
,
s
Scope
)
error
{
if
len
(
*
input
)
==
0
{
...
...
@@ -384,7 +446,7 @@ func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) {
Baz
int
Other
string
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
if
err
:=
c
.
RegisterConversionFunc
(
func
(
input
*
[]
string
,
out
*
string
,
s
Scope
)
error
{
if
len
(
*
input
)
==
0
{
...
...
@@ -434,7 +496,7 @@ func TestConverter_fuzz(t *testing.T) {
}
f
:=
fuzz
.
New
()
.
NilChance
(
.5
)
.
NumElements
(
0
,
100
)
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
nameFunc
=
func
(
t
reflect
.
Type
)
string
{
// Hide the fact that we don't have separate packages for these things.
return
map
[
reflect
.
Type
]
string
{
...
...
@@ -473,7 +535,7 @@ func TestConverter_MapElemAddr(t *testing.T) {
type
Bar
struct
{
A
map
[
string
]
string
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
int
,
out
*
string
,
s
Scope
)
error
{
...
...
@@ -519,7 +581,7 @@ func TestConverter_tags(t *testing.T) {
type
Bar
struct
{
A
string
`test:"bar"`
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
err
:=
c
.
RegisterConversionFunc
(
func
(
in
*
string
,
out
*
string
,
s
Scope
)
error
{
...
...
@@ -544,7 +606,7 @@ func TestConverter_tags(t *testing.T) {
func
TestConverter_meta
(
t
*
testing
.
T
)
{
type
Foo
struct
{
A
string
}
type
Bar
struct
{
A
string
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
checks
:=
0
err
:=
c
.
RegisterConversionFunc
(
...
...
@@ -653,7 +715,7 @@ func TestConverter_flags(t *testing.T) {
},
}
f
:=
fuzz
.
New
()
.
NilChance
(
.5
)
.
NumElements
(
0
,
100
)
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
c
.
Debug
=
testLogger
(
t
)
for
i
,
item
:=
range
table
{
...
...
@@ -691,7 +753,7 @@ func TestConverter_FieldRename(t *testing.T) {
NameMeta
}
c
:=
NewConverter
()
c
:=
NewConverter
(
DefaultNameFunc
)
err
:=
c
.
SetStructFieldCopy
(
WeirdMeta
{},
"WeirdMeta"
,
TypeMeta
{},
"TypeMeta"
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error %v"
,
err
)
...
...
@@ -764,131 +826,22 @@ func TestConverter_FieldRename(t *testing.T) {
}
}
func
TestMetaValues
(
t
*
testing
.
T
)
{
type
InternalSimple
struct
{
APIVersion
string
`json:"apiVersion,omitempty"`
Kind
string
`json:"kind,omitempty"`
TestString
string
`json:"testString"`
}
type
ExternalSimple
struct
{
APIVersion
string
`json:"apiVersion,omitempty"`
Kind
string
`json:"kind,omitempty"`
TestString
string
`json:"testString"`
}
internalGV
:=
unversioned
.
GroupVersion
{
Group
:
"test.group"
,
Version
:
"__internal"
}
externalGV
:=
unversioned
.
GroupVersion
{
Group
:
"test.group"
,
Version
:
"externalVersion"
}
s
:=
NewScheme
()
s
.
AddKnownTypeWithName
(
internalGV
.
WithKind
(
"Simple"
),
&
InternalSimple
{})
s
.
AddKnownTypeWithName
(
externalGV
.
WithKind
(
"Simple"
),
&
ExternalSimple
{})
internalToExternalCalls
:=
0
externalToInternalCalls
:=
0
// Register functions to verify that scope.Meta() gets set correctly.
err
:=
s
.
AddConversionFuncs
(
func
(
in
*
InternalSimple
,
out
*
ExternalSimple
,
scope
Scope
)
error
{
t
.
Logf
(
"internal -> external"
)
if
e
,
a
:=
internalGV
.
String
(),
scope
.
Meta
()
.
SrcVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
if
e
,
a
:=
externalGV
.
String
(),
scope
.
Meta
()
.
DestVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
scope
.
Convert
(
&
in
.
TestString
,
&
out
.
TestString
,
0
)
internalToExternalCalls
++
return
nil
},
func
(
in
*
ExternalSimple
,
out
*
InternalSimple
,
scope
Scope
)
error
{
t
.
Logf
(
"external -> internal"
)
if
e
,
a
:=
externalGV
.
String
(),
scope
.
Meta
()
.
SrcVersion
;
e
!=
a
{
t
.
Errorf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
if
e
,
a
:=
internalGV
.
String
(),
scope
.
Meta
()
.
DestVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
scope
.
Convert
(
&
in
.
TestString
,
&
out
.
TestString
,
0
)
externalToInternalCalls
++
return
nil
},
)
func
objDiff
(
a
,
b
interface
{})
string
{
ab
,
err
:=
json
.
Marshal
(
a
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
simple
:=
&
InternalSimple
{
TestString
:
"foo"
,
panic
(
"a"
)
}
s
.
Log
(
t
)
out
,
err
:=
s
.
ConvertToVersion
(
simple
,
externalGV
.
String
())
bb
,
err
:=
json
.
Marshal
(
b
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
panic
(
"b"
)
}
return
util
.
StringDiff
(
string
(
ab
),
string
(
bb
))
internal
,
err
:=
s
.
ConvertToVersion
(
out
,
internalGV
.
String
())
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
e
,
a
:=
simple
,
internal
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected:
\n
%#v,
\n
Got:
\n
%#v"
,
e
,
a
)
}
if
e
,
a
:=
1
,
internalToExternalCalls
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
if
e
,
a
:=
1
,
externalToInternalCalls
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
func
TestMetaValuesUnregisteredConvert
(
t
*
testing
.
T
)
{
type
InternalSimple
struct
{
Version
string
`json:"apiVersion,omitempty"`
Kind
string
`json:"kind,omitempty"`
TestString
string
`json:"testString"`
}
type
ExternalSimple
struct
{
Version
string
`json:"apiVersion,omitempty"`
Kind
string
`json:"kind,omitempty"`
TestString
string
`json:"testString"`
}
s
:=
NewScheme
()
// We deliberately don't register the types.
internalToExternalCalls
:=
0
// Register functions to verify that scope.Meta() gets set correctly.
err
:=
s
.
AddConversionFuncs
(
func
(
in
*
InternalSimple
,
out
*
ExternalSimple
,
scope
Scope
)
error
{
if
e
,
a
:=
"unknown/unknown"
,
scope
.
Meta
()
.
SrcVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
if
e
,
a
:=
"unknown/unknown"
,
scope
.
Meta
()
.
DestVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
scope
.
Convert
(
&
in
.
TestString
,
&
out
.
TestString
,
0
)
internalToExternalCalls
++
return
nil
},
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
simple
:=
&
InternalSimple
{
TestString
:
"foo"
}
external
:=
&
ExternalSimple
{}
err
=
s
.
Convert
(
simple
,
external
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error: %v"
,
err
)
}
if
e
,
a
:=
simple
.
TestString
,
external
.
TestString
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
// Verify that our conversion handler got called.
if
e
,
a
:=
1
,
internalToExternalCalls
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
// An alternate diff attempt, in case json isn't showing you
// the difference. (reflect.DeepEqual makes a distinction between
// nil and empty slices, for example.)
//return util.StringDiff(
// fmt.Sprintf("%#v", a),
// fmt.Sprintf("%#v", b),
//)
}
pkg/conversion/error.go
deleted
100644 → 0
View file @
0e889a1a
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
conversion
import
(
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
)
type
notRegisteredErr
struct
{
gvk
unversioned
.
GroupVersionKind
t
reflect
.
Type
}
// NewNotRegisteredErr is exposed for testing.
func
NewNotRegisteredErr
(
gvk
unversioned
.
GroupVersionKind
,
t
reflect
.
Type
)
error
{
return
&
notRegisteredErr
{
gvk
:
gvk
,
t
:
t
}
}
func
(
k
*
notRegisteredErr
)
Error
()
string
{
if
k
.
t
!=
nil
{
return
fmt
.
Sprintf
(
"no kind is registered for the type %v"
,
k
.
t
)
}
if
len
(
k
.
gvk
.
Kind
)
==
0
{
return
fmt
.
Sprintf
(
"no version %q has been registered"
,
k
.
gvk
.
GroupVersion
())
}
if
len
(
k
.
gvk
.
Version
)
==
0
{
return
fmt
.
Sprintf
(
"no kind %q is registered for the default version of group %q"
,
k
.
gvk
.
Kind
,
k
.
gvk
.
Group
)
}
return
fmt
.
Sprintf
(
"no kind %q is registered for version %q"
,
k
.
gvk
.
Kind
,
k
.
gvk
.
GroupVersion
())
}
// IsNotRegisteredError returns true if the error indicates the provided
// object or input data is not registered.
func
IsNotRegisteredError
(
err
error
)
bool
{
if
err
==
nil
{
return
false
}
_
,
ok
:=
err
.
(
*
notRegisteredErr
)
return
ok
}
type
missingKindErr
struct
{
data
string
}
func
NewMissingKindErr
(
data
string
)
error
{
return
&
missingKindErr
{
data
}
}
func
(
k
*
missingKindErr
)
Error
()
string
{
return
fmt
.
Sprintf
(
"Object 'Kind' is missing in '%s'"
,
k
.
data
)
}
func
IsMissingKind
(
err
error
)
bool
{
if
err
==
nil
{
return
false
}
_
,
ok
:=
err
.
(
*
missingKindErr
)
return
ok
}
type
missingVersionErr
struct
{
data
string
}
func
NewMissingVersionErr
(
data
string
)
error
{
return
&
missingVersionErr
{
data
}
}
func
(
k
*
missingVersionErr
)
Error
()
string
{
return
fmt
.
Sprintf
(
"Object 'apiVersion' is missing in '%s'"
,
k
.
data
)
}
func
IsMissingVersion
(
err
error
)
bool
{
if
err
==
nil
{
return
false
}
_
,
ok
:=
err
.
(
*
missingVersionErr
)
return
ok
}
pkg/conversion/scheme.go
deleted
100644 → 0
View file @
0e889a1a
/*
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
,
&
notRegisteredErr
{
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
,
&
notRegisteredErr
{
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
()
}
pkg/conversion/scheme_test.go
deleted
100644 → 0
View file @
0e889a1a
/*
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"
)
}
}
pkg/kubectl/resource_printer.go
View file @
60b66665
...
...
@@ -36,7 +36,6 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors
"k8s.io/kubernetes/pkg/util/errors"
...
...
@@ -181,7 +180,7 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
continue
}
converted
,
err
:=
p
.
convertor
.
ConvertToVersion
(
obj
,
version
.
String
())
if
conversion
.
IsNotRegisteredError
(
err
)
{
if
runtime
.
IsNotRegisteredError
(
err
)
{
continue
}
if
err
!=
nil
{
...
...
pkg/runtime/conversion_generator.go
View file @
60b66665
...
...
@@ -27,7 +27,6 @@ import (
"strings"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/util/sets"
)
...
...
@@ -42,7 +41,7 @@ type ConversionGenerator interface {
AssumePrivateConversions
()
}
func
NewConversionGenerator
(
scheme
*
conversion
.
Scheme
,
targetPkg
string
)
ConversionGenerator
{
func
NewConversionGenerator
(
scheme
*
Scheme
,
targetPkg
string
)
ConversionGenerator
{
g
:=
&
conversionGenerator
{
scheme
:
scheme
,
...
...
@@ -66,7 +65,7 @@ func NewConversionGenerator(scheme *conversion.Scheme, targetPkg string) Convers
var
complexTypes
[]
reflect
.
Kind
=
[]
reflect
.
Kind
{
reflect
.
Map
,
reflect
.
Ptr
,
reflect
.
Slice
,
reflect
.
Interface
,
reflect
.
Struct
}
type
conversionGenerator
struct
{
scheme
*
conversion
.
Scheme
scheme
*
Scheme
nameFormat
string
generatedNamePrefix
string
...
...
@@ -105,7 +104,7 @@ func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVer
internalVersion
:=
gv
internalVersion
.
Version
=
APIVersionInternal
internalObj
,
err
:=
g
.
scheme
.
New
Object
(
internalVersion
.
WithKind
(
kind
))
internalObj
,
err
:=
g
.
scheme
.
New
(
internalVersion
.
WithKind
(
kind
))
if
err
!=
nil
{
return
fmt
.
Errorf
(
"cannot create an object of type %v in internal version"
,
kind
)
}
...
...
pkg/runtime/deep_copy_generator.go
View file @
60b66665
...
...
@@ -24,7 +24,6 @@ import (
"sort"
"strings"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/util/sets"
)
...
...
@@ -69,7 +68,7 @@ type DeepCopyGenerator interface {
OverwritePackage
(
pkg
,
overwrite
string
)
}
func
NewDeepCopyGenerator
(
scheme
*
conversion
.
Scheme
,
targetPkg
string
,
include
sets
.
String
)
DeepCopyGenerator
{
func
NewDeepCopyGenerator
(
scheme
*
Scheme
,
targetPkg
string
,
include
sets
.
String
)
DeepCopyGenerator
{
g
:=
&
deepCopyGenerator
{
scheme
:
scheme
,
targetPkg
:
targetPkg
,
...
...
@@ -91,7 +90,7 @@ type pkgPathNamePair struct {
}
type
deepCopyGenerator
struct
{
scheme
*
conversion
.
Scheme
scheme
*
Scheme
targetPkg
string
copyables
map
[
reflect
.
Type
]
bool
// map of package names to shortname
...
...
pkg/runtime/error.go
View file @
60b66665
...
...
@@ -17,31 +17,86 @@ limitations under the License.
package
runtime
import
(
"k8s.io/kubernetes/pkg/conversion"
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api/unversioned"
)
type
notRegisteredErr
struct
{
gvk
unversioned
.
GroupVersionKind
t
reflect
.
Type
}
// NewNotRegisteredErr is exposed for testing.
func
NewNotRegisteredErr
(
gvk
unversioned
.
GroupVersionKind
,
t
reflect
.
Type
)
error
{
return
&
notRegisteredErr
{
gvk
:
gvk
,
t
:
t
}
}
func
(
k
*
notRegisteredErr
)
Error
()
string
{
if
k
.
t
!=
nil
{
return
fmt
.
Sprintf
(
"no kind is registered for the type %v"
,
k
.
t
)
}
if
len
(
k
.
gvk
.
Kind
)
==
0
{
return
fmt
.
Sprintf
(
"no version %q has been registered"
,
k
.
gvk
.
GroupVersion
())
}
if
len
(
k
.
gvk
.
Version
)
==
0
{
return
fmt
.
Sprintf
(
"no kind %q is registered for the default version of group %q"
,
k
.
gvk
.
Kind
,
k
.
gvk
.
Group
)
}
return
fmt
.
Sprintf
(
"no kind %q is registered for version %q"
,
k
.
gvk
.
Kind
,
k
.
gvk
.
GroupVersion
())
}
// IsNotRegisteredError returns true if the error indicates the provided
// object or input data is not registered.
func
IsNotRegisteredError
(
err
error
)
bool
{
return
conversion
.
IsNotRegisteredError
(
err
)
if
err
==
nil
{
return
false
}
_
,
ok
:=
err
.
(
*
notRegisteredErr
)
return
ok
}
type
missingKindErr
struct
{
data
string
}
func
NewMissingKindErr
(
data
string
)
error
{
return
&
missingKindErr
{
data
}
}
func
(
k
*
missingKindErr
)
Error
()
string
{
return
fmt
.
Sprintf
(
"Object 'Kind' is missing in '%s'"
,
k
.
data
)
}
// IsMissingKind returns true if the error indicates that the provided object
// is missing a 'Kind' field.
func
IsMissingKind
(
err
error
)
bool
{
return
conversion
.
IsMissingKind
(
err
)
if
err
==
nil
{
return
false
}
_
,
ok
:=
err
.
(
*
missingKindErr
)
return
ok
}
type
missingVersionErr
struct
{
data
string
}
// IsMissingVersion returns true if the error indicates that the provided object
// is missing a 'Versioj' field.
func
IsMissingVersion
(
err
error
)
bool
{
return
conversion
.
IsMissingVersion
(
err
)
func
NewMissingVersionErr
(
data
string
)
error
{
return
&
missingVersionErr
{
data
}
}
func
NewMissingKindErr
(
data
string
)
error
{
return
conversion
.
NewMissingKindErr
(
data
)
func
(
k
*
missingVersionErr
)
Error
()
string
{
return
fmt
.
Sprintf
(
"Object 'apiVersion' is missing in '%s'"
,
k
.
data
)
}
func
NewMissingVersionErr
(
data
string
)
error
{
return
conversion
.
NewMissingVersionErr
(
data
)
func
IsMissingVersion
(
err
error
)
bool
{
if
err
==
nil
{
return
false
}
_
,
ok
:=
err
.
(
*
missingVersionErr
)
return
ok
}
pkg/runtime/interfaces.go
View file @
60b66665
...
...
@@ -24,8 +24,10 @@ import (
)
const
(
APIVersionInternal
=
"__internal"
APIVersionUnversioned
=
"__unversioned"
// APIVersionInternal may be used if you are registering a type that should not
// be considered stable or serialized - it is a convention only and has no
// special behavior in this package.
APIVersionInternal
=
"__internal"
)
// Typer retrieves information about an object's group, version, and kind.
...
...
pkg/runtime/scheme.go
View file @
60b66665
...
...
@@ -25,49 +25,117 @@ import (
"k8s.io/kubernetes/pkg/conversion"
)
// Scheme defines methods for serializing and deserializing API objects. It
// is an adaptation of conversion's Scheme for our API objects.
// Scheme defines methods for serializing and deserializing API objects, a type
// registry for converting group, version, and kind information to and from Go
// schemas, and mappings between Go schemas of different versions. A scheme is the
// foundation for a versioned API and versioned configuration over time.
//
// In a Scheme, a Type is a particular Go struct, a Version is a point-in-time
// identifier for a particular representation of that Type (typically backwards
// compatible), a Kind is the unique name for that Type within the Version, and a
// Group identifies a set of Versions, Kinds, and Types that evolve over time. An
// Unversioned Type is one that is not yet formally bound to a type and is promised
// to be backwards compatible (effectively a "v1" of a Type that does not expect
// to break in the future).
//
// Schemes are not expected to change at runtime and are only threadsafe after
// registration is complete.
type
Scheme
struct
{
raw
*
conversion
.
Scheme
// versionMap allows one to figure out the go type of an object with
// the given version and name.
gvkToType
map
[
unversioned
.
GroupVersionKind
]
reflect
.
Type
// typeToGroupVersion allows one to find metadata for a given go object.
// The reflect.Type we index by should *not* be a pointer.
typeToGVK
map
[
reflect
.
Type
][]
unversioned
.
GroupVersionKind
// unversionedTypes are transformed without conversion in ConvertToVersion.
unversionedTypes
map
[
reflect
.
Type
]
unversioned
.
GroupVersionKind
// unversionedKinds are the names of kinds that can be created in the context of any group
// or version
// TODO: resolve the status of unversioned types.
unversionedKinds
map
[
string
]
reflect
.
Type
// Map from version and resource to the corresponding func to convert
// resource field labels in that version to internal version.
fieldLabelConversionFuncs
map
[
string
]
map
[
string
]
FieldLabelConversionFunc
}
// Function to convert a field selector to internal representation.
type
FieldLabelConversionFunc
func
(
label
,
value
string
)
(
internalLabel
,
internalValue
string
,
err
error
)
// converter stores all registered conversion functions. It also has
// default coverting behavior.
converter
*
conversion
.
Converter
func
(
self
*
Scheme
)
Raw
()
*
conversion
.
Scheme
{
return
self
.
raw
// cloner stores all registered copy functions. It also has default
// deep copy behavior.
cloner
*
conversion
.
Cloner
}
// fromScope gets the input version, desired output version, and desired Scheme
// from a conversion.Scope.
func
(
self
*
Scheme
)
fromScope
(
s
conversion
.
Scope
)
(
inVersion
,
outVersion
string
,
scheme
*
Scheme
)
{
scheme
=
self
inVersion
=
s
.
Meta
()
.
SrcVersion
outVersion
=
s
.
Meta
()
.
DestVersion
return
inVersion
,
outVersion
,
scheme
}
// Function to convert a field selector to internal representation.
type
FieldLabelConversionFunc
func
(
label
,
value
string
)
(
internalLabel
,
internalValue
string
,
err
error
)
// NewScheme creates a new Scheme. This scheme is pluggable by default.
func
NewScheme
()
*
Scheme
{
s
:=
&
Scheme
{
conversion
.
NewScheme
(),
map
[
string
]
map
[
string
]
FieldLabelConversionFunc
{}}
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
.
raw
.
AddConversionFuncs
(
DefaultStringConversions
...
);
err
!=
nil
{
if
err
:=
s
.
AddConversionFuncs
(
DefaultStringConversions
...
);
err
!=
nil
{
panic
(
err
)
}
if
err
:=
s
.
raw
.
RegisterInputDefaults
(
&
map
[
string
][]
string
{},
JSONKeyMapper
,
conversion
.
AllowDifferentFieldTypeNames
|
conversion
.
IgnoreMissingFields
);
err
!=
nil
{
if
err
:=
s
.
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
{
if
err
:=
s
.
RegisterInputDefaults
(
&
url
.
Values
{},
JSONKeyMapper
,
conversion
.
AllowDifferentFieldTypeNames
|
conversion
.
IgnoreMissingFields
);
err
!=
nil
{
panic
(
err
)
}
return
s
}
// nameFunc returns the name of the type that we wish to use to determine when two types attempt
// a conversion. Defaults to the go name of the type if the type is not registered.
func
(
s
*
Scheme
)
nameFunc
(
t
reflect
.
Type
)
string
{
// find the preferred names for this type
gvks
,
ok
:=
s
.
typeToGVK
[
t
]
if
!
ok
{
return
t
.
Name
()
}
for
_
,
gvk
:=
range
gvks
{
internalGV
:=
gvk
.
GroupVersion
()
internalGV
.
Version
=
"__internal"
// this is hacky and maybe should be passed in
internalGVK
:=
internalGV
.
WithKind
(
gvk
.
Kind
)
if
internalType
,
exists
:=
s
.
gvkToType
[
internalGVK
];
exists
{
return
s
.
typeToGVK
[
internalType
][
0
]
.
Kind
}
}
return
gvks
[
0
]
.
Kind
}
// fromScope gets the input version, desired output version, and desired Scheme
// from a conversion.Scope.
func
(
s
*
Scheme
)
fromScope
(
scope
conversion
.
Scope
)
(
inVersion
,
outVersion
string
,
scheme
*
Scheme
)
{
scheme
=
s
inVersion
=
scope
.
Meta
()
.
SrcVersion
outVersion
=
scope
.
Meta
()
.
DestVersion
return
inVersion
,
outVersion
,
scheme
}
// Converter allows access to the converter for the scheme
func
(
s
*
Scheme
)
Converter
()
*
conversion
.
Converter
{
return
s
.
converter
}
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules.
// Whenever an object of this type is serialized, it is serialized with the provided group version and is not
// converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an
...
...
@@ -75,111 +143,221 @@ func NewScheme() *Scheme {
//
// TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into
// every version with particular schemas. Resolve tihs method at that point.
func
(
s
*
Scheme
)
AddUnversionedTypes
(
gv
unversioned
.
GroupVersion
,
types
...
Object
)
{
interfaces
:=
make
([]
interface
{},
len
(
types
))
for
i
:=
range
types
{
interfaces
[
i
]
=
types
[
i
]
func
(
s
*
Scheme
)
AddUnversionedTypes
(
version
unversioned
.
GroupVersion
,
types
...
Object
)
{
s
.
AddKnownTypes
(
version
,
types
...
)
for
_
,
obj
:=
range
types
{
t
:=
reflect
.
TypeOf
(
obj
)
.
Elem
()
gvk
:=
version
.
WithKind
(
t
.
Name
())
s
.
unversionedTypes
[
t
]
=
gvk
if
_
,
ok
:=
s
.
unversionedKinds
[
gvk
.
Kind
];
ok
{
panic
(
fmt
.
Sprintf
(
"%v has already been registered as unversioned kind %q - kind name must be unique"
,
reflect
.
TypeOf
(
t
),
gvk
.
Kind
))
}
s
.
unversionedKinds
[
gvk
.
Kind
]
=
t
}
s
.
raw
.
AddUnversionedTypes
(
gv
,
interfaces
...
)
}
// AddKnownTypes registers the types of the arguments to the marshaller of the package api.
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'.
// All objects passed to types should be pointers to structs. The name that go reports for
// the struct becomes the "kind" field when encoding. Version may not be empty - use the
// APIVersionInternal constant if you have a type that does not have a formal version.
func
(
s
*
Scheme
)
AddKnownTypes
(
gv
unversioned
.
GroupVersion
,
types
...
Object
)
{
interfaces
:=
make
([]
interface
{},
len
(
types
))
for
i
:=
range
types
{
interfaces
[
i
]
=
types
[
i
]
if
len
(
gv
.
Version
)
==
0
{
panic
(
fmt
.
Sprintf
(
"version is required on all types: %s %v"
,
gv
,
types
[
0
]))
}
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
)
}
s
.
raw
.
AddKnownTypes
(
gv
,
interfaces
...
)
}
// AddIgnoredConversionType declares a particular conversion that should be ignored - during conversion
// this method is not invoked.
func
(
s
*
Scheme
)
AddIgnoredConversionType
(
from
,
to
interface
{})
error
{
return
s
.
raw
.
AddIgnoredConversionType
(
from
,
to
)
}
// AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
// be encoded as. Useful for testing when you don't want to make multiple packages to define
// your structs.
// your structs. Version may not be empty - use the APIVersionInternal constant if you have a
// type that does not have a formal version.
func
(
s
*
Scheme
)
AddKnownTypeWithName
(
gvk
unversioned
.
GroupVersionKind
,
obj
Object
)
{
s
.
raw
.
AddKnownTypeWithName
(
gvk
,
obj
)
t
:=
reflect
.
TypeOf
(
obj
)
if
len
(
gvk
.
Version
)
==
0
{
panic
(
fmt
.
Sprintf
(
"version is required on all types: %s %v"
,
gvk
,
t
))
}
if
t
.
Kind
()
!=
reflect
.
Ptr
{
panic
(
"All types must be pointers to structs."
)
}
t
=
t
.
Elem
()
if
t
.
Kind
()
!=
reflect
.
Struct
{
panic
(
"All types must be pointers to structs."
)
}
s
.
gvkToType
[
gvk
]
=
t
s
.
typeToGVK
[
t
]
=
append
(
s
.
typeToGVK
[
t
],
gvk
)
}
// KnownTypes returns the types known for the given version.
// Return value must be treated as read-only.
func
(
s
*
Scheme
)
KnownTypes
(
gv
unversioned
.
GroupVersion
)
map
[
string
]
reflect
.
Type
{
return
s
.
raw
.
KnownTypes
(
gv
)
types
:=
make
(
map
[
string
]
reflect
.
Type
)
for
gvk
,
t
:=
range
s
.
gvkToType
{
if
gv
!=
gvk
.
GroupVersion
()
{
continue
}
types
[
gvk
.
Kind
]
=
t
}
return
types
}
// ObjectKind returns the default group,version,kind of the given Object.
// ObjectKind returns the group,version,kind of the go object,
// or an error if it's not a pointer or is unregistered.
func
(
s
*
Scheme
)
ObjectKind
(
obj
Object
)
(
unversioned
.
GroupVersionKind
,
error
)
{
return
s
.
raw
.
ObjectKind
(
obj
)
gvks
,
err
:=
s
.
ObjectKinds
(
obj
)
if
err
!=
nil
{
return
unversioned
.
GroupVersionKind
{},
err
}
return
gvks
[
0
],
nil
}
// ObjectKinds returns the all possible group,version,kind of the given Object.
// ObjectKinds returns all possible group,version,kind of the go object,
// or an error if it's not a pointer or is unregistered.
func
(
s
*
Scheme
)
ObjectKinds
(
obj
Object
)
([]
unversioned
.
GroupVersionKind
,
error
)
{
return
s
.
raw
.
ObjectKinds
(
obj
)
v
,
err
:=
conversion
.
EnforcePtr
(
obj
)
if
err
!=
nil
{
return
nil
,
err
}
t
:=
v
.
Type
()
gvks
,
ok
:=
s
.
typeToGVK
[
t
]
if
!
ok
{
return
nil
,
&
notRegisteredErr
{
t
:
t
}
}
return
gvks
,
nil
}
// Recognizes returns true if the scheme is able to handle the provided group,version,kind
// of an object.
func
(
s
*
Scheme
)
Recognizes
(
gvk
unversioned
.
GroupVersionKind
)
bool
{
return
s
.
raw
.
Recognizes
(
gvk
)
_
,
exists
:=
s
.
gvkToType
[
gvk
]
return
exists
}
func
(
s
*
Scheme
)
IsUnversioned
(
obj
Object
)
(
bool
,
bool
)
{
return
s
.
raw
.
IsUnversioned
(
obj
)
v
,
err
:=
conversion
.
EnforcePtr
(
obj
)
if
err
!=
nil
{
return
false
,
false
}
t
:=
v
.
Type
()
if
_
,
ok
:=
s
.
typeToGVK
[
t
];
!
ok
{
return
false
,
false
}
_
,
ok
:=
s
.
unversionedTypes
[
t
]
return
ok
,
true
}
// New returns a new API object of the given version
("" for internal
//
representation) and name, or an error if it hasn't been register
ed.
// New returns a new API object of the given version
and name, or an error if it hasn't
//
been registered. The version and kind fields must be specifi
ed.
func
(
s
*
Scheme
)
New
(
kind
unversioned
.
GroupVersionKind
)
(
Object
,
error
)
{
obj
,
err
:=
s
.
raw
.
NewObject
(
kind
)
if
err
!=
nil
{
return
nil
,
err
if
t
,
exists
:=
s
.
gvkToType
[
kind
];
exists
{
return
reflect
.
New
(
t
)
.
Interface
()
.
(
Object
),
nil
}
return
obj
.
(
Object
),
nil
if
t
,
exists
:=
s
.
unversionedKinds
[
kind
.
Kind
];
exists
{
return
reflect
.
New
(
t
)
.
Interface
()
.
(
Object
),
nil
}
return
nil
,
&
notRegisteredErr
{
gvk
:
kind
}
}
// Log sets a logger on the scheme. For test purposes only
func
(
s
*
Scheme
)
Log
(
l
conversion
.
DebugLogger
)
{
s
.
raw
.
Log
(
l
)
s
.
converter
.
Debug
=
l
}
// AddIgnoredConversionType identifies a pair of types that should be skipped by
// conversion (because the data inside them is explicitly dropped during
// conversion).
func
(
s
*
Scheme
)
AddIgnoredConversionType
(
from
,
to
interface
{})
error
{
return
s
.
converter
.
RegisterIgnoredConversion
(
from
,
to
)
}
// AddConversionFuncs adds a function to the list of conversion functions. The given
// function should know how to convert between two API objects. We deduce how to call
// it from the types of its two parameters; see the comment for
// Converter.RegisterConversionFunction.
// 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
// },
// )
//
// Note that, if you need to copy sub-objects that didn't change, it's safe to call
// Convert() inside your conversionFuncs, as long as you don't start a conversion
// chain that's infinitely recursive.
// (For more detail about conversion functions, see Converter.Register's comment.)
//
// Also note that the default behavior, if you don't add a conversion function, is to
// sanely copy fields that have the same names
. It's OK if the destination type has
//
extra fields, but it must not remove any. So you only need to add a conversion
//
function
for things with changed/removed fields.
// sanely copy fields that have the same names
and same type names. It's OK if the
//
destination type has extra fields, but it must not remove any. So you only need to
//
add conversion functions
for things with changed/removed fields.
func
(
s
*
Scheme
)
AddConversionFuncs
(
conversionFuncs
...
interface
{})
error
{
return
s
.
raw
.
AddConversionFuncs
(
conversionFuncs
...
)
for
_
,
f
:=
range
conversionFuncs
{
if
err
:=
s
.
converter
.
RegisterConversionFunc
(
f
);
err
!=
nil
{
return
err
}
}
return
nil
}
// Similar to AddConversionFuncs, but registers conversion functions that were
// automatically generated.
func
(
s
*
Scheme
)
AddGeneratedConversionFuncs
(
conversionFuncs
...
interface
{})
error
{
return
s
.
raw
.
AddGeneratedConversionFuncs
(
conversionFuncs
...
)
for
_
,
f
:=
range
conversionFuncs
{
if
err
:=
s
.
converter
.
RegisterGeneratedConversionFunc
(
f
);
err
!=
nil
{
return
err
}
}
return
nil
}
// AddDeepCopyFuncs adds a function to the list of deep-copy functions.
// For the expected format of deep-copy function, see the comment for
// Copier.RegisterDeepCopyFunction.
func
(
s
*
Scheme
)
AddDeepCopyFuncs
(
deepCopyFuncs
...
interface
{})
error
{
return
s
.
raw
.
AddDeepCopyFuncs
(
deepCopyFuncs
...
)
for
_
,
f
:=
range
deepCopyFuncs
{
if
err
:=
s
.
cloner
.
RegisterDeepCopyFunc
(
f
);
err
!=
nil
{
return
err
}
}
return
nil
}
// Similar to AddDeepCopyFuncs, but registers deep-copy functions that were
// automatically generated.
func
(
s
*
Scheme
)
AddGeneratedDeepCopyFuncs
(
deepCopyFuncs
...
interface
{})
error
{
return
s
.
raw
.
AddGeneratedDeepCopyFuncs
(
deepCopyFuncs
...
)
for
_
,
f
:=
range
deepCopyFuncs
{
if
err
:=
s
.
cloner
.
RegisterGeneratedDeepCopyFunc
(
f
);
err
!=
nil
{
return
err
}
}
return
nil
}
// AddFieldLabelConversionFunc adds a conversion function to convert field selectors
...
...
@@ -198,19 +376,43 @@ func (s *Scheme) AddFieldLabelConversionFunc(version, kind string, conversionFun
// the comment in conversion.Converter.SetStructFieldCopy for parameter details.
// Call as many times as needed, even on the same fields.
func
(
s
*
Scheme
)
AddStructFieldConversion
(
srcFieldType
interface
{},
srcFieldName
string
,
destFieldType
interface
{},
destFieldName
string
)
error
{
return
s
.
raw
.
AddStructFieldConversion
(
srcFieldType
,
srcFieldName
,
destFieldType
,
destFieldName
)
return
s
.
converter
.
SetStructFieldCopy
(
srcFieldType
,
srcFieldName
,
destFieldType
,
destFieldName
)
}
// RegisterInputDefaults sets the provided field mapping function and field matching
// as the defaults for the provided input type. The fn may be nil, in which case no
// mapping will happen by default. Use this method to register a mechanism for handling
// a specific input type in conversion, such as a map[string]string to structs.
func
(
s
*
Scheme
)
RegisterInputDefaults
(
in
interface
{},
fn
conversion
.
FieldMappingFunc
,
defaultFlags
conversion
.
FieldMatchingFlags
)
error
{
return
s
.
converter
.
RegisterInputDefaults
(
in
,
fn
,
defaultFlags
)
}
// AddDefaultingFuncs adds a function to the list of value-defaulting functions.
// We deduce how to call it from the types of its two parameters; see the
// comment for Converter.RegisterDefaultingFunction.
// AddDefaultingFuncs adds functions to the list of default-value functions.
// Each of the given functions is responsible for applying default values
// when converting an instance of a versioned API object into an internal
// API object. These functions do not need to handle sub-objects. We deduce
// how to call these functions from the types of their two parameters.
//
// s.AddDefaultingFuncs(
// func(obj *v1.Pod) {
// if obj.OptionalField == "" {
// obj.OptionalField = "DefaultValue"
// }
// },
// )
func
(
s
*
Scheme
)
AddDefaultingFuncs
(
defaultingFuncs
...
interface
{})
error
{
return
s
.
raw
.
AddDefaultingFuncs
(
defaultingFuncs
...
)
for
_
,
f
:=
range
defaultingFuncs
{
err
:=
s
.
converter
.
RegisterDefaultingFunc
(
f
)
if
err
!=
nil
{
return
err
}
}
return
nil
}
// Copy does a deep copy of an API object.
func
(
s
*
Scheme
)
Copy
(
src
Object
)
(
Object
,
error
)
{
dst
,
err
:=
s
.
raw
.
DeepCopy
(
src
)
dst
,
err
:=
s
.
DeepCopy
(
src
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -219,25 +421,33 @@ func (s *Scheme) Copy(src Object) (Object, error) {
// Performs a deep copy of the given object.
func
(
s
*
Scheme
)
DeepCopy
(
src
interface
{})
(
interface
{},
error
)
{
return
s
.
raw
.
DeepCopy
(
src
)
}
// WithConversions returns an ObjectConvertor that has the additional conversion functions
// defined in fns. The current scheme is not altered.
func
(
s
*
Scheme
)
WithConversions
(
fns
*
conversion
.
ConversionFuncs
)
ObjectConvertor
{
if
fns
==
nil
{
return
s
}
copied
:=
*
s
copied
.
raw
=
s
.
raw
.
WithConversions
(
*
fns
)
return
&
copied
return
s
.
cloner
.
DeepCopy
(
src
)
}
// Convert will attempt to convert in into out. Both must be pointers.
// For easy testing of conversion functions. Returns an error if the conversion isn't
// possible.
// Convert will attempt to convert in into out. Both must be pointers. For easy
// testing of conversion functions. Returns an error if the conversion isn't
// possible. You can call this with types that haven't been registered (for example,
// a to test conversion of types that are nested within registered types), but in
// that case, the conversion.Scope object passed to your conversion functions won't
// have SrcVersion or DestVersion fields set correctly in Meta().
func
(
s
*
Scheme
)
Convert
(
in
,
out
interface
{})
error
{
return
s
.
raw
.
Convert
(
in
,
out
)
inVersion
:=
unversioned
.
GroupVersion
{
Group
:
"unknown"
,
Version
:
"unknown"
}
outVersion
:=
unversioned
.
GroupVersion
{
Group
:
"unknown"
,
Version
:
"unknown"
}
if
inObj
,
ok
:=
in
.
(
Object
);
ok
{
if
gvk
,
err
:=
s
.
ObjectKind
(
inObj
);
err
==
nil
{
inVersion
=
gvk
.
GroupVersion
()
}
}
if
outObj
,
ok
:=
out
.
(
Object
);
ok
{
if
gvk
,
err
:=
s
.
ObjectKind
(
outObj
);
err
==
nil
{
outVersion
=
gvk
.
GroupVersion
()
}
}
flags
,
meta
:=
s
.
generateConvertMeta
(
inVersion
,
outVersion
,
in
)
if
flags
==
0
{
flags
=
conversion
.
AllowDifferentFieldTypeNames
}
return
s
.
converter
.
Convert
(
in
,
out
,
flags
,
meta
)
}
// Converts the given field label and value for an kind field selector from
...
...
@@ -267,22 +477,60 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error)
case
*
Unknown
,
*
Unstructured
:
old
:=
in
.
GetObjectKind
()
.
GroupVersionKind
()
defer
in
.
GetObjectKind
()
.
SetGroupVersionKind
(
old
)
setTargetVersion
(
in
,
s
.
raw
,
gv
)
setTargetVersion
(
in
,
s
,
gv
)
return
in
,
nil
}
unknown
,
err
:=
s
.
raw
.
ConvertToVersion
(
in
,
outVersion
)
t
:=
reflect
.
TypeOf
(
in
)
if
t
.
Kind
()
!=
reflect
.
Ptr
{
return
nil
,
fmt
.
Errorf
(
"only pointer types may be converted: %v"
,
t
)
}
t
=
t
.
Elem
()
if
t
.
Kind
()
!=
reflect
.
Struct
{
return
nil
,
fmt
.
Errorf
(
"only pointers to struct types may be converted: %v"
,
t
)
}
var
kind
unversioned
.
GroupVersionKind
if
unversionedKind
,
ok
:=
s
.
unversionedTypes
[
t
];
ok
{
kind
=
unversionedKind
}
else
{
kinds
,
ok
:=
s
.
typeToGVK
[
t
]
if
!
ok
||
len
(
kinds
)
==
0
{
return
nil
,
fmt
.
Errorf
(
"%v is not a registered type and cannot be converted into version %q"
,
t
,
outVersion
)
}
kind
=
kinds
[
0
]
}
outKind
:=
gv
.
WithKind
(
kind
.
Kind
)
inKind
,
err
:=
s
.
ObjectKind
(
in
)
if
err
!=
nil
{
return
nil
,
err
}
obj
,
ok
:=
unknown
.
(
Object
)
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"the provided object cannot be converted to a runtime.Object: %#v"
,
unknown
)
out
,
err
:=
s
.
New
(
outKind
)
if
err
!=
nil
{
return
nil
,
err
}
setTargetVersion
(
obj
,
s
.
raw
,
gv
)
return
obj
,
nil
flags
,
meta
:=
s
.
generateConvertMeta
(
inKind
.
GroupVersion
(),
gv
,
in
)
if
err
:=
s
.
converter
.
Convert
(
in
,
out
,
flags
,
meta
);
err
!=
nil
{
return
nil
,
err
}
setTargetVersion
(
out
,
s
,
gv
)
return
out
,
nil
}
// 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
*
conversion
.
Scheme
,
gv
unversioned
.
GroupVersion
)
{
func
setTargetVersion
(
obj
Object
,
raw
*
Scheme
,
gv
unversioned
.
GroupVersion
)
{
if
gv
.
Version
==
APIVersionInternal
{
// internal is a special case
obj
.
GetObjectKind
()
.
SetGroupVersionKind
(
nil
)
...
...
pkg/runtime/scheme_test.go
View file @
60b66665
...
...
@@ -20,6 +20,9 @@ import (
"reflect"
"testing"
"github.com/google/gofuzz"
flag
"github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
...
...
@@ -27,29 +30,16 @@ import (
"k8s.io/kubernetes/pkg/util"
)
type
TypeMeta
struct
{
Kind
string
`json:"kind,omitempty"`
APIVersion
string
`json:"apiVersion,omitempty"`
}
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func
(
obj
*
TypeMeta
)
SetGroupVersionKind
(
gvk
*
unversioned
.
GroupVersionKind
)
{
obj
.
APIVersion
,
obj
.
Kind
=
gvk
.
ToAPIVersionAndKind
()
}
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func
(
obj
*
TypeMeta
)
GroupVersionKind
()
*
unversioned
.
GroupVersionKind
{
return
unversioned
.
FromAPIVersionAndKind
(
obj
.
APIVersion
,
obj
.
Kind
)
}
var
fuzzIters
=
flag
.
Int
(
"fuzz-iters"
,
50
,
"How many fuzzing iterations to do."
)
type
InternalSimple
struct
{
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
runtime
.
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
}
type
ExternalSimple
struct
{
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
runtime
.
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
}
func
(
obj
*
InternalSimple
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
TypeMeta
}
...
...
@@ -135,7 +125,7 @@ func TestScheme(t *testing.T) {
}
// clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
// does not automatically clear TypeMeta anymore).
simple
.
TypeMeta
=
TypeMeta
{
Kind
:
"Simple"
,
APIVersion
:
externalGV
.
String
()}
simple
.
TypeMeta
=
runtime
.
TypeMeta
{
Kind
:
"Simple"
,
APIVersion
:
externalGV
.
String
()}
if
e
,
a
:=
simple
,
obj3
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected:
\n
%#v,
\n
Got:
\n
%#v"
,
e
,
a
)
}
...
...
@@ -188,33 +178,33 @@ func TestBadJSONRejection(t *testing.T) {
}
type
ExtensionA
struct
{
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
runtime
.
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
}
type
ExtensionB
struct
{
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
runtime
.
TypeMeta
`json:",inline"`
TestString
string
`json:"testString"`
}
type
ExternalExtensionType
struct
{
TypeMeta
`json:",inline"`
Extension
runtime
.
RawExtension
`json:"extension"`
runtime
.
TypeMeta
`json:",inline"`
Extension
runtime
.
RawExtension
`json:"extension"`
}
type
InternalExtensionType
struct
{
TypeMeta
`json:",inline"`
Extension
runtime
.
Object
`json:"extension"`
runtime
.
TypeMeta
`json:",inline"`
Extension
runtime
.
Object
`json:"extension"`
}
type
ExternalOptionalExtensionType
struct
{
TypeMeta
`json:",inline"`
Extension
runtime
.
RawExtension
`json:"extension,omitempty"`
runtime
.
TypeMeta
`json:",inline"`
Extension
runtime
.
RawExtension
`json:"extension,omitempty"`
}
type
InternalOptionalExtensionType
struct
{
TypeMeta
`json:",inline"`
Extension
runtime
.
Object
`json:"extension,omitempty"`
runtime
.
TypeMeta
`json:",inline"`
Extension
runtime
.
Object
`json:"extension,omitempty"`
}
func
(
obj
*
ExtensionA
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
TypeMeta
}
...
...
@@ -284,28 +274,28 @@ func TestExtensionMapping(t *testing.T) {
},
&
InternalExtensionType
{
Extension
:
&
runtime
.
Unknown
{
RawJSON
:
[]
byte
(
`{"
kind":"A","apiVersion":"test.group/testExternal
","testString":"foo"}`
),
RawJSON
:
[]
byte
(
`{"
apiVersion":"test.group/testExternal","kind":"A
","testString":"foo"}`
),
},
},
// apiVersion is set in the serialized object for easier consumption by clients
`{"
kind":"ExtensionType","apiVersion":"`
+
externalGV
.
String
()
+
`","extension":{"kind":"A","apiVersion":"test.group/testExternal
","testString":"foo"}}
`{"
apiVersion":"`
+
externalGV
.
String
()
+
`","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A
","testString":"foo"}}
`
,
},
{
&
InternalExtensionType
{
Extension
:
runtime
.
NewEncodable
(
codec
,
&
ExtensionB
{
TestString
:
"bar"
})},
&
InternalExtensionType
{
Extension
:
&
runtime
.
Unknown
{
RawJSON
:
[]
byte
(
`{"
kind":"B","apiVersion":"test.group/testExternal
","testString":"bar"}`
),
RawJSON
:
[]
byte
(
`{"
apiVersion":"test.group/testExternal","kind":"B
","testString":"bar"}`
),
},
},
// apiVersion is set in the serialized object for easier consumption by clients
`{"
kind":"ExtensionType","apiVersion":"`
+
externalGV
.
String
()
+
`","extension":{"kind":"B","apiVersion":"test.group/testExternal
","testString":"bar"}}
`{"
apiVersion":"`
+
externalGV
.
String
()
+
`","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B
","testString":"bar"}}
`
,
},
{
&
InternalExtensionType
{
Extension
:
nil
},
&
InternalExtensionType
{
Extension
:
nil
,
},
`{"
kind":"ExtensionType","apiVersion":"`
+
externalGV
.
String
()
+
`
","extension":null}
`{"
apiVersion":"`
+
externalGV
.
String
()
+
`","kind":"ExtensionType
","extension":null}
`
,
},
}
...
...
@@ -411,7 +401,273 @@ func TestUnversionedTypes(t *testing.T) {
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
string
(
data
)
!=
`{"
kind":"InternalSimple","apiVersion":"test.group/testExternal
","testString":"I'm the same"}`
+
"
\n
"
{
if
string
(
data
)
!=
`{"
apiVersion":"test.group/testExternal","kind":"InternalSimple
","testString":"I'm the same"}`
+
"
\n
"
{
t
.
Errorf
(
"unexpected data: %s"
,
data
)
}
}
// Test a weird version/kind embedding format.
type
MyWeirdCustomEmbeddedVersionKindField
struct
{
ID
string
`json:"ID,omitempty"`
APIVersion
string
`json:"myVersionKey,omitempty"`
ObjectKind
string
`json:"myKindKey,omitempty"`
Z
string
`json:"Z,omitempty"`
Y
uint64
`json:"Y,omitempty"`
}
type
TestType1
struct
{
MyWeirdCustomEmbeddedVersionKindField
`json:",inline"`
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
C
int8
`json:"C,omitempty"`
D
int16
`json:"D,omitempty"`
E
int32
`json:"E,omitempty"`
F
int64
`json:"F,omitempty"`
G
uint
`json:"G,omitempty"`
H
uint8
`json:"H,omitempty"`
I
uint16
`json:"I,omitempty"`
J
uint32
`json:"J,omitempty"`
K
uint64
`json:"K,omitempty"`
L
bool
`json:"L,omitempty"`
M
map
[
string
]
int
`json:"M,omitempty"`
N
map
[
string
]
TestType2
`json:"N,omitempty"`
O
*
TestType2
`json:"O,omitempty"`
P
[]
TestType2
`json:"Q,omitempty"`
}
type
TestType2
struct
{
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
}
type
ExternalTestType2
struct
{
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
}
type
ExternalTestType1
struct
{
MyWeirdCustomEmbeddedVersionKindField
`json:",inline"`
A
string
`json:"A,omitempty"`
B
int
`json:"B,omitempty"`
C
int8
`json:"C,omitempty"`
D
int16
`json:"D,omitempty"`
E
int32
`json:"E,omitempty"`
F
int64
`json:"F,omitempty"`
G
uint
`json:"G,omitempty"`
H
uint8
`json:"H,omitempty"`
I
uint16
`json:"I,omitempty"`
J
uint32
`json:"J,omitempty"`
K
uint64
`json:"K,omitempty"`
L
bool
`json:"L,omitempty"`
M
map
[
string
]
int
`json:"M,omitempty"`
N
map
[
string
]
ExternalTestType2
`json:"N,omitempty"`
O
*
ExternalTestType2
`json:"O,omitempty"`
P
[]
ExternalTestType2
`json:"Q,omitempty"`
}
type
ExternalInternalSame
struct
{
MyWeirdCustomEmbeddedVersionKindField
`json:",inline"`
A
TestType2
`json:"A,omitempty"`
}
func
(
obj
*
MyWeirdCustomEmbeddedVersionKindField
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
obj
}
func
(
obj
*
MyWeirdCustomEmbeddedVersionKindField
)
SetGroupVersionKind
(
gvk
*
unversioned
.
GroupVersionKind
)
{
obj
.
APIVersion
,
obj
.
ObjectKind
=
gvk
.
ToAPIVersionAndKind
()
}
func
(
obj
*
MyWeirdCustomEmbeddedVersionKindField
)
GroupVersionKind
()
*
unversioned
.
GroupVersionKind
{
return
unversioned
.
FromAPIVersionAndKind
(
obj
.
APIVersion
,
obj
.
ObjectKind
)
}
func
(
obj
*
ExternalInternalSame
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
MyWeirdCustomEmbeddedVersionKindField
}
func
(
obj
*
TestType1
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
MyWeirdCustomEmbeddedVersionKindField
}
func
(
obj
*
ExternalTestType1
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
&
obj
.
MyWeirdCustomEmbeddedVersionKindField
}
func
(
obj
*
TestType2
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
unversioned
.
EmptyObjectKind
}
func
(
obj
*
ExternalTestType2
)
GetObjectKind
()
unversioned
.
ObjectKind
{
return
unversioned
.
EmptyObjectKind
}
// TestObjectFuzzer can randomly populate all the above objects.
var
TestObjectFuzzer
=
fuzz
.
New
()
.
NilChance
(
.5
)
.
NumElements
(
1
,
100
)
.
Funcs
(
func
(
j
*
MyWeirdCustomEmbeddedVersionKindField
,
c
fuzz
.
Continue
)
{
// We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their
// APIVersion and Kind must remain blank in memory.
j
.
APIVersion
=
""
j
.
ObjectKind
=
""
j
.
ID
=
c
.
RandString
()
},
)
// Returns a new Scheme set up with the test objects.
func
GetTestScheme
()
*
runtime
.
Scheme
{
internalGV
:=
unversioned
.
GroupVersion
{
Version
:
"__internal"
}
externalGV
:=
unversioned
.
GroupVersion
{
Version
:
"v1"
}
s
:=
runtime
.
NewScheme
()
// Ordinarily, we wouldn't add TestType2, but because this is a test and
// both types are from the same package, we need to get it into the system
// so that converter will match it with ExternalType2.
s
.
AddKnownTypes
(
internalGV
,
&
TestType1
{},
&
TestType2
{},
&
ExternalInternalSame
{})
s
.
AddKnownTypes
(
externalGV
,
&
ExternalInternalSame
{})
s
.
AddKnownTypeWithName
(
externalGV
.
WithKind
(
"TestType1"
),
&
ExternalTestType1
{})
s
.
AddKnownTypeWithName
(
externalGV
.
WithKind
(
"TestType2"
),
&
ExternalTestType2
{})
s
.
AddKnownTypeWithName
(
internalGV
.
WithKind
(
"TestType3"
),
&
TestType1
{})
s
.
AddKnownTypeWithName
(
externalGV
.
WithKind
(
"TestType3"
),
&
ExternalTestType1
{})
return
s
}
func
TestKnownTypes
(
t
*
testing
.
T
)
{
s
:=
GetTestScheme
()
if
len
(
s
.
KnownTypes
(
unversioned
.
GroupVersion
{
Group
:
"group"
,
Version
:
"v2"
}))
!=
0
{
t
.
Errorf
(
"should have no known types for v2"
)
}
types
:=
s
.
KnownTypes
(
unversioned
.
GroupVersion
{
Version
:
"v1"
})
for
_
,
s
:=
range
[]
string
{
"TestType1"
,
"TestType2"
,
"TestType3"
,
"ExternalInternalSame"
}
{
if
_
,
ok
:=
types
[
s
];
!
ok
{
t
.
Errorf
(
"missing type %q"
,
s
)
}
}
}
func
TestConvertToVersion
(
t
*
testing
.
T
)
{
s
:=
GetTestScheme
()
tt
:=
&
TestType1
{
A
:
"I'm not a pointer object"
}
other
,
err
:=
s
.
ConvertToVersion
(
tt
,
"v1"
)
if
err
!=
nil
{
t
.
Fatalf
(
"Failure: %v"
,
err
)
}
converted
,
ok
:=
other
.
(
*
ExternalTestType1
)
if
!
ok
{
t
.
Fatalf
(
"Got wrong type"
)
}
if
tt
.
A
!=
converted
.
A
{
t
.
Fatalf
(
"Failed to convert object correctly: %#v"
,
converted
)
}
}
func
TestMetaValues
(
t
*
testing
.
T
)
{
internalGV
:=
unversioned
.
GroupVersion
{
Group
:
"test.group"
,
Version
:
"__internal"
}
externalGV
:=
unversioned
.
GroupVersion
{
Group
:
"test.group"
,
Version
:
"externalVersion"
}
s
:=
runtime
.
NewScheme
()
s
.
AddKnownTypeWithName
(
internalGV
.
WithKind
(
"Simple"
),
&
InternalSimple
{})
s
.
AddKnownTypeWithName
(
externalGV
.
WithKind
(
"Simple"
),
&
ExternalSimple
{})
internalToExternalCalls
:=
0
externalToInternalCalls
:=
0
// Register functions to verify that scope.Meta() gets set correctly.
err
:=
s
.
AddConversionFuncs
(
func
(
in
*
InternalSimple
,
out
*
ExternalSimple
,
scope
conversion
.
Scope
)
error
{
t
.
Logf
(
"internal -> external"
)
if
e
,
a
:=
internalGV
.
String
(),
scope
.
Meta
()
.
SrcVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
if
e
,
a
:=
externalGV
.
String
(),
scope
.
Meta
()
.
DestVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
scope
.
Convert
(
&
in
.
TestString
,
&
out
.
TestString
,
0
)
internalToExternalCalls
++
return
nil
},
func
(
in
*
ExternalSimple
,
out
*
InternalSimple
,
scope
conversion
.
Scope
)
error
{
t
.
Logf
(
"external -> internal"
)
if
e
,
a
:=
externalGV
.
String
(),
scope
.
Meta
()
.
SrcVersion
;
e
!=
a
{
t
.
Errorf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
if
e
,
a
:=
internalGV
.
String
(),
scope
.
Meta
()
.
DestVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
scope
.
Convert
(
&
in
.
TestString
,
&
out
.
TestString
,
0
)
externalToInternalCalls
++
return
nil
},
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
simple
:=
&
InternalSimple
{
TestString
:
"foo"
,
}
s
.
Log
(
t
)
out
,
err
:=
s
.
ConvertToVersion
(
simple
,
externalGV
.
String
())
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
internal
,
err
:=
s
.
ConvertToVersion
(
out
,
internalGV
.
String
())
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
e
,
a
:=
simple
,
internal
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected:
\n
%#v,
\n
Got:
\n
%#v"
,
e
,
a
)
}
if
e
,
a
:=
1
,
internalToExternalCalls
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
if
e
,
a
:=
1
,
externalToInternalCalls
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
func
TestMetaValuesUnregisteredConvert
(
t
*
testing
.
T
)
{
type
InternalSimple
struct
{
Version
string
`json:"apiVersion,omitempty"`
Kind
string
`json:"kind,omitempty"`
TestString
string
`json:"testString"`
}
type
ExternalSimple
struct
{
Version
string
`json:"apiVersion,omitempty"`
Kind
string
`json:"kind,omitempty"`
TestString
string
`json:"testString"`
}
s
:=
runtime
.
NewScheme
()
// We deliberately don't register the types.
internalToExternalCalls
:=
0
// Register functions to verify that scope.Meta() gets set correctly.
err
:=
s
.
AddConversionFuncs
(
func
(
in
*
InternalSimple
,
out
*
ExternalSimple
,
scope
conversion
.
Scope
)
error
{
if
e
,
a
:=
"unknown/unknown"
,
scope
.
Meta
()
.
SrcVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
if
e
,
a
:=
"unknown/unknown"
,
scope
.
Meta
()
.
DestVersion
;
e
!=
a
{
t
.
Fatalf
(
"Expected '%v', got '%v'"
,
e
,
a
)
}
scope
.
Convert
(
&
in
.
TestString
,
&
out
.
TestString
,
0
)
internalToExternalCalls
++
return
nil
},
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
simple
:=
&
InternalSimple
{
TestString
:
"foo"
}
external
:=
&
ExternalSimple
{}
err
=
s
.
Convert
(
simple
,
external
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error: %v"
,
err
)
}
if
e
,
a
:=
simple
.
TestString
,
external
.
TestString
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
// Verify that our conversion handler got called.
if
e
,
a
:=
1
,
internalToExternalCalls
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
pkg/runtime/serializer/json/json_test.go
View file @
60b66665
...
...
@@ -23,7 +23,6 @@ import (
"testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/serializer/json"
"k8s.io/kubernetes/pkg/util"
...
...
@@ -141,7 +140,7 @@ func TestDecode(t *testing.T) {
{
data
:
[]
byte
(
`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`
),
into
:
&
testDecodable
{},
typer
:
&
mockTyper
{
err
:
conversion
.
NewNotRegisteredErr
(
unversioned
.
GroupVersionKind
{},
nil
)},
typer
:
&
mockTyper
{
err
:
runtime
.
NewNotRegisteredErr
(
unversioned
.
GroupVersionKind
{},
nil
)},
expectedGVK
:
&
unversioned
.
GroupVersionKind
{
Kind
:
"Test"
,
Group
:
"other"
,
Version
:
"blah"
},
expectedObject
:
&
testDecodable
{
Other
:
"test"
,
...
...
pkg/runtime/unstructured.go
View file @
60b66665
...
...
@@ -21,7 +21,6 @@ import (
"io"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/conversion"
)
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
...
...
@@ -52,7 +51,7 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionK
}
if
len
(
unstruct
.
APIVersion
)
==
0
{
return
nil
,
nil
,
conversion
.
NewMissingVersionErr
(
string
(
data
))
return
nil
,
nil
,
NewMissingVersionErr
(
string
(
data
))
}
gv
,
err
:=
unversioned
.
ParseGroupVersion
(
unstruct
.
APIVersion
)
if
err
!=
nil
{
...
...
@@ -60,7 +59,7 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionK
}
gvk
:=
gv
.
WithKind
(
unstruct
.
Kind
)
if
len
(
unstruct
.
Kind
)
==
0
{
return
nil
,
&
gvk
,
conversion
.
NewMissingKindErr
(
string
(
data
))
return
nil
,
&
gvk
,
NewMissingKindErr
(
string
(
data
))
}
unstruct
.
Object
=
m
return
unstruct
,
&
gvk
,
nil
...
...
pkg/
conversion
/unversioned_test.go
→
pkg/
runtime
/unversioned_test.go
View file @
60b66665
...
...
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package
conversion
_test
package
runtime
_test
import
(
"encoding/json"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment