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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ import (
|
|||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"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/sets"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
|
@ -181,10 +180,14 @@ func TestSetControllerConversion(t *testing.T) {
|
|||
t.Fatalf("unexpected encoding error: %v", err)
|
||||
}
|
||||
|
||||
decoder := api.Codecs.UniversalDecoder(*extGroup.GroupVersion(), *defaultGroup.GroupVersion())
|
||||
if err := versioning.EnableCrossGroupDecoding(decoder, extGroup.GroupVersion().Group, defaultGroup.GroupVersion().Group); err != nil {
|
||||
t.Fatalf("unexpected error while enabling cross-group decoding: %v", err)
|
||||
}
|
||||
decoder := api.Codecs.DecoderToVersion(
|
||||
api.Codecs.UniversalDeserializer(),
|
||||
runtime.NewMultiGroupVersioner(
|
||||
*defaultGroup.GroupVersion(),
|
||||
unversioned.GroupKind{Group: defaultGroup.GroupVersion().Group},
|
||||
unversioned.GroupKind{Group: extGroup.GroupVersion().Group},
|
||||
),
|
||||
)
|
||||
|
||||
t.Logf("rs.v1beta1.extensions -> rc._internal")
|
||||
if err := runtime.DecodeInto(decoder, data, rc); err != nil {
|
||||
|
|
|
@ -281,7 +281,7 @@ func (g TestGroup) Codec() runtime.Codec {
|
|||
if serializer.Serializer == nil {
|
||||
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.
|
||||
|
@ -309,7 +309,7 @@ func (g TestGroup) StorageCodec() runtime.Codec {
|
|||
}
|
||||
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
|
||||
|
@ -393,7 +393,7 @@ func (g TestGroup) RESTMapper() meta.RESTMapper {
|
|||
}
|
||||
|
||||
// ExternalGroupVersions returns all external group versions allowed for the server.
|
||||
func ExternalGroupVersions() []unversioned.GroupVersion {
|
||||
func ExternalGroupVersions() unversioned.GroupVersions {
|
||||
versions := []unversioned.GroupVersion{}
|
||||
for _, g := range Groups {
|
||||
gv := g.GroupVersion()
|
||||
|
|
|
@ -179,6 +179,25 @@ func (gv GroupVersion) String() string {
|
|||
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
|
||||
// if it cannot parse the string.
|
||||
func ParseGroupVersion(gv string) (GroupVersion, error) {
|
||||
|
@ -241,6 +260,25 @@ func (gv *GroupVersion) UnmarshalText(value []byte) error {
|
|||
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
|
||||
// do not use TypeMeta.
|
||||
func (gvk *GroupVersionKind) ToAPIVersionAndKind() (string, string) {
|
||||
|
|
|
@ -320,7 +320,7 @@ type StripVersionNegotiatedSerializer struct {
|
|||
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)
|
||||
if !ok {
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ func init() {
|
|||
Scheme,
|
||||
yamlSerializer,
|
||||
yamlSerializer,
|
||||
[]unversioned.GroupVersion{{Version: Version}},
|
||||
[]unversioned.GroupVersion{{Version: runtime.APIVersionInternal}},
|
||||
unversioned.GroupVersion{Version: Version},
|
||||
runtime.InternalGroupVersioner,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -213,6 +213,8 @@ type Meta struct {
|
|||
// KeyNameMapping is an optional function which may map the listed key (field name)
|
||||
// into a source and destination value.
|
||||
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.
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
||||
"k8s.io/kubernetes/pkg/storage/storagebackend"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
|
||||
|
@ -262,18 +261,25 @@ func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, stor
|
|||
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())
|
||||
encoder := ns.EncoderForVersion(s, storageVersion)
|
||||
decoder := ns.DecoderToVersion(ds, memoryVersion)
|
||||
if memoryVersion.Group != storageVersion.Group {
|
||||
// Allow this codec to translate between groups.
|
||||
if err := versioning.EnableCrossGroupEncoding(encoder, memoryVersion.Group, storageVersion.Group); err != nil {
|
||||
return nil, fmt.Errorf("error setting up encoder from %v to %v: %v", memoryVersion, storageVersion, err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
decoder := ns.DecoderToVersion(
|
||||
ds,
|
||||
runtime.NewMultiGroupVersioner(
|
||||
memoryVersion,
|
||||
unversioned.GroupKind{Group: memoryVersion.Group},
|
||||
unversioned.GroupKind{Group: storageVersion.Group},
|
||||
),
|
||||
)
|
||||
|
||||
return runtime.NewCodec(encoder, decoder), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ type thirdPartyObjectConverter struct {
|
|||
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) {
|
||||
// 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.
|
||||
|
@ -234,11 +234,11 @@ func (t *thirdPartyResourceDataCodecFactory) StreamingSerializerForMediaType(med
|
|||
}
|
||||
}
|
||||
|
||||
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||
return &thirdPartyResourceDataEncoder{delegate: t.delegate.EncoderForVersion(s, gv), gvk: gv.WithKind(t.kind)}
|
||||
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -517,6 +517,10 @@ func (t *thirdPartyResourceDataEncoder) Encode(obj runtime.Object, stream io.Wri
|
|||
listItems[ix] = json.RawMessage(buff.Bytes())
|
||||
}
|
||||
|
||||
if t.gvk.IsEmpty() {
|
||||
return fmt.Errorf("thirdPartyResourceDataEncoder was not given a target version")
|
||||
}
|
||||
|
||||
encMap := struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,12 +30,23 @@ const (
|
|||
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 {
|
||||
// Encode writes an object to a stream. Implementations may return errors if the versions are
|
||||
// incompatible, or if no conversion is defined.
|
||||
Encode(obj Object, w io.Writer) error
|
||||
}
|
||||
|
||||
// Decoders attempt to load an object from data.
|
||||
type Decoder interface {
|
||||
// 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
|
||||
|
@ -117,12 +128,10 @@ type NegotiatedSerializer interface {
|
|||
|
||||
// EncoderForVersion returns an encoder that ensures objects being written to the provided
|
||||
// serializer are in the provided group version.
|
||||
// TODO: take multiple group versions
|
||||
EncoderForVersion(serializer Encoder, gv unversioned.GroupVersion) Encoder
|
||||
EncoderForVersion(serializer Encoder, gv GroupVersioner) Encoder
|
||||
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
||||
// serializer are in the provided group version by default.
|
||||
// TODO: take multiple group versions
|
||||
DecoderToVersion(serializer Decoder, gv unversioned.GroupVersion) Decoder
|
||||
DecoderToVersion(serializer Decoder, gv GroupVersioner) Decoder
|
||||
}
|
||||
|
||||
// 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
|
||||
// serializer are in the provided group version.
|
||||
// TODO: take multiple group versions
|
||||
EncoderForVersion(serializer Encoder, gv unversioned.GroupVersion) Encoder
|
||||
EncoderForVersion(serializer Encoder, gv GroupVersioner) Encoder
|
||||
// DecoderForVersion returns a decoder that ensures objects being read by the provided
|
||||
// serializer are in the provided group version by default.
|
||||
// TODO: take multiple group versions
|
||||
DecoderToVersion(serializer Decoder, gv unversioned.GroupVersion) Decoder
|
||||
DecoderToVersion(serializer Decoder, gv GroupVersioner) Decoder
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Non-codec interfaces
|
||||
|
||||
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.
|
||||
|
@ -161,7 +168,7 @@ type ObjectConvertor interface {
|
|||
Convert(in, out interface{}) error
|
||||
// ConvertToVersion takes the provided object and converts it the provided version. This
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@ func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, bool,
|
|||
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return nil, false, ¬RegisteredErr{t: t}
|
||||
return nil, false, NewNotRegisteredErr(unversioned.GroupVersionKind{}, 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 {
|
||||
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
|
||||
|
@ -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
|
||||
// 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 inObj, ok := in.(Object); ok {
|
||||
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)
|
||||
// a to test conversion of types that are nested within registered types). The
|
||||
// context interface is passed to the convertor.
|
||||
// TODO: identify whether context should be hidden, or behind a formal context/scope
|
||||
// interface
|
||||
func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
|
||||
flags, meta := s.generateConvertMeta(in)
|
||||
meta.Context = context
|
||||
if flags == 0 {
|
||||
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
|
||||
// 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
|
||||
// returned. The serializer handles loading/serializing nested objects.
|
||||
func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
switch in.(type) {
|
||||
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
|
||||
// returned. Passes target down to the conversion methods as the Context on the scope.
|
||||
func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||
return s.convertToVersion(true, in, target)
|
||||
}
|
||||
|
||||
// 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
|
||||
// efficient as possible when doing conversion.
|
||||
func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
switch t := in.(type) {
|
||||
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
|
||||
}
|
||||
func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||
return s.convertToVersion(false, in, target)
|
||||
}
|
||||
|
||||
// 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.
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
|
@ -556,64 +493,69 @@ func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupV
|
|||
}
|
||||
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)
|
||||
return nil, NewNotRegisteredErr(unversioned.GroupVersionKind{}, t)
|
||||
}
|
||||
|
||||
// if the Go type is also registered to the destination kind, no conversion is necessary
|
||||
for i := range kinds {
|
||||
if kinds[i].Version == outVersion.Version && kinds[i].Group == outVersion.Group {
|
||||
setTargetKind(in, kinds[i])
|
||||
return in, nil
|
||||
gvk, ok := target.KindForGroupVersionKinds(kinds)
|
||||
if !ok {
|
||||
// TODO: should this be a typed error?
|
||||
return nil, fmt.Errorf("%v is not suitable for converting to %q", t, target)
|
||||
}
|
||||
|
||||
// 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
|
||||
// it should be possible to avoid this allocation
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind := unversionedKind
|
||||
outKind := outVersion.WithKind(kind.Kind)
|
||||
setTargetKind(in, outKind)
|
||||
return in, nil
|
||||
if gvk, ok := target.KindForGroupVersionKinds([]unversioned.GroupVersionKind{unversionedKind}); ok {
|
||||
return copyAndSetTargetKind(copy, s, in, gvk)
|
||||
}
|
||||
return copyAndSetTargetKind(copy, s, in, unversionedKind)
|
||||
}
|
||||
|
||||
// allocate a new object as the target using the target kind
|
||||
// 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)
|
||||
out, err := s.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: try to avoid the allocations here - in fast paths we are not likely to need these flags or meta
|
||||
flags, meta := s.converter.DefaultMeta(t)
|
||||
if copy {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setTargetKind(out, kind)
|
||||
setTargetKind(out, gvk)
|
||||
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) {
|
||||
func (s *Scheme) generateConvertMeta(in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
return s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||
}
|
||||
|
||||
// setTargetVersion is deprecated and should be replaced by use of setTargetKind
|
||||
func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
||||
if gv.Version == APIVersionInternal {
|
||||
// internal is a special case
|
||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
|
||||
return
|
||||
}
|
||||
if gvks, _, _ := raw.ObjectKinds(obj); len(gvks) > 0 {
|
||||
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})
|
||||
// copyAndSetTargetKind performs a conditional copy before returning the object, or an error if copy was not successful.
|
||||
func copyAndSetTargetKind(copy bool, copier ObjectCopier, obj Object, kind unversioned.GroupVersionKind) (Object, error) {
|
||||
if copy {
|
||||
copied, err := copier.Copy(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj = copied
|
||||
}
|
||||
setTargetKind(obj, kind)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// 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 (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
|
@ -460,6 +461,16 @@ type ExternalInternalSame struct {
|
|||
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) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
|
||||
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
|
||||
|
@ -500,6 +511,8 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
|
|||
func GetTestScheme() *runtime.Scheme {
|
||||
internalGV := unversioned.GroupVersion{Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Version: "v1"}
|
||||
alternateExternalGV := unversioned.GroupVersion{Group: "custom", Version: "v1"}
|
||||
differentExternalGV := unversioned.GroupVersion{Group: "other", Version: "v2"}
|
||||
|
||||
s := runtime.NewScheme()
|
||||
// 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(internalGV.WithKind("TestType3"), &TestType1{})
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -528,7 +546,7 @@ func TestKnownTypes(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConvertToVersion(t *testing.T) {
|
||||
func TestConvertToVersionBasic(t *testing.T) {
|
||||
s := GetTestScheme()
|
||||
tt := &TestType1{A: "I'm not a pointer object"}
|
||||
other, err := s.ConvertToVersion(tt, unversioned.GroupVersion{Version: "v1"})
|
||||
|
@ -537,13 +555,258 @@ func TestConvertToVersion(t *testing.T) {
|
|||
}
|
||||
converted, ok := other.(*ExternalTestType1)
|
||||
if !ok {
|
||||
t.Fatalf("Got wrong type")
|
||||
t.Fatalf("Got wrong type: %T", other)
|
||||
}
|
||||
if tt.A != converted.A {
|
||||
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) {
|
||||
internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"}
|
||||
externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"}
|
||||
|
|
|
@ -188,13 +188,17 @@ func (f CodecFactory) SupportedStreamingMediaTypes() []string {
|
|||
return f.streamingAccepts
|
||||
}
|
||||
|
||||
// LegacyCodec encodes output to a given API version, and decodes output into the internal form from
|
||||
// any recognized source. The returned codec will always encode output to JSON.
|
||||
// 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. 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
|
||||
// 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 {
|
||||
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
|
||||
|
@ -211,25 +215,39 @@ func (f CodecFactory) UniversalDeserializer() runtime.Decoder {
|
|||
// defaulting.
|
||||
//
|
||||
// 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 {
|
||||
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,
|
||||
// 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.
|
||||
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)
|
||||
}
|
||||
|
||||
// DecoderToVersion returns a decoder that targets the provided group version.
|
||||
func (f CodecFactory) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
||||
return f.CodecForVersions(nil, decoder, nil, []unversioned.GroupVersion{gv})
|
||||
func (f CodecFactory) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||
return f.CodecForVersions(nil, decoder, nil, gv)
|
||||
}
|
||||
|
||||
// EncoderForVersion returns an encoder that targets the provided group version.
|
||||
func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||
return f.CodecForVersions(encoder, nil, []unversioned.GroupVersion{gv}, nil)
|
||||
func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||
return f.CodecForVersions(encoder, nil, gv, nil)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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{
|
||||
runtime.NewCodec(serializer, nil),
|
||||
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.
|
||||
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{
|
||||
runtime.NewCodec(nil, serializer),
|
||||
nil,
|
||||
|
|
|
@ -254,7 +254,7 @@ func TestVersionedEncoding(t *testing.T) {
|
|||
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
|
||||
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{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -263,19 +263,19 @@ func TestVersionedEncoding(t *testing.T) {
|
|||
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{})
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 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{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(out) != `{"myVersionKey":"__internal","myKindKey":"TestType1"}`+"\n" {
|
||||
if string(out) != `{}`+"\n" {
|
||||
t.Fatal(string(out))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package serializer
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
|
@ -48,10 +47,10 @@ func (n *negotiatedSerializerWrapper) StreamingSerializerForMediaType(mediaType
|
|||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -17,59 +17,20 @@ limitations under the License.
|
|||
package versioning
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"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.
|
||||
func NewCodecForScheme(
|
||||
// TODO: I should be a scheme interface?
|
||||
scheme *runtime.Scheme,
|
||||
encoder runtime.Encoder,
|
||||
decoder runtime.Decoder,
|
||||
encodeVersion []unversioned.GroupVersion,
|
||||
decodeVersion []unversioned.GroupVersion,
|
||||
encodeVersion runtime.GroupVersioner,
|
||||
decodeVersion runtime.GroupVersioner,
|
||||
) runtime.Codec {
|
||||
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion)
|
||||
}
|
||||
|
@ -84,8 +45,8 @@ func NewCodec(
|
|||
creater runtime.ObjectCreater,
|
||||
copier runtime.ObjectCopier,
|
||||
typer runtime.ObjectTyper,
|
||||
encodeVersion []unversioned.GroupVersion,
|
||||
decodeVersion []unversioned.GroupVersion,
|
||||
encodeVersion runtime.GroupVersioner,
|
||||
decodeVersion runtime.GroupVersioner,
|
||||
) runtime.Codec {
|
||||
internal := &codec{
|
||||
encoder: encoder,
|
||||
|
@ -94,33 +55,10 @@ func NewCodec(
|
|||
creater: creater,
|
||||
copier: copier,
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -132,10 +70,8 @@ type codec struct {
|
|||
copier runtime.ObjectCopier
|
||||
typer runtime.ObjectTyper
|
||||
|
||||
encodeVersion map[string]unversioned.GroupVersion
|
||||
decodeVersion map[string]unversioned.GroupVersion
|
||||
|
||||
preferredEncodeVersion []unversioned.GroupVersion
|
||||
encodeVersion runtime.GroupVersioner
|
||||
decodeVersion runtime.GroupVersioner
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// invoke a version conversion
|
||||
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
|
||||
}
|
||||
|
||||
// Convert if needed.
|
||||
if isVersioned {
|
||||
// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
|
||||
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}
|
||||
}
|
||||
|
||||
// Convert if needed.
|
||||
out, err := c.convertor.ConvertToVersion(obj, targetGV)
|
||||
out, err := c.convertor.ConvertToVersion(obj, c.decodeVersion)
|
||||
if err != nil {
|
||||
return nil, gvk, err
|
||||
}
|
||||
if isVersioned {
|
||||
versioned.Objects = append(versioned.Objects, out)
|
||||
if versioned.Last() != out {
|
||||
versioned.Objects = append(versioned.Objects, out)
|
||||
}
|
||||
return versioned, 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
|
||||
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
|
||||
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)
|
||||
}
|
||||
|
||||
gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
|
||||
if c.encodeVersion == nil || isUnversioned {
|
||||
objectKind := obj.GetObjectKind()
|
||||
old := objectKind.GroupVersionKind()
|
||||
objectKind.SetGroupVersionKind(gvk)
|
||||
objectKind.SetGroupVersionKind(gvks[0])
|
||||
err = c.encoder.Encode(obj, w)
|
||||
objectKind.SetGroupVersionKind(old)
|
||||
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
|
||||
objectKind := obj.GetObjectKind()
|
||||
old := objectKind.GroupVersionKind()
|
||||
out, err := c.convertor.ConvertToVersion(obj, targetGV)
|
||||
out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
|
||||
if err != nil {
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
obj = out
|
||||
return err
|
||||
}
|
||||
// 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
|
||||
objectKind.SetGroupVersionKind(old)
|
||||
return err
|
||||
|
|
|
@ -53,7 +53,7 @@ func TestDecode(t *testing.T) {
|
|||
yaml bool
|
||||
pretty bool
|
||||
|
||||
encodes, decodes []unversioned.GroupVersion
|
||||
encodes, decodes runtime.GroupVersioner
|
||||
|
||||
defaultGVK *unversioned.GroupVersionKind
|
||||
into runtime.Object
|
||||
|
@ -67,12 +67,14 @@ func TestDecode(t *testing.T) {
|
|||
serializer: &mockSerializer{actual: gvk1},
|
||||
convertor: &checkConvertor{groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
||||
expectedGVK: gvk1,
|
||||
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||
},
|
||||
{
|
||||
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: decodable2,
|
||||
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||
},
|
||||
// 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"}},
|
||||
expectedGVK: gvk1,
|
||||
sameObject: decodable2,
|
||||
decodes: unversioned.GroupVersion{Group: "force", Version: "__internal"},
|
||||
},
|
||||
// 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"}},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
||||
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||
},
|
||||
{
|
||||
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"}},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
|
||||
decodes: unversioned.GroupVersion{Group: "other", Version: "__internal"},
|
||||
},
|
||||
|
||||
// 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},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "other", Version: "blah"}}},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: decodable1,
|
||||
},
|
||||
{
|
||||
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
|
||||
decodes: []unversioned.GroupVersion{gvk1.GroupVersion()},
|
||||
decodes: unversioned.GroupVersions{gvk1.GroupVersion()},
|
||||
|
||||
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,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
||||
},
|
||||
|
||||
// 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},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "something", Version: "else"}}},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: decodable1,
|
||||
},
|
||||
{
|
||||
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},
|
||||
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: unversioned.GroupVersions{{Group: "something", Version: "else"}}},
|
||||
copier: &checkCopy{in: decodable1, obj: decodable1, err: nil},
|
||||
expectedGVK: gvk1,
|
||||
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
|
||||
},
|
||||
|
@ -228,7 +239,7 @@ func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) {
|
|||
type checkConvertor struct {
|
||||
err error
|
||||
in, obj runtime.Object
|
||||
groupVersion unversioned.GroupVersion
|
||||
groupVersion runtime.GroupVersioner
|
||||
directConvert bool
|
||||
}
|
||||
|
||||
|
@ -244,15 +255,15 @@ func (c *checkConvertor) Convert(in, out interface{}) error {
|
|||
}
|
||||
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 {
|
||||
return nil, fmt.Errorf("unexpected call to ConvertToVersion")
|
||||
}
|
||||
if c.in != nil && c.in != in {
|
||||
return nil, fmt.Errorf("unexpected in: %s", in)
|
||||
}
|
||||
if c.groupVersion != outVersion {
|
||||
return nil, fmt.Errorf("unexpected outversion: %s", outVersion)
|
||||
if !reflect.DeepEqual(c.groupVersion, outVersion) {
|
||||
return nil, fmt.Errorf("unexpected outversion: %s (%s)", outVersion, c.groupVersion)
|
||||
}
|
||||
return c.obj, c.err
|
||||
}
|
||||
|
|
|
@ -187,9 +187,14 @@ func (UnstructuredObjectConverter) Convert(in, out interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (UnstructuredObjectConverter) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
if gvk := in.GetObjectKind().GroupVersionKind(); gvk.GroupVersion() != outVersion {
|
||||
return nil, errors.New("unstructured converter cannot convert versions")
|
||||
func (UnstructuredObjectConverter) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||
if kind := in.GetObjectKind().GroupVersionKind(); !kind.IsEmpty() {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -30,14 +30,13 @@ import (
|
|||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
var status = &unversioned.Status{
|
||||
Status: unversioned.StatusFailure,
|
||||
Code: 200,
|
||||
Reason: unversioned.StatusReasonUnknown,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
func TestV1EncodeDecodeStatus(t *testing.T) {
|
||||
status := &unversioned.Status{
|
||||
Status: unversioned.StatusFailure,
|
||||
Code: 200,
|
||||
Reason: unversioned.StatusReasonUnknown,
|
||||
Message: "",
|
||||
}
|
||||
|
||||
v1Codec := testapi.Default.Codec()
|
||||
|
||||
|
@ -65,6 +64,12 @@ func TestV1EncodeDecodeStatus(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
|
||||
// moves experimental from v1 to v1beta1 got merged.
|
||||
expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion)
|
||||
|
|
|
@ -47,7 +47,7 @@ func init() {
|
|||
api.Scheme,
|
||||
jsonSerializer,
|
||||
jsonSerializer,
|
||||
[]unversioned.GroupVersion{{Version: Version}},
|
||||
[]unversioned.GroupVersion{{Version: runtime.APIVersionInternal}},
|
||||
unversioned.GroupVersion{Version: Version},
|
||||
runtime.InternalGroupVersioner,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package framework
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/versioning"
|
||||
)
|
||||
|
@ -58,10 +57,10 @@ func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder {
|
|||
return s.serializer
|
||||
}
|
||||
|
||||
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder {
|
||||
return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, s.scheme, []unversioned.GroupVersion{gv}, nil)
|
||||
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, gv, nil)
|
||||
}
|
||||
|
||||
func (s *wrappedSerializer) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder {
|
||||
return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, s.scheme, nil, []unversioned.GroupVersion{gv})
|
||||
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, gv)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue