Merge pull request #25018 from smarterclayton/fast_conversion

Automatic merge from submit-queue

Reduce allocations during conversion, enable new UnsafeConvertToVersion path

Cleans up the conversion path to avoid a few unnecessary allocations, then creates a new UnsafeConvertToVersion path that will allow encode/decode to bypass copying the object for performance. In that subsequent PR, ConvertToVersion will start to call Copy() and we will refactor conversions to reuse as much of the existing object as possible.

Also changes the unversioned.ObjectKind signature to not require allocations - speeds up a few common paths.
This commit is contained in:
k8s-merge-robot 2016-05-12 14:50:01 -07:00
commit 5c30f983d9
42 changed files with 269 additions and 237 deletions

View File

@ -50,11 +50,11 @@ func BenchmarkPodConversion(b *testing.B) {
var result *api.Pod
for i := 0; i < b.N; i++ {
pod := &items[i%width]
versionedObj, err := scheme.ConvertToVersion(pod, testapi.Default.GroupVersion().String())
versionedObj, err := scheme.UnsafeConvertToVersion(pod, *testapi.Default.GroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String())
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
@ -76,11 +76,11 @@ func BenchmarkNodeConversion(b *testing.B) {
scheme := api.Scheme
var result *api.Node
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.ConvertToVersion(&node, testapi.Default.GroupVersion().String())
versionedObj, err := scheme.UnsafeConvertToVersion(&node, *testapi.Default.GroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String())
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
@ -104,11 +104,11 @@ func BenchmarkReplicationControllerConversion(b *testing.B) {
scheme := api.Scheme
var result *api.ReplicationController
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.ConvertToVersion(&replicationController, testapi.Default.GroupVersion().String())
versionedObj, err := scheme.UnsafeConvertToVersion(&replicationController, *testapi.Default.GroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String())
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}

View File

@ -123,33 +123,21 @@ type objectAccessor struct {
}
func (obj objectAccessor) GetKind() string {
if gvk := obj.GetObjectKind().GroupVersionKind(); gvk != nil {
return gvk.Kind
}
return ""
return obj.GetObjectKind().GroupVersionKind().Kind
}
func (obj objectAccessor) SetKind(kind string) {
gvk := obj.GetObjectKind().GroupVersionKind()
if gvk == nil {
gvk = &unversioned.GroupVersionKind{}
}
gvk.Kind = kind
obj.GetObjectKind().SetGroupVersionKind(gvk)
}
func (obj objectAccessor) GetAPIVersion() string {
if gvk := obj.GetObjectKind().GroupVersionKind(); gvk != nil {
return gvk.GroupVersion().String()
}
return ""
return obj.GetObjectKind().GroupVersionKind().GroupVersion().String()
}
func (obj objectAccessor) SetAPIVersion(version string) {
gvk := obj.GetObjectKind().GroupVersionKind()
if gvk == nil {
gvk = &unversioned.GroupVersionKind{}
}
gv, err := unversioned.ParseGroupVersion(version)
if err != nil {
gv = unversioned.GroupVersion{Version: version}

View File

@ -253,10 +253,10 @@ type InternalObject struct {
}
func (obj *InternalObject) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *InternalObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *InternalObject) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *InternalObject) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *InternalObject) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.TypeMeta.APIVersion, obj.TypeMeta.Kind)
}
@ -610,10 +610,10 @@ type MyAPIObject struct {
}
func (obj *MyAPIObject) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *MyAPIObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *MyAPIObject) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *MyAPIObject) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *MyAPIObject) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.TypeMeta.APIVersion, obj.TypeMeta.Kind)
}

View File

@ -32,7 +32,7 @@ func (fakeConvertor) Convert(in, out interface{}) error {
return nil
}
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ string) (runtime.Object, error) {
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ unversioned.GroupVersion) (runtime.Object, error) {
return in, nil
}

View File

@ -54,10 +54,7 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
// if the object referenced is actually persisted, we can just get kind from meta
// if we are building an object reference to something not yet persisted, we should fallback to scheme
var kind string
if gvk != nil {
kind = gvk.Kind
}
kind := gvk.Kind
if len(kind) == 0 {
// TODO: this is wrong
gvk, err := Scheme.ObjectKind(obj)
@ -68,10 +65,7 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
}
// if the object referenced is actually persisted, we can also get version from meta
var version string
if gvk != nil {
version = gvk.GroupVersion().String()
}
version := gvk.GroupVersion().String()
if len(version) == 0 {
selfLink := meta.GetSelfLink()
if len(selfLink) == 0 {
@ -111,9 +105,9 @@ func GetPartialReference(obj runtime.Object, fieldPath string) (*ObjectReference
// IsAnAPIObject allows clients to preemptively get a reference to an API object and pass it to places that
// intend only to get a reference to that object. This simplifies the event recording interface.
func (obj *ObjectReference) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *ObjectReference) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *ObjectReference) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *ObjectReference) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

View File

@ -281,7 +281,7 @@ func TestObjectWatchFraming(t *testing.T) {
secret.Data["binary"] = []byte{0x00, 0x10, 0x30, 0x55, 0xff, 0x00}
secret.Data["utf8"] = []byte("a string with \u0345 characters")
secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000)
converted, _ := api.Scheme.ConvertToVersion(secret, "v1")
converted, _ := api.Scheme.ConvertToVersion(secret, v1.SchemeGroupVersion)
v1secret := converted.(*v1.Secret)
for _, streamingMediaType := range api.Codecs.SupportedStreamingMediaTypes() {
s, _ := api.Codecs.StreamingSerializerForMediaType(streamingMediaType, nil)
@ -358,7 +358,7 @@ func benchmarkItems() []v1.Pod {
for i := range items {
var pod api.Pod
apiObjectFuzzer.Fuzz(&pod)
out, err := api.Scheme.ConvertToVersion(&pod, "v1")
out, err := api.Scheme.ConvertToVersion(&pod, v1.SchemeGroupVersion)
if err != nil {
panic(err)
}

View File

@ -259,11 +259,11 @@ func (gvk *GroupVersionKind) ToAPIVersionAndKind() (string, string) {
// do not use TypeMeta. This method exists to support test types and legacy serializations
// that have a distinct group and kind.
// TODO: further reduce usage of this method.
func FromAPIVersionAndKind(apiVersion, kind string) *GroupVersionKind {
func FromAPIVersionAndKind(apiVersion, kind string) GroupVersionKind {
if gv, err := ParseGroupVersion(apiVersion); err == nil {
return &GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
}
return &GroupVersionKind{Kind: kind}
return GroupVersionKind{Kind: kind}
}
// All objects that are serialized from a Scheme encode their type information. This interface is used
@ -273,10 +273,10 @@ func FromAPIVersionAndKind(apiVersion, kind string) *GroupVersionKind {
type ObjectKind interface {
// SetGroupVersionKind sets or clears the intended serialized kind of an object. Passing kind nil
// should clear the current setting.
SetGroupVersionKind(kind *GroupVersionKind)
SetGroupVersionKind(kind GroupVersionKind)
// GroupVersionKind returns the stored group, version, and kind of an object, or nil if the object does
// not expose or provide these fields.
GroupVersionKind() *GroupVersionKind
GroupVersionKind() GroupVersionKind
}
// EmptyObjectKind implements the ObjectKind interface as a noop
@ -286,7 +286,7 @@ var EmptyObjectKind = emptyObjectKind{}
type emptyObjectKind struct{}
// SetGroupVersionKind implements the ObjectKind interface
func (emptyObjectKind) SetGroupVersionKind(gvk *GroupVersionKind) {}
func (emptyObjectKind) SetGroupVersionKind(gvk GroupVersionKind) {}
// GroupVersionKind implements the ObjectKind interface
func (emptyObjectKind) GroupVersionKind() *GroupVersionKind { return nil }
func (emptyObjectKind) GroupVersionKind() GroupVersionKind { return GroupVersionKind{} }

View File

@ -25,12 +25,12 @@ func Kind(kind string) GroupKind {
}
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) SetGroupVersionKind(gvk *GroupVersionKind) {
func (obj *TypeMeta) SetGroupVersionKind(gvk GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) GroupVersionKind() *GroupVersionKind {
func (obj *TypeMeta) GroupVersionKind() GroupVersionKind {
return FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

View File

@ -149,7 +149,7 @@ func validateOwnerReference(ownerReference api.OwnerReference, fldPath *field.Pa
if len(ownerReference.UID) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
}
if _, ok := BannedOwners[*gvk]; ok {
if _, ok := BannedOwners[gvk]; ok {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
}
return allErrs

View File

@ -264,7 +264,7 @@ func (c stripVersionEncoder) EncodeToStream(obj runtime.Object, w io.Writer, ove
}
gvk.Group = ""
gvk.Version = ""
roundTrippedObj.GetObjectKind().SetGroupVersionKind(gvk)
roundTrippedObj.GetObjectKind().SetGroupVersionKind(*gvk)
return c.serializer.EncodeToStream(roundTrippedObj, w)
}

View File

@ -459,7 +459,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion().String())
versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion())
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return

View File

@ -174,7 +174,7 @@ func (tc *patchTestCase) Run(t *testing.T) {
namer := &testNamer{namespace, name}
versionedObj, err := api.Scheme.ConvertToVersion(&api.Pod{}, "v1")
versionedObj, err := api.Scheme.ConvertToVersion(&api.Pod{}, unversioned.GroupVersion{Version: "v1"})
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
return

View File

@ -35,9 +35,9 @@ func init() {
}
func (obj *Config) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *Config) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *Config) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *Config) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *Config) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

View File

@ -32,9 +32,9 @@ func init() {
}
func (obj *Config) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *Config) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *Config) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *Config) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *Config) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

