Merge pull request #31818 from juanvallejo/jvallejo_suggest-explain-cmd-in-kube-get

Automatic merge from submit-queue

suggest use of `kube explain <resource>` in kube get output

**Release note**:
```release-note
release-note-none
```

This patch improves usability flow, making it easier for a user to
discover the command `kube explain <resource>` through `kube get` output.

##### After
```
$ kube get
You must specify the type of resource to get. Valid resource types include:
   * componentstatuses (aka 'cs')
   * configmaps (aka 'cm')
   * daemonsets (aka 'ds')
   * deployments (aka 'deploy')
   * events (aka 'ev')
   * endpoints (aka 'ep')
   * horizontalpodautoscalers (aka 'hpa')
   * ingress (aka 'ing')
   * jobs
   * limitranges (aka 'limits')
   * nodes (aka 'no')
   * namespaces (aka 'ns')
   * petsets (alpha feature, may be unstable)
   * pods (aka 'po')
   * persistentvolumes (aka 'pv')
   * persistentvolumeclaims (aka 'pvc')
   * quota
   * resourcequotas (aka 'quota')
   * replicasets (aka 'rs')
   * replicationcontrollers (aka 'rc')
   * secrets
   * serviceaccounts (aka 'sa')
   * services (aka 'svc')
error: Required resource not specified.
Use "kubectl explain <resource>" for a detailed description of that resource (e.g. kubectl explain pods).
See 'kubectl get -h' for help and examples.
```
This commit is contained in:
Kubernetes Submit Queue 2016-09-28 21:59:29 -07:00 committed by GitHub
commit faac71c0dc
4 changed files with 42 additions and 14 deletions

View File

@ -287,7 +287,7 @@ Find more information at https://github.com/kubernetes/kubernetes.`,
NewCmdDescribe(f, out, err),
NewCmdLogs(f, out),
NewCmdAttach(f, in, out, err),
NewCmdExec("kubectl", f, in, out, err),
NewCmdExec(f, in, out, err),
NewCmdPortForward(f, out, err),
NewCmdProxy(f, out),
},

View File

@ -52,7 +52,7 @@ const (
execUsageStr = "expected 'exec POD_NAME COMMAND [ARG1] [ARG2] ... [ARGN]'.\nPOD_NAME and COMMAND are required arguments for the exec command"
)
func NewCmdExec(cmdFullName string, f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
func NewCmdExec(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
options := &ExecOptions{
StreamOptions: StreamOptions{
In: cmdIn,
@ -60,8 +60,7 @@ func NewCmdExec(cmdFullName string, f *cmdutil.Factory, cmdIn io.Reader, cmdOut,
Err: cmdErr,
},
FullCmdName: cmdFullName,
Executor: &DefaultRemoteExecutor{},
Executor: &DefaultRemoteExecutor{},
}
cmd := &cobra.Command{
Use: "exec POD [-c CONTAINER] -- COMMAND [args...]",
@ -131,18 +130,16 @@ type ExecOptions struct {
Command []string
FullCmdName string
Executor RemoteExecutor
PodClient coreclient.PodsGetter
Config *restclient.Config
FullCmdName string
SuggestedCmdUsage string
Executor RemoteExecutor
PodClient coreclient.PodsGetter
Config *restclient.Config
}
// Complete verifies command line arguments and loads data from the command environment
func (p *ExecOptions) Complete(f *cmdutil.Factory, cmd *cobra.Command, argsIn []string, argsLenAtDash int) error {
if len(p.FullCmdName) == 0 {
p.FullCmdName = "kubectl"
}
// Let kubectl exec follow rules for `--`, see #13004 issue
if len(p.PodName) == 0 && (len(argsIn) == 0 || argsLenAtDash == 0) {
return cmdutil.UsageError(cmd, execUsageStr)
@ -161,6 +158,14 @@ func (p *ExecOptions) Complete(f *cmdutil.Factory, cmd *cobra.Command, argsIn []
}
}
cmdParent := cmd.Parent()
if cmdParent != nil {
p.FullCmdName = cmdParent.CommandPath()
}
if len(p.FullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") {
p.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe pod/%s' to see all of the containers in this pod.", p.FullCmdName, p.PodName)
}
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
@ -265,7 +270,11 @@ func (p *ExecOptions) Run() error {
containerName := p.ContainerName
if len(containerName) == 0 {
if len(pod.Spec.Containers) > 1 {
fmt.Fprintf(p.Err, "defaulting container name to %s, use '%s describe pod/%s' to see all container names\n", pod.Spec.Containers[0].Name, p.FullCmdName, p.PodName)
usageString := fmt.Sprintf("Defaulting container name to %s.", pod.Spec.Containers[0].Name)
if len(p.SuggestedCmdUsage) > 0 {
usageString = fmt.Sprintf("%s\n%s", usageString, p.SuggestedCmdUsage)
}
fmt.Fprintf(p.Err, "%s\n", usageString)
}
containerName = pod.Spec.Containers[0].Name
}

View File

@ -171,7 +171,14 @@ func RunGet(f *cmdutil.Factory, out io.Writer, errOut io.Writer, cmd *cobra.Comm
if len(args) == 0 && cmdutil.IsFilenameEmpty(options.Filenames) {
fmt.Fprint(errOut, "You must specify the type of resource to get. ", valid_resources)
return cmdutil.UsageError(cmd, "Required resource not specified.")
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageError(cmd, usageString)
}
// determine if args contains "all"

View File

@ -716,3 +716,15 @@ func ObjectListToVersionedObject(objects []runtime.Object, version unversioned.G
}
return converted, nil
}
// IsSiblingCommandExists receives a pointer to a cobra command and a target string.
// Returns true if the target string is found in the list of sibling commands.
func IsSiblingCommandExists(cmd *cobra.Command, targetCmdName string) bool {
for _, c := range cmd.Parent().Commands() {
if c.Name() == targetCmdName {
return true
}
}
return false
}