diff --git a/pkg/api/conversion_test.go b/pkg/api/conversion_test.go index 2a91f0aec5e..f691d2669f5 100644 --- a/pkg/api/conversion_test.go +++ b/pkg/api/conversion_test.go @@ -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) } diff --git a/pkg/api/meta/meta.go b/pkg/api/meta/meta.go index 8a08ded2600..4001d6969bd 100644 --- a/pkg/api/meta/meta.go +++ b/pkg/api/meta/meta.go @@ -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} diff --git a/pkg/api/meta/meta_test.go b/pkg/api/meta/meta_test.go index 5b6c35230e4..052812c6fa5 100644 --- a/pkg/api/meta/meta_test.go +++ b/pkg/api/meta/meta_test.go @@ -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) } diff --git a/pkg/api/meta/restmapper_test.go b/pkg/api/meta/restmapper_test.go index 8f494d0a617..5baad6e6223 100644 --- a/pkg/api/meta/restmapper_test.go +++ b/pkg/api/meta/restmapper_test.go @@ -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 } diff --git a/pkg/api/ref.go b/pkg/api/ref.go index 95690b821ad..a6d1d627dc6 100644 --- a/pkg/api/ref.go +++ b/pkg/api/ref.go @@ -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) } diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index e52e965bd11..555d3ab44fb 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -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) } diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index 1405b9d4beb..9d29341cfcc 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -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{} } diff --git a/pkg/api/unversioned/register.go b/pkg/api/unversioned/register.go index babc21dfe84..3078e88ecfc 100644 --- a/pkg/api/unversioned/register.go +++ b/pkg/api/unversioned/register.go @@ -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) } diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index b20b8dc0bdf..5acba71cb78 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -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 diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index baa4d3ca1a5..49db40d618d 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -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) } diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index f42ea04d1bb..8bf9f4e7d2c 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -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 diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index 30e8b9c2c42..7eb64f29177 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -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 diff --git a/pkg/client/unversioned/clientcmd/api/register.go b/pkg/client/unversioned/clientcmd/api/register.go index e4e23998abb..f26a6cd1b11 100644 --- a/pkg/client/unversioned/clientcmd/api/register.go +++ b/pkg/client/unversioned/clientcmd/api/register.go @@ -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) } diff --git a/pkg/client/unversioned/clientcmd/api/v1/register.go b/pkg/client/unversioned/clientcmd/api/v1/register.go index edf9fe1a70a..e5c9e88ef99 100644 --- a/pkg/client/unversioned/clientcmd/api/v1/register.go +++ b/pkg/client/unversioned/clientcmd/api/v1/register.go @@ -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) } diff --git a/pkg/conversion/converter.go b/pkg/conversion/converter.go index 51394564ced..e045dcd2f77 100644 --- a/pkg/conversion/converter.go +++ b/pkg/conversion/converter.go @@ -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 diff --git a/pkg/conversion/converter_test.go b/pkg/conversion/converter_test.go index 6dde9af9610..cdd61435fb7 100644 --- a/pkg/conversion/converter_test.go +++ b/pkg/conversion/converter_test.go @@ -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) } diff --git a/pkg/conversion/deep_copy_generated.go b/pkg/conversion/deep_copy_generated.go index c8de3276df5..717feaf18f2 100644 --- a/pkg/conversion/deep_copy_generated.go +++ b/pkg/conversion/deep_copy_generated.go @@ -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 { diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 3a081ad2d43..bb0266ddfed 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -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 } diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 2e63261411b..e88df0dd69a 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -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) } diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index 103bb96481b..e6810aa21fd 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -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 } diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index 78f613a8670..f806e489ed0 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -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 } diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 286cdf27781..c09563fb3f1 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -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 diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index b6a28d0888a..b39f7e4cfcc 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -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 } diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index f382da0d7a3..f37fcf1c6ae 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -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) diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index a649ac861a6..cc336a0ce22 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -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 } diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index f0a30edeeb8..c6c709085fe 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -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) } diff --git a/pkg/registry/thirdpartyresourcedata/codec.go b/pkg/registry/thirdpartyresourcedata/codec.go index 947957a9498..377cdf88b02 100644 --- a/pkg/registry/thirdpartyresourcedata/codec.go +++ b/pkg/registry/thirdpartyresourcedata/codec.go @@ -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. diff --git a/pkg/runtime/codec.go b/pkg/runtime/codec.go index 6379e71f5ba..c9dee29123d 100644 --- a/pkg/runtime/codec.go +++ b/pkg/runtime/codec.go @@ -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 } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index 8cb4c207d56..4606ddea7cd 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -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. diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index c388a12fe5f..04f0af29715 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -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) } diff --git a/pkg/runtime/register.go b/pkg/runtime/register.go index 07d41963080..5201a15ffd5 100644 --- a/pkg/runtime/register.go +++ b/pkg/runtime/register.go @@ -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) } diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 842e8c67adc..203d5fa43b7 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -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) } diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index 52cb796daa2..8f922f34a4c 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -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 diff --git a/pkg/runtime/serializer/codec_test.go b/pkg/runtime/serializer/codec_test.go index 342a2a599fd..e1307d3c8e5 100644 --- a/pkg/runtime/serializer/codec_test.go +++ b/pkg/runtime/serializer/codec_test.go @@ -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) } diff --git a/pkg/runtime/serializer/json/json.go b/pkg/runtime/serializer/json/json.go index 1788d980287..4b9b2b61436 100644 --- a/pkg/runtime/serializer/json/json.go +++ b/pkg/runtime/serializer/json/json.go @@ -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 } diff --git a/pkg/runtime/serializer/json/json_test.go b/pkg/runtime/serializer/json/json_test.go index f9fbf47a054..1202f74cad7 100644 --- a/pkg/runtime/serializer/json/json_test.go +++ b/pkg/runtime/serializer/json/json_test.go @@ -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 diff --git a/pkg/runtime/serializer/protobuf/protobuf.go b/pkg/runtime/serializer/protobuf/protobuf.go index a1312b305c2..8190809271c 100644 --- a/pkg/runtime/serializer/protobuf/protobuf.go +++ b/pkg/runtime/serializer/protobuf/protobuf.go @@ -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 } diff --git a/pkg/runtime/serializer/protobuf/protobuf_test.go b/pkg/runtime/serializer/protobuf/protobuf_test.go index 06b3d2e557c..8474f712254 100644 --- a/pkg/runtime/serializer/protobuf/protobuf_test.go +++ b/pkg/runtime/serializer/protobuf/protobuf_test.go @@ -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{ diff --git a/pkg/runtime/serializer/versioning/versioning.go b/pkg/runtime/serializer/versioning/versioning.go index e12682a6c28..389c31e4dd3 100644 --- a/pkg/runtime/serializer/versioning/versioning.go +++ b/pkg/runtime/serializer/versioning/versioning.go @@ -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. diff --git a/pkg/runtime/serializer/versioning/versioning_test.go b/pkg/runtime/serializer/versioning/versioning_test.go index 1b45abfd5da..4bb6469bf29 100644 --- a/pkg/runtime/serializer/versioning/versioning_test.go +++ b/pkg/runtime/serializer/versioning/versioning_test.go @@ -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") } diff --git a/pkg/runtime/types.go b/pkg/runtime/types.go index 44a309e782c..58b7843ffbc 100644 --- a/pkg/runtime/types.go +++ b/pkg/runtime/types.go @@ -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 diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index 8c39a5f3b68..24fe1357ac3 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -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 {