View File

@ -210,9 +210,6 @@ func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {
// Meta is supplied by Scheme, when it calls Convert.
type Meta struct {
SrcVersion string
DestVersion string
// KeyNameMapping is an optional function which may map the listed key (field name)
// into a source and destination value.
KeyNameMapping FieldMappingFunc

View File

@ -611,7 +611,7 @@ func TestConverter_meta(t *testing.T) {
checks := 0
err := c.RegisterConversionFunc(
func(in *Foo, out *Bar, s Scope) error {
if s.Meta() == nil || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" {
if s.Meta() == nil {
t.Errorf("Meta did not get passed!")
}
checks++
@ -624,7 +624,7 @@ func TestConverter_meta(t *testing.T) {
}
err = c.RegisterConversionFunc(
func(in *string, out *string, s Scope) error {
if s.Meta() == nil || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" {
if s.Meta() == nil {
t.Errorf("Meta did not get passed a second time!")
}
checks++
@ -634,7 +634,7 @@ func TestConverter_meta(t *testing.T) {
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
err = c.Convert(&Foo{}, &Bar{}, 0, &Meta{SrcVersion: "test", DestVersion: "passes"})
err = c.Convert(&Foo{}, &Bar{}, 0, &Meta{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}

View File

@ -174,8 +174,6 @@ func DeepCopy_conversion_Equalities(in Equalities, out *Equalities, c *Cloner) e
}
func DeepCopy_conversion_Meta(in Meta, out *Meta, c *Cloner) error {
out.SrcVersion = in.SrcVersion
out.DestVersion = in.DestVersion
if in.KeyNameMapping == nil {
out.KeyNameMapping = nil
} else if newVal, err := c.DeepCopy(in.KeyNameMapping); err != nil {

View File

@ -221,7 +221,7 @@ func (o AnnotateOptions) RunAnnotate() error {
return err
}
obj, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion().String())
obj, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
return err
}

View File

@ -87,24 +87,24 @@ type ExternalType2 struct {
}
func (obj *internalType) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *internalType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *internalType) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *internalType) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *internalType) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
func (obj *externalType) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *externalType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *externalType) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *externalType) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *externalType) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
func (obj *ExternalType2) GetObjectKind() unversioned.ObjectKind { return obj }
func (obj *ExternalType2) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *ExternalType2) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *ExternalType2) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *ExternalType2) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

View File

@ -167,7 +167,7 @@ func (o *ConvertOptions) RunConvert() error {
}
infos := []*resource.Info{info}
objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String(), o.encoder)
objects, err := resource.AsVersionedObject(infos, false, o.outputVersion, o.encoder)
if err != nil {
return err
}

View File

@ -182,7 +182,7 @@ func RunEdit(f *cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args
if err != nil {
return err
}
objs, err := resource.AsVersionedObjects(infos, defaultVersion.String(), encoder)
objs, err := resource.AsVersionedObjects(infos, defaultVersion, encoder)
if err != nil {
return err
}

View File

@ -246,7 +246,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
if err != nil {
return err
}
obj, err := resource.AsVersionedObject(infos, !singular, version.String(), f.JSONEncoder())
obj, err := resource.AsVersionedObject(infos, !singular, version, f.JSONEncoder())
if err != nil {
return err
}
@ -273,7 +273,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
}
for ix := range infos {
objs[ix], err = infos[ix].Mapping.ConvertToVersion(infos[ix].Object, version.String())
objs[ix], err = infos[ix].Mapping.ConvertToVersion(infos[ix].Object, version)
if err != nil {
allErrs = append(allErrs, err)
continue

View File

@ -246,7 +246,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
}
outputObj = info.Object
} else {
obj, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion().String())
obj, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
return err
}

View File

@ -211,7 +211,7 @@ func (r *Result) Watch(resourceVersion string) (watch.Interface, error) {
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func AsVersionedObject(infos []*Info, forceList bool, version string, encoder runtime.Encoder) (runtime.Object, error) {
func AsVersionedObject(infos []*Info, forceList bool, version unversioned.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
objects, err := AsVersionedObjects(infos, version, encoder)
if err != nil {
return nil, err
@ -222,7 +222,7 @@ func AsVersionedObject(infos []*Info, forceList bool, version string, encoder ru
object = objects[0]
} else {
object = &api.List{Items: objects}
converted, err := tryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion.Version)
converted, err := tryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
return nil, err
}
@ -234,7 +234,7 @@ func AsVersionedObject(infos []*Info, forceList bool, version string, encoder ru
// AsVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder) ([]runtime.Object, error) {
func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
objects := []runtime.Object{}
for _, info := range infos {
if info.Object == nil {
@ -250,7 +250,7 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder)
// objects that are not part of api.Scheme must be converted to JSON
// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
if len(version) > 0 {
if !version.IsEmpty() {
if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) {
// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
data, err := runtime.Encode(encoder, info.Object)
@ -263,7 +263,7 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder)
}
}
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion().String())
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
return nil, err
}
@ -274,10 +274,10 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder)
// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(convertor runtime.ObjectConvertor, object runtime.Object, versions ...string) (runtime.Object, error) {
func tryConvert(convertor runtime.ObjectConvertor, object runtime.Object, versions ...unversioned.GroupVersion) (runtime.Object, error) {
var last error
for _, version := range versions {
if len(version) == 0 {
if version.IsEmpty() {
return object, nil
}
obj, err := convertor.ConvertToVersion(object, version)

View File

@ -182,7 +182,7 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
if version.IsEmpty() {
continue
}
converted, err := p.convertor.ConvertToVersion(obj, version.String())
converted, err := p.convertor.ConvertToVersion(obj, version)
if runtime.IsNotRegisteredError(err) {
continue
}

View File

@ -105,7 +105,7 @@ func TestPrinter(t *testing.T) {
},
}
emptyListTest := &api.PodList{}
testapi, err := api.Scheme.ConvertToVersion(podTest, testapi.Default.GroupVersion().String())
testapi, err := api.Scheme.ConvertToVersion(podTest, *testapi.Default.GroupVersion())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@ -40,7 +40,7 @@ type thirdPartyObjectConverter struct {
converter runtime.ObjectConvertor
}
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion string) (out runtime.Object, err error) {
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion unversioned.GroupVersion) (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.

View File

@ -162,7 +162,7 @@ func (c *parameterCodec) EncodeParameters(obj Object, to unversioned.GroupVersio
return nil, err
}
if to != gvk.GroupVersion() {
out, err := c.convertor.ConvertToVersion(obj, to.String())
out, err := c.convertor.ConvertToVersion(obj, to)
if err != nil {
return nil, err
}

View File

@ -43,10 +43,31 @@ func (t objectTyperToTyper) ObjectKind(obj Object) (*unversioned.GroupVersionKin
return &gvk, unversionedType, nil
}
// ObjectTyperToTyper casts the old typer interface to the new typer interface
func ObjectTyperToTyper(typer ObjectTyper) Typer {
return objectTyperToTyper{typer: typer}
}
// unsafeObjectConvertor implements ObjectConvertor using the unsafe conversion path.
type unsafeObjectConvertor struct {
*Scheme
}
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) {
return c.Scheme.UnsafeConvertToVersion(in, outVersion)
}
// UnsafeObjectConvertor performs object conversion without copying the object structure,
// for use when the converted object will not be reused or mutated. Primarily for use within
// versioned codecs, which use the external object for serialization but do not return it.
func UnsafeObjectConvertor(scheme *Scheme) ObjectConvertor {
return unsafeObjectConvertor{scheme}
}
// fieldPtr puts the address of fieldName, which must be a member of v,
// into dest, which must be an address of a variable to which this field's
// address can be assigned.

View File

@ -161,7 +161,7 @@ type StorageSerializer interface {
// Non-codec interfaces
type ObjectVersioner interface {
ConvertToVersion(in Object, outVersion string) (out Object, err error)
ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (out Object, err error)
}
// ObjectConvertor converts an object to a different version.
@ -171,7 +171,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 string) (out Object, err error)
ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (out Object, err error)
ConvertFieldLabel(version, kind, label, value string) (string, string, error)
}

View File

@ -21,12 +21,12 @@ import (
)
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (obj *TypeMeta) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *TypeMeta) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

View File

@ -124,11 +124,8 @@ func (s *Scheme) nameFunc(t reflect.Type) string {
// fromScope gets the input version, desired output version, and desired Scheme
// from a conversion.Scope.
func (s *Scheme) fromScope(scope conversion.Scope) (inVersion, outVersion string, scheme *Scheme) {
scheme = s
inVersion = scope.Meta().SrcVersion
outVersion = scope.Meta().DestVersion
return inVersion, outVersion, scheme
func (s *Scheme) fromScope(scope conversion.Scope) *Scheme {
return s
}
// Converter allows access to the converter for the scheme
@ -476,16 +473,12 @@ func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string,
// 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 string) (Object, error) {
gv, err := unversioned.ParseGroupVersion(outVersion)
if err != nil {
return nil, err
}
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, gv)
setTargetVersion(in, s, outVersion)
return in, nil
}
t := reflect.TypeOf(in)
@ -509,7 +502,7 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error)
kind = kinds[0]
}
outKind := gv.WithKind(kind.Kind)
outKind := outVersion.WithKind(kind.Kind)
inKind, err := s.ObjectKind(in)
if err != nil {
@ -521,29 +514,106 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error)
return nil, err
}
flags, meta := s.generateConvertMeta(inKind.GroupVersion(), gv, in)
flags, meta := s.generateConvertMeta(inKind.GroupVersion(), outVersion, in)
if err := s.converter.Convert(in, out, flags, meta); err != nil {
return nil, err
}
setTargetVersion(out, s, gv)
setTargetVersion(out, s, outVersion)
return out, nil
}
// UnsafeConvertToVersion will convert in to the provided outVersion 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
}
// determine the incoming kinds with as few allocations as possible.
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)
}
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)
}
// 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
}
}
// 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
}
// 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)
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 err := s.converter.Convert(in, out, flags, meta); err != nil {
return nil, err
}
setTargetKind(out, kind)
return out, nil
}
// generateConvertMeta constructs the meta value we pass to Convert.
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
flags, meta := s.converter.DefaultMeta(reflect.TypeOf(in))
meta.SrcVersion = srcGroupVersion.String()
meta.DestVersion = destGroupVersion.String()
return flags, meta
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(nil)
} else {
gvk, _ := raw.ObjectKind(obj)
obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind})
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
return
}
gvk, _ := raw.ObjectKind(obj)
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind})
}
// setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
func setTargetKind(obj Object, kind unversioned.GroupVersionKind) {
if kind.Version == APIVersionInternal {
// internal is a special case
// TODO: look at removing the need to special case this
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
return
}
obj.GetObjectKind().SetGroupVersionKind(kind)
}

