Merge pull request #63421 from liggitt/discover-completions

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Cache preferred resources, use in kubectl resource name autocomplete

Fixes #63145
Fixes https://github.com/kubernetes/kubectl/issues/357
Alternative to #61928 

* starts to unify preferred resource logic on top of ServerGroups()/ServerResourcesForGroupVersion() methods
* allows indicating a cached list of resources is acceptable when calling `kubectl api-resources` (default is still to rediscover)
* uses `kubectl api-resources` in bash completion

```sh
$ kubectl get [TAB][TAB]
apiservices.apiregistration.k8s.io                            networkpolicies.extensions
certificatesigningrequests.certificates.k8s.io                networkpolicies.networking.k8s.io
clusterrolebindings.rbac.authorization.k8s.io                 nodes
clusterroles.rbac.authorization.k8s.io                        persistentvolumeclaims
componentstatuses                                             persistentvolumes
configmaps                                                    poddisruptionbudgets.policy
controllerrevisions.apps                                      pods
cronjobs.batch                                                podsecuritypolicies.extensions
customresourcedefinitions.apiextensions.k8s.io                podsecuritypolicies.policy
daemonsets.apps                                               podtemplates
daemonsets.extensions                                         replicasets.apps
deployments.apps                                              replicasets.extensions
deployments.extensions                                        replicationcontrollers
endpoints                                                     resourcequotas
events                                                        rolebindings.rbac.authorization.k8s.io
events.events.k8s.io                                          roles.rbac.authorization.k8s.io
horizontalpodautoscalers.autoscaling                          secrets
ingresses.extensions                                          serviceaccounts
initializerconfigurations.admissionregistration.k8s.io        services
jobs.batch                                                    statefulsets.apps
limitranges                                                   storageclasses.storage.k8s.io
mutatingwebhookconfigurations.admissionregistration.k8s.io    validatingwebhookconfigurations.admissionregistration.k8s.io
namespaces                                                    volumeattachments.storage.k8s.io
```


