diff --git a/pkg/kubectl/cmd/util/shortcut_restmapper.go b/pkg/kubectl/cmd/util/shortcut_restmapper.go index e8f43d3324..4226c8a710 100644 --- a/pkg/kubectl/cmd/util/shortcut_restmapper.go +++ b/pkg/kubectl/cmd/util/shortcut_restmapper.go @@ -71,23 +71,23 @@ func (e ShortcutExpander) getAll() []schema.GroupResource { } func (e ShortcutExpander) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { - return e.RESTMapper.KindFor(expandResourceShortcut(resource)) + return e.RESTMapper.KindFor(e.expandResourceShortcut(resource)) } func (e ShortcutExpander) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) { - return e.RESTMapper.KindsFor(expandResourceShortcut(resource)) + return e.RESTMapper.KindsFor(e.expandResourceShortcut(resource)) } func (e ShortcutExpander) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) { - return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource)) + return e.RESTMapper.ResourcesFor(e.expandResourceShortcut(resource)) } func (e ShortcutExpander) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) { - return e.RESTMapper.ResourceFor(expandResourceShortcut(resource)) + return e.RESTMapper.ResourceFor(e.expandResourceShortcut(resource)) } func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) { - return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource) + return e.RESTMapper.ResourceSingularizer(e.expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource) } func (e ShortcutExpander) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) { @@ -124,17 +124,60 @@ func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) { } return aliases, true } - expanded := expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource + expanded := e.expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource return []string{expanded}, (expanded != resource) } +// getShortcutMappings returns a hardcoded set of tuples. +// First the list of potential resources will be taken from the instance variable +// which holds the anticipated result of the discovery API. +// Next we will fall back to the hardcoded list of resources. +// Note that the list is ordered by group priority. +// TODO: Wire this to discovery API. +func (e ShortcutExpander) getShortcutMappings() ([]kubectl.ResourceShortcuts, error) { + res := []kubectl.ResourceShortcuts{ + { + ShortForm: schema.GroupResource{Group: "storage.k8s.io", Resource: "sc"}, + LongForm: schema.GroupResource{Group: "storage.k8s.io", Resource: "storageclasses"}, + }, + } + + // append hardcoded short forms at the end of the list + res = append(res, kubectl.ResourcesShortcutStatic...) + return res, nil +} + // 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 schema.GroupVersionResource) schema.GroupVersionResource { - if expanded, ok := kubectl.ShortForms[resource.Resource]; ok { - resource.Resource = expanded - return resource +// indeed a shortcut. If no match has been found, we will match on group prefixing. +// Lastly we will return resource unmodified. +func (e ShortcutExpander) expandResourceShortcut(resource schema.GroupVersionResource) schema.GroupVersionResource { + // get the shortcut mappings and return on first match. + if resources, err := e.getShortcutMappings(); err == nil { + for _, item := range resources { + if len(resource.Group) != 0 && resource.Group != item.ShortForm.Group { + continue + } + if resource.Resource == item.ShortForm.Resource { + resource.Resource = item.LongForm.Resource + return resource + } + } + + // we didn't find exact match so match on group prefixing. This allows autoscal to match autoscaling + if len(resource.Group) == 0 { + return resource + } + for _, item := range resources { + if !strings.HasPrefix(item.ShortForm.Group, resource.Group) { + continue + } + if resource.Resource == item.ShortForm.Resource { + resource.Resource = item.LongForm.Resource + return resource + } + } } + return resource } diff --git a/pkg/kubectl/cmd/util/shortcut_restmapper_test.go b/pkg/kubectl/cmd/util/shortcut_restmapper_test.go index 20d671ec8e..7788872fc2 100644 --- a/pkg/kubectl/cmd/util/shortcut_restmapper_test.go +++ b/pkg/kubectl/cmd/util/shortcut_restmapper_test.go @@ -21,6 +21,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/runtime/schema" ) func TestReplaceAliases(t *testing.T) { @@ -44,6 +45,16 @@ func TestReplaceAliases(t *testing.T) { arg: "all,secrets", expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets,secrets", }, + { + name: "sc-resolves-to-storageclasses", + arg: "sc", + expected: "storageclasses", + }, + { + name: "storageclasses-no-replacement", + arg: "storageclasses", + expected: "storageclasses", + }, } mapper := NewShortcutExpander(testapi.Default.RESTMapper(), nil) @@ -59,3 +70,30 @@ func TestReplaceAliases(t *testing.T) { } } } +func TestKindFor(t *testing.T) { + tests := []struct { + in schema.GroupVersionResource + expected schema.GroupVersionKind + }{ + { + in: schema.GroupVersionResource{Group: "storage.k8s.io", Version: "", Resource: "sc"}, + expected: schema.GroupVersionKind{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}, + }, + { + in: schema.GroupVersionResource{Group: "", Version: "", Resource: "sc"}, + expected: schema.GroupVersionKind{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}, + }, + } + + mapper := NewShortcutExpander(testapi.Default.RESTMapper(), nil) + + for i, test := range tests { + ret, err := mapper.KindFor(test.in) + if err != nil { + t.Errorf("%d: unexpected error returned %s", i, err.Error()) + } + if ret != test.expected { + t.Errorf("%d: unexpected data returned %#v, expected %#v", i, ret, test.expected) + } + } +} diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index ac6cdd2b7e..aa8ee733a0 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -75,42 +75,121 @@ func (m OutputVersionMapper) RESTMapping(gk schema.GroupKind, versions ...string return m.RESTMapper.RESTMapping(gk, versions...) } -// ShortForms is the list of short names to their expanded names -var ShortForms = map[string]string{ - // Please keep this alphabetized +// ResourceShortcuts represents a structure that holds the information how to +// transition from resource's shortcut to its full name. +type ResourceShortcuts struct { + ShortForm schema.GroupResource + LongForm schema.GroupResource +} + +// ResourcesShortcutStatic is the list of short names to their expanded names. +// Note that the list is ordered by group. +var ResourcesShortcutStatic = []ResourceShortcuts{ // 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. - "cm": "configmaps", - "cs": "componentstatuses", - "csr": "certificatesigningrequests", - "deploy": "deployments", - "ds": "daemonsets", - "ep": "endpoints", - "ev": "events", - "hpa": "horizontalpodautoscalers", - "ing": "ingresses", - "limits": "limitranges", - "no": "nodes", - "ns": "namespaces", - "pdb": "poddisruptionbudgets", - "po": "pods", - "psp": "podSecurityPolicies", - "pvc": "persistentvolumeclaims", - "pv": "persistentvolumes", - "quota": "resourcequotas", - "rc": "replicationcontrollers", - "rs": "replicasets", - "sa": "serviceaccounts", - "svc": "services", + { + ShortForm: schema.GroupResource{Resource: "cm"}, + LongForm: schema.GroupResource{Resource: "configmaps"}, + }, + { + ShortForm: schema.GroupResource{Resource: "cs"}, + LongForm: schema.GroupResource{Resource: "componentstatuses"}, + }, + { + ShortForm: schema.GroupResource{Resource: "ep"}, + LongForm: schema.GroupResource{Resource: "endpoints"}, + }, + { + ShortForm: schema.GroupResource{Resource: "ev"}, + LongForm: schema.GroupResource{Resource: "events"}, + }, + { + ShortForm: schema.GroupResource{Resource: "limits"}, + LongForm: schema.GroupResource{Resource: "limitranges"}, + }, + { + ShortForm: schema.GroupResource{Resource: "no"}, + LongForm: schema.GroupResource{Resource: "nodes"}, + }, + { + ShortForm: schema.GroupResource{Resource: "ns"}, + LongForm: schema.GroupResource{Resource: "namespaces"}, + }, + { + ShortForm: schema.GroupResource{Resource: "po"}, + LongForm: schema.GroupResource{Resource: "pods"}, + }, + { + ShortForm: schema.GroupResource{Resource: "pvc"}, + LongForm: schema.GroupResource{Resource: "persistentvolumeclaims"}, + }, + { + ShortForm: schema.GroupResource{Resource: "pv"}, + LongForm: schema.GroupResource{Resource: "persistentvolumes"}, + }, + { + ShortForm: schema.GroupResource{Resource: "quota"}, + LongForm: schema.GroupResource{Resource: "resourcequotas"}, + }, + { + ShortForm: schema.GroupResource{Resource: "rc"}, + LongForm: schema.GroupResource{Resource: "replicationcontrollers"}, + }, + { + ShortForm: schema.GroupResource{Resource: "rs"}, + LongForm: schema.GroupResource{Resource: "replicasets"}, + }, + { + ShortForm: schema.GroupResource{Resource: "sa"}, + LongForm: schema.GroupResource{Resource: "serviceaccounts"}, + }, + { + ShortForm: schema.GroupResource{Resource: "svc"}, + LongForm: schema.GroupResource{Resource: "services"}, + }, + { + ShortForm: schema.GroupResource{Group: "autoscaling", Resource: "hpa"}, + LongForm: schema.GroupResource{Group: "autoscaling", Resource: "horizontalpodautoscalers"}, + }, + { + ShortForm: schema.GroupResource{Group: "certificates.k8s.io", Resource: "csr"}, + LongForm: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"}, + }, + { + ShortForm: schema.GroupResource{Group: "policy", Resource: "pdb"}, + LongForm: schema.GroupResource{Group: "policy", Resource: "poddisruptionbudgets"}, + }, + { + ShortForm: schema.GroupResource{Group: "extensions", Resource: "deploy"}, + LongForm: schema.GroupResource{Group: "extensions", Resource: "deployments"}, + }, + { + ShortForm: schema.GroupResource{Group: "extensions", Resource: "ds"}, + LongForm: schema.GroupResource{Group: "extensions", Resource: "daemonsets"}, + }, + { + ShortForm: schema.GroupResource{Group: "extensions", Resource: "hpa"}, + LongForm: schema.GroupResource{Group: "extensions", Resource: "horizontalpodautoscalers"}, + }, + { + ShortForm: schema.GroupResource{Group: "extensions", Resource: "ing"}, + LongForm: schema.GroupResource{Group: "extensions", Resource: "ingresses"}, + }, + { + ShortForm: schema.GroupResource{Group: "extensions", Resource: "psp"}, + LongForm: schema.GroupResource{Group: "extensions", Resource: "podSecurityPolicies"}, + }, } // ResourceShortFormFor looks up for a short form of resource names. +// TODO: Change the signature of this function so that it can +// make use of ResourceShortcuts. func ResourceShortFormFor(resource string) (string, bool) { var alias string exists := false - for k, val := range ShortForms { - if val == resource { - alias = k + for _, item := range ResourcesShortcutStatic { + if item.LongForm.Resource == resource { + alias = item.ShortForm.Resource exists = true break } @@ -139,9 +218,9 @@ func ResourceAliases(rs []string) []string { plurals[plural] = struct{}{} } - for sf, r := range ShortForms { - if _, found := plurals[r]; found { - as = append(as, sf) + for _, item := range ResourcesShortcutStatic { + if _, found := plurals[item.LongForm.Resource]; found { + as = append(as, item.ShortForm.Resource) } } return as