diff --git a/pkg/api/ref.go b/pkg/api/ref.go index 552ec5bd1d..e04af0474d 100644 --- a/pkg/api/ref.go +++ b/pkg/api/ref.go @@ -53,10 +53,11 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) { // if we are building an object reference to something not yet persisted, we should fallback to scheme kind := meta.Kind() if kind == "" { - _, kind, err = Scheme.ObjectVersionAndKind(obj) + gvk, err := Scheme.ObjectKind(obj) if err != nil { return nil, err } + kind = gvk.Kind } // if the object referenced is actually persisted, we can also get version from meta diff --git a/pkg/api/rest/create.go b/pkg/api/rest/create.go index 574cdc587f..321a343711 100644 --- a/pkg/api/rest/create.go +++ b/pkg/api/rest/create.go @@ -111,9 +111,9 @@ func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.Obje if err != nil { return nil, "", errors.NewInternalError(err) } - _, kind, err := typer.ObjectVersionAndKind(obj) + gvk, err := typer.ObjectKind(obj) if err != nil { return nil, "", errors.NewInternalError(err) } - return objectMeta, kind, nil + return objectMeta, gvk.Kind, nil } diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 467efdce3b..40f0cb3cef 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -63,6 +63,9 @@ func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, se func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { printer := spew.ConfigState{DisableMethods: true} + gvk, err := api.Scheme.ObjectKind(item) + t.Logf("fully qualified kind for %v is %v with codec %v", reflect.TypeOf(item), gvk, codec) + name := reflect.TypeOf(item).Elem().Name() data, err := codec.Encode(item) if err != nil { @@ -96,7 +99,7 @@ func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { func roundTripSame(t *testing.T, item runtime.Object, except ...string) { set := sets.NewString(except...) seed := rand.Int63() - fuzzInternalObject(t, "", item, seed) + fuzzInternalObject(t, testapi.Default.InternalGroupVersion().String(), item, seed) version := testapi.Default.VersionUnderTest codecs := []runtime.Codec{} @@ -154,6 +157,7 @@ func TestRoundTripTypes(t *testing.T) { // defer api.Scheme.Log(nil) for kind := range api.Scheme.KnownTypes(testapi.Default.InternalGroupVersion()) { + t.Logf("working on %v in %v", kind, testapi.Default.InternalGroupVersion()) if nonRoundTrippableTypes.Has(kind) { continue } @@ -168,18 +172,18 @@ func TestRoundTripTypes(t *testing.T) { } func doRoundTripTest(kind string, t *testing.T) { - item, err := api.Scheme.New("", kind) + item, err := api.Scheme.New(testapi.Default.InternalGroupVersion().String(), kind) if err != nil { t.Fatalf("Couldn't make a %v? %v", kind, err) } if _, err := meta.TypeAccessor(item); err != nil { t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) } - if api.Scheme.Recognizes(testapi.Default.VersionUnderTest, kind) { + if api.Scheme.Recognizes(testapi.Default.GroupVersion().WithKind(kind)) { roundTripSame(t, item, nonRoundTrippableTypesByVersion[kind]...) } if !nonInternalRoundTrippableTypes.Has(kind) { - roundTrip(t, api.Codec, fuzzInternalObject(t, "", item, rand.Int63())) + roundTrip(t, api.Codec, fuzzInternalObject(t, testapi.Default.InternalGroupVersion().String(), item, rand.Int63())) } } diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index e6bddc4508..f08bb66736 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -214,22 +214,23 @@ func (g TestGroup) RESTMapper() meta.RESTMapper { // Get codec based on runtime.Object func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) { - _, kind, err := api.Scheme.ObjectVersionAndKind(obj) + gvk, err := api.Scheme.ObjectKind(obj) if err != nil { return nil, fmt.Errorf("unexpected encoding error: %v", err) } - // TODO: caesarxuchao: we should detect which group an object belongs to - // by using the version returned by Schem.ObjectVersionAndKind() once we - // split the schemes for internal objects. - // TODO: caesarxuchao: we should add a map from kind to group in Scheme. + for _, group := range Groups { - if api.Scheme.Recognizes(group.GroupAndVersion(), kind) { + if group.GroupVersion().Group != gvk.Group { + continue + } + + if api.Scheme.Recognizes(gvk) { return group.Codec(), nil } } // Codec used for unversioned types - if api.Scheme.Recognizes("", kind) { + if api.Scheme.Recognizes(gvk) { return api.Codec, nil } - return nil, fmt.Errorf("unexpected kind: %v", kind) + return nil, fmt.Errorf("unexpected kind: %v", gvk) } diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index b1d795c9b9..c2620551f2 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -175,8 +175,10 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { // TODO: uncomment when round trip starts from a versioned object if true { //c.RandBool() { *j = &runtime.Unknown{ - TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, - RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), + // apiVersion has rules now. Since it includes / and only `v1` can be bare, + // then this must choose a valid format to deserialize + TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown.group/unknown"}, + RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index 9ee8387c27..cc1a8efb04 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -78,7 +78,7 @@ func (gvk GroupVersionKind) GroupVersion() GroupVersion { return GroupVersion{Group: gvk.Group, Version: gvk.Version} } -func (gvk *GroupVersionKind) String() string { +func (gvk GroupVersionKind) String() string { return gvk.Group + "/" + gvk.Version + ", Kind=" + gvk.Kind } diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index ff8d88d141..5b10c8eeed 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -122,11 +122,31 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag hasSubresource := len(subresource) > 0 object := storage.New() - _, kind, err := a.group.Typer.ObjectVersionAndKind(object) + fqKinds, err := a.group.Typer.ObjectKinds(object) if err != nil { return nil, err } - gvk := a.group.GroupVersion.WithKind(kind) + // a given go type can have multiple potential fully qualified kinds. Find the one that corresponds with the group + // we're trying to register here + fqKindToRegister := unversioned.GroupVersionKind{} + for _, fqKind := range fqKinds { + if fqKind.Group == a.group.GroupVersion.Group { + fqKindToRegister = fqKind + break + } + + // TODO This keeps it doing what it was doing before, but it doesn't feel right. + if fqKind.Group == "extensions" && fqKind.Kind == "ThirdPartyResourceData" { + fqKindToRegister = fqKind + fqKindToRegister.Group = a.group.GroupVersion.Group + fqKindToRegister.Version = a.group.GroupVersion.Version + } + } + + if fqKindToRegister.IsEmpty() { + return nil, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion) + } + kind := fqKindToRegister.Kind versionedPtr, err := a.group.Creater.New(a.group.GroupVersion.String(), kind) if err != nil { @@ -134,7 +154,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag } versionedObject := indirectArbitraryPointer(versionedPtr) - mapping, err := a.group.Mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + mapping, err := a.group.Mapper.RESTMapping(fqKindToRegister.GroupKind(), a.group.GroupVersion.Version) if err != nil { return nil, err } @@ -146,13 +166,25 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag return nil, fmt.Errorf("subresources can only be declared when the parent is also registered: %s needs %s", path, resource) } parentObject := parentStorage.New() - _, parentKind, err := a.group.Typer.ObjectVersionAndKind(parentObject) + + parentFQKinds, err := a.group.Typer.ObjectKinds(parentObject) if err != nil { return nil, err } - parentGVK := a.group.GroupVersion.WithKind(parentKind) + // a given go type can have multiple potential fully qualified kinds. Find the one that corresponds with the group + // we're trying to register here + parentFQKindToRegister := unversioned.GroupVersionKind{} + for _, fqKind := range parentFQKinds { + if fqKind.Group == a.group.GroupVersion.Group { + parentFQKindToRegister = fqKind + break + } + } + if parentFQKindToRegister.IsEmpty() { + return nil, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion) + } - parentMapping, err := a.group.Mapper.RESTMapping(parentGVK.GroupKind(), parentGVK.Version) + parentMapping, err := a.group.Mapper.RESTMapping(parentFQKindToRegister.GroupKind(), a.group.GroupVersion.Version) if err != nil { return nil, err } @@ -184,8 +216,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag var versionedList interface{} if isLister { list := lister.NewList() - _, listKind, err := a.group.Typer.ObjectVersionAndKind(list) - versionedListPtr, err := a.group.Creater.New(a.group.GroupVersion.String(), listKind) + listGVK, err := a.group.Typer.ObjectKind(list) + versionedListPtr, err := a.group.Creater.New(a.group.GroupVersion.String(), listGVK.Kind) if err != nil { return nil, err } @@ -225,19 +257,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag ) if isGetterWithOptions { getOptions, getSubpath, getSubpathKey = getterWithOptions.NewGetOptions() - getOptionsGVString, getOptionsKind, err := a.group.Typer.ObjectVersionAndKind(getOptions) + getOptionsInternalKind, err = a.group.Typer.ObjectKind(getOptions) if err != nil { return nil, err } - gv, err := unversioned.ParseGroupVersion(getOptionsGVString) - if err != nil { - return nil, err - } - getOptionsInternalKind = gv.WithKind(getOptionsKind) // TODO this should be a list of all the different external versions we can coerce into the internalKind - getOptionsExternalKind = serverGroupVersion.WithKind(getOptionsKind) + getOptionsExternalKind = serverGroupVersion.WithKind(getOptionsInternalKind.Kind) - versionedGetOptions, err = a.group.Creater.New(serverGroupVersion.String(), getOptionsKind) + versionedGetOptions, err = a.group.Creater.New(serverGroupVersion.String(), getOptionsInternalKind.Kind) if err != nil { return nil, err } @@ -255,19 +282,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag if isConnecter { connectOptions, connectSubpath, connectSubpathKey = connecter.NewConnectOptions() if connectOptions != nil { - connectOptionsGVString, connectOptionsKind, err := a.group.Typer.ObjectVersionAndKind(connectOptions) + connectOptionsInternalKind, err = a.group.Typer.ObjectKind(connectOptions) if err != nil { return nil, err } - gv, err := unversioned.ParseGroupVersion(connectOptionsGVString) - if err != nil { - return nil, err - } - connectOptionsInternalKind = gv.WithKind(connectOptionsKind) // TODO this should be a list of all the different external versions we can coerce into the internalKind - connectOptionsExternalKind = serverGroupVersion.WithKind(connectOptionsKind) + connectOptionsExternalKind = serverGroupVersion.WithKind(connectOptionsInternalKind.Kind) - versionedConnectOptions, err = a.group.Creater.New(serverGroupVersion.String(), connectOptionsKind) + versionedConnectOptions, err = a.group.Creater.New(serverGroupVersion.String(), connectOptionsInternalKind.Kind) } } diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 803230f5a5..40dd8a6d38 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -745,14 +745,14 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, // transformDecodeError adds additional information when a decode fails. func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, body []byte) error { - _, kind, err := typer.ObjectVersionAndKind(into) + objectGroupVersionKind, err := typer.ObjectKind(into) if err != nil { return err } - if version, dataKind, err := typer.DataVersionAndKind(body); err == nil && len(dataKind) > 0 { - return errors.NewBadRequest(fmt.Sprintf("%s in version %s cannot be handled as a %s: %v", dataKind, version, kind, baseErr)) + if dataGroupVersionKind, err := typer.DataKind(body); err == nil && len(dataGroupVersionKind.Kind) > 0 { + return errors.NewBadRequest(fmt.Sprintf("%s in version %v cannot be handled as a %s: %v", dataGroupVersionKind.Kind, dataGroupVersionKind.GroupVersion(), objectGroupVersionKind.Kind, baseErr)) } - return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v", kind, baseErr)) + return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v", objectGroupVersionKind.Kind, baseErr)) } // setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request diff --git a/pkg/auth/authorizer/abac/abac.go b/pkg/auth/authorizer/abac/abac.go index 77e90a9581..0ef8768714 100644 --- a/pkg/auth/authorizer/abac/abac.go +++ b/pkg/auth/authorizer/abac/abac.go @@ -77,12 +77,12 @@ func NewFromFile(path string) (policyList, error) { continue } - version, kind, err := api.Scheme.DataVersionAndKind(b) + dataKind, err := api.Scheme.DataKind(b) if err != nil { return nil, policyLoadError{path, i, b, err} } - if version == "" && kind == "" { + if dataKind.IsEmpty() { unversionedLines++ // Migrate unversioned policy object oldPolicy := &v0.Policy{} diff --git a/pkg/client/unversioned/client_test.go b/pkg/client/unversioned/client_test.go index 95ac1a6e3d..f86e066760 100644 --- a/pkg/client/unversioned/client_test.go +++ b/pkg/client/unversioned/client_test.go @@ -207,7 +207,7 @@ func validateFields(a, b string) bool { func body(t *testing.T, obj runtime.Object, raw *string) *string { if obj != nil { - _, kind, err := api.Scheme.ObjectVersionAndKind(obj) + fqKind, err := api.Scheme.ObjectKind(obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } @@ -216,18 +216,18 @@ func body(t *testing.T, obj runtime.Object, raw *string) *string { // split the schemes for internal objects. // TODO: caesarxuchao: we should add a map from kind to group in Scheme. var bs []byte - if api.Scheme.Recognizes(testapi.Default.GroupAndVersion(), kind) { + if api.Scheme.Recognizes(testapi.Default.GroupVersion().WithKind(fqKind.Kind)) { bs, err = testapi.Default.Codec().Encode(obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } - } else if api.Scheme.Recognizes(testapi.Extensions.GroupAndVersion(), kind) { + } else if api.Scheme.Recognizes(testapi.Extensions.GroupVersion().WithKind(fqKind.Kind)) { bs, err = testapi.Extensions.Codec().Encode(obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } } else { - t.Errorf("unexpected kind: %v", kind) + t.Errorf("unexpected kind: %v", fqKind.Kind) } body := string(bs) return &body diff --git a/pkg/client/unversioned/testclient/fixture.go b/pkg/client/unversioned/testclient/fixture.go index 4f7a62e0bd..7d138ea87b 100644 --- a/pkg/client/unversioned/testclient/fixture.go +++ b/pkg/client/unversioned/testclient/fixture.go @@ -210,10 +210,11 @@ func (o objects) Kind(gvk unversioned.GroupVersionKind, name string) (runtime.Ob } func (o objects) Add(obj runtime.Object) error { - _, kind, err := o.scheme.ObjectVersionAndKind(obj) + gvk, err := o.scheme.ObjectKind(obj) if err != nil { return err } + kind := gvk.Kind switch { case meta.IsListType(obj): diff --git a/pkg/conversion/decode.go b/pkg/conversion/decode.go index f99509af03..c2507034f1 100644 --- a/pkg/conversion/decode.go +++ b/pkg/conversion/decode.go @@ -27,28 +27,27 @@ import ( ) func (s *Scheme) DecodeToVersionedObject(data []byte) (interface{}, string, string, error) { - version, kind, err := s.DataVersionAndKind(data) + gvk, err := s.DataKind(data) if err != nil { return nil, "", "", err } - gv, err := unversioned.ParseGroupVersion(version) - if err != nil { - return nil, "", "", err - } - - internalGV, exists := s.InternalVersions[gv.Group] + internalGV, exists := s.InternalVersions[gvk.Group] if !exists { - return nil, "", "", fmt.Errorf("no internalVersion specified for %v", gv) + return nil, "", "", fmt.Errorf("no internalVersion specified for %v", gvk) } - if len(gv.Version) == 0 && len(internalGV.Version) != 0 { + if len(gvk.Group) == 0 && len(internalGV.Group) != 0 { + return nil, "", "", fmt.Errorf("group not set in '%s'", string(data)) + } + if len(gvk.Version) == 0 && len(internalGV.Version) != 0 { return nil, "", "", fmt.Errorf("version not set in '%s'", string(data)) } - if kind == "" { + if gvk.Kind == "" { return nil, "", "", fmt.Errorf("kind not set in '%s'", string(data)) } - obj, err := s.NewObject(version, kind) + + obj, err := s.NewObject(gvk.GroupVersion().String(), gvk.Kind) if err != nil { return nil, "", "", err } @@ -56,7 +55,7 @@ func (s *Scheme) DecodeToVersionedObject(data []byte) (interface{}, string, stri if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil { return nil, "", "", err } - return obj, version, kind, nil + return obj, gvk.GroupVersion().String(), gvk.Kind, nil } // Decode converts a JSON string back into a pointer to an api object. @@ -106,7 +105,7 @@ func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (inte if err != nil { return nil, err } - flags, meta := s.generateConvertMeta(sourceVersion, gv.String(), obj) + flags, meta := s.generateConvertMeta(sourceGV, gv, obj) if err := s.converter.Convert(obj, objOut, flags, meta); err != nil { return nil, err } @@ -135,46 +134,54 @@ func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{} if len(data) == 0 { return errors.New("empty input") } - dataVersion, dataKind, err := s.DataVersionAndKind(data) + dataGVK, err := s.DataKind(data) if err != nil { return err } - if dataVersion == "" { - dataVersion = requestedGVK.GroupVersion().String() + if len(dataGVK.Group) == 0 { + dataGVK.Group = requestedGVK.Group } - if dataKind == "" { - dataKind = requestedGVK.Kind + if len(dataGVK.Version) == 0 { + dataGVK.Version = requestedGVK.Version } - if (len(requestedGVK.Group) > 0 || len(requestedGVK.Version) > 0) && (dataVersion != requestedGVK.GroupVersion().String()) { - return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%v)", dataVersion, requestedGVK.GroupVersion())) - } - if len(requestedGVK.Kind) > 0 && (dataKind != requestedGVK.Kind) { - return errors.New(fmt.Sprintf("The kind in the data (%s) does not match the specified kind(%v)", dataKind, requestedGVK)) + if len(dataGVK.Kind) == 0 { + dataGVK.Kind = requestedGVK.Kind } - objVersion, objKind, err := s.ObjectVersionAndKind(obj) + if len(requestedGVK.Group) > 0 && requestedGVK.Group != dataGVK.Group { + return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataGVK, requestedGVK)) + } + if len(requestedGVK.Version) > 0 && requestedGVK.Version != dataGVK.Version { + return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataGVK, requestedGVK)) + } + if len(requestedGVK.Kind) > 0 && requestedGVK.Kind != dataGVK.Kind { + return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataGVK, requestedGVK)) + } + + objGVK, err := s.ObjectKind(obj) if err != nil { return err } - if dataKind == "" { - // Assume objects with unset Kind fields are being unmarshalled into the - // correct type. - dataKind = objKind + // Assume objects with unset fields are being unmarshalled into the + // correct type. + if len(dataGVK.Group) == 0 { + dataGVK.Group = objGVK.Group } - if dataVersion == "" { - // Assume objects with unset Version fields are being unmarshalled into the - // correct type. - dataVersion = objVersion + if len(dataGVK.Version) == 0 { + dataGVK.Version = objGVK.Version + } + if len(dataGVK.Kind) == 0 { + dataGVK.Kind = objGVK.Kind } - external, err := s.NewObject(dataVersion, dataKind) + external, err := s.NewObject(dataGVK.GroupVersion().String(), dataGVK.Kind) if err != nil { return err } if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(external); err != nil { return err } - flags, meta := s.generateConvertMeta(dataVersion, objVersion, external) + flags, meta := s.generateConvertMeta(dataGVK.GroupVersion(), objGVK.GroupVersion(), external) if err := s.converter.Convert(external, obj, flags, meta); err != nil { return err } diff --git a/pkg/conversion/encode.go b/pkg/conversion/encode.go index 2455bc5a1a..4896e20c8d 100644 --- a/pkg/conversion/encode.go +++ b/pkg/conversion/encode.go @@ -22,6 +22,8 @@ import ( "fmt" "io" "path" + + "k8s.io/kubernetes/pkg/api/unversioned" ) // EncodeToVersion turns the given api object into an appropriate JSON string. @@ -81,33 +83,34 @@ func (s *Scheme) EncodeToVersionStream(obj interface{}, destVersion string, stre return fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destVersion) } - objVersion, objKind, err := s.ObjectVersionAndKind(obj) + objGVK, err := s.ObjectKind(obj) if err != nil { return err } // Perform a conversion if necessary. - if objVersion != destVersion { - objOut, err := s.NewObject(destVersion, objKind) + if objGVK.GroupVersion().String() != destVersion { + objOut, err := s.NewObject(destVersion, objGVK.Kind) if err != nil { return err } - flags, meta := s.generateConvertMeta(objVersion, destVersion, obj) + flags, meta := s.generateConvertMeta(objGVK.GroupVersion(), unversioned.ParseGroupVersionOrDie(destVersion), obj) err = s.converter.Convert(obj, objOut, flags, meta) if err != nil { return err } obj = objOut - } - // ensure the output object name comes from the destination type - _, objKind, err = s.ObjectVersionAndKind(obj) - if err != nil { - return err + // ensure the output object name comes from the destination type + newGroupVersionKind, err := s.ObjectKind(obj) + if err != nil { + return err + } + objGVK.Kind = newGroupVersionKind.Kind } // Version and Kind should be set on the wire. - err = s.SetVersionAndKind(destVersion, objKind, obj) + err = s.SetVersionAndKind(destVersion, objGVK.Kind, obj) if err != nil { return err } @@ -129,11 +132,11 @@ func (s *Scheme) EncodeToVersionStream(obj interface{}, destVersion string, stre } func (s *Scheme) encodeUnversionedObject(obj interface{}) (data []byte, err error) { - _, objKind, err := s.ObjectVersionAndKind(obj) + objGVK, err := s.ObjectKind(obj) if err != nil { return nil, err } - if err = s.SetVersionAndKind("", objKind, obj); err != nil { + if err = s.SetVersionAndKind("", objGVK.Kind, obj); err != nil { return nil, err } data, err = json.Marshal(obj) diff --git a/pkg/conversion/meta.go b/pkg/conversion/meta.go index 1488570f30..a418de04b4 100644 --- a/pkg/conversion/meta.go +++ b/pkg/conversion/meta.go @@ -21,6 +21,8 @@ import ( "fmt" "path" "reflect" + + "k8s.io/kubernetes/pkg/api/unversioned" ) // MetaFactory is used to store and retrieve the version and kind @@ -28,9 +30,9 @@ import ( type MetaFactory interface { // Update sets the given version and kind onto the object. Update(version, kind string, obj interface{}) error - // Interpret should return the version and kind of the wire-format of + // Interpret should return the group,version,kind of the wire-format of // the object. - Interpret(data []byte) (version, kind string, err error) + Interpret(data []byte) (gvk unversioned.GroupVersionKind, err error) } // DefaultMetaFactory is a default factory for versioning objects in JSON. The object @@ -51,18 +53,23 @@ type SimpleMetaFactory struct { BaseFields []string } -// Interpret will return the APIVersion and Kind of the JSON wire-format +// Interpret will return the group,version,kind of the JSON wire-format // encoding of an object, or an error. -func (SimpleMetaFactory) Interpret(data []byte) (version, kind string, err error) { +func (SimpleMetaFactory) Interpret(data []byte) (unversioned.GroupVersionKind, error) { findKind := struct { APIVersion string `json:"apiVersion,omitempty"` Kind string `json:"kind,omitempty"` }{} - err = json.Unmarshal(data, &findKind) + err := json.Unmarshal(data, &findKind) if err != nil { - return "", "", fmt.Errorf("couldn't get version/kind; json parse error: %v", err) + return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't get version/kind; json parse error: %v", err) } - return findKind.APIVersion, findKind.Kind, nil + gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) + if err != nil { + return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't parse apiVersion: %v", err) + } + + return gv.WithKind(findKind.Kind), nil } func (f SimpleMetaFactory) Update(version, kind string, obj interface{}) error { diff --git a/pkg/conversion/meta_test.go b/pkg/conversion/meta_test.go index b1b98b6bb7..4cf92fb068 100644 --- a/pkg/conversion/meta_test.go +++ b/pkg/conversion/meta_test.go @@ -26,25 +26,26 @@ import ( func TestSimpleMetaFactoryInterpret(t *testing.T) { factory := SimpleMetaFactory{} - version, kind, err := factory.Interpret([]byte(`{"apiVersion":"1","kind":"object"}`)) + fqKind, err := factory.Interpret([]byte(`{"apiVersion":"g/1","kind":"object"}`)) if err != nil { t.Fatalf("unexpected error: %v", err) } - if version != "1" || kind != "object" { - t.Errorf("unexpected interpret: %s %s", version, kind) + expectedFQKind := unversioned.GroupVersionKind{Group: "g", Version: "1", Kind: "object"} + if expectedFQKind != fqKind { + t.Errorf("unexpected interpret: %s %s", expectedFQKind, fqKind) } // no kind or version - version, kind, err = factory.Interpret([]byte(`{}`)) + fqKind, err = factory.Interpret([]byte(`{}`)) if err != nil { t.Fatalf("unexpected error: %v", err) } - if version != "" || kind != "" { - t.Errorf("unexpected interpret: %s %s", version, kind) + if !fqKind.IsEmpty() { + t.Errorf("unexpected interpret: %s %s", fqKind) } // unparsable - version, kind, err = factory.Interpret([]byte(`{`)) + fqKind, err = factory.Interpret([]byte(`{`)) if err == nil { t.Errorf("unexpected non-error") } @@ -237,10 +238,10 @@ func TestMetaValuesUnregisteredConvert(t *testing.T) { // Register functions to verify that scope.Meta() gets set correctly. err := s.AddConversionFuncs( func(in *InternalSimple, out *ExternalSimple, scope Scope) error { - if e, a := "unknown", scope.Meta().SrcVersion; e != a { + if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a { t.Fatalf("Expected '%v', got '%v'", e, a) } - if e, a := "unknown", scope.Meta().DestVersion; 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) diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index 1bb6a02e5a..c4ec96ca18 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -281,15 +281,9 @@ func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error { return nil } -// Recognizes returns true if the scheme is able to handle the provided version and kind +// Recognizes returns true if the scheme is able to handle the provided group,version,kind // of an object. -func (s *Scheme) Recognizes(gvString, kind string) bool { - gv, err := unversioned.ParseGroupVersion(gvString) - if err != nil { - return false - } - gvk := gv.WithKind(kind) - +func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { _, exists := s.gvkToType[gvk] return exists } @@ -314,13 +308,13 @@ func (s *Scheme) DeepCopy(in interface{}) (interface{}, error) { // that case, the conversion.Scope object passed to your conversion functions won't // have SrcVersion or DestVersion fields set correctly in Meta(). func (s *Scheme) Convert(in, out interface{}) error { - inVersion := "unknown" - outVersion := "unknown" - if v, _, err := s.ObjectVersionAndKind(in); err == nil { - inVersion = v + inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"} + outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"} + if gvk, err := s.ObjectKind(in); err == nil { + inVersion = gvk.GroupVersion() } - if v, _, err := s.ObjectVersionAndKind(out); err == nil { - outVersion = v + if gvk, err := s.ObjectKind(out); err == nil { + outVersion = gvk.GroupVersion() } flags, meta := s.generateConvertMeta(inVersion, outVersion, in) if flags == 0 { @@ -346,27 +340,36 @@ func (s *Scheme) ConvertToVersion(in interface{}, outVersion string) (interface{ if !ok { return nil, fmt.Errorf("%v cannot be converted into version %q", t, outVersion) } - outKind := gvks[0] + outGV, err := unversioned.ParseGroupVersion(outVersion) + if err != nil { + return nil, err + } + outGVK := outGV.WithKind(gvks[0].Kind) - inVersion, _, err := s.ObjectVersionAndKind(in) + inGVK, err := s.ObjectKind(in) if err != nil { return nil, err } - out, err := s.NewObject(outVersion, outKind.Kind) + out, err := s.NewObject(outGV.String(), outGVK.Kind) if err != nil { return nil, err } - flags, meta := s.generateConvertMeta(inVersion, outVersion, in) + flags, meta := s.generateConvertMeta(inGVK.GroupVersion(), outGV, in) if err := s.converter.Convert(in, out, flags, meta); err != nil { return nil, err } - if len(outVersion) != 0 { - if err := s.SetVersionAndKind(outVersion, outKind.Kind, out); err != nil { - return nil, err - } + // <<<<<<< HEAD + // if len(outVersion) != 0 { + // if err := s.SetVersionAndKind(outVersion, outKind.Kind, out); err != nil { + // return nil, err + // } + // ======= + if err := s.SetVersionAndKind(outGV.String(), outGVK.Kind, out); err != nil { + return nil, err + // >>>>>>> Update ObjectTyper to GroupVersion } return out, nil @@ -378,37 +381,46 @@ func (s *Scheme) Converter() *Converter { } // generateConvertMeta constructs the meta value we pass to Convert. -func (s *Scheme) generateConvertMeta(srcVersion, destVersion string, in interface{}) (FieldMatchingFlags, *Meta) { +func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (FieldMatchingFlags, *Meta) { t := reflect.TypeOf(in) return s.converter.inputDefaultFlags[t], &Meta{ - SrcVersion: srcVersion, - DestVersion: destVersion, + SrcVersion: srcGroupVersion.String(), + DestVersion: destGroupVersion.String(), KeyNameMapping: s.converter.inputFieldMappingFuncs[t], } } -// DataVersionAndKind will return the APIVersion and Kind of the given wire-format +// DataKind will return the group,version,kind of the given wire-format // encoding of an API Object, or an error. -func (s *Scheme) DataVersionAndKind(data []byte) (version, kind string, err error) { +func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { return s.MetaFactory.Interpret(data) } -// ObjectVersionAndKind returns the API version and kind of the go object, +// ObjectKind returns the group,version,kind of the go object, // or an error if it's not a pointer or is unregistered. -func (s *Scheme) ObjectVersionAndKind(obj interface{}) (apiVersion, kind string, err error) { +func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, error) { + gvks, err := s.ObjectKinds(obj) + if err != nil { + return unversioned.GroupVersionKind{}, err + } + + return gvks[0], nil +} + +// ObjectKinds returns all possible group,version,kind of the go object, +// or an error if it's not a pointer or is unregistered. +func (s *Scheme) ObjectKinds(obj interface{}) ([]unversioned.GroupVersionKind, error) { v, err := EnforcePtr(obj) if err != nil { - return "", "", err + return []unversioned.GroupVersionKind{}, err } t := v.Type() gvks, ok := s.typeToGVK[t] if !ok { - return "", "", ¬RegisteredErr{t: t} + return []unversioned.GroupVersionKind{}, ¬RegisteredErr{t: t} } - apiVersion = gvks[0].GroupVersion().String() - kind = gvks[0].Kind - return + return gvks, nil } // SetVersionAndKind sets the version and kind fields (with help from diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index f7cf484db6..9c62f407d2 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -128,18 +128,22 @@ func GetTestScheme() *Scheme { type testMetaFactory struct{} -func (testMetaFactory) Interpret(data []byte) (version, kind string, err error) { +func (testMetaFactory) Interpret(data []byte) (unversioned.GroupVersionKind, error) { findKind := struct { APIVersion string `json:"myVersionKey,omitempty"` ObjectKind string `json:"myKindKey,omitempty"` }{} // yaml is a superset of json, so we use it to decode here. That way, // we understand both. - err = yaml.Unmarshal(data, &findKind) + err := yaml.Unmarshal(data, &findKind) if err != nil { - return "", "", fmt.Errorf("couldn't get version/kind: %v", err) + return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't get version/kind: %v", err) } - return findKind.APIVersion, findKind.ObjectKind, nil + gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) + if err != nil { + return unversioned.GroupVersionKind{}, err + } + return gv.WithKind(findKind.ObjectKind), nil } func (testMetaFactory) Update(version, kind string, obj interface{}) error { diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 76e55d5c16..41ba600467 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -253,11 +253,11 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil default: - _, kind, err := api.Scheme.ObjectVersionAndKind(object) + fqKind, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot get the logs from %s", kind) + return nil, fmt.Errorf("cannot get the logs from %v", fqKind) } }, } diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index d462b92859..ca23b1c56e 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -216,8 +216,8 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg } newRc, ok = obj.(*api.ReplicationController) if !ok { - if _, kind, err := typer.ObjectVersionAndKind(obj); err == nil { - return cmdutil.UsageError(cmd, "%s contains a %s not a ReplicationController", filename, kind) + if gvk, err := typer.ObjectKind(obj); err == nil { + return cmdutil.UsageError(cmd, "%s contains a %v not a ReplicationController", filename, gvk) } glog.V(4).Infof("Object %#v is not a ReplicationController", obj) return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename) @@ -358,11 +358,11 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg if outputFormat != "" { return f.PrintObject(cmd, newRc, out) } - _, kind, err := api.Scheme.ObjectVersionAndKind(newRc) + gvk, err := api.Scheme.ObjectKind(newRc) if err != nil { return err } - _, res := meta.KindToResource(kind, false) + _, res := meta.KindToResource(gvk.Kind, false) cmdutil.PrintSuccess(mapper, false, out, res, oldName, message) return nil } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index d9e9de4d28..429abef094 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -25,7 +25,6 @@ import ( "github.com/spf13/cobra" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -371,24 +370,19 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub } mapper, typer := f.Object() - gvString, kind, err := typer.ObjectVersionAndKind(obj) + groupVersionKind, err := typer.ObjectKind(obj) if err != nil { return nil, "", nil, nil, err } - gv, err := unversioned.ParseGroupVersion(gvString) - if err != nil { - return nil, "", nil, nil, err - } - gvk := gv.WithKind(kind) if len(overrides) > 0 { - obj, err = cmdutil.Merge(obj, overrides, kind) + obj, err = cmdutil.Merge(obj, overrides, groupVersionKind.Kind) if err != nil { return nil, "", nil, nil, err } } - mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + mapping, err := mapper.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version) if err != nil { return nil, "", nil, nil, err } @@ -414,5 +408,5 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub return nil, "", nil, nil, err } } - return obj, kind, mapper, mapping, err + return obj, groupVersionKind.Kind, mapper, mapping, err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 5b8376cd38..82e28a2c80 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -199,11 +199,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return kubectl.MakeLabels(t.Spec.Selector), nil default: - _, kind, err := api.Scheme.ObjectVersionAndKind(object) + gvk, err := api.Scheme.ObjectKind(object) if err != nil { return "", err } - return "", fmt.Errorf("cannot extract pod selector from %s", kind) + return "", fmt.Errorf("cannot extract pod selector from %v", gvk) } }, PortsForObject: func(object runtime.Object) ([]string, error) { @@ -216,11 +216,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { case *api.Service: return getServicePorts(t.Spec), nil default: - _, kind, err := api.Scheme.ObjectVersionAndKind(object) + gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot extract ports from %s", kind) + return nil, fmt.Errorf("cannot extract ports from %v", gvk) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { @@ -240,11 +240,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil default: - _, kind, err := api.Scheme.ObjectVersionAndKind(object) + gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot get the logs from %s", kind) + return nil, fmt.Errorf("cannot get the logs from %v", gvk) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { @@ -323,11 +323,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { case *api.Pod: return t, nil default: - _, kind, err := api.Scheme.ObjectVersionAndKind(object) + gvk, err := api.Scheme.ObjectKind(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot attach to %s: not implemented", kind) + return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk) } }, EditorEnvs: func() []string { @@ -489,29 +489,20 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac } func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { - version, kind, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data) + gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) if err != nil { return err } - gv, err := unversioned.ParseGroupVersion(version) - if err != nil { - return fmt.Errorf("unable to parse group/version from %q: %v", version, err) - } - if ok := registered.IsRegisteredAPIGroupVersion(gv); !ok { - return fmt.Errorf("API version %q isn't supported, only supports API versions %q", version, registered.RegisteredGroupVersions) - } - resource, _ := meta.KindToResource(kind, false) - gvk, err := c.mapper.KindFor(resource) - if err != nil { - return fmt.Errorf("could not find api group for %s: %v", kind, err) + if ok := registered.IsRegisteredAPIGroupVersion(gvk.GroupVersion()); !ok { + return fmt.Errorf("API version %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), registered.RegisteredGroupVersions) } if gvk.Group == "extensions" { if c.c.ExtensionsClient == nil { return errors.New("unable to validate: no experimental client") } - return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", version, c.cacheDir) + return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir) } - return getSchemaAndValidate(c.c.RESTClient, data, "api", version, c.cacheDir) + return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir) } // DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy: @@ -571,16 +562,12 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { // PrintObject prints an api object given command line flags to modify the output format func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error { mapper, _ := f.Object() - gvString, kind, err := api.Scheme.ObjectVersionAndKind(obj) - if err != nil { - return err - } - gv, err := unversioned.ParseGroupVersion(gvString) + gvk, err := api.Scheme.ObjectKind(obj) if err != nil { return err } - mapping, err := mapper.RESTMapping(unversioned.GroupKind{Group: gv.Group, Kind: kind}) + mapping, err := mapper.RESTMapping(gvk.GroupKind()) if err != nil { return err } diff --git a/pkg/kubectl/resource/mapper.go b/pkg/kubectl/resource/mapper.go index 385303a041..edf54d10ed 100644 --- a/pkg/kubectl/resource/mapper.go +++ b/pkg/kubectl/resource/mapper.go @@ -23,7 +23,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/registered" - "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/yaml" ) @@ -45,21 +44,17 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { return nil, fmt.Errorf("unable to parse %q: %v", source, err) } data = json - version, kind, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data) + gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) if err != nil { return nil, fmt.Errorf("unable to get type info from %q: %v", source, err) } - gv, err := unversioned.ParseGroupVersion(version) - if err != nil { - return nil, fmt.Errorf("unable to parse group/version from %q: %v", version, err) + if ok := registered.IsRegisteredAPIGroupVersion(gvk.GroupVersion()); !ok { + return nil, fmt.Errorf("API version %q in %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), source, registered.RegisteredGroupVersions) } - if ok := registered.IsRegisteredAPIGroupVersion(gv); !ok { - return nil, fmt.Errorf("API version %q in %q isn't supported, only supports API versions %q", version, source, registered.RegisteredGroupVersions) - } - if kind == "" { + if gvk.Kind == "" { return nil, fmt.Errorf("kind not set in %q", source) } - mapping, err := m.RESTMapping(unversioned.GroupKind{Group: gv.Group, Kind: kind}, gv.Version) + mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version) if err != nil { return nil, fmt.Errorf("unable to recognize %q: %v", source, err) } @@ -97,17 +92,13 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { // if the object cannot be introspected. Name and namespace will be set into Info // if the mapping's MetadataAccessor can retrieve them. func (m *Mapper) InfoForObject(obj runtime.Object) (*Info, error) { - gvString, kind, err := m.ObjectVersionAndKind(obj) + groupVersionKind, err := m.ObjectKind(obj) if err != nil { return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err) } - gv, err := unversioned.ParseGroupVersion(gvString) + mapping, err := m.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version) if err != nil { - return nil, fmt.Errorf("unable to parse group/version from %q: %v", gvString, err) - } - mapping, err := m.RESTMapping(unversioned.GroupKind{Group: gv.Group, Kind: kind}, gv.Version) - if err != nil { - return nil, fmt.Errorf("unable to recognize %q: %v", kind, err) + return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, err) } client, err := m.ClientForMapping(mapping) if err != nil { diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index 6c1381dc6e..5a439776a3 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -243,7 +243,7 @@ func AsVersionedObjects(infos []*Info, version string) ([]runtime.Object, error) // 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 _, _, err := api.Scheme.ObjectVersionAndKind(info.Object); runtime.IsNotRegisteredError(err) { + 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 := info.Mapping.Codec.Encode(info.Object) if err != nil { diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 6b042a8eb9..55954668ee 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -213,7 +213,7 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { for i := 0; i < items.Len(); i++ { rawObj := items.Index(i).FieldByName("RawJSON").Interface().([]byte) scheme := api.Scheme - version, kind, err := scheme.DataVersionAndKind(rawObj) + groupVersionKind, err := scheme.DataKind(rawObj) if err != nil { return err } @@ -222,8 +222,8 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { return err } tpmeta := unversioned.TypeMeta{ - APIVersion: version, - Kind: kind, + APIVersion: groupVersionKind.GroupVersion().String(), + Kind: groupVersionKind.Kind, } s := reflect.ValueOf(decodedObj).Elem() s.FieldByName("TypeMeta").Set(reflect.ValueOf(tpmeta)) diff --git a/pkg/registry/registrytest/etcd.go b/pkg/registry/registrytest/etcd.go index b2f74dfeeb..6714e9d5f6 100644 --- a/pkg/registry/registrytest/etcd.go +++ b/pkg/registry/registrytest/etcd.go @@ -146,7 +146,7 @@ func (t *Tester) TestWatch(valid runtime.Object, labelsPass, labelsFail []labels // ============================================================================= // get codec based on runtime.Object func getCodec(obj runtime.Object) (runtime.Codec, error) { - _, kind, err := api.Scheme.ObjectVersionAndKind(obj) + fqKind, err := api.Scheme.ObjectKind(obj) if err != nil { return nil, fmt.Errorf("unexpected encoding error: %v", err) } @@ -155,12 +155,12 @@ func getCodec(obj runtime.Object) (runtime.Codec, error) { // split the schemes for internal objects. // TODO: caesarxuchao: we should add a map from kind to group in Scheme. var codec runtime.Codec - if api.Scheme.Recognizes(testapi.Default.GroupAndVersion(), kind) { + if api.Scheme.Recognizes(testapi.Default.GroupVersion().WithKind(fqKind.Kind)) { codec = testapi.Default.Codec() - } else if api.Scheme.Recognizes(testapi.Extensions.GroupAndVersion(), kind) { + } else if api.Scheme.Recognizes(testapi.Extensions.GroupVersion().WithKind(fqKind.Kind)) { codec = testapi.Extensions.Codec() } else { - return nil, fmt.Errorf("unexpected kind: %v", kind) + return nil, fmt.Errorf("unexpected kind: %v", fqKind) } return codec, nil } diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index bc29ea0b2b..d648c41dc2 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -106,7 +106,7 @@ func TestArrayOfRuntimeObject(t *testing.T) { &EmbeddedTest{ID: "foo"}, &EmbeddedTest{ID: "bar"}, // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization - &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown","foo":"bar","kind":"OtherTest"}`)}, + &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)}, &ObjectTest{ Items: []runtime.Object{ &EmbeddedTest{ID: "baz"}, @@ -150,7 +150,7 @@ func TestArrayOfRuntimeObject(t *testing.T) { } internal.Items[2].(*runtime.Unknown).Kind = "OtherTest" - internal.Items[2].(*runtime.Unknown).APIVersion = "unknown" + internal.Items[2].(*runtime.Unknown).APIVersion = "unknown.group/unknown" if e, a := internal.Items, list; !reflect.DeepEqual(e, a) { t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a)) } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index 0dab10f974..92626fc7e4 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -20,6 +20,7 @@ import ( "fmt" "reflect" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" ) @@ -56,7 +57,13 @@ func DecodeList(objects []Object, decoders ...ObjectDecoder) []error { switch t := obj.(type) { case *Unknown: for _, decoder := range decoders { - if !decoder.Recognizes(t.APIVersion, t.Kind) { + gv, err := unversioned.ParseGroupVersion(t.APIVersion) + if err != nil { + errs = append(errs, err) + break + } + + if !decoder.Recognizes(gv.WithKind(t.Kind)) { continue } obj, err := decoder.Decode(t.RawJSON) @@ -77,9 +84,9 @@ type MultiObjectTyper []ObjectTyper var _ ObjectTyper = MultiObjectTyper{} -func (m MultiObjectTyper) DataVersionAndKind(data []byte) (version, kind string, err error) { +func (m MultiObjectTyper) DataKind(data []byte) (gvk unversioned.GroupVersionKind, err error) { for _, t := range m { - version, kind, err = t.DataVersionAndKind(data) + gvk, err = t.DataKind(data) if err == nil { return } @@ -87,9 +94,9 @@ func (m MultiObjectTyper) DataVersionAndKind(data []byte) (version, kind string, return } -func (m MultiObjectTyper) ObjectVersionAndKind(obj Object) (version, kind string, err error) { +func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) { for _, t := range m { - version, kind, err = t.ObjectVersionAndKind(obj) + gvk, err = t.ObjectKind(obj) if err == nil { return } @@ -97,9 +104,19 @@ func (m MultiObjectTyper) ObjectVersionAndKind(obj Object) (version, kind string return } -func (m MultiObjectTyper) Recognizes(version, kind string) bool { +func (m MultiObjectTyper) ObjectKinds(obj Object) (gvks []unversioned.GroupVersionKind, err error) { for _, t := range m { - if t.Recognizes(version, kind) { + gvks, err = t.ObjectKinds(obj) + if err == nil { + return + } + } + return +} + +func (m MultiObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool { + for _, t := range m { + if t.Recognizes(gvk) { return true } } diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index b050a9ad02..349c83bd5c 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -69,13 +69,13 @@ type ObjectCodec interface { // TODO: Consider removing this interface? type ObjectDecoder interface { Decoder - // DataVersionAndKind returns the version and kind of the provided data, or an error + // DataVersionAndKind returns the group,version,kind of the provided data, or an error // if another problem is detected. In many cases this method can be as expensive to // invoke as the Decode method. - DataVersionAndKind([]byte) (version, kind string, err error) - // Recognizes returns true if the scheme is able to handle the provided version and kind + DataKind([]byte) (unversioned.GroupVersionKind, error) + // Recognizes returns true if the scheme is able to handle the provided group,version,kind // of an object. - Recognizes(version, kind string) bool + Recognizes(unversioned.GroupVersionKind) bool } /////////////////////////////////////////////////////////////////////////////// @@ -91,17 +91,20 @@ type ObjectConvertor interface { // ObjectTyper contains methods for extracting the APIVersion and Kind // of objects. type ObjectTyper interface { - // DataVersionAndKind returns the version and kind of the provided data, or an error + // DataKind returns the group,version,kind of the provided data, or an error // if another problem is detected. In many cases this method can be as expensive to // invoke as the Decode method. - DataVersionAndKind([]byte) (version, kind string, err error) - // ObjectVersionAndKind returns the version and kind of the provided object, or an + DataKind([]byte) (unversioned.GroupVersionKind, error) + // ObjectKind returns the default group,version,kind of the provided object, or an // error if the object is not recognized (IsNotRegisteredError will return true). - ObjectVersionAndKind(Object) (version, kind string, err error) + ObjectKind(Object) (unversioned.GroupVersionKind, error) + // ObjectKinds returns the all possible group,version,kind of the provided object, or an + // error if the object is not recognized (IsNotRegisteredError will return true). + ObjectKinds(Object) ([]unversioned.GroupVersionKind, error) // Recognizes returns true if the scheme is able to handle the provided version and kind, // or more precisely that the provided version is a possible conversion or decoding // target. - Recognizes(version, kind string) bool + Recognizes(gvk unversioned.GroupVersionKind) bool } // ObjectCreater contains methods for instantiating an object by kind and version. diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 6e1162d468..75846054fa 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -37,6 +37,7 @@ type Scheme struct { } var _ Decoder = &Scheme{} +var _ ObjectTyper = &Scheme{} // Function to convert a field selector to internal representation. type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error) @@ -70,13 +71,13 @@ func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExt // Figure out the type and kind of the output object. _, outVersion, scheme := self.fromScope(s) - _, kind, err := scheme.raw.ObjectVersionAndKind(in.Object) + gvk, err := scheme.raw.ObjectKind(in.Object) if err != nil { return err } // Manufacture an object of this type and kind. - outObj, err := scheme.New(outVersion, kind) + outObj, err := scheme.New(outVersion, gvk.Kind) if err != nil { return err } @@ -89,7 +90,7 @@ func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExt // Copy the kind field into the output object. err = s.Convert( - &emptyPlugin{PluginBase: PluginBase{Kind: kind}}, + &emptyPlugin{PluginBase: PluginBase{Kind: gvk.Kind}}, outObj, conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, ) @@ -117,14 +118,14 @@ func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *Embedded } // Figure out the type and kind of the output object. inVersion, outVersion, scheme := self.fromScope(s) - _, kind, err := scheme.raw.DataVersionAndKind(in.RawJSON) + gvk, err := scheme.raw.DataKind(in.RawJSON) if err != nil { return err } // We have to make this object ourselves because we don't store the version field for // plugin objects. - inObj, err := scheme.New(inVersion, kind) + inObj, err := scheme.New(inVersion, gvk.Kind) if err != nil { return err } @@ -135,7 +136,7 @@ func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *Embedded } // Make the desired internal version, and do the conversion. - outObj, err := scheme.New(outVersion, kind) + outObj, err := scheme.New(outVersion, gvk.Kind) if err != nil { return err } @@ -182,14 +183,9 @@ func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExt version := outVersion // if the object exists // this code is try to set the outputVersion, but only if the object has a non-internal group version - if inGVString, _, err := scheme.ObjectVersionAndKind(src[i]); err == nil && len(inGVString) != 0 { - inGV, err := unversioned.ParseGroupVersion(inGVString) - if err != nil { - return err - } - - if self.raw.InternalVersions[inGV.Group] != inGV { - version = inGV.String() + if inGVK, err := scheme.ObjectKind(src[i]); err == nil && !inGVK.GroupVersion().IsEmpty() { + if self.raw.InternalVersions[inGVK.Group] != inGVK.GroupVersion() { + version = inGVK.GroupVersion().String() } } data, err := scheme.EncodeToVersion(src[i], version) @@ -213,14 +209,14 @@ func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[] for i := range src { data := src[i].RawJSON - version, kind, err := scheme.raw.DataVersionAndKind(data) + gvk, err := scheme.raw.DataKind(data) if err != nil { return err } dest[i] = &Unknown{ TypeMeta: TypeMeta{ - APIVersion: version, - Kind: kind, + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, }, RawJSON: data, } @@ -288,21 +284,26 @@ func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type return s.raw.KnownTypes(gv) } -// DataVersionAndKind will return the APIVersion and Kind of the given wire-format +// DataKind will return the group,version,kind of the given wire-format // encoding of an API Object, or an error. -func (s *Scheme) DataVersionAndKind(data []byte) (version, kind string, err error) { - return s.raw.DataVersionAndKind(data) +func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { + return s.raw.DataKind(data) } -// ObjectVersionAndKind returns the version and kind of the given Object. -func (s *Scheme) ObjectVersionAndKind(obj Object) (version, kind string, err error) { - return s.raw.ObjectVersionAndKind(obj) +// ObjectKind returns the default group,version,kind of the given Object. +func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) { + return s.raw.ObjectKind(obj) } -// Recognizes returns true if the scheme is able to handle the provided version and kind +// ObjectKinds returns the all possible group,version,kind of the given Object. +func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, error) { + return s.raw.ObjectKinds(obj) +} + +// Recognizes returns true if the scheme is able to handle the provided group,version,kind // of an object. -func (s *Scheme) Recognizes(version, kind string) bool { - return s.raw.Recognizes(version, kind) +func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { + return s.raw.Recognizes(gvk) } // New returns a new API object of the given version ("" for internal diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index 03e8ca2c55..25b47fade6 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -144,7 +144,7 @@ func TestInvalidObjectValueKind(t *testing.T) { embedded := &runtime.EmbeddedObject{} switch obj := embedded.Object.(type) { default: - _, _, err := scheme.ObjectVersionAndKind(obj) + _, err := scheme.ObjectKind(obj) if err == nil { t.Errorf("Expected error on invalid kind") } diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index 92506d4c1d..66eba1b70d 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -33,11 +33,12 @@ var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{} type unstructuredJSONScheme struct{} var _ Decoder = unstructuredJSONScheme{} +var _ ObjectDecoder = unstructuredJSONScheme{} // Recognizes returns true for any version or kind that is specified (internal // versions are specifically excluded). -func (unstructuredJSONScheme) Recognizes(version, kind string) bool { - return len(version) > 0 && len(kind) > 0 +func (unstructuredJSONScheme) Recognizes(gvk unversioned.GroupVersionKind) bool { + return !gvk.GroupVersion().IsEmpty() && len(gvk.Kind) > 0 } func (s unstructuredJSONScheme) Decode(data []byte) (Object, error) { @@ -90,16 +91,22 @@ func (unstructuredJSONScheme) DecodeParametersInto(paramaters url.Values, obj Ob return nil } -func (unstructuredJSONScheme) DataVersionAndKind(data []byte) (version, kind string, err error) { +func (unstructuredJSONScheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { obj := TypeMeta{} if err := json.Unmarshal(data, &obj); err != nil { - return "", "", err + return unversioned.GroupVersionKind{}, err } if len(obj.APIVersion) == 0 { - return "", "", conversion.NewMissingVersionErr(string(data)) + return unversioned.GroupVersionKind{}, conversion.NewMissingVersionErr(string(data)) } if len(obj.Kind) == 0 { - return "", "", conversion.NewMissingKindErr(string(data)) + return unversioned.GroupVersionKind{}, conversion.NewMissingKindErr(string(data)) } - return obj.APIVersion, obj.Kind, nil + + gv, err := unversioned.ParseGroupVersion(obj.APIVersion) + if err != nil { + return unversioned.GroupVersionKind{}, err + } + + return gv.WithKind(obj.Kind), nil }