mirror of https://github.com/k3s-io/k3s
Introduce GroupVersioner for capturing desired target version
Convert single GV and lists of GVs into an interface that can handle more complex scenarios (everything internal, nothing supported). Pass the interface down into conversion.pull/6/head
parent
9d2a5fe5e8
commit
12a5eeea17
|
@ -32,7 +32,7 @@ func (fakeConvertor) Convert(in, out interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ unversioned.GroupVersion) (runtime.Object, error) {
|
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ runtime.GroupVersioner) (runtime.Object, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/streaming"
|
"k8s.io/kubernetes/pkg/runtime/serializer/streaming"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
|
||||||
"k8s.io/kubernetes/pkg/util/diff"
|
"k8s.io/kubernetes/pkg/util/diff"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
"k8s.io/kubernetes/pkg/watch"
|
"k8s.io/kubernetes/pkg/watch"
|
||||||
|
@ -181,10 +180,14 @@ func TestSetControllerConversion(t *testing.T) {
|
||||||
t.Fatalf("unexpected encoding error: %v", err)
|
t.Fatalf("unexpected encoding error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder := api.Codecs.UniversalDecoder(*extGroup.GroupVersion(), *defaultGroup.GroupVersion())
|
decoder := api.Codecs.DecoderToVersion(
|
||||||
if err := versioning.EnableCrossGroupDecoding(decoder, extGroup.GroupVersion().Group, defaultGroup.GroupVersion().Group); err != nil {
|
api.Codecs.UniversalDeserializer(),
|
||||||
t.Fatalf("unexpected error while enabling cross-group decoding: %v", err)
|
runtime.NewMultiGroupVersioner(
|
||||||
}
|
*defaultGroup.GroupVersion(),
|
||||||
|
unversioned.GroupKind{Group: defaultGroup.GroupVersion().Group},
|
||||||
|
unversioned.GroupKind{Group: extGroup.GroupVersion().Group},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
t.Logf("rs.v1beta1.extensions -> rc._internal")
|
t.Logf("rs.v1beta1.extensions -> rc._internal")
|
||||||
if err := runtime.DecodeInto(decoder, data, rc); err != nil {
|
if err := runtime.DecodeInto(decoder, data, rc); err != nil {
|
||||||
|
|
|
@ -281,7 +281,7 @@ func (g TestGroup) Codec() runtime.Codec {
|
||||||
if serializer.Serializer == nil {
|
if serializer.Serializer == nil {
|
||||||
return api.Codecs.LegacyCodec(g.externalGroupVersion)
|
return api.Codecs.LegacyCodec(g.externalGroupVersion)
|
||||||
}
|
}
|
||||||
return api.Codecs.CodecForVersions(serializer, api.Codecs.UniversalDeserializer(), []unversioned.GroupVersion{g.externalGroupVersion}, nil)
|
return api.Codecs.CodecForVersions(serializer, api.Codecs.UniversalDeserializer(), unversioned.GroupVersions{g.externalGroupVersion}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NegotiatedSerializer returns the negotiated serializer for the server.
|
// NegotiatedSerializer returns the negotiated serializer for the server.
|
||||||
|
@ -309,7 +309,7 @@ func (g TestGroup) StorageCodec() runtime.Codec {
|
||||||
}
|
}
|
||||||
ds := recognizer.NewDecoder(s, api.Codecs.UniversalDeserializer())
|
ds := recognizer.NewDecoder(s, api.Codecs.UniversalDeserializer())
|
||||||
|
|
||||||
return api.Codecs.CodecForVersions(s, ds, []unversioned.GroupVersion{g.externalGroupVersion}, nil)
|
return api.Codecs.CodecForVersions(s, ds, unversioned.GroupVersions{g.externalGroupVersion}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converter returns the api.Scheme for the API version to test against, as set by the
|
// Converter returns the api.Scheme for the API version to test against, as set by the
|
||||||
|
@ -393,7 +393,7 @@ func (g TestGroup) RESTMapper() meta.RESTMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalGroupVersions returns all external group versions allowed for the server.
|
// ExternalGroupVersions returns all external group versions allowed for the server.
|
||||||
func ExternalGroupVersions() []unversioned.GroupVersion {
|
func ExternalGroupVersions() unversioned.GroupVersions {
|
||||||
versions := []unversioned.GroupVersion{}
|
versions := []unversioned.GroupVersion{}
|
||||||
for _, g := range Groups {
|
for _, g := range Groups {
|
||||||
gv := g.GroupVersion()
|
gv := g.GroupVersion()
|
||||||
|
|
|
@ -179,6 +179,25 @@ func (gv GroupVersion) String() string {
|
||||||
return gv.Version
|
return gv.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
|
||||||
|
// if none of the options match the group. It prefers a match to group and version over just group.
|
||||||
|
// TODO: Move GroupVersion to a package under pkg/runtime, since it's used by scheme.
|
||||||
|
// TODO: Introduce an adapter type between GroupVersion and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
|
||||||
|
// in fewer places.
|
||||||
|
func (gv GroupVersion) KindForGroupVersionKinds(kinds []GroupVersionKind) (target GroupVersionKind, ok bool) {
|
||||||
|
for _, gvk := range kinds {
|
||||||
|
if gvk.Group == gv.Group && gvk.Version == gv.Version {
|
||||||
|
return gvk, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, gvk := range kinds {
|
||||||
|
if gvk.Group == gv.Group {
|
||||||
|
return gv.WithKind(gvk.Kind), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GroupVersionKind{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
|
// ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
|
||||||
// if it cannot parse the string.
|
// if it cannot parse the string.
|
||||||
func ParseGroupVersion(gv string) (GroupVersion, error) {
|
func ParseGroupVersion(gv string) (GroupVersion, error) {
|
||||||
|
@ -241,6 +260,25 @@ func (gv *GroupVersion) UnmarshalText(value []byte) error {
|
||||||
return gv.unmarshal(value)
|
return gv.unmarshal(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupVersions can be used to represent a set of desired group versions.
|
||||||
|
// TODO: Move GroupVersions to a package under pkg/runtime, since it's used by scheme.
|
||||||
|
// TODO: Introduce an adapter type between GroupVersions and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
|
||||||
|
// in fewer places.
|
||||||
|
type GroupVersions []GroupVersion
|
||||||
|
|
||||||
|
// KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
|
||||||
|
// if none of the options match the group.
|
||||||
|
func (gvs GroupVersions) KindForGroupVersionKinds(kinds []GroupVersionKind) (target GroupVersionKind, ok bool) {
|
||||||
|
for _, gv := range gvs {
|
||||||
|
target, ok := gv.KindForGroupVersionKinds(kinds)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return target, true
|
||||||
|
}
|
||||||
|
return GroupVersionKind{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// ToAPIVersionAndKind is a convenience method for satisfying runtime.Object on types that
|
// ToAPIVersionAndKind is a convenience method for satisfying runtime.Object on types that
|
||||||
// do not use TypeMeta.
|
// do not use TypeMeta.
|
||||||
func (gvk *GroupVersionKind) ToAPIVersionAndKind() (string, string) {
|
func (gvk *GroupVersionKind) ToAPIVersionAndKind() (string, string) {
|
||||||
|
|
|
@ -320,7 +320,7 @@ type StripVersionNegotiatedSerializer struct {
|
||||||
runtime.NegotiatedSerializer
|
runtime.NegotiatedSerializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
func (n StripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
serializer, ok := encoder.(runtime.Serializer)
|
serializer, ok := encoder.(runtime.Serializer)
|
||||||
if !ok {
|
if !ok {
|
||||||
// The stripVersionEncoder needs both an encoder and decoder, but is called from a context that doesn't have access to the
|
// The stripVersionEncoder needs both an encoder and decoder, but is called from a context that doesn't have access to the
|
||||||
|
|
|
@ -64,11 +64,11 @@ func (n *fakeNegotiater) StreamingSerializerForMediaType(mediaType string, optio
|
||||||
}, n.streamSerializer != nil
|
}, n.streamSerializer != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
return n.serializer
|
return n.serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||||
return n.serializer
|
return n.serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ func init() {
|
||||||
Scheme,
|
Scheme,
|
||||||
yamlSerializer,
|
yamlSerializer,
|
||||||
yamlSerializer,
|
yamlSerializer,
|
||||||
[]unversioned.GroupVersion{{Version: Version}},
|
unversioned.GroupVersion{Version: Version},
|
||||||
[]unversioned.GroupVersion{{Version: runtime.APIVersionInternal}},
|
runtime.InternalGroupVersioner,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,8 @@ type Meta struct {
|
||||||
// KeyNameMapping is an optional function which may map the listed key (field name)
|
// KeyNameMapping is an optional function which may map the listed key (field name)
|
||||||
// into a source and destination value.
|
// into a source and destination value.
|
||||||
KeyNameMapping FieldMappingFunc
|
KeyNameMapping FieldMappingFunc
|
||||||
|
// Context is an optional field that callers may use to pass info to conversion functions.
|
||||||
|
Context interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// scope contains information about an ongoing conversion.
|
// scope contains information about an ongoing conversion.
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
|
||||||
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
|
|
||||||
|
@ -262,18 +261,25 @@ func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, stor
|
||||||
s = runtime.NewBase64Serializer(s)
|
s = runtime.NewBase64Serializer(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encoder := ns.EncoderForVersion(
|
||||||
|
s,
|
||||||
|
runtime.NewMultiGroupVersioner(
|
||||||
|
storageVersion,
|
||||||
|
unversioned.GroupKind{Group: storageVersion.Group},
|
||||||
|
unversioned.GroupKind{Group: memoryVersion.Group},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
ds := recognizer.NewDecoder(s, ns.UniversalDeserializer())
|
ds := recognizer.NewDecoder(s, ns.UniversalDeserializer())
|
||||||
encoder := ns.EncoderForVersion(s, storageVersion)
|
decoder := ns.DecoderToVersion(
|
||||||
decoder := ns.DecoderToVersion(ds, memoryVersion)
|
ds,
|
||||||
if memoryVersion.Group != storageVersion.Group {
|
runtime.NewMultiGroupVersioner(
|
||||||
// Allow this codec to translate between groups.
|
memoryVersion,
|
||||||
if err := versioning.EnableCrossGroupEncoding(encoder, memoryVersion.Group, storageVersion.Group); err != nil {
|
unversioned.GroupKind{Group: memoryVersion.Group},
|
||||||
return nil, fmt.Errorf("error setting up encoder from %v to %v: %v", memoryVersion, storageVersion, err)
|
unversioned.GroupKind{Group: storageVersion.Group},
|
||||||
}
|
),
|
||||||
if err := versioning.EnableCrossGroupDecoding(decoder, storageVersion.Group, memoryVersion.Group); err != nil {
|
)
|
||||||
return nil, fmt.Errorf("error setting up decoder from %v to %v: %v", storageVersion, memoryVersion, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return runtime.NewCodec(encoder, decoder), nil
|
return runtime.NewCodec(encoder, decoder), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ type thirdPartyObjectConverter struct {
|
||||||
converter runtime.ObjectConvertor
|
converter runtime.ObjectConvertor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion unversioned.GroupVersion) (out runtime.Object, err error) {
|
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
|
||||||
switch in.(type) {
|
switch in.(type) {
|
||||||
// This seems weird, but in this case the ThirdPartyResourceData is really just a wrapper on the raw 3rd party data.
|
// This seems weird, but in this case the ThirdPartyResourceData is really just a wrapper on the raw 3rd party data.
|
||||||
// The actual thing printed/sent to server is the actual raw third party resource data, which only has one version.
|
// The actual thing printed/sent to server is the actual raw third party resource data, which only has one version.
|
||||||
|
@ -234,11 +234,11 @@ func (t *thirdPartyResourceDataCodecFactory) StreamingSerializerForMediaType(med
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
return &thirdPartyResourceDataEncoder{delegate: t.delegate.EncoderForVersion(s, gv), gvk: gv.WithKind(t.kind)}
|
return &thirdPartyResourceDataEncoder{delegate: t.delegate.EncoderForVersion(s, gv), gvk: t.encodeGV.WithKind(t.kind)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||||
return NewDecoder(t.delegate.DecoderToVersion(s, gv), t.kind)
|
return NewDecoder(t.delegate.DecoderToVersion(s, gv), t.kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +517,10 @@ func (t *thirdPartyResourceDataEncoder) Encode(obj runtime.Object, stream io.Wri
|
||||||
listItems[ix] = json.RawMessage(buff.Bytes())
|
listItems[ix] = json.RawMessage(buff.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.gvk.IsEmpty() {
|
||||||
|
return fmt.Errorf("thirdPartyResourceDataEncoder was not given a target version")
|
||||||
|
}
|
||||||
|
|
||||||
encMap := struct {
|
encMap := struct {
|
||||||
Kind string `json:"kind,omitempty"`
|
Kind string `json:"kind,omitempty"`
|
||||||
Items []json.RawMessage `json:"items"`
|
Items []json.RawMessage `json:"items"`
|
||||||
|
|
|
@ -198,3 +198,83 @@ func (s base64Serializer) Decode(data []byte, defaults *unversioned.GroupVersion
|
||||||
}
|
}
|
||||||
return s.Serializer.Decode(out[:n], defaults, into)
|
return s.Serializer.Decode(out[:n], defaults, into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// InternalGroupVersioner will always prefer the internal version for a given group version kind.
|
||||||
|
InternalGroupVersioner GroupVersioner = internalGroupVersioner{}
|
||||||
|
// DisabledGroupVersioner will reject all kinds passed to it.
|
||||||
|
DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type internalGroupVersioner struct{}
|
||||||
|
|
||||||
|
// KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
|
||||||
|
func (internalGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
|
||||||
|
for _, kind := range kinds {
|
||||||
|
if kind.Version == APIVersionInternal {
|
||||||
|
return kind, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, kind := range kinds {
|
||||||
|
return unversioned.GroupVersionKind{Group: kind.Group, Version: APIVersionInternal, Kind: kind.Kind}, true
|
||||||
|
}
|
||||||
|
return unversioned.GroupVersionKind{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type disabledGroupVersioner struct{}
|
||||||
|
|
||||||
|
// KindForGroupVersionKinds returns false for any input.
|
||||||
|
func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
|
||||||
|
return unversioned.GroupVersionKind{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
|
||||||
|
type GroupVersioners []GroupVersioner
|
||||||
|
|
||||||
|
// KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occured.
|
||||||
|
func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
|
||||||
|
for _, gv := range gvs {
|
||||||
|
target, ok := gv.KindForGroupVersionKinds(kinds)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return target, true
|
||||||
|
}
|
||||||
|
return unversioned.GroupVersionKind{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that unversioned.GroupVersion and GroupVersions implement GroupVersioner
|
||||||
|
var _ GroupVersioner = unversioned.GroupVersion{}
|
||||||
|
var _ GroupVersioner = unversioned.GroupVersions{}
|
||||||
|
var _ GroupVersioner = multiGroupVersioner{}
|
||||||
|
|
||||||
|
type multiGroupVersioner struct {
|
||||||
|
target unversioned.GroupVersion
|
||||||
|
acceptedGroupKinds []unversioned.GroupKind
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
|
||||||
|
// Kind may be empty in the provided group kind, in which case any kind will match.
|
||||||
|
func NewMultiGroupVersioner(gv unversioned.GroupVersion, groupKinds ...unversioned.GroupKind) GroupVersioner {
|
||||||
|
if len(groupKinds) == 0 || (len(groupKinds) == 1 && groupKinds[0].Group == gv.Group) {
|
||||||
|
return gv
|
||||||
|
}
|
||||||
|
return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
|
||||||
|
// use the originating kind where possible.
|
||||||
|
func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
|
||||||
|
for _, src := range kinds {
|
||||||
|
for _, kind := range v.acceptedGroupKinds {
|
||||||
|
if kind.Group != src.Group {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(kind.Kind) > 0 && kind.Kind != src.Kind {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return v.target.WithKind(src.Kind), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unversioned.GroupVersionKind{}, false
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ var _ ObjectConvertor = unsafeObjectConvertor{}
|
||||||
|
|
||||||
// ConvertToVersion converts in to the provided outVersion without copying the input first, which
|
// ConvertToVersion converts in to the provided outVersion without copying the input first, which
|
||||||
// is only safe if the output object is not mutated or reused.
|
// is only safe if the output object is not mutated or reused.
|
||||||
func (c unsafeObjectConvertor) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
func (c unsafeObjectConvertor) ConvertToVersion(in Object, outVersion GroupVersioner) (Object, error) {
|
||||||
return c.Scheme.UnsafeConvertToVersion(in, outVersion)
|
return c.Scheme.UnsafeConvertToVersion(in, outVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,23 @@ const (
|
||||||
APIVersionInternal = "__internal"
|
APIVersionInternal = "__internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GroupVersioner refines a set of possible conversion targets into a single option.
|
||||||
|
type GroupVersioner interface {
|
||||||
|
// KindForGroupVersionKinds returns a desired target group version kind for the given input, or returns ok false if no
|
||||||
|
// target is known. In general, if the return target is not in the input list, the caller is expected to invoke
|
||||||
|
// Scheme.New(target) and then perform a conversion between the current Go type and the destination Go type.
|
||||||
|
// Sophisticated implementations may use additional information about the input kinds to pick a destination kind.
|
||||||
|
KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (target unversioned.GroupVersionKind, ok bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoders write objects to a serialized form
|
||||||
type Encoder interface {
|
type Encoder interface {
|
||||||
// Encode writes an object to a stream. Implementations may return errors if the versions are
|
// Encode writes an object to a stream. Implementations may return errors if the versions are
|
||||||
// incompatible, or if no conversion is defined.
|
// incompatible, or if no conversion is defined.
|
||||||
Encode(obj Object, w io.Writer) error
|
Encode(obj Object, w io.Writer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decoders attempt to load an object from data.
|
||||||
type Decoder interface {
|
type Decoder interface {
|
||||||
// Decode attempts to deserialize the provided data using either the innate typing of the scheme or the
|
// Decode attempts to deserialize the provided data using either the innate typing of the scheme or the
|
||||||
// default kind, group, and version provided. It returns a decoded object as well as the kind, group, and
|
// default kind, group, and version provided. It returns a decoded object as well as the kind, group, and
|
||||||
|
@ -117,12 +128,10 @@ type NegotiatedSerializer interface {
|
||||||
|
|
||||||
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
||||||
// serializer are in the provided group version.
|
// serializer are in the provided group version.
|
||||||
// TODO: take multiple group versions
|
EncoderForVersion(serializer Encoder, gv GroupVersioner) Encoder
|
||||||
EncoderForVersion(serializer Encoder, gv unversioned.GroupVersion) Encoder
|
|
||||||
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
||||||
// serializer are in the provided group version by default.
|
// serializer are in the provided group version by default.
|
||||||
// TODO: take multiple group versions
|
DecoderToVersion(serializer Decoder, gv GroupVersioner) Decoder
|
||||||
DecoderToVersion(serializer Decoder, gv unversioned.GroupVersion) Decoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageSerializer is an interface used for obtaining encoders, decoders, and serializers
|
// StorageSerializer is an interface used for obtaining encoders, decoders, and serializers
|
||||||
|
@ -139,19 +148,17 @@ type StorageSerializer interface {
|
||||||
|
|
||||||
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
||||||
// serializer are in the provided group version.
|
// serializer are in the provided group version.
|
||||||
// TODO: take multiple group versions
|
EncoderForVersion(serializer Encoder, gv GroupVersioner) Encoder
|
||||||
EncoderForVersion(serializer Encoder, gv unversioned.GroupVersion) Encoder
|
|
||||||
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
||||||
// serializer are in the provided group version by default.
|
// serializer are in the provided group version by default.
|
||||||
// TODO: take multiple group versions
|
DecoderToVersion(serializer Decoder, gv GroupVersioner) Decoder
|
||||||
DecoderToVersion(serializer Decoder, gv unversioned.GroupVersion) Decoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Non-codec interfaces
|
// Non-codec interfaces
|
||||||
|
|
||||||
type ObjectVersioner interface {
|
type ObjectVersioner interface {
|
||||||
ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (out Object, err error)
|
ConvertToVersion(in Object, gv GroupVersioner) (out Object, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectConvertor converts an object to a different version.
|
// ObjectConvertor converts an object to a different version.
|
||||||
|
@ -161,7 +168,7 @@ type ObjectConvertor interface {
|
||||||
Convert(in, out interface{}) error
|
Convert(in, out interface{}) error
|
||||||
// ConvertToVersion takes the provided object and converts it the provided version. This
|
// ConvertToVersion takes the provided object and converts it the provided version. This
|
||||||
// method does not guarantee that the in object is not mutated.
|
// method does not guarantee that the in object is not mutated.
|
||||||
ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (out Object, err error)
|
ConvertToVersion(in Object, gv GroupVersioner) (out Object, err error)
|
||||||
ConvertFieldLabel(version, kind, label, value string) (string, string, error)
|
ConvertFieldLabel(version, kind, label, value string) (string, string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -237,7 +237,7 @@ func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, bool,
|
||||||
|
|
||||||
gvks, ok := s.typeToGVK[t]
|
gvks, ok := s.typeToGVK[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false, ¬RegisteredErr{t: t}
|
return nil, false, NewNotRegisteredErr(unversioned.GroupVersionKind{}, t)
|
||||||
}
|
}
|
||||||
_, unversionedType := s.unversionedTypes[t]
|
_, unversionedType := s.unversionedTypes[t]
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
||||||
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
||||||
return reflect.New(t).Interface().(Object), nil
|
return reflect.New(t).Interface().(Object), nil
|
||||||
}
|
}
|
||||||
return nil, ¬RegisteredErr{gvk: kind}
|
return nil, NewNotRegisteredErr(kind, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
|
// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
|
||||||
|
@ -438,23 +438,13 @@ func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) {
|
||||||
// Convert will attempt to convert in into out. Both must be pointers. For easy
|
// 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
|
// 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,
|
// 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
|
// a to test conversion of types that are nested within registered types). The
|
||||||
// that case, the conversion.Scope object passed to your conversion functions won't
|
// context interface is passed to the convertor.
|
||||||
// have SrcVersion or DestVersion fields set correctly in Meta().
|
// TODO: identify whether context should be hidden, or behind a formal context/scope
|
||||||
func (s *Scheme) Convert(in, out interface{}) error {
|
// interface
|
||||||
inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
|
||||||
outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
flags, meta := s.generateConvertMeta(in)
|
||||||
if inObj, ok := in.(Object); ok {
|
meta.Context = context
|
||||||
if gvks, _, err := s.ObjectKinds(inObj); err == nil {
|
|
||||||
inVersion = gvks[0].GroupVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if outObj, ok := out.(Object); ok {
|
|
||||||
if gvks, _, err := s.ObjectKinds(outObj); err == nil {
|
|
||||||
outVersion = gvks[0].GroupVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flags, meta := s.generateConvertMeta(inVersion, outVersion, in)
|
|
||||||
if flags == 0 {
|
if flags == 0 {
|
||||||
flags = conversion.AllowDifferentFieldTypeNames
|
flags = conversion.AllowDifferentFieldTypeNames
|
||||||
}
|
}
|
||||||
|
@ -478,73 +468,20 @@ func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string,
|
||||||
// version within this scheme. Will return an error if the provided version does not
|
// 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). Will also
|
// contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also
|
||||||
// return an error if the conversion does not result in a valid Object being
|
// return an error if the conversion does not result in a valid Object being
|
||||||
// returned. The serializer handles loading/serializing nested objects.
|
// returned. Passes target down to the conversion methods as the Context on the scope.
|
||||||
func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||||
switch in.(type) {
|
return s.convertToVersion(true, in, target)
|
||||||
case *Unknown, *Unstructured, *UnstructuredList:
|
|
||||||
old := in.GetObjectKind().GroupVersionKind()
|
|
||||||
defer in.GetObjectKind().SetGroupVersionKind(old)
|
|
||||||
setTargetVersion(in, s, outVersion)
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
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 := outVersion.WithKind(kind.Kind)
|
|
||||||
|
|
||||||
inKinds, _, err := s.ObjectKinds(in)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := s.New(outKind)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
flags, meta := s.generateConvertMeta(inKinds[0].GroupVersion(), outVersion, in)
|
|
||||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
setTargetVersion(out, s, outVersion)
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnsafeConvertToVersion will convert in to the provided outVersion if such a conversion is possible,
|
// UnsafeConvertToVersion will convert in to the provided target if such a conversion is possible,
|
||||||
// but does not guarantee the output object does not share fields with the input object. It attempts to be as
|
// but does not guarantee the output object does not share fields with the input object. It attempts to be as
|
||||||
// efficient as possible when doing conversion.
|
// efficient as possible when doing conversion.
|
||||||
func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||||
switch t := in.(type) {
|
return s.convertToVersion(false, in, target)
|
||||||
case *Unknown:
|
}
|
||||||
t.APIVersion = outVersion.String()
|
|
||||||
return t, nil
|
|
||||||
case *Unstructured:
|
|
||||||
t.SetAPIVersion(outVersion.String())
|
|
||||||
return t, nil
|
|
||||||
case *UnstructuredList:
|
|
||||||
t.SetAPIVersion(outVersion.String())
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// convertToVersion handles conversion with an optional copy.
|
||||||
|
func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error) {
|
||||||
// determine the incoming kinds with as few allocations as possible.
|
// determine the incoming kinds with as few allocations as possible.
|
||||||
t := reflect.TypeOf(in)
|
t := reflect.TypeOf(in)
|
||||||
if t.Kind() != reflect.Ptr {
|
if t.Kind() != reflect.Ptr {
|
||||||
|
@ -556,64 +493,69 @@ func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupV
|
||||||
}
|
}
|
||||||
kinds, ok := s.typeToGVK[t]
|
kinds, ok := s.typeToGVK[t]
|
||||||
if !ok || len(kinds) == 0 {
|
if !ok || len(kinds) == 0 {
|
||||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion)
|
return nil, NewNotRegisteredErr(unversioned.GroupVersionKind{}, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the Go type is also registered to the destination kind, no conversion is necessary
|
gvk, ok := target.KindForGroupVersionKinds(kinds)
|
||||||
for i := range kinds {
|
if !ok {
|
||||||
if kinds[i].Version == outVersion.Version && kinds[i].Group == outVersion.Group {
|
// TODO: should this be a typed error?
|
||||||
setTargetKind(in, kinds[i])
|
return nil, fmt.Errorf("%v is not suitable for converting to %q", t, target)
|
||||||
return in, nil
|
}
|
||||||
|
|
||||||
|
// target wants to use the existing type, set kind and return (no conversion necessary)
|
||||||
|
for _, kind := range kinds {
|
||||||
|
if gvk == kind {
|
||||||
|
return copyAndSetTargetKind(copy, s, in, gvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type is unversioned, no conversion necessary
|
// type is unversioned, no conversion necessary
|
||||||
// it should be possible to avoid this allocation
|
|
||||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||||
kind := unversionedKind
|
if gvk, ok := target.KindForGroupVersionKinds([]unversioned.GroupVersionKind{unversionedKind}); ok {
|
||||||
outKind := outVersion.WithKind(kind.Kind)
|
return copyAndSetTargetKind(copy, s, in, gvk)
|
||||||
setTargetKind(in, outKind)
|
}
|
||||||
return in, nil
|
return copyAndSetTargetKind(copy, s, in, unversionedKind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate a new object as the target using the target kind
|
out, err := s.New(gvk)
|
||||||
// TODO: this should look in the target group version and find the first kind that matches, rather than the
|
|
||||||
// first kind registered in typeToGVK
|
|
||||||
kind := kinds[0]
|
|
||||||
kind.Version = outVersion.Version
|
|
||||||
kind.Group = outVersion.Group
|
|
||||||
out, err := s.New(kind)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: try to avoid the allocations here - in fast paths we are not likely to need these flags or meta
|
if copy {
|
||||||
flags, meta := s.converter.DefaultMeta(t)
|
copied, err := s.Copy(in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
in = copied
|
||||||
|
}
|
||||||
|
|
||||||
|
flags, meta := s.generateConvertMeta(in)
|
||||||
|
meta.Context = target
|
||||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
setTargetKind(out, kind)
|
setTargetKind(out, gvk)
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||||
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
func (s *Scheme) generateConvertMeta(in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||||
return s.converter.DefaultMeta(reflect.TypeOf(in))
|
return s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||||
}
|
}
|
||||||
|
|
||||||
// setTargetVersion is deprecated and should be replaced by use of setTargetKind
|
// copyAndSetTargetKind performs a conditional copy before returning the object, or an error if copy was not successful.
|
||||||
func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
func copyAndSetTargetKind(copy bool, copier ObjectCopier, obj Object, kind unversioned.GroupVersionKind) (Object, error) {
|
||||||
if gv.Version == APIVersionInternal {
|
if copy {
|
||||||
// internal is a special case
|
copied, err := copier.Copy(obj)
|
||||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
if gvks, _, _ := raw.ObjectKinds(obj); len(gvks) > 0 {
|
obj = copied
|
||||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvks[0].Kind})
|
|
||||||
} else {
|
|
||||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version})
|
|
||||||
}
|
}
|
||||||
|
setTargetKind(obj, kind)
|
||||||
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
|
// setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
|
||||||
|
|
|
@ -18,6 +18,7 @@ package runtime_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/gofuzz"
|
"github.com/google/gofuzz"
|
||||||
|
@ -460,6 +461,16 @@ type ExternalInternalSame struct {
|
||||||
A TestType2 `json:"A,omitempty"`
|
A TestType2 `json:"A,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UnversionedType struct {
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||||
|
A string `json:"A,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnknownType struct {
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||||
|
A string `json:"A,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
|
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj }
|
||||||
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
|
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
|
||||||
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
|
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
|
||||||
|
@ -500,6 +511,8 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
|
||||||
func GetTestScheme() *runtime.Scheme {
|
func GetTestScheme() *runtime.Scheme {
|
||||||
internalGV := unversioned.GroupVersion{Version: "__internal"}
|
internalGV := unversioned.GroupVersion{Version: "__internal"}
|
||||||
externalGV := unversioned.GroupVersion{Version: "v1"}
|
externalGV := unversioned.GroupVersion{Version: "v1"}
|
||||||
|
alternateExternalGV := unversioned.GroupVersion{Group: "custom", Version: "v1"}
|
||||||
|
differentExternalGV := unversioned.GroupVersion{Group: "other", Version: "v2"}
|
||||||
|
|
||||||
s := runtime.NewScheme()
|
s := runtime.NewScheme()
|
||||||
// Ordinarily, we wouldn't add TestType2, but because this is a test and
|
// Ordinarily, we wouldn't add TestType2, but because this is a test and
|
||||||
|
@ -511,6 +524,11 @@ func GetTestScheme() *runtime.Scheme {
|
||||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
|
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
|
||||||
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
|
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
|
||||||
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
|
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
|
||||||
|
s.AddKnownTypeWithName(externalGV.WithKind("TestType4"), &ExternalTestType1{})
|
||||||
|
s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType3"), &ExternalTestType1{})
|
||||||
|
s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType5"), &ExternalTestType1{})
|
||||||
|
s.AddKnownTypeWithName(differentExternalGV.WithKind("TestType1"), &ExternalTestType1{})
|
||||||
|
s.AddUnversionedTypes(externalGV, &UnversionedType{})
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +546,7 @@ func TestKnownTypes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertToVersion(t *testing.T) {
|
func TestConvertToVersionBasic(t *testing.T) {
|
||||||
s := GetTestScheme()
|
s := GetTestScheme()
|
||||||
tt := &TestType1{A: "I'm not a pointer object"}
|
tt := &TestType1{A: "I'm not a pointer object"}
|
||||||
other, err := s.ConvertToVersion(tt, unversioned.GroupVersion{Version: "v1"})
|
other, err := s.ConvertToVersion(tt, unversioned.GroupVersion{Version: "v1"})
|
||||||
|
@ -537,13 +555,258 @@ func TestConvertToVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
converted, ok := other.(*ExternalTestType1)
|
converted, ok := other.(*ExternalTestType1)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Got wrong type")
|
t.Fatalf("Got wrong type: %T", other)
|
||||||
}
|
}
|
||||||
if tt.A != converted.A {
|
if tt.A != converted.A {
|
||||||
t.Fatalf("Failed to convert object correctly: %#v", converted)
|
t.Fatalf("Failed to convert object correctly: %#v", converted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testGroupVersioner struct {
|
||||||
|
target unversioned.GroupVersionKind
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m testGroupVersioner) KindForGroupVersionKinds(kinds []unversioned.GroupVersionKind) (unversioned.GroupVersionKind, bool) {
|
||||||
|
return m.target, m.ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertToVersion(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
scheme *runtime.Scheme
|
||||||
|
in runtime.Object
|
||||||
|
gv runtime.GroupVersioner
|
||||||
|
same bool
|
||||||
|
out runtime.Object
|
||||||
|
errFn func(error) bool
|
||||||
|
}{
|
||||||
|
// errors if the type is not registered in the scheme
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &UnknownType{},
|
||||||
|
errFn: func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
|
||||||
|
},
|
||||||
|
// errors if the group versioner returns no target
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: testGroupVersioner{},
|
||||||
|
errFn: func(err error) bool {
|
||||||
|
return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// converts to internal
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: unversioned.GroupVersion{Version: "__internal"},
|
||||||
|
out: &TestType1{A: "test"},
|
||||||
|
},
|
||||||
|
// prefers the first group version in the list
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: unversioned.GroupVersions{{Version: "__internal"}, {Version: "v1"}},
|
||||||
|
out: &TestType1{A: "test"},
|
||||||
|
},
|
||||||
|
// unversioned type returned as-is
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &UnversionedType{A: "test"},
|
||||||
|
gv: unversioned.GroupVersions{{Version: "v1"}},
|
||||||
|
same: true,
|
||||||
|
out: &UnversionedType{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// detected as already being in the target version
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: unversioned.GroupVersions{{Version: "v1"}},
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// detected as already being in the first target version
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: unversioned.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// detected as already being in the first target version
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: unversioned.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType3", Version: "v1"}},
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}},
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}},
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "other", Version: "v2"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}, unversioned.GroupKind{Kind: "TestType1"}),
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "other", Version: "v2"}, unversioned.GroupKind{Kind: "TestType1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}),
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// multi group versioner is unable to find a match when kind AND group don't match (there is no TestType1 kind in group "other", and no kind "TestType5" in the default group)
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &TestType1{A: "test"},
|
||||||
|
gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "custom", Version: "v1"}, unversioned.GroupKind{Group: "other"}, unversioned.GroupKind{Kind: "TestType5"}),
|
||||||
|
errFn: func(err error) bool {
|
||||||
|
return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "", Version: "v1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}, unversioned.GroupKind{Kind: "TestType1"}),
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &ExternalTestType1{A: "test"},
|
||||||
|
gv: runtime.NewMultiGroupVersioner(unversioned.GroupVersion{Group: "", Version: "v1"}, unversioned.GroupKind{Kind: "TestType1"}, unversioned.GroupKind{Group: "custom", Kind: "TestType3"}),
|
||||||
|
same: true,
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// group versioner can choose a particular target kind for a given input when kind is the same across group versions
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &TestType1{A: "test"},
|
||||||
|
gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Version: "v1", Kind: "TestType3"}},
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// group versioner can choose a different kind
|
||||||
|
{
|
||||||
|
scheme: GetTestScheme(),
|
||||||
|
in: &TestType1{A: "test"},
|
||||||
|
gv: testGroupVersioner{ok: true, target: unversioned.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}},
|
||||||
|
out: &ExternalTestType1{
|
||||||
|
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
|
||||||
|
A: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, test := range testCases {
|
||||||
|
original, _ := test.scheme.DeepCopy(test.in)
|
||||||
|
out, err := test.scheme.ConvertToVersion(test.in, test.gv)
|
||||||
|
switch {
|
||||||
|
case test.errFn != nil:
|
||||||
|
if !test.errFn(err) {
|
||||||
|
t.Errorf("%d: unexpected error: %v", i, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case err != nil:
|
||||||
|
t.Errorf("%d: unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if out == test.in {
|
||||||
|
t.Errorf("%d: ConvertToVersion should always copy out: %#v", i, out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.same {
|
||||||
|
if !reflect.DeepEqual(original, test.in) {
|
||||||
|
t.Errorf("%d: unexpected mutation of input: %s", i, diff.ObjectReflectDiff(original, test.in))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, test.out) {
|
||||||
|
t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(unsafe, test.out) {
|
||||||
|
t.Errorf("%d: unexpected unsafe: %s", i, diff.ObjectReflectDiff(unsafe, test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if unsafe != test.in {
|
||||||
|
t.Errorf("%d: UnsafeConvertToVersion should return same object: %#v", i, unsafe)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, test.out) {
|
||||||
|
t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMetaValues(t *testing.T) {
|
func TestMetaValues(t *testing.T) {
|
||||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
|
internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
|
||||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
|
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
|
||||||
|
|
|
@ -188,13 +188,17 @@ func (f CodecFactory) SupportedStreamingMediaTypes() []string {
|
||||||
return f.streamingAccepts
|
return f.streamingAccepts
|
||||||
}
|
}
|
||||||
|
|
||||||
// LegacyCodec encodes output to a given API version, and decodes output into the internal form from
|
// LegacyCodec encodes output to a given API versions, and decodes output into the internal form from
|
||||||
// any recognized source. The returned codec will always encode output to JSON.
|
// any recognized source. The returned codec will always encode output to JSON. If a type is not
|
||||||
|
// found in the list of versions an error will be returned.
|
||||||
//
|
//
|
||||||
// This method is deprecated - clients and servers should negotiate a serializer by mime-type and
|
// This method is deprecated - clients and servers should negotiate a serializer by mime-type and
|
||||||
// invoke CodecForVersions. Callers that need only to read data should use UniversalDecoder().
|
// invoke CodecForVersions. Callers that need only to read data should use UniversalDecoder().
|
||||||
|
//
|
||||||
|
// TODO: make this call exist only in pkg/api, and initialize it with the set of default versions.
|
||||||
|
// All other callers will be forced to request a Codec directly.
|
||||||
func (f CodecFactory) LegacyCodec(version ...unversioned.GroupVersion) runtime.Codec {
|
func (f CodecFactory) LegacyCodec(version ...unversioned.GroupVersion) runtime.Codec {
|
||||||
return versioning.NewCodecForScheme(f.scheme, f.legacySerializer, f.universal, version, nil)
|
return versioning.NewCodecForScheme(f.scheme, f.legacySerializer, f.universal, unversioned.GroupVersions(version), runtime.InternalGroupVersioner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UniversalDeserializer can convert any stored data recognized by this factory into a Go object that satisfies
|
// UniversalDeserializer can convert any stored data recognized by this factory into a Go object that satisfies
|
||||||
|
@ -211,25 +215,39 @@ func (f CodecFactory) UniversalDeserializer() runtime.Decoder {
|
||||||
// defaulting.
|
// defaulting.
|
||||||
//
|
//
|
||||||
// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form
|
// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form
|
||||||
|
// TODO: only accept a group versioner
|
||||||
func (f CodecFactory) UniversalDecoder(versions ...unversioned.GroupVersion) runtime.Decoder {
|
func (f CodecFactory) UniversalDecoder(versions ...unversioned.GroupVersion) runtime.Decoder {
|
||||||
return f.CodecForVersions(nil, f.universal, nil, versions)
|
var versioner runtime.GroupVersioner
|
||||||
|
if len(versions) == 0 {
|
||||||
|
versioner = runtime.InternalGroupVersioner
|
||||||
|
} else {
|
||||||
|
versioner = unversioned.GroupVersions(versions)
|
||||||
|
}
|
||||||
|
return f.CodecForVersions(nil, f.universal, nil, versioner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodecForVersions creates a codec with the provided serializer. If an object is decoded and its group is not in the list,
|
// CodecForVersions creates a codec with the provided serializer. If an object is decoded and its group is not in the list,
|
||||||
// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not
|
// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not
|
||||||
// converted. If encode or decode are nil, no conversion is performed.
|
// converted. If encode or decode are nil, no conversion is performed.
|
||||||
func (f CodecFactory) CodecForVersions(encoder runtime.Encoder, decoder runtime.Decoder, encode []unversioned.GroupVersion, decode []unversioned.GroupVersion) runtime.Codec {
|
func (f CodecFactory) CodecForVersions(encoder runtime.Encoder, decoder runtime.Decoder, encode runtime.GroupVersioner, decode runtime.GroupVersioner) runtime.Codec {
|
||||||
|
// TODO: these are for backcompat, remove them in the future
|
||||||
|
if encode == nil {
|
||||||
|
encode = runtime.DisabledGroupVersioner
|
||||||
|
}
|
||||||
|
if decode == nil {
|
||||||
|
decode = runtime.InternalGroupVersioner
|
||||||
|
}
|
||||||
return versioning.NewCodecForScheme(f.scheme, encoder, decoder, encode, decode)
|
return versioning.NewCodecForScheme(f.scheme, encoder, decoder, encode, decode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecoderToVersion returns a decoder that targets the provided group version.
|
// DecoderToVersion returns a decoder that targets the provided group version.
|
||||||
func (f CodecFactory) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
func (f CodecFactory) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||||
return f.CodecForVersions(nil, decoder, nil, []unversioned.GroupVersion{gv})
|
return f.CodecForVersions(nil, decoder, nil, gv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncoderForVersion returns an encoder that targets the provided group version.
|
// EncoderForVersion returns an encoder that targets the provided group version.
|
||||||
func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
return f.CodecForVersions(encoder, nil, []unversioned.GroupVersion{gv}, nil)
|
return f.CodecForVersions(encoder, nil, gv, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such
|
||||||
|
@ -317,7 +335,7 @@ type DirectCodecFactory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncoderForVersion returns an encoder that does not do conversion. gv is ignored.
|
// EncoderForVersion returns an encoder that does not do conversion. gv is ignored.
|
||||||
func (f DirectCodecFactory) EncoderForVersion(serializer runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
func (f DirectCodecFactory) EncoderForVersion(serializer runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
||||||
return DirectCodec{
|
return DirectCodec{
|
||||||
runtime.NewCodec(serializer, nil),
|
runtime.NewCodec(serializer, nil),
|
||||||
f.CodecFactory.scheme,
|
f.CodecFactory.scheme,
|
||||||
|
@ -325,7 +343,7 @@ func (f DirectCodecFactory) EncoderForVersion(serializer runtime.Encoder, gv unv
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecoderToVersion returns an decoder that does not do conversion. gv is ignored.
|
// DecoderToVersion returns an decoder that does not do conversion. gv is ignored.
|
||||||
func (f DirectCodecFactory) DecoderToVersion(serializer runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
func (f DirectCodecFactory) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
|
||||||
return DirectCodec{
|
return DirectCodec{
|
||||||
runtime.NewCodec(nil, serializer),
|
runtime.NewCodec(nil, serializer),
|
||||||
nil,
|
nil,
|
||||||
|
|
|
@ -254,7 +254,7 @@ func TestVersionedEncoding(t *testing.T) {
|
||||||
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
||||||
encoder, _ := cf.SerializerForFileExtension("json")
|
encoder, _ := cf.SerializerForFileExtension("json")
|
||||||
|
|
||||||
codec := cf.CodecForVersions(encoder, nil, []unversioned.GroupVersion{{Version: "v2"}}, nil)
|
codec := cf.CodecForVersions(encoder, nil, unversioned.GroupVersion{Version: "v2"}, nil)
|
||||||
out, err := runtime.Encode(codec, &TestType1{})
|
out, err := runtime.Encode(codec, &TestType1{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -263,19 +263,19 @@ func TestVersionedEncoding(t *testing.T) {
|
||||||
t.Fatal(string(out))
|
t.Fatal(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
codec = cf.CodecForVersions(encoder, nil, []unversioned.GroupVersion{{Version: "v3"}}, nil)
|
codec = cf.CodecForVersions(encoder, nil, unversioned.GroupVersion{Version: "v3"}, nil)
|
||||||
_, err = runtime.Encode(codec, &TestType1{})
|
_, err = runtime.Encode(codec, &TestType1{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unversioned encode with no versions is written directly to wire
|
// unversioned encode with no versions is written directly to wire
|
||||||
codec = cf.CodecForVersions(encoder, nil, nil, nil)
|
codec = cf.CodecForVersions(encoder, nil, runtime.InternalGroupVersioner, nil)
|
||||||
out, err = runtime.Encode(codec, &TestType1{})
|
out, err = runtime.Encode(codec, &TestType1{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if string(out) != `{"myVersionKey":"__internal","myKindKey":"TestType1"}`+"\n" {
|
if string(out) != `{}`+"\n" {
|
||||||
t.Fatal(string(out))
|
t.Fatal(string(out))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
package serializer
|
package serializer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,10 +47,10 @@ func (n *negotiatedSerializerWrapper) StreamingSerializerForMediaType(mediaType
|
||||||
return n.streamInfo, true
|
return n.streamInfo, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) EncoderForVersion(e runtime.Encoder, _ unversioned.GroupVersion) runtime.Encoder {
|
func (n *negotiatedSerializerWrapper) EncoderForVersion(e runtime.Encoder, _ runtime.GroupVersioner) runtime.Encoder {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *negotiatedSerializerWrapper) DecoderToVersion(d runtime.Decoder, _gv unversioned.GroupVersion) runtime.Decoder {
|
func (n *negotiatedSerializerWrapper) DecoderToVersion(d runtime.Decoder, _gv runtime.GroupVersioner) runtime.Decoder {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,59 +17,20 @@ limitations under the License.
|
||||||
package versioning
|
package versioning
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EnableCrossGroupDecoding modifies the given decoder in place, if it is a codec
|
|
||||||
// from this package. It allows objects from one group to be auto-decoded into
|
|
||||||
// another group. 'destGroup' must already exist in the codec.
|
|
||||||
// TODO: this is an encapsulation violation and should be refactored
|
|
||||||
func EnableCrossGroupDecoding(d runtime.Decoder, sourceGroup, destGroup string) error {
|
|
||||||
internal, ok := d.(*codec)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unsupported decoder type")
|
|
||||||
}
|
|
||||||
|
|
||||||
dest, ok := internal.decodeVersion[destGroup]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("group %q is not a possible destination group in the given codec", destGroup)
|
|
||||||
}
|
|
||||||
internal.decodeVersion[sourceGroup] = dest
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableCrossGroupEncoding modifies the given encoder in place, if it is a codec
|
|
||||||
// from this package. It allows objects from one group to be auto-decoded into
|
|
||||||
// another group. 'destGroup' must already exist in the codec.
|
|
||||||
// TODO: this is an encapsulation violation and should be refactored
|
|
||||||
func EnableCrossGroupEncoding(e runtime.Encoder, sourceGroup, destGroup string) error {
|
|
||||||
internal, ok := e.(*codec)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unsupported encoder type")
|
|
||||||
}
|
|
||||||
|
|
||||||
dest, ok := internal.encodeVersion[destGroup]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("group %q is not a possible destination group in the given codec", destGroup)
|
|
||||||
}
|
|
||||||
internal.encodeVersion[sourceGroup] = dest
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCodecForScheme is a convenience method for callers that are using a scheme.
|
// NewCodecForScheme is a convenience method for callers that are using a scheme.
|
||||||
func NewCodecForScheme(
|
func NewCodecForScheme(
|
||||||
// TODO: I should be a scheme interface?
|
// TODO: I should be a scheme interface?
|
||||||
scheme *runtime.Scheme,
|
scheme *runtime.Scheme,
|
||||||
encoder runtime.Encoder,
|
encoder runtime.Encoder,
|
||||||
decoder runtime.Decoder,
|
decoder runtime.Decoder,
|
||||||
encodeVersion []unversioned.GroupVersion,
|
encodeVersion runtime.GroupVersioner,
|
||||||
decodeVersion []unversioned.GroupVersion,
|
decodeVersion runtime.GroupVersioner,
|
||||||
) runtime.Codec {
|
) runtime.Codec {
|
||||||
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion)
|
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion)
|
||||||
}
|
}
|
||||||
|
@ -84,8 +45,8 @@ func NewCodec(
|
||||||
creater runtime.ObjectCreater,
|
creater runtime.ObjectCreater,
|
||||||
copier runtime.ObjectCopier,
|
copier runtime.ObjectCopier,
|
||||||
typer runtime.ObjectTyper,
|
typer runtime.ObjectTyper,
|
||||||
encodeVersion []unversioned.GroupVersion,
|
encodeVersion runtime.GroupVersioner,
|
||||||
decodeVersion []unversioned.GroupVersion,
|
decodeVersion runtime.GroupVersioner,
|
||||||
) runtime.Codec {
|
) runtime.Codec {
|
||||||
internal := &codec{
|
internal := &codec{
|
||||||
encoder: encoder,
|
encoder: encoder,
|
||||||
|
@ -94,33 +55,10 @@ func NewCodec(
|
||||||
creater: creater,
|
creater: creater,
|
||||||
copier: copier,
|
copier: copier,
|
||||||
typer: typer,
|
typer: typer,
|
||||||
}
|
|
||||||
if encodeVersion != nil {
|
|
||||||
internal.encodeVersion = make(map[string]unversioned.GroupVersion)
|
|
||||||
for _, v := range encodeVersion {
|
|
||||||
// first one for a group wins. This is consistent with best to worst order throughout the codebase
|
|
||||||
if _, ok := internal.encodeVersion[v.Group]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
internal.encodeVersion[v.Group] = v
|
|
||||||
}
|
|
||||||
if len(internal.encodeVersion) == 1 {
|
|
||||||
for _, v := range internal.encodeVersion {
|
|
||||||
internal.preferredEncodeVersion = []unversioned.GroupVersion{v}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if decodeVersion != nil {
|
|
||||||
internal.decodeVersion = make(map[string]unversioned.GroupVersion)
|
|
||||||
for _, v := range decodeVersion {
|
|
||||||
// first one for a group wins. This is consistent with best to worst order throughout the codebase
|
|
||||||
if _, ok := internal.decodeVersion[v.Group]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
internal.decodeVersion[v.Group] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
encodeVersion: encodeVersion,
|
||||||
|
decodeVersion: decodeVersion,
|
||||||
|
}
|
||||||
return internal
|
return internal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,10 +70,8 @@ type codec struct {
|
||||||
copier runtime.ObjectCopier
|
copier runtime.ObjectCopier
|
||||||
typer runtime.ObjectTyper
|
typer runtime.ObjectTyper
|
||||||
|
|
||||||
encodeVersion map[string]unversioned.GroupVersion
|
encodeVersion runtime.GroupVersioner
|
||||||
decodeVersion map[string]unversioned.GroupVersion
|
decodeVersion runtime.GroupVersioner
|
||||||
|
|
||||||
preferredEncodeVersion []unversioned.GroupVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
|
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
|
||||||
|
@ -170,37 +106,7 @@ func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, in
|
||||||
return into, gvk, nil
|
return into, gvk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoke a version conversion
|
// Convert if needed.
|
||||||
group := gvk.Group
|
|
||||||
if defaultGVK != nil {
|
|
||||||
group = defaultGVK.Group
|
|
||||||
}
|
|
||||||
var targetGV unversioned.GroupVersion
|
|
||||||
if c.decodeVersion == nil {
|
|
||||||
// convert to internal by default
|
|
||||||
targetGV.Group = group
|
|
||||||
targetGV.Version = runtime.APIVersionInternal
|
|
||||||
} else {
|
|
||||||
gv, ok := c.decodeVersion[group]
|
|
||||||
if !ok {
|
|
||||||
// unknown objects are left in their original version
|
|
||||||
if isVersioned {
|
|
||||||
versioned.Objects = []runtime.Object{obj}
|
|
||||||
return versioned, gvk, nil
|
|
||||||
}
|
|
||||||
return obj, gvk, nil
|
|
||||||
}
|
|
||||||
targetGV = gv
|
|
||||||
}
|
|
||||||
|
|
||||||
if gvk.GroupVersion() == targetGV {
|
|
||||||
if isVersioned {
|
|
||||||
versioned.Objects = []runtime.Object{obj}
|
|
||||||
return versioned, gvk, nil
|
|
||||||
}
|
|
||||||
return obj, gvk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if isVersioned {
|
if isVersioned {
|
||||||
// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
|
// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
|
||||||
copied, err := c.copier.Copy(obj)
|
copied, err := c.copier.Copy(obj)
|
||||||
|
@ -209,14 +115,14 @@ func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, in
|
||||||
}
|
}
|
||||||
versioned.Objects = []runtime.Object{copied}
|
versioned.Objects = []runtime.Object{copied}
|
||||||
}
|
}
|
||||||
|
out, err := c.convertor.ConvertToVersion(obj, c.decodeVersion)
|
||||||
// Convert if needed.
|
|
||||||
out, err := c.convertor.ConvertToVersion(obj, targetGV)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gvk, err
|
return nil, gvk, err
|
||||||
}
|
}
|
||||||
if isVersioned {
|
if isVersioned {
|
||||||
versioned.Objects = append(versioned.Objects, out)
|
if versioned.Last() != out {
|
||||||
|
versioned.Objects = append(versioned.Objects, out)
|
||||||
|
}
|
||||||
return versioned, gvk, nil
|
return versioned, gvk, nil
|
||||||
}
|
}
|
||||||
return out, gvk, nil
|
return out, gvk, nil
|
||||||
|
@ -225,50 +131,47 @@ func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, in
|
||||||
// Encode ensures the provided object is output in the appropriate group and version, invoking
|
// Encode ensures the provided object is output in the appropriate group and version, invoking
|
||||||
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
|
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
|
||||||
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
|
||||||
if _, ok := obj.(*runtime.Unknown); ok {
|
switch t := obj.(type) {
|
||||||
|
case *runtime.Unknown:
|
||||||
|
if gv, ok := runtime.PreferredGroupVersion(c.encodeVersion); ok {
|
||||||
|
t.APIVersion = gv.String()
|
||||||
|
}
|
||||||
|
return c.encoder.Encode(obj, w)
|
||||||
|
case *runtime.Unstructured:
|
||||||
|
if gv, ok := runtime.PreferredGroupVersion(c.encodeVersion); ok {
|
||||||
|
t.SetAPIVersion(gv.String())
|
||||||
|
}
|
||||||
|
return c.encoder.Encode(obj, w)
|
||||||
|
case *runtime.UnstructuredList:
|
||||||
|
if gv, ok := runtime.PreferredGroupVersion(c.encodeVersion); ok {
|
||||||
|
t.SetAPIVersion(gv.String())
|
||||||
|
}
|
||||||
return c.encoder.Encode(obj, w)
|
return c.encoder.Encode(obj, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
|
gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gvk := gvks[0]
|
|
||||||
|
|
||||||
if c.encodeVersion == nil || isUnversioned {
|
if c.encodeVersion == nil || isUnversioned {
|
||||||
objectKind := obj.GetObjectKind()
|
objectKind := obj.GetObjectKind()
|
||||||
old := objectKind.GroupVersionKind()
|
old := objectKind.GroupVersionKind()
|
||||||
objectKind.SetGroupVersionKind(gvk)
|
objectKind.SetGroupVersionKind(gvks[0])
|
||||||
err = c.encoder.Encode(obj, w)
|
err = c.encoder.Encode(obj, w)
|
||||||
objectKind.SetGroupVersionKind(old)
|
objectKind.SetGroupVersionKind(old)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
targetGV, ok := c.encodeVersion[gvk.Group]
|
|
||||||
|
|
||||||
// attempt a conversion to the sole encode version
|
|
||||||
if !ok && c.preferredEncodeVersion != nil {
|
|
||||||
ok = true
|
|
||||||
targetGV = c.preferredEncodeVersion[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no fallback is available, error
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("the codec does not recognize group %q for kind %q and cannot encode it", gvk.Group, gvk.Kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform a conversion if necessary
|
// Perform a conversion if necessary
|
||||||
objectKind := obj.GetObjectKind()
|
objectKind := obj.GetObjectKind()
|
||||||
old := objectKind.GroupVersionKind()
|
old := objectKind.GroupVersionKind()
|
||||||
out, err := c.convertor.ConvertToVersion(obj, targetGV)
|
out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ok {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj = out
|
|
||||||
}
|
}
|
||||||
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
|
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
|
||||||
err = c.encoder.Encode(obj, w)
|
err = c.encoder.Encode(out, w)
|
||||||
// restore the old GVK, in case conversion returned the same object
|
// restore the old GVK, in case conversion returned the same object
|
||||||
objectKind.SetGroupVersionKind(old)
|
objectKind.SetGroupVersionKind(old)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -53,7 +53,7 @@ func TestDecode(t *testing.T) {
|
||||||
yaml bool
|
yaml bool
|
||||||
pretty bool
|
pretty bool
|
||||||
|
|
||||||
encodes, decodes []unversioned.GroupVersion
|
encodes, decodes runtime.GroupVersioner
|
||||||
|
|
||||||
defaultGVK *unversioned.GroupVersionKind
|
defaultGVK *unversioned.GroupVersionKind
|
||||||
into runtime.Object
|
into runtime.Object
|
||||||
|
@ -67,12 +67,14 @@ func TestDecode(t *testing.T) {
|
||||||
serializer: &mockSerializer{actual: gvk1},
|
serializer: &mockSerializer{actual: gvk1},
|
||||||
convertor: &checkConvertor{groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
convertor: &checkConvertor{groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
|
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
sameObject: decodable2,
|
sameObject: decodable2,
|
||||||
|
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||||
},
|
},
|
||||||
// defaultGVK.Group is allowed to force a conversion to the destination group
|
// defaultGVK.Group is allowed to force a conversion to the destination group
|
||||||
{
|
{
|
||||||
|
@ -81,6 +83,7 @@ func TestDecode(t *testing.T) {
|
||||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "force", Version: "__internal"}},
|
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "force", Version: "__internal"}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
sameObject: decodable2,
|
sameObject: decodable2,
|
||||||
|
decodes: unversioned.GroupVersion{Group: "force", Version: "__internal"},
|
||||||
},
|
},
|
||||||
// uses direct conversion for into when objects differ
|
// uses direct conversion for into when objects differ
|
||||||
{
|
{
|
||||||
|
@ -121,6 +124,7 @@ func TestDecode(t *testing.T) {
|
||||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
||||||
|
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||||
|
@ -130,38 +134,45 @@ func TestDecode(t *testing.T) {
|
||||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
||||||
|
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||||
},
|
},
|
||||||
|
|
||||||
// decode into the same version as the serialized object
|
// decode into the same version as the serialized object
|
||||||
{
|
{
|
||||||
decodes: []unversioned.GroupVersion{gvk1.GroupVersion()},
|
decodes: unversioned.GroupVersions{gvk1.GroupVersion()},
|
||||||
|
|
||||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||||
|
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "other", Version: "blah"}}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
expectedObject: decodable1,
|
expectedObject: decodable1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||||
decodes: []unversioned.GroupVersion{gvk1.GroupVersion()},
|
decodes: unversioned.GroupVersions{gvk1.GroupVersion()},
|
||||||
|
|
||||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||||
|
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "other", Version: "blah"}}},
|
||||||
|
copier: &checkCopy{in: decodable1, obj: decodable1, err: nil},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
||||||
},
|
},
|
||||||
|
|
||||||
// codec with non matching version skips conversion altogether
|
// codec with non matching version skips conversion altogether
|
||||||
{
|
{
|
||||||
decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}},
|
decodes: unversioned.GroupVersions{{Group: "something", Version: "else"}},
|
||||||
|
|
||||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||||
|
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "something", Version: "else"}}},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
expectedObject: decodable1,
|
expectedObject: decodable1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||||
decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}},
|
decodes: unversioned.GroupVersions{{Group: "something", Version: "else"}},
|
||||||
|
|
||||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||||
|
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "something", Version: "else"}}},
|
||||||
|
copier: &checkCopy{in: decodable1, obj: decodable1, err: nil},
|
||||||
expectedGVK: gvk1,
|
expectedGVK: gvk1,
|
||||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
||||||
},
|
},
|
||||||
|
@ -228,7 +239,7 @@ func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) {
|
||||||
type checkConvertor struct {
|
type checkConvertor struct {
|
||||||
err error
|
err error
|
||||||
in, obj runtime.Object
|
in, obj runtime.Object
|
||||||
groupVersion unversioned.GroupVersion
|
groupVersion runtime.GroupVersioner
|
||||||
directConvert bool
|
directConvert bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,15 +255,15 @@ func (c *checkConvertor) Convert(in, out interface{}) error {
|
||||||
}
|
}
|
||||||
return c.err
|
return c.err
|
||||||
}
|
}
|
||||||
func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion unversioned.GroupVersion) (out runtime.Object, err error) {
|
func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
|
||||||
if c.directConvert {
|
if c.directConvert {
|
||||||
return nil, fmt.Errorf("unexpected call to ConvertToVersion")
|
return nil, fmt.Errorf("unexpected call to ConvertToVersion")
|
||||||
}
|
}
|
||||||
if c.in != nil && c.in != in {
|
if c.in != nil && c.in != in {
|
||||||
return nil, fmt.Errorf("unexpected in: %s", in)
|
return nil, fmt.Errorf("unexpected in: %s", in)
|
||||||
}
|
}
|
||||||
if c.groupVersion != outVersion {
|
if !reflect.DeepEqual(c.groupVersion, outVersion) {
|
||||||
return nil, fmt.Errorf("unexpected outversion: %s", outVersion)
|
return nil, fmt.Errorf("unexpected outversion: %s (%s)", outVersion, c.groupVersion)
|
||||||
}
|
}
|
||||||
return c.obj, c.err
|
return c.obj, c.err
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,9 +187,14 @@ func (UnstructuredObjectConverter) Convert(in, out interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnstructuredObjectConverter) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
func (UnstructuredObjectConverter) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||||
if gvk := in.GetObjectKind().GroupVersionKind(); gvk.GroupVersion() != outVersion {
|
if kind := in.GetObjectKind().GroupVersionKind(); !kind.IsEmpty() {
|
||||||
return nil, errors.New("unstructured converter cannot convert versions")
|
gvk, ok := target.KindForGroupVersionKinds([]unversioned.GroupVersionKind{kind})
|
||||||
|
if !ok {
|
||||||
|
// TODO: should this be a typed error?
|
||||||
|
return nil, fmt.Errorf("%v is unstructured and is not suitable for converting to %q", kind, target)
|
||||||
|
}
|
||||||
|
in.GetObjectKind().SetGroupVersionKind(gvk)
|
||||||
}
|
}
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,13 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var status = &unversioned.Status{
|
|
||||||
Status: unversioned.StatusFailure,
|
|
||||||
Code: 200,
|
|
||||||
Reason: unversioned.StatusReasonUnknown,
|
|
||||||
Message: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestV1EncodeDecodeStatus(t *testing.T) {
|
func TestV1EncodeDecodeStatus(t *testing.T) {
|
||||||
|
status := &unversioned.Status{
|
||||||
|
Status: unversioned.StatusFailure,
|
||||||
|
Code: 200,
|
||||||
|
Reason: unversioned.StatusReasonUnknown,
|
||||||
|
Message: "",
|
||||||
|
}
|
||||||
|
|
||||||
v1Codec := testapi.Default.Codec()
|
v1Codec := testapi.Default.Codec()
|
||||||
|
|
||||||
|
@ -65,6 +64,12 @@ func TestV1EncodeDecodeStatus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExperimentalEncodeDecodeStatus(t *testing.T) {
|
func TestExperimentalEncodeDecodeStatus(t *testing.T) {
|
||||||
|
status := &unversioned.Status{
|
||||||
|
Status: unversioned.StatusFailure,
|
||||||
|
Code: 200,
|
||||||
|
Reason: unversioned.StatusReasonUnknown,
|
||||||
|
Message: "",
|
||||||
|
}
|
||||||
// TODO: caesarxuchao: use the testapi.Extensions.Codec() once the PR that
|
// TODO: caesarxuchao: use the testapi.Extensions.Codec() once the PR that
|
||||||
// moves experimental from v1 to v1beta1 got merged.
|
// moves experimental from v1 to v1beta1 got merged.
|
||||||
expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion)
|
expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion)
|
||||||
|
|
|
@ -47,7 +47,7 @@ func init() {
|
||||||
api.Scheme,
|
api.Scheme,
|
||||||
jsonSerializer,
|
jsonSerializer,
|
||||||
jsonSerializer,
|
jsonSerializer,
|
||||||
[]unversioned.GroupVersion{{Version: Version}},
|
unversioned.GroupVersion{Version: Version},
|
||||||
[]unversioned.GroupVersion{{Version: runtime.APIVersionInternal}},
|
runtime.InternalGroupVersioner,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
package framework
|
package framework
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
||||||
)
|
)
|
||||||
|
@ -58,10 +57,10 @@ func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder {
|
||||||
return s.serializer
|
return s.serializer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||||
return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, s.scheme, []unversioned.GroupVersion{gv}, nil)
|
return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, s.scheme, gv, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *wrappedSerializer) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
func (s *wrappedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||||
return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, s.scheme, nil, []unversioned.GroupVersion{gv})
|
return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, s.scheme, nil, gv)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue