Commit 1c10f538 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #32493 from deads2k/client-06-discoveryrestmapper

Automatic merge from submit-queue use discovery restmapper for kubectl Updates the `kubectl` factory to use a discovery rest mapper for locating resources. This allows generic gets. @kargakis @sttts @fabianofranz I'll let you guys fight over it. :)
parents 5766776c 771915c6
...@@ -86,9 +86,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { ...@@ -86,9 +86,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error {
return nil return nil
} }
// userResources is a group of resources mostly used by a kubectl user
var userResources = []string{"rc", "svc", "pods", "pvc"}
func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
// the list of kinds that are scoped at the root of the api hierarchy // the list of kinds that are scoped at the root of the api hierarchy
// if a kind is not enumerated here, it is assumed to have a namespace scope // if a kind is not enumerated here, it is assumed to have a namespace scope
...@@ -115,8 +112,6 @@ func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper ...@@ -115,8 +112,6 @@ func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper
"ThirdPartyResourceList") "ThirdPartyResourceList")
mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
// setup aliases for groups of resources
mapper.AddResourceAlias("all", userResources...)
return mapper return mapper
} }
......
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"fmt"
"k8s.io/kubernetes/pkg/api/unversioned"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
)
// FirstHitRESTMapper is a wrapper for multiple RESTMappers which returns the
// first successful result for the singular requests
type FirstHitRESTMapper struct {
MultiRESTMapper
}
func (m FirstHitRESTMapper) String() string {
return fmt.Sprintf("FirstHitRESTMapper{\n\t%v\n}", m.MultiRESTMapper)
}
func (m FirstHitRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
errors := []error{}
for _, t := range m.MultiRESTMapper {
ret, err := t.ResourceFor(resource)
if err == nil {
return ret, nil
}
errors = append(errors, err)
}
return unversioned.GroupVersionResource{}, collapseAggregateErrors(errors)
}
func (m FirstHitRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
errors := []error{}
for _, t := range m.MultiRESTMapper {
ret, err := t.KindFor(resource)
if err == nil {
return ret, nil
}
errors = append(errors, err)
}
return unversioned.GroupVersionKind{}, collapseAggregateErrors(errors)
}
// RESTMapping provides the REST mapping for the resource based on the
// kind and version. This implementation supports multiple REST schemas and
// return the first match.
func (m FirstHitRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) {
errors := []error{}
for _, t := range m.MultiRESTMapper {
ret, err := t.RESTMapping(gk, versions...)
if err == nil {
return ret, nil
}
errors = append(errors, err)
}
return nil, collapseAggregateErrors(errors)
}
// collapseAggregateErrors returns the minimal errors. it handles empty as nil, handles one item in a list
// by returning the item, and collapses all NoMatchErrors to a single one (since they should all be the same)
func collapseAggregateErrors(errors []error) error {
if len(errors) == 0 {
return nil
}
if len(errors) == 1 {
return errors[0]
}
allNoMatchErrors := true
for _, err := range errors {
allNoMatchErrors = allNoMatchErrors && IsNoMatchError(err)
}
if allNoMatchErrors {
return errors[0]
}
return utilerrors.NewAggregate(errors)
}
...@@ -526,7 +526,7 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st ...@@ -526,7 +526,7 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st
interfaces, err := m.interfacesFunc(gvk.GroupVersion()) interfaces, err := m.interfacesFunc(gvk.GroupVersion())
if err != nil { if err != nil {
return nil, fmt.Errorf("the provided version %q has no relevant versions", gvk.GroupVersion().String()) return nil, fmt.Errorf("the provided version %q has no relevant versions: %v", gvk.GroupVersion().String(), err)
} }
retVal := &RESTMapping{ retVal := &RESTMapping{
...@@ -565,7 +565,7 @@ func (m *DefaultRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMappi ...@@ -565,7 +565,7 @@ func (m *DefaultRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMappi
interfaces, err := m.interfacesFunc(gvk.GroupVersion()) interfaces, err := m.interfacesFunc(gvk.GroupVersion())
if err != nil { if err != nil {
return nil, fmt.Errorf("the provided version %q has no relevant versions", gvk.GroupVersion().String()) return nil, fmt.Errorf("the provided version %q has no relevant versions: %v", gvk.GroupVersion().String(), err)
} }
mappings = append(mappings, &RESTMapping{ mappings = append(mappings, &RESTMapping{
......
...@@ -111,6 +111,7 @@ var ( ...@@ -111,6 +111,7 @@ var (
EnableVersions = DefaultAPIRegistrationManager.EnableVersions EnableVersions = DefaultAPIRegistrationManager.EnableVersions
RegisterGroup = DefaultAPIRegistrationManager.RegisterGroup RegisterGroup = DefaultAPIRegistrationManager.RegisterGroup
RegisterVersions = DefaultAPIRegistrationManager.RegisterVersions RegisterVersions = DefaultAPIRegistrationManager.RegisterVersions
InterfacesFor = DefaultAPIRegistrationManager.InterfacesFor
) )
// RegisterVersions adds the given group versions to the list of registered group versions. // RegisterVersions adds the given group versions to the list of registered group versions.
...@@ -265,6 +266,15 @@ func (m *APIRegistrationManager) AddThirdPartyAPIGroupVersions(gvs ...unversione ...@@ -265,6 +266,15 @@ func (m *APIRegistrationManager) AddThirdPartyAPIGroupVersions(gvs ...unversione
return skippedGVs return skippedGVs
} }
// InterfacesFor is a union meta.VersionInterfacesFunc func for all registered types
func (m *APIRegistrationManager) InterfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
groupMeta, err := m.Group(version.Group)
if err != nil {
return nil, err
}
return groupMeta.InterfacesFor(version)
}
// TODO: This is an expedient function, because we don't check if a Group is // TODO: This is an expedient function, because we don't check if a Group is
// supported throughout the code base. We will abandon this function and // supported throughout the code base. We will abandon this function and
// checking the error returned by the Group() function. // checking the error returned by the Group() function.
......
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package discovery package discovery
import ( import (
"fmt"
"sync" "sync"
"k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/errors"
...@@ -259,5 +260,13 @@ func (d *DeferredDiscoveryRESTMapper) ResourceSingularizer(resource string) (sin ...@@ -259,5 +260,13 @@ func (d *DeferredDiscoveryRESTMapper) ResourceSingularizer(resource string) (sin
return del.ResourceSingularizer(resource) return del.ResourceSingularizer(resource)
} }
func (d *DeferredDiscoveryRESTMapper) String() string {
del, err := d.getDelegate()
if err != nil {
return fmt.Sprintf("DeferredDiscoveryRESTMapper{%v}", err)
}
return fmt.Sprintf("DeferredDiscoveryRESTMapper{\n\t%v\n}", del)
}
// Make sure it satisfies the interface // Make sure it satisfies the interface
var _ meta.RESTMapper = &DeferredDiscoveryRESTMapper{} var _ meta.RESTMapper = &DeferredDiscoveryRESTMapper{}
...@@ -299,7 +299,7 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec, runtime.Neg ...@@ -299,7 +299,7 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec, runtime.Neg
mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured) mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured)
typer := discovery.NewUnstructuredObjectTyper(groupResources) typer := discovery.NewUnstructuredObjectTyper(groupResources)
return kubectl.ShortcutExpander{RESTMapper: mapper}, typer, nil return cmdutil.NewShortcutExpander(mapper), typer, nil
}, },
ClientSet: func() (*internalclientset.Clientset, error) { ClientSet: func() (*internalclientset.Clientset, error) {
// Swap out the HTTP client out of the client with the fake's version. // Swap out the HTTP client out of the client with the fake's version.
......
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
)
// ShortcutExpander is a RESTMapper that can be used for OpenShift resources. It expands the resource first, then invokes the wrapped
type ShortcutExpander struct {
RESTMapper meta.RESTMapper
All []string
}
var _ meta.RESTMapper = &ShortcutExpander{}
func NewShortcutExpander(delegate meta.RESTMapper) ShortcutExpander {
return ShortcutExpander{All: userResources, RESTMapper: delegate}
}
func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
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)
}
// userResources are the resource names that apply to the primary, user facing resources used by
// client tools. They are in deletion-first order - dependent resources should be last.
var userResources = []string{"rc", "svc", "pods", "pvc"}
// AliasesForResource returns whether a resource has an alias or not
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
if resource == "all" {
return e.All, true
}
expanded := expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource
return []string{expanded}, (expanded != resource)
}
// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
if expanded, ok := kubectl.ShortForms[resource.Resource]; ok {
resource.Resource = expanded
return resource
}
return resource
}
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/api/testapi"
)
func TestReplaceAliases(t *testing.T) {
tests := []struct {
name string
arg string
expected string
}{
{
name: "no-replacement",
arg: "service",
expected: "service",
},
{
name: "all-replacement",
arg: "all",
expected: "rc,svc,pods,pvc",
},
{
name: "alias-in-comma-separated-arg",
arg: "all,secrets",
expected: "rc,svc,pods,pvc,secrets",
},
}
mapper := NewShortcutExpander(testapi.Default.RESTMapper())
for _, test := range tests {
resources := []string{}
for _, arg := range strings.Split(test.arg, ",") {
curr, _ := mapper.AliasesForResource(arg)
resources = append(resources, curr...)
}
if strings.Join(resources, ",") != test.expected {
t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, resources)
}
}
}
...@@ -97,48 +97,8 @@ func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...s ...@@ -97,48 +97,8 @@ func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...s
return m.RESTMapper.RESTMapping(gk, versions...) return m.RESTMapper.RESTMapping(gk, versions...)
} }
// ShortcutExpander is a RESTMapper that can be used for Kubernetes // ShortForms is the list of short names to their expanded names
// resources. It expands the resource first, then invokes the wrapped RESTMapper var ShortForms = map[string]string{
type ShortcutExpander struct {
RESTMapper meta.RESTMapper
}
var _ meta.RESTMapper = &ShortcutExpander{}
func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourceFor(expandResourceShortcut(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)
}
// shortForms is the list of short names to their expanded names
var shortForms = map[string]string{
// Please keep this alphabetized // Please keep this alphabetized
// If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go // If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go
// and add an entry to valid_resources when appropriate. // and add an entry to valid_resources when appropriate.
...@@ -165,30 +125,20 @@ var shortForms = map[string]string{ ...@@ -165,30 +125,20 @@ var shortForms = map[string]string{
"svc": "services", "svc": "services",
} }
// Look-up for resource short forms by value // ResourceShortFormFor looks up for a short form of resource names.
func ResourceShortFormFor(resource string) (string, bool) { func ResourceShortFormFor(resource string) (string, bool) {
var alias string var alias string
exists := false exists := false
for k, val := range shortForms { for k, val := range ShortForms {
if val == resource { if val == resource {
alias = k alias = k
exists = true exists = true
break
} }
} }
return alias, exists return alias, exists
} }
// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
if expanded, ok := shortForms[resource.Resource]; ok {
// don't change the group or version that's already been specified
resource.Resource = expanded
}
return resource
}
// ResourceAliases returns the resource shortcuts and plural forms for the given resources. // ResourceAliases returns the resource shortcuts and plural forms for the given resources.
func ResourceAliases(rs []string) []string { func ResourceAliases(rs []string) []string {
as := make([]string, 0, len(rs)) as := make([]string, 0, len(rs))
...@@ -210,7 +160,7 @@ func ResourceAliases(rs []string) []string { ...@@ -210,7 +160,7 @@ func ResourceAliases(rs []string) []string {
plurals[plural] = struct{}{} plurals[plural] = struct{}{}
} }
for sf, r := range shortForms { for sf, r := range ShortForms {
if _, found := plurals[r]; found { if _, found := plurals[r]; found {
as = append(as, sf) as = append(as, sf)
} }
......
...@@ -1177,39 +1177,6 @@ func TestReceiveMultipleErrors(t *testing.T) { ...@@ -1177,39 +1177,6 @@ func TestReceiveMultipleErrors(t *testing.T) {
} }
} }
func TestReplaceAliases(t *testing.T) {
tests := []struct {
name string
arg string
expected string
}{
{
name: "no-replacement",
arg: "service",
expected: "service",
},
{
name: "all-replacement",
arg: "all",
expected: "rc,svc,pods,pvc",
},
{
name: "alias-in-comma-separated-arg",
arg: "all,secrets",
expected: "rc,svc,pods,pvc,secrets",
},
}
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec())
for _, test := range tests {
replaced := b.replaceAliases(test.arg)
if replaced != test.expected {
t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, replaced)
}
}
}
func TestHasNames(t *testing.T) { func TestHasNames(t *testing.T) {
tests := []struct { tests := []struct {
args []string args []string
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment