From c077bac34153c89c42517fdff1f389b3b5effd31 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 5 Jul 2016 18:34:36 -0400 Subject: [PATCH] Implement a RESTMappings method With the introduction of batch/v1 and batch/v2alpha1, any MultiRESTMapper that has per groupversion mappers (as a naive discovery client would create) would end up being unable to call RESTMapping() on batch.Jobs. As we finish up discovery we will need to be able to choose prioritized RESTMappings based on the service discovery doc. This change implements RESTMappings(groupversion) which returns all possible RESTMappings for that kind. That allows a higher level call to prioritize the returned mappings by server or client preferred version. --- pkg/api/meta/errors.go | 53 ++++++++-- pkg/api/meta/interfaces.go | 3 + pkg/api/meta/multirestmapper.go | 41 +++++++- pkg/api/meta/multirestmapper_test.go | 61 +++++++++++ pkg/api/meta/priority.go | 55 +++++++++- pkg/api/meta/priority_test.go | 103 +++++++++++++++++++ pkg/api/meta/restmapper.go | 46 ++++++++- pkg/kubectl/cmd/util/factory.go | 27 ++--- pkg/kubectl/cmd/util/helpers.go | 4 +- pkg/kubectl/kubectl.go | 12 ++- pkg/registry/thirdpartyresourcedata/codec.go | 21 ++++ 11 files changed, 390 insertions(+), 36 deletions(-) diff --git a/pkg/api/meta/errors.go b/pkg/api/meta/errors.go index dc4ec07a371..70452965e58 100644 --- a/pkg/api/meta/errors.go +++ b/pkg/api/meta/errors.go @@ -38,19 +38,40 @@ func (e *AmbiguousResourceError) Error() string { return fmt.Sprintf("%v matches multiple kinds %v", e.PartialResource, e.MatchingKinds) case len(e.MatchingResources) > 0: return fmt.Sprintf("%v matches multiple resources %v", e.PartialResource, e.MatchingResources) - } - return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialResource) } -func IsAmbiguousResourceError(err error) bool { +// AmbiguousKindError is returned if the RESTMapper finds multiple matches for a kind +type AmbiguousKindError struct { + PartialKind unversioned.GroupVersionKind + + MatchingResources []unversioned.GroupVersionResource + MatchingKinds []unversioned.GroupVersionKind +} + +func (e *AmbiguousKindError) Error() string { + switch { + case len(e.MatchingKinds) > 0 && len(e.MatchingResources) > 0: + return fmt.Sprintf("%v matches multiple resources %v and kinds %v", e.PartialKind, e.MatchingResources, e.MatchingKinds) + case len(e.MatchingKinds) > 0: + return fmt.Sprintf("%v matches multiple kinds %v", e.PartialKind, e.MatchingKinds) + case len(e.MatchingResources) > 0: + return fmt.Sprintf("%v matches multiple resources %v", e.PartialKind, e.MatchingResources) + } + return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialKind) +} + +func IsAmbiguousError(err error) bool { if err == nil { return false } - - _, ok := err.(*AmbiguousResourceError) - return ok + switch err.(type) { + case *AmbiguousResourceError, *AmbiguousKindError: + return true + default: + return false + } } // NoResourceMatchError is returned if the RESTMapper can't find any match for a resource @@ -62,11 +83,23 @@ func (e *NoResourceMatchError) Error() string { return fmt.Sprintf("no matches for %v", e.PartialResource) } -func IsNoResourceMatchError(err error) bool { +// NoKindMatchError is returned if the RESTMapper can't find any match for a kind +type NoKindMatchError struct { + PartialKind unversioned.GroupVersionKind +} + +func (e *NoKindMatchError) Error() string { + return fmt.Sprintf("no matches for %v", e.PartialKind) +} + +func IsNoMatchError(err error) bool { if err == nil { return false } - - _, ok := err.(*NoResourceMatchError) - return ok + switch err.(type) { + case *NoResourceMatchError, *NoKindMatchError: + return true + default: + return false + } } diff --git a/pkg/api/meta/interfaces.go b/pkg/api/meta/interfaces.go index 34c51e39b8c..f8179869d88 100644 --- a/pkg/api/meta/interfaces.go +++ b/pkg/api/meta/interfaces.go @@ -173,7 +173,10 @@ type RESTMapper interface { // ResourcesFor takes a partial resource and returns back the list of potential resource in priority order ResourcesFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) + // RESTMapping identifies a preferred resource mapping for the provided group kind. RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) + // RESTMappings returns all resource mappings for the provided group kind. + RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error) AliasesForResource(resource string) ([]string, bool) ResourceSingularizer(resource string) (singular string, err error) diff --git a/pkg/api/meta/multirestmapper.go b/pkg/api/meta/multirestmapper.go index 790795a2812..7b239e0340f 100644 --- a/pkg/api/meta/multirestmapper.go +++ b/pkg/api/meta/multirestmapper.go @@ -56,7 +56,7 @@ func (m MultiRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) for _, t := range m { gvrs, err := t.ResourcesFor(resource) // ignore "no match" errors, but any other error percolates back up - if IsNoResourceMatchError(err) { + if IsNoMatchError(err) { continue } if err != nil { @@ -91,7 +91,7 @@ func (m MultiRESTMapper) KindsFor(resource unversioned.GroupVersionResource) (gv for _, t := range m { gvks, err := t.KindsFor(resource) // ignore "no match" errors, but any other error percolates back up - if IsNoResourceMatchError(err) { + if IsNoMatchError(err) { continue } if err != nil { @@ -155,7 +155,7 @@ func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...strin for _, t := range m { currMapping, err := t.RESTMapping(gk, versions...) // ignore "no match" errors, but any other error percolates back up - if IsNoResourceMatchError(err) { + if IsNoMatchError(err) { continue } if err != nil { @@ -171,12 +171,43 @@ func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...strin return allMappings[0], nil } if len(allMappings) > 1 { - return nil, fmt.Errorf("multiple matches found for %v in %v", gk, versions) + var kinds []unversioned.GroupVersionKind + for _, m := range allMappings { + kinds = append(kinds, m.GroupVersionKind) + } + return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds} } if len(errors) > 0 { return nil, utilerrors.NewAggregate(errors) } - return nil, fmt.Errorf("no match found for %v in %v", gk, versions) + return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")} +} + +// RESTMappings returns all possible RESTMappings for the provided group kind, or an error +// if the type is not recognized. +func (m MultiRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error) { + var allMappings []*RESTMapping + var errors []error + + for _, t := range m { + currMappings, err := t.RESTMappings(gk) + // ignore "no match" errors, but any other error percolates back up + if IsNoMatchError(err) { + continue + } + if err != nil { + errors = append(errors, err) + continue + } + allMappings = append(allMappings, currMappings...) + } + if len(errors) > 0 { + return nil, utilerrors.NewAggregate(errors) + } + if len(allMappings) == 0 { + return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")} + } + return allMappings, nil } // AliasesForResource finds the first alias response for the provided mappers. diff --git a/pkg/api/meta/multirestmapper_test.go b/pkg/api/meta/multirestmapper_test.go index 2c6c3b2bd63..c0a77f0cf4e 100644 --- a/pkg/api/meta/multirestmapper_test.go +++ b/pkg/api/meta/multirestmapper_test.go @@ -256,11 +256,68 @@ func TestMultiRESTMapperKindFor(t *testing.T) { } } +func TestMultiRESTMapperRESTMappings(t *testing.T) { + mapping1, mapping2 := &RESTMapping{}, &RESTMapping{} + tcs := []struct { + name string + + mapper MultiRESTMapper + input unversioned.GroupKind + result []*RESTMapping + err error + }{ + { + name: "empty", + mapper: MultiRESTMapper{}, + input: unversioned.GroupKind{Kind: "Foo"}, + result: nil, + err: &NoKindMatchError{PartialKind: unversioned.GroupVersionKind{Kind: "Foo"}}, + }, + { + name: "ignore not found", + mapper: MultiRESTMapper{fixedRESTMapper{err: &NoKindMatchError{PartialKind: unversioned.GroupVersionKind{Kind: "IGNORE_THIS"}}}}, + input: unversioned.GroupKind{Kind: "Foo"}, + result: nil, + err: &NoKindMatchError{PartialKind: unversioned.GroupVersionKind{Kind: "Foo"}}, + }, + { + name: "accept first failure", + mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{mappings: []*RESTMapping{mapping1}}}, + input: unversioned.GroupKind{Kind: "Foo"}, + result: nil, + err: errors.New("fail on this"), + }, + { + name: "return both", + mapper: MultiRESTMapper{fixedRESTMapper{mappings: []*RESTMapping{mapping1}}, fixedRESTMapper{mappings: []*RESTMapping{mapping2}}}, + input: unversioned.GroupKind{Kind: "Foo"}, + result: []*RESTMapping{mapping1, mapping2}, + }, + } + + for _, tc := range tcs { + actualResult, actualErr := tc.mapper.RESTMappings(tc.input) + if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) { + t.Errorf("%s: expected %v, got %v", tc.name, e, a) + } + switch { + case tc.err == nil && actualErr == nil: + case tc.err == nil: + t.Errorf("%s: unexpected error: %v", tc.name, actualErr) + case actualErr == nil: + t.Errorf("%s: expected error: %v got nil", tc.name, tc.err) + case tc.err.Error() != actualErr.Error(): + t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr) + } + } +} + type fixedRESTMapper struct { resourcesFor []unversioned.GroupVersionResource kindsFor []unversioned.GroupVersionKind resourceFor unversioned.GroupVersionResource kindFor unversioned.GroupVersionKind + mappings []*RESTMapping err error } @@ -289,6 +346,10 @@ func (m fixedRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...strin return nil, m.err } +func (m fixedRESTMapper) RESTMappings(gk unversioned.GroupKind) (mappings []*RESTMapping, err error) { + return m.mappings, m.err +} + func (m fixedRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) { return nil, false } diff --git a/pkg/api/meta/priority.go b/pkg/api/meta/priority.go index 1e44e45f65b..2e460609d10 100644 --- a/pkg/api/meta/priority.go +++ b/pkg/api/meta/priority.go @@ -153,7 +153,60 @@ func kindMatches(pattern unversioned.GroupVersionKind, kind unversioned.GroupVer } func (m PriorityRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) { - return m.Delegate.RESTMapping(gk, versions...) + mappings, err := m.Delegate.RESTMappings(gk) + if err != nil { + return nil, err + } + + // any versions the user provides take priority + priorities := m.KindPriority + if len(versions) > 0 { + priorities = make([]unversioned.GroupVersionKind, 0, len(m.KindPriority)+len(versions)) + for _, version := range versions { + gv, err := unversioned.ParseGroupVersion(version) + if err != nil { + return nil, err + } + priorities = append(priorities, gv.WithKind(AnyKind)) + } + priorities = append(priorities, m.KindPriority...) + } + + remaining := append([]*RESTMapping{}, mappings...) + for _, pattern := range priorities { + var matching []*RESTMapping + for _, m := range remaining { + if kindMatches(pattern, m.GroupVersionKind) { + matching = append(matching, m) + } + } + + switch len(matching) { + case 0: + // if you have no matches, then nothing matched this pattern just move to the next + continue + case 1: + // one match, return + return matching[0], nil + default: + // more than one match, use the matched hits as the list moving to the next pattern. + // this way you can have a series of selection criteria + remaining = matching + } + } + if len(remaining) == 1 { + return remaining[0], nil + } + + var kinds []unversioned.GroupVersionKind + for _, m := range mappings { + kinds = append(kinds, m.GroupVersionKind) + } + return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds} +} + +func (m PriorityRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error) { + return m.Delegate.RESTMappings(gk) } func (m PriorityRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) { diff --git a/pkg/api/meta/priority_test.go b/pkg/api/meta/priority_test.go index 1489fe88707..0aa7be52431 100644 --- a/pkg/api/meta/priority_test.go +++ b/pkg/api/meta/priority_test.go @@ -17,6 +17,8 @@ limitations under the License. package meta import ( + "errors" + "reflect" "strings" "testing" @@ -204,3 +206,104 @@ func TestPriorityRESTMapperKindForErrorHandling(t *testing.T) { } } } + +func TestPriorityRESTMapperRESTMapping(t *testing.T) { + mapping1 := &RESTMapping{ + GroupVersionKind: unversioned.GroupVersionKind{Kind: "Foo", Version: "v1alpha1"}, + } + mapping2 := &RESTMapping{ + GroupVersionKind: unversioned.GroupVersionKind{Kind: "Foo", Version: "v1"}, + } + mapping3 := &RESTMapping{ + GroupVersionKind: unversioned.GroupVersionKind{Group: "other", Kind: "Foo", Version: "v1"}, + } + allMappers := MultiRESTMapper{ + fixedRESTMapper{mappings: []*RESTMapping{mapping1}}, + fixedRESTMapper{mappings: []*RESTMapping{mapping2}}, + fixedRESTMapper{mappings: []*RESTMapping{mapping3}}, + } + tcs := []struct { + name string + + mapper PriorityRESTMapper + input unversioned.GroupKind + result *RESTMapping + err error + }{ + { + name: "empty", + mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{}}, + input: unversioned.GroupKind{Kind: "Foo"}, + err: &NoKindMatchError{PartialKind: unversioned.GroupVersionKind{Kind: "Foo"}}, + }, + { + name: "ignore not found", + mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{fixedRESTMapper{err: &NoKindMatchError{PartialKind: unversioned.GroupVersionKind{Kind: "IGNORE_THIS"}}}}}, + input: unversioned.GroupKind{Kind: "Foo"}, + err: &NoKindMatchError{PartialKind: unversioned.GroupVersionKind{Kind: "Foo"}}, + }, + { + name: "accept first failure", + mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{mappings: []*RESTMapping{mapping1}}}}, + input: unversioned.GroupKind{Kind: "Foo"}, + err: errors.New("fail on this"), + }, + { + name: "return error for ambiguous", + mapper: PriorityRESTMapper{ + Delegate: allMappers, + }, + input: unversioned.GroupKind{Kind: "Foo"}, + err: &AmbiguousKindError{ + PartialKind: unversioned.GroupVersionKind{Kind: "Foo"}, + MatchingKinds: []unversioned.GroupVersionKind{ + {Kind: "Foo", Version: "v1alpha1"}, + {Kind: "Foo", Version: "v1"}, + {Group: "other", Kind: "Foo", Version: "v1"}, + }, + }, + }, + { + name: "accept only item", + mapper: PriorityRESTMapper{ + Delegate: fixedRESTMapper{mappings: []*RESTMapping{mapping1}}, + }, + input: unversioned.GroupKind{Kind: "Foo"}, + result: mapping1, + }, + { + name: "return single priority", + mapper: PriorityRESTMapper{ + Delegate: allMappers, + KindPriority: []unversioned.GroupVersionKind{{Version: "v1", Kind: AnyKind}, {Version: "v1alpha1", Kind: AnyKind}}, + }, + input: unversioned.GroupKind{Kind: "Foo"}, + result: mapping2, + }, + { + name: "return out of group match", + mapper: PriorityRESTMapper{ + Delegate: allMappers, + KindPriority: []unversioned.GroupVersionKind{{Group: AnyGroup, Version: "v1", Kind: AnyKind}, {Group: "other", Version: AnyVersion, Kind: AnyKind}}, + }, + input: unversioned.GroupKind{Kind: "Foo"}, + result: mapping3, + }, + } + + for _, tc := range tcs { + actualResult, actualErr := tc.mapper.RESTMapping(tc.input) + if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) { + t.Errorf("%s: expected %v, got %v", tc.name, e, a) + } + switch { + case tc.err == nil && actualErr == nil: + case tc.err == nil: + t.Errorf("%s: unexpected error: %v", tc.name, actualErr) + case actualErr == nil: + t.Errorf("%s: expected error: %v got nil", tc.name, tc.err) + case tc.err.Error() != actualErr.Error(): + t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr) + } + } +} diff --git a/pkg/api/meta/restmapper.go b/pkg/api/meta/restmapper.go index bf2567ea6ed..3ec95925982 100644 --- a/pkg/api/meta/restmapper.go +++ b/pkg/api/meta/restmapper.go @@ -431,6 +431,7 @@ func (o resourceByPreferredGroupVersion) Less(i, j int) bool { // RESTClient should use to operate on the provided group/kind in order of versions. If a version search // order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which // version should be used to access the named group/kind. +// TODO: consider refactoring to use RESTMappings in a way that preserves version ordering and preference func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) { // Pick an appropriate version var gvk *unversioned.GroupVersionKind @@ -462,7 +463,7 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st } } if gvk == nil { - return nil, fmt.Errorf("no kind named %q is registered in versions %q", gk, versions) + return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")} } // Ensure we have a REST mapping @@ -503,6 +504,49 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st return retVal, nil } +// RESTMappings returns the RESTMappings for the provided group kind in a rough internal preferred order. If no +// kind is found it will return a NoResourceMatchError. +func (m *DefaultRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error) { + // Use the default preferred versions + var mappings []*RESTMapping + for _, gv := range m.defaultGroupVersions { + if gv.Group != gk.Group { + continue + } + + gvk := gk.WithVersion(gv.Version) + gvr, ok := m.kindToPluralResource[gvk] + if !ok { + continue + } + + // Ensure we have a REST scope + scope, ok := m.kindToScope[gvk] + if !ok { + return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion(), gvk.Kind) + } + + interfaces, err := m.interfacesFunc(gvk.GroupVersion()) + if err != nil { + return nil, fmt.Errorf("the provided version %q has no relevant versions", gvk.GroupVersion().String()) + } + + mappings = append(mappings, &RESTMapping{ + Resource: gvr.Resource, + GroupVersionKind: gvk, + Scope: scope, + + ObjectConvertor: interfaces.ObjectConvertor, + MetadataAccessor: interfaces.MetadataAccessor, + }) + } + + if len(mappings) == 0 { + return nil, &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Group: gk.Group, Resource: gk.Kind}} + } + return mappings, nil +} + // AddResourceAlias maps aliases to resources func (m *DefaultRESTMapper) AddResourceAlias(alias string, resources ...string) { if len(resources) == 0 { diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 2c22149519b..5f553f29e79 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -318,18 +318,21 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} priorityRESTMapper := meta.PriorityRESTMapper{ Delegate: outputRESTMapper, - ResourcePriority: []unversioned.GroupVersionResource{ - {Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, - {Group: autoscaling.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, - {Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, - {Group: federation.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, - }, - KindPriority: []unversioned.GroupVersionKind{ - {Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, - {Group: autoscaling.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, - {Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, - {Group: federation.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, - }, + } + // TODO: this should come from registered versions + groups := []string{api.GroupName, autoscaling.GroupName, extensions.GroupName, federation.GroupName, batch.GroupName} + // set a preferred version + for _, group := range groups { + gvs := registered.EnabledVersionsForGroup(group) + if len(gvs) == 0 { + continue + } + priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: gvs[0].Version, Resource: meta.AnyResource}) + priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: gvs[0].Version, Kind: meta.AnyKind}) + } + for _, group := range groups { + priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource}) + priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind}) } return priorityRESTMapper, api.Scheme }, diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index b00b0d13dba..073b15610ff 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -125,9 +125,7 @@ func checkErr(pref string, err error, handleErr func(string)) { handleErr(MultilineError(prefix, errs)) } - if meta.IsNoResourceMatchError(err) { - noMatch := err.(*meta.NoResourceMatchError) - + if noMatch, ok := err.(*meta.NoResourceMatchError); ok { switch { case len(noMatch.PartialResource.Group) > 0 && len(noMatch.PartialResource.Version) > 0: handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q and version %q", pref, noMatch.PartialResource.Resource, noMatch.PartialResource.Group, noMatch.PartialResource.Version)) diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 0c4706dd043..667fb2ecd8e 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -127,14 +127,18 @@ func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) return e.RESTMapper.ResourceFor(expandResourceShortcut(resource)) } -func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) { - return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource) -} - func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) { return e.RESTMapper.RESTMapping(gk, versions...) } +func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) { + return e.RESTMapper.RESTMappings(gk) +} + +func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) { + return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource) +} + func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) { return e.RESTMapper.AliasesForResource(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource) } diff --git a/pkg/registry/thirdpartyresourcedata/codec.go b/pkg/registry/thirdpartyresourcedata/codec.go index c7630675413..c8ebfb16437 100644 --- a/pkg/registry/thirdpartyresourcedata/codec.go +++ b/pkg/registry/thirdpartyresourcedata/codec.go @@ -152,6 +152,27 @@ func (t *thirdPartyResourceDataMapper) RESTMapping(gk unversioned.GroupKind, ver return mapping, nil } +func (t *thirdPartyResourceDataMapper) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) { + if gk.Group != t.group { + return nil, fmt.Errorf("unknown group %q expected %s", gk.Group, t.group) + } + if gk.Kind != "ThirdPartyResourceData" { + return nil, fmt.Errorf("unknown kind %s expected %s", gk.Kind, t.kind) + } + + // TODO figure out why we're doing this rewriting + extensionGK := unversioned.GroupKind{Group: extensions.GroupName, Kind: "ThirdPartyResourceData"} + + mappings, err := t.mapper.RESTMappings(extensionGK) + if err != nil { + return nil, err + } + for _, m := range mappings { + m.ObjectConvertor = &thirdPartyObjectConverter{m.ObjectConvertor} + } + return mappings, nil +} + func (t *thirdPartyResourceDataMapper) AliasesForResource(resource string) ([]string, bool) { return t.mapper.AliasesForResource(resource) }