diff --git a/pkg/api/install/install.go b/pkg/api/install/install.go index 0cc911e7392..517521dc50c 100644 --- a/pkg/api/install/install.go +++ b/pkg/api/install/install.go @@ -86,9 +86,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { 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 { // 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 @@ -115,8 +112,6 @@ func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper "ThirdPartyResourceList") mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) - // setup aliases for groups of resources - mapper.AddResourceAlias("all", userResources...) return mapper } diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 8063f481b56..552d5578087 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -299,7 +299,7 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec, runtime.Neg mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured) typer := discovery.NewUnstructuredObjectTyper(groupResources) - return kubectl.ShortcutExpander{RESTMapper: mapper}, typer, nil + return cmdutil.NewShortcutExpander(mapper), typer, nil }, ClientSet: func() (*internalclientset.Clientset, error) { // Swap out the HTTP client out of the client with the fake's version. diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index b8305bcb867..13f18aeaafe 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -326,7 +326,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } // wrap with shortcuts - mapper = kubectl.ShortcutExpander{RESTMapper: mapper} + mapper = NewShortcutExpander(mapper) // wrap with output preferences mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} return mapper, api.Scheme @@ -363,7 +363,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { typer := discovery.NewUnstructuredObjectTyper(groupResources) - return kubectl.ShortcutExpander{RESTMapper: mapper}, typer, nil + return NewShortcutExpander(mapper), typer, nil }, RESTClient: func() (*restclient.RESTClient, error) { clientConfig, err := clients.ClientConfigForVersion(nil) diff --git a/pkg/kubectl/cmd/util/shortcut_restmapper.go b/pkg/kubectl/cmd/util/shortcut_restmapper.go new file mode 100644 index 00000000000..389796ddce0 --- /dev/null +++ b/pkg/kubectl/cmd/util/shortcut_restmapper.go @@ -0,0 +1,89 @@ +/* +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 +} diff --git a/pkg/kubectl/cmd/util/shortcut_restmapper_test.go b/pkg/kubectl/cmd/util/shortcut_restmapper_test.go new file mode 100644 index 00000000000..888418bc7aa --- /dev/null +++ b/pkg/kubectl/cmd/util/shortcut_restmapper_test.go @@ -0,0 +1,61 @@ +/* +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) + } + } +} diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 70e1a47d788..b8f5580d49b 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -97,48 +97,8 @@ func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...s return m.RESTMapper.RESTMapping(gk, versions...) } -// ShortcutExpander is a RESTMapper that can be used for Kubernetes -// resources. It expands the resource first, then invokes the wrapped RESTMapper -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{ +// ShortForms is the list of short names to their expanded names +var ShortForms = map[string]string{ // Please keep this alphabetized // 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. @@ -165,30 +125,20 @@ var shortForms = map[string]string{ "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) { var alias string exists := false - for k, val := range shortForms { + for k, val := range ShortForms { if val == resource { alias = k exists = true + break } } 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. func ResourceAliases(rs []string) []string { as := make([]string, 0, len(rs)) @@ -210,7 +160,7 @@ func ResourceAliases(rs []string) []string { plurals[plural] = struct{}{} } - for sf, r := range shortForms { + for sf, r := range ShortForms { if _, found := plurals[r]; found { as = append(as, sf) } diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index 32a2264a15c..47c2630d61b 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -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) { tests := []struct { args []string