```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2018-05-04 10:08:53 -07:00 committed by GitHub
commit 068b7befa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 27 additions and 60 deletions

View File

@ -120,7 +120,6 @@ func NewAnnotateOptions(ioStreams genericclioptions.IOStreams) *AnnotateOptions
func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewAnnotateOptions(ioStreams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
@ -133,8 +132,6 @@ func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioption
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunAnnotate())
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs

View File

@ -59,6 +59,7 @@ type ApiResourcesOptions struct {
Namespaced bool
Verbs []string
NoHeaders bool
Cached bool
genericclioptions.IOStreams
}
@ -94,6 +95,7 @@ func NewCmdApiResources(f cmdutil.Factory, ioStreams genericclioptions.IOStreams
cmd.Flags().StringVar(&o.APIGroup, "api-group", "", "Limit to resources in the specified API group.")
cmd.Flags().BoolVar(&o.Namespaced, "namespaced", true, "Namespaced indicates if a resource is namespaced or not.")
cmd.Flags().StringSliceVar(&o.Verbs, "verbs", o.Verbs, "Limit to resources that support the specified verbs.")
cmd.Flags().BoolVar(&o.Cached, "cached", o.Cached, "Use the cached list of resources if available.")
return cmd
}
@ -125,8 +127,10 @@ func (o *ApiResourcesOptions) RunApiResources(cmd *cobra.Command, f cmdutil.Fact
return err
}
// Always request fresh data from the server
discoveryclient.Invalidate()
if !o.Cached {
// Always request fresh data from the server
discoveryclient.Invalidate()
}
lists, err := discoveryclient.ServerPreferredResources()
if err != nil {

View File

@ -19,7 +19,6 @@ package cmd
import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
@ -59,8 +58,6 @@ var (
func NewCmdApplyEditLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := editor.NewEditOptions(editor.ApplyEditMode, ioStreams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "edit-last-applied (RESOURCE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true,
@ -75,8 +72,6 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I
cmdutil.CheckErr(err)
}
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs

View File

@ -67,7 +67,6 @@ func NewViewLastAppliedOptions(ioStreams genericclioptions.IOStreams) *ViewLastA
func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := NewViewLastAppliedOptions(ioStreams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "view-last-applied (TYPE [NAME | -l label] | TYPE/NAME | -f FILENAME)",
@ -80,8 +79,6 @@ func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I
cmdutil.CheckErr(options.Validate(cmd))
cmdutil.CheckErr(options.RunApplyViewLastApplied(cmd))
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", options.OutputFormat, "Output format. Must be one of yaml|json")

View File

@ -60,9 +60,6 @@ __kubectl_override_flags()
;;
esac
done
if [ "${w}" == "--all-namespaces" ]; then
namespace="--all-namespaces"
fi
done
for var in "${__kubectl_override_flag_list[@]##*-}"; do
if eval "test -n \"\$${var}\""; then
@ -109,7 +106,12 @@ __kubectl_parse_get()
__kubectl_get_resource()
{
if [[ ${#nouns[@]} -eq 0 ]]; then
return 1
local kubectl_out
if kubectl_out=$(kubectl api-resources $(__kubectl_override_flags) -o name --cached --request-timeout=5s --verbs=get 2>/dev/null); then
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
return 0
fi
return 1
fi
__kubectl_parse_get "${nouns[${#nouns[@]} -1]}"
}

View File

@ -115,7 +115,6 @@ type DeleteOptions struct {
func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
@ -138,8 +137,6 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
}
},
SuggestFor: []string{"rm"},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
deleteFlags.AddFlags(cmd)

View File

@ -21,7 +21,6 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
@ -72,8 +71,6 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra
o := editor.NewEditOptions(editor.NormalEditMode, ioStreams)
o.ValidateOptions = cmdutil.ValidateOptions{EnableValidation: true}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "edit (RESOURCE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true,
@ -88,8 +85,6 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra
cmdutil.CheckErr(err)
}
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs

View File

@ -148,7 +148,6 @@ func NewGetOptions(parent string, streams genericclioptions.IOStreams) *GetOptio
// retrieves one or more resources from a server.
func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewGetOptions(parent, streams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "get [(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]",
@ -162,8 +161,6 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStr
cmdutil.CheckErr(o.Run(f, cmd, args))
},
SuggestFor: []string{"list", "ps"},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
o.PrintFlags.AddFlags(cmd)

View File

@ -34,7 +34,6 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@ -124,8 +123,6 @@ func NewLabelOptions(ioStreams genericclioptions.IOStreams) *LabelOptions {
func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewLabelOptions(ioStreams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
DisableFlagsInUseLine: true,
@ -137,8 +134,6 @@ func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunLabel())
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
o.RecordFlags.AddFlags(cmd)

View File

@ -33,7 +33,6 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@ -106,7 +105,6 @@ func NewPatchOptions(ioStreams genericclioptions.IOStreams) *PatchOptions {
func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewPatchOptions(ioStreams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "patch (-f FILENAME | TYPE NAME) -p PATCH",
@ -119,8 +117,6 @@ func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunPatch())
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
o.RecordFlags.AddFlags(cmd)

View File

@ -220,11 +220,11 @@ func (d *CachedDiscoveryClient) RESTClient() restclient.Interface {
}
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return d.delegate.ServerPreferredResources()
return discovery.ServerPreferredResources(d)
}
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return d.delegate.ServerPreferredNamespacedResources()
return discovery.ServerPreferredNamespacedResources(d)
}
func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) {

View File

@ -239,15 +239,3 @@ func maybeWrapSortingPrinter(printer printers.ResourcePrinter, printOpts printer
func SuggestApiResources(parent string) string {
return templates.LongDesc(fmt.Sprintf("Use \"%s api-resources\" for a complete list of supported resources.", parent))
}
// Retrieve a list of handled resources from printer as valid args
// TODO: This function implementation should be replaced with a real implementation from the discovery service.
func ValidArgList(f ClientAccessFactory) []string {
validArgs := []string{}
humanReadablePrinter := printers.NewHumanReadablePrinter(nil, printers.PrintOptions{})
printersinternal.AddHandlers(humanReadablePrinter)
validArgs = humanReadablePrinter.HandledResources()
return validArgs
}

View File

@ -96,18 +96,12 @@ func (d *memCacheClient) RESTClient() restclient.Interface {
return d.delegate.RESTClient()
}
// TODO: Should this also be cached? The results seem more likely to be
// inconsistent with ServerGroups and ServerResources given the requirement to
// actively Invalidate.
func (d *memCacheClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return d.delegate.ServerPreferredResources()
return discovery.ServerPreferredResources(d)
}
// TODO: Should this also be cached? The results seem more likely to be
// inconsistent with ServerGroups and ServerResources given the requirement to
// actively Invalidate.
func (d *memCacheClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return d.delegate.ServerPreferredNamespacedResources()
return discovery.ServerPreferredNamespacedResources(d)
}
func (d *memCacheClient) ServerVersion() (*version.Info, error) {

View File

@ -250,6 +250,11 @@ func IsGroupDiscoveryFailedError(err error) bool {
// serverPreferredResources returns the supported resources with the version preferred by the server.
func (d *DiscoveryClient) serverPreferredResources() ([]*metav1.APIResourceList, error) {
return ServerPreferredResources(d)
}
// ServerPreferredResources uses the provided discovery interface to look up preferred resources
func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) {
serverGroupList, err := d.ServerGroups()
if err != nil {
return nil, err
@ -319,7 +324,12 @@ func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList,
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server.
func (d *DiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
all, err := d.ServerPreferredResources()
return ServerPreferredNamespacedResources(d)
}
// ServerPreferredNamespacedResources uses the provided discovery interface to look up preferred namespaced resources
func ServerPreferredNamespacedResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) {
all, err := ServerPreferredResources(d)
return FilteredBy(ResourcePredicateFunc(func(groupVersion string, r *metav1.APIResource) bool {
return r.Namespaced
}), all), err