View File

@ -65,24 +65,12 @@ func TestScheme(t *testing.T) {
// Register functions to verify that scope.Meta() gets set correctly.
err := scheme.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a)
}
if e, a := externalGV.String(), scope.Meta().DestVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a)
}
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
scope.Convert(&in.TestString, &out.TestString, 0)
internalToExternalCalls++
return nil
},
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a)
}
if e, a := internalGV.String(), scope.Meta().DestVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a)
}
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
scope.Convert(&in.TestString, &out.TestString, 0)
externalToInternalCalls++
@ -472,10 +460,10 @@ type ExternalInternalSame struct {
}
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()
}
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
}
@ -542,7 +530,7 @@ func TestKnownTypes(t *testing.T) {
func TestConvertToVersion(t *testing.T) {
s := GetTestScheme()
tt := &TestType1{A: "I'm not a pointer object"}
other, err := s.ConvertToVersion(tt, "v1")
other, err := s.ConvertToVersion(tt, unversioned.GroupVersion{Version: "v1"})
if err != nil {
t.Fatalf("Failure: %v", err)
}
@ -570,24 +558,12 @@ func TestMetaValues(t *testing.T) {
err := s.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
t.Logf("internal -> external")
if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a {
t.Fatalf("Expected '%v', got '%v'", e, a)
}
if e, a := externalGV.String(), scope.Meta().DestVersion; e != a {
t.Fatalf("Expected '%v', got '%v'", e, a)
}
scope.Convert(&in.TestString, &out.TestString, 0)
internalToExternalCalls++
return nil
},
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
t.Logf("external -> internal")
if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a {
t.Errorf("Expected '%v', got '%v'", e, a)
}
if e, a := internalGV.String(), scope.Meta().DestVersion; e != a {
t.Fatalf("Expected '%v', got '%v'", e, a)
}
scope.Convert(&in.TestString, &out.TestString, 0)
externalToInternalCalls++
return nil
@ -602,12 +578,12 @@ func TestMetaValues(t *testing.T) {
s.Log(t)
out, err := s.ConvertToVersion(simple, externalGV.String())
out, err := s.ConvertToVersion(simple, externalGV)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
internal, err := s.ConvertToVersion(out, internalGV.String())
internal, err := s.ConvertToVersion(out, internalGV)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -643,12 +619,6 @@ func TestMetaValuesUnregisteredConvert(t *testing.T) {
// Register functions to verify that scope.Meta() gets set correctly.
err := s.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a {
t.Fatalf("Expected '%v', got '%v'", e, a)
}
if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a {
t.Fatalf("Expected '%v', got '%v'", e, a)
}
scope.Convert(&in.TestString, &out.TestString, 0)
internalToExternalCalls++
return nil

View File

@ -129,10 +129,10 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
)
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()
}
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind {
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() unversioned.GroupVersionKind {
return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
}

View File

@ -111,7 +111,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi
if unk, ok := into.(*runtime.Unknown); ok && unk != nil {
unk.Raw = originalData
unk.ContentType = runtime.ContentTypeJSON
unk.GetObjectKind().SetGroupVersionKind(actual)
unk.GetObjectKind().SetGroupVersionKind(*actual)
return unk, actual, nil
}

View File

@ -31,12 +31,12 @@ import (
type testDecodable struct {
Other string
Value int `json:"value"`
gvk *unversioned.GroupVersionKind
gvk unversioned.GroupVersionKind
}
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk }
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() unversioned.GroupVersionKind { return d.gvk }
func TestDecode(t *testing.T) {
testCases := []struct {
@ -76,34 +76,28 @@ func TestDecode(t *testing.T) {
errFn: func(err error) bool { return err.Error() == "fake error" },
},
{
data: []byte("{}"),
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{
gvk: nil, // json serializer does NOT set GVK
},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
data: []byte("{}"),
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
},
// version without group is not defaulted
{
data: []byte(`{"apiVersion":"blah"}`),
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{
gvk: nil, // json serializer does NOT set GVK
},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"},
data: []byte(`{"apiVersion":"blah"}`),
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"},
},
// group without version is defaulted
{
data: []byte(`{"apiVersion":"other/"}`),
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{
gvk: nil, // json serializer does NOT set GVK
},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
data: []byte(`{"apiVersion":"other/"}`),
defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{},
expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
},
// accept runtime.Unknown as into and bypass creator

View File

@ -120,14 +120,14 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi
}
actual := unk.GroupVersionKind()
copyKindDefaults(actual, gvk)
copyKindDefaults(&actual, gvk)
if intoUnknown, ok := into.(*runtime.Unknown); ok && intoUnknown != nil {
*intoUnknown = unk
if len(intoUnknown.ContentType) == 0 {
intoUnknown.ContentType = s.contentType
}
return intoUnknown, actual, nil
return intoUnknown, &actual, nil
}
if into != nil {
@ -136,16 +136,16 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi
case runtime.IsNotRegisteredError(err):
pb, ok := into.(proto.Message)
if !ok {
return nil, actual, errNotMarshalable{reflect.TypeOf(into)}
return nil, &actual, errNotMarshalable{reflect.TypeOf(into)}
}
if err := proto.Unmarshal(unk.Raw, pb); err != nil {
return nil, actual, err
return nil, &actual, err
}
return into, actual, nil
return into, &actual, nil
case err != nil:
return nil, actual, err
return nil, &actual, err
default:
copyKindDefaults(actual, typed)
copyKindDefaults(&actual, typed)
// if the result of defaulting did not set a version or group, ensure that at least group is set
// (copyKindDefaults will not assign Group if version is already set). This guarantees that the group
// of into is set if there is no better information from the caller or object.
@ -156,25 +156,24 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi
}
if len(actual.Kind) == 0 {
return nil, actual, runtime.NewMissingKindErr(fmt.Sprintf("%#v", unk.TypeMeta))
return nil, &actual, runtime.NewMissingKindErr(fmt.Sprintf("%#v", unk.TypeMeta))
}
if len(actual.Version) == 0 {
return nil, actual, runtime.NewMissingVersionErr(fmt.Sprintf("%#v", unk.TypeMeta))
return nil, &actual, runtime.NewMissingVersionErr(fmt.Sprintf("%#v", unk.TypeMeta))
}
return unmarshalToObject(s.typer, s.creater, actual, into, unk.Raw)
return unmarshalToObject(s.typer, s.creater, &actual, into, unk.Raw)
}
// EncodeToStream serializes the provided object to the given writer. Overrides is ignored.
func (s *Serializer) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unversioned.GroupVersion) error {
var unk runtime.Unknown
if kind := obj.GetObjectKind().GroupVersionKind(); kind != nil {
unk = runtime.Unknown{
TypeMeta: runtime.TypeMeta{
Kind: kind.Kind,
APIVersion: kind.GroupVersion().String(),
},
}
kind := obj.GetObjectKind().GroupVersionKind()
unk = runtime.Unknown{
TypeMeta: runtime.TypeMeta{
Kind: kind.Kind,
APIVersion: kind.GroupVersion().String(),
},
}
prefixSize := uint64(len(s.prefix))
@ -334,7 +333,7 @@ func (s *RawSerializer) Decode(originalData []byte, gvk *unversioned.GroupVersio
intoUnknown.Raw = data
intoUnknown.ContentEncoding = ""
intoUnknown.ContentType = s.contentType
intoUnknown.SetGroupVersionKind(actual)
intoUnknown.SetGroupVersionKind(*actual)
return intoUnknown, actual, nil
}

View File

@ -34,12 +34,12 @@ import (
)
type testObject struct {
gvk *unversioned.GroupVersionKind
gvk unversioned.GroupVersionKind
}
func (d *testObject) GetObjectKind() unversioned.ObjectKind { return d }
func (d *testObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk }
func (d *testObject) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk }
func (d *testObject) GetObjectKind() unversioned.ObjectKind { return d }
func (d *testObject) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
func (d *testObject) GroupVersionKind() unversioned.GroupVersionKind { return d.gvk }
type testMarshalable struct {
testObject
@ -106,7 +106,7 @@ func TestEncode(t *testing.T) {
0x22, 0x00, // content-encoding
}
obj2 := &testMarshalable{
testObject: testObject{gvk: &unversioned.GroupVersionKind{Kind: "test", Group: "other", Version: "version"}},
testObject: testObject{gvk: unversioned.GroupVersionKind{Kind: "test", Group: "other", Version: "version"}},
data: []byte{0x01, 0x02, 0x03},
}
wire2 := []byte{

View File

@ -71,7 +71,7 @@ func NewCodecForScheme(
encodeVersion []unversioned.GroupVersion,
decodeVersion []unversioned.GroupVersion,
) runtime.Codec {
return NewCodec(encoder, decoder, scheme, scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion)
return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion)
}
// NewCodec takes objects in their internal versions and converts them to external versions before
@ -204,7 +204,7 @@ func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, in
}
// Convert if needed.
out, err := c.convertor.ConvertToVersion(obj, targetGV.String())
out, err := c.convertor.ConvertToVersion(obj, targetGV)
if err != nil {
return nil, gvk, err
}
@ -227,10 +227,12 @@ func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unv
}
if (c.encodeVersion == nil && len(overrides) == 0) || isUnversioned {
old := obj.GetObjectKind().GroupVersionKind()
obj.GetObjectKind().SetGroupVersionKind(gvk)
defer obj.GetObjectKind().SetGroupVersionKind(old)
return c.encoder.EncodeToStream(obj, w, overrides...)
objectKind := obj.GetObjectKind()
old := objectKind.GroupVersionKind()
objectKind.SetGroupVersionKind(*gvk)
err = c.encoder.EncodeToStream(obj, w, overrides...)
objectKind.SetGroupVersionKind(old)
return err
}
targetGV, ok := c.encodeVersion[gvk.Group]
@ -261,22 +263,21 @@ func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unv
}
// Perform a conversion if necessary
if gvk.GroupVersion() != targetGV {
out, err := c.convertor.ConvertToVersion(obj, targetGV.String())
if err != nil {
if ok {
return err
}
} else {
obj = out
objectKind := obj.GetObjectKind()
old := objectKind.GroupVersionKind()
out, err := c.convertor.ConvertToVersion(obj, targetGV)
if err != nil {
if ok {
return err
}
} else {
old := obj.GetObjectKind().GroupVersionKind()
defer obj.GetObjectKind().SetGroupVersionKind(old)
obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: targetGV.Group, Version: targetGV.Version, Kind: gvk.Kind})
obj = out
}
return c.encoder.EncodeToStream(obj, w, overrides...)
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
err = c.encoder.EncodeToStream(obj, w, overrides...)
// restore the old GVK, in case conversion returned the same object
objectKind.SetGroupVersionKind(old)
return err
}
// promoteOrPrependGroupVersion finds the group version in the provided group versions that has the same group as target.

View File

@ -30,12 +30,12 @@ import (
type testDecodable struct {
Other string
Value int `json:"value"`
gvk *unversioned.GroupVersionKind
gvk unversioned.GroupVersionKind
}
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk }
func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk unversioned.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() unversioned.GroupVersionKind { return d.gvk }
func TestDecode(t *testing.T) {
gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
@ -65,12 +65,12 @@ func TestDecode(t *testing.T) {
}{
{
serializer: &mockSerializer{actual: gvk1},
convertor: &checkConvertor{groupVersion: "other/__internal"},
convertor: &checkConvertor{groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
},
{
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
sameObject: decodable2,
},
@ -78,7 +78,7 @@ func TestDecode(t *testing.T) {
{
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
defaultGVK: &unversioned.GroupVersionKind{Group: "force"},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "force/__internal"},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "force", Version: "__internal"}},
expectedGVK: gvk1,
sameObject: decodable2,
},
@ -118,7 +118,7 @@ func TestDecode(t *testing.T) {
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
copier: &checkCopy{in: decodable1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
},
@ -127,7 +127,7 @@ func TestDecode(t *testing.T) {
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
copier: &checkCopy{in: decodable1, obj: nil, err: fmt.Errorf("error on copy")},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: unversioned.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
},
@ -228,7 +228,7 @@ func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) {
type checkConvertor struct {
err error
in, obj runtime.Object
groupVersion string
groupVersion unversioned.GroupVersion
directConvert bool
}
@ -244,7 +244,7 @@ func (c *checkConvertor) Convert(in, out interface{}) error {
}
return c.err
}
func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion string) (out runtime.Object, err error) {
func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion unversioned.GroupVersion) (out runtime.Object, err error) {
if c.directConvert {
return nil, fmt.Errorf("unexpected call to ConvertToVersion")
}

View File

@ -301,18 +301,18 @@ func (u *Unstructured) SetAnnotations(annotations map[string]string) {
u.setNestedMap(annotations, "metadata", "annotations")
}
func (u *Unstructured) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (u *Unstructured) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
u.SetAPIVersion(gvk.GroupVersion().String())
u.SetKind(gvk.Kind)
}
func (u *Unstructured) GroupVersionKind() *unversioned.GroupVersionKind {
func (u *Unstructured) GroupVersionKind() unversioned.GroupVersionKind {
gv, err := unversioned.ParseGroupVersion(u.GetAPIVersion())
if err != nil {
return nil
return unversioned.GroupVersionKind{}
}
gvk := gv.WithKind(u.GetKind())
return &gvk
return gvk
}
// UnstructuredList allows lists that do not have Golang structs
@ -364,18 +364,18 @@ func (u *UnstructuredList) SetSelfLink(selfLink string) {
u.setNestedField(selfLink, "metadata", "selfLink")
}
func (u *UnstructuredList) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {
func (u *UnstructuredList) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
u.SetAPIVersion(gvk.GroupVersion().String())
u.SetKind(gvk.Kind)
}
func (u *UnstructuredList) GroupVersionKind() *unversioned.GroupVersionKind {
func (u *UnstructuredList) GroupVersionKind() unversioned.GroupVersionKind {
gv, err := unversioned.ParseGroupVersion(u.GetAPIVersion())
if err != nil {
return nil
return unversioned.GroupVersionKind{}
}
gvk := gv.WithKind(u.GetKind())
return &gvk
return gvk
}
// VersionedObjects is used by Decoders to give callers a way to access all versions

View File

@ -45,10 +45,10 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionK
gvk := obj.GetObjectKind().GroupVersionKind()
if len(gvk.Kind) == 0 {
return nil, gvk, NewMissingKindErr(string(data))
return nil, &gvk, NewMissingKindErr(string(data))
}
return obj, gvk, nil
return obj, &gvk, nil
}
func (unstructuredJSONScheme) EncodeToStream(obj Object, w io.Writer, overrides ...unversioned.GroupVersion) error {