2016-06-23 11:25:51 -04:00
|
|
|
package cli
|
2016-04-19 12:59:48 -04:00
|
|
|
|
|
|
|
import (
|
2016-06-22 18:36:51 -04:00
|
|
|
"fmt"
|
2016-11-15 11:18:33 -05:00
|
|
|
"strings"
|
2016-06-22 18:36:51 -04:00
|
|
|
|
2018-12-11 09:50:04 -05:00
|
|
|
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
2018-12-18 05:16:52 -05:00
|
|
|
cliconfig "github.com/docker/cli/cli/config"
|
|
|
|
cliflags "github.com/docker/cli/cli/flags"
|
2017-02-01 11:20:51 -05:00
|
|
|
"github.com/docker/docker/pkg/term"
|
2017-03-09 13:23:45 -05:00
|
|
|
"github.com/pkg/errors"
|
2016-04-19 12:59:48 -04:00
|
|
|
"github.com/spf13/cobra"
|
2018-12-18 05:16:52 -05:00
|
|
|
"github.com/spf13/pflag"
|
2016-04-19 12:59:48 -04:00
|
|
|
)
|
|
|
|
|
2018-12-10 10:30:19 -05:00
|
|
|
// setupCommonRootCommand contains the setup common to
|
|
|
|
// SetupRootCommand and SetupPluginRootCommand.
|
2018-12-11 09:50:04 -05:00
|
|
|
func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet, *cobra.Command) {
|
2018-12-18 05:16:52 -05:00
|
|
|
opts := cliflags.NewClientOptions()
|
|
|
|
flags := rootCmd.Flags()
|
|
|
|
|
|
|
|
flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files")
|
|
|
|
opts.Common.InstallFlags(flags)
|
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
cobra.AddTemplateFunc("hasSubCommands", hasSubCommands)
|
|
|
|
cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands)
|
2018-12-19 06:29:01 -05:00
|
|
|
cobra.AddTemplateFunc("hasInvalidPlugins", hasInvalidPlugins)
|
2016-09-12 11:37:00 -04:00
|
|
|
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
|
|
|
|
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
|
2018-12-19 06:29:01 -05:00
|
|
|
cobra.AddTemplateFunc("invalidPlugins", invalidPlugins)
|
2017-02-01 11:20:51 -05:00
|
|
|
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
|
2018-12-11 09:50:04 -05:00
|
|
|
cobra.AddTemplateFunc("commandVendor", commandVendor)
|
|
|
|
cobra.AddTemplateFunc("isFirstLevelCommand", isFirstLevelCommand) // is it an immediate sub-command of the root
|
2018-12-19 06:29:01 -05:00
|
|
|
cobra.AddTemplateFunc("invalidPluginReason", invalidPluginReason)
|
2016-09-12 11:37:00 -04:00
|
|
|
|
2016-05-16 17:20:29 -04:00
|
|
|
rootCmd.SetUsageTemplate(usageTemplate)
|
|
|
|
rootCmd.SetHelpTemplate(helpTemplate)
|
2016-06-22 18:36:51 -04:00
|
|
|
rootCmd.SetFlagErrorFunc(FlagErrorFunc)
|
2016-11-15 11:18:33 -05:00
|
|
|
rootCmd.SetHelpCommand(helpCommand)
|
2018-12-18 05:16:52 -05:00
|
|
|
|
2018-12-11 09:50:04 -05:00
|
|
|
return opts, flags, helpCommand
|
2018-12-18 05:02:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetupRootCommand sets default usage, help, and error handling for the
|
|
|
|
// root command.
|
2018-12-11 09:50:04 -05:00
|
|
|
func SetupRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet, *cobra.Command) {
|
|
|
|
opts, flags, helpCmd := setupCommonRootCommand(rootCmd)
|
2018-12-18 05:02:47 -05:00
|
|
|
|
2018-05-18 16:57:28 -04:00
|
|
|
rootCmd.SetVersionTemplate("Docker version {{.Version}}\n")
|
2016-05-16 17:20:29 -04:00
|
|
|
|
2016-05-31 17:47:51 -04:00
|
|
|
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
|
|
|
rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
|
2017-10-26 12:17:35 -04:00
|
|
|
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
2018-12-18 05:16:52 -05:00
|
|
|
|
2018-12-11 09:50:04 -05:00
|
|
|
return opts, flags, helpCmd
|
2016-04-19 12:59:48 -04:00
|
|
|
}
|
|
|
|
|
2018-12-10 10:30:19 -05:00
|
|
|
// SetupPluginRootCommand sets default usage, help and error handling for a plugin root command.
|
|
|
|
func SetupPluginRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) {
|
2018-12-11 09:50:04 -05:00
|
|
|
opts, flags, _ := setupCommonRootCommand(rootCmd)
|
2018-12-10 10:30:19 -05:00
|
|
|
|
|
|
|
rootCmd.PersistentFlags().BoolP("help", "", false, "Print usage")
|
|
|
|
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
|
|
|
|
|
|
|
return opts, flags
|
|
|
|
}
|
|
|
|
|
2016-08-28 09:30:14 -04:00
|
|
|
// FlagErrorFunc prints an error message which matches the format of the
|
2017-04-24 14:31:08 -04:00
|
|
|
// docker/cli/cli error messages
|
2016-06-22 18:36:51 -04:00
|
|
|
func FlagErrorFunc(cmd *cobra.Command, err error) error {
|
|
|
|
if err == nil {
|
2017-03-06 07:01:04 -05:00
|
|
|
return nil
|
2016-06-22 18:36:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
usage := ""
|
|
|
|
if cmd.HasSubCommands() {
|
|
|
|
usage = "\n\n" + cmd.UsageString()
|
|
|
|
}
|
2016-08-03 12:20:46 -04:00
|
|
|
return StatusError{
|
|
|
|
Status: fmt.Sprintf("%s\nSee '%s --help'.%s", err, cmd.CommandPath(), usage),
|
|
|
|
StatusCode: 125,
|
|
|
|
}
|
2016-06-22 18:36:51 -04:00
|
|
|
}
|
|
|
|
|
2018-12-17 11:59:11 -05:00
|
|
|
// VisitAll will traverse all commands from the root.
|
|
|
|
// This is different from the VisitAll of cobra.Command where only parents
|
|
|
|
// are checked.
|
|
|
|
func VisitAll(root *cobra.Command, fn func(*cobra.Command)) {
|
|
|
|
for _, cmd := range root.Commands() {
|
|
|
|
VisitAll(cmd, fn)
|
|
|
|
}
|
|
|
|
fn(root)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisableFlagsInUseLine sets the DisableFlagsInUseLine flag on all
|
|
|
|
// commands within the tree rooted at cmd.
|
|
|
|
func DisableFlagsInUseLine(cmd *cobra.Command) {
|
|
|
|
VisitAll(cmd, func(ccmd *cobra.Command) {
|
|
|
|
// do not add a `[flags]` to the end of the usage line.
|
|
|
|
ccmd.DisableFlagsInUseLine = true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-11-15 11:18:33 -05:00
|
|
|
var helpCommand = &cobra.Command{
|
|
|
|
Use: "help [command]",
|
|
|
|
Short: "Help about the command",
|
|
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
|
|
|
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {},
|
|
|
|
RunE: func(c *cobra.Command, args []string) error {
|
|
|
|
cmd, args, e := c.Root().Find(args)
|
|
|
|
if cmd == nil || e != nil || len(args) > 0 {
|
2017-03-09 13:23:45 -05:00
|
|
|
return errors.Errorf("unknown help topic: %v", strings.Join(args, " "))
|
2016-11-15 11:18:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
helpFunc := cmd.HelpFunc()
|
|
|
|
helpFunc(cmd, args)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-12-19 06:29:01 -05:00
|
|
|
func isPlugin(cmd *cobra.Command) bool {
|
|
|
|
return cmd.Annotations[pluginmanager.CommandAnnotationPlugin] == "true"
|
|
|
|
}
|
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
func hasSubCommands(cmd *cobra.Command) bool {
|
|
|
|
return len(operationSubCommands(cmd)) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func hasManagementSubCommands(cmd *cobra.Command) bool {
|
|
|
|
return len(managementSubCommands(cmd)) > 0
|
|
|
|
}
|
|
|
|
|
2018-12-19 06:29:01 -05:00
|
|
|
func hasInvalidPlugins(cmd *cobra.Command) bool {
|
|
|
|
return len(invalidPlugins(cmd)) > 0
|
|
|
|
}
|
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
func operationSubCommands(cmd *cobra.Command) []*cobra.Command {
|
|
|
|
cmds := []*cobra.Command{}
|
|
|
|
for _, sub := range cmd.Commands() {
|
2018-12-19 06:29:01 -05:00
|
|
|
if isPlugin(sub) && invalidPluginReason(sub) != "" {
|
|
|
|
continue
|
|
|
|
}
|
2016-09-12 11:37:00 -04:00
|
|
|
if sub.IsAvailableCommand() && !sub.HasSubCommands() {
|
|
|
|
cmds = append(cmds, sub)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmds
|
|
|
|
}
|
|
|
|
|
2017-02-01 11:20:51 -05:00
|
|
|
func wrappedFlagUsages(cmd *cobra.Command) string {
|
|
|
|
width := 80
|
|
|
|
if ws, err := term.GetWinsize(0); err == nil {
|
|
|
|
width = int(ws.Width)
|
|
|
|
}
|
|
|
|
return cmd.Flags().FlagUsagesWrapped(width - 1)
|
|
|
|
}
|
|
|
|
|
2018-12-11 09:50:04 -05:00
|
|
|
func isFirstLevelCommand(cmd *cobra.Command) bool {
|
|
|
|
return cmd.Parent() == cmd.Root()
|
|
|
|
}
|
|
|
|
|
|
|
|
func commandVendor(cmd *cobra.Command) string {
|
|
|
|
width := 13
|
|
|
|
if v, ok := cmd.Annotations[pluginmanager.CommandAnnotationPluginVendor]; ok {
|
|
|
|
if len(v) > width-2 {
|
|
|
|
v = v[:width-3] + "…"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%-*s", width, "("+v+")")
|
|
|
|
}
|
|
|
|
return strings.Repeat(" ", width)
|
|
|
|
}
|
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
|
|
|
|
cmds := []*cobra.Command{}
|
|
|
|
for _, sub := range cmd.Commands() {
|
2018-12-19 06:29:01 -05:00
|
|
|
if isPlugin(sub) && invalidPluginReason(sub) != "" {
|
|
|
|
continue
|
|
|
|
}
|
2016-09-12 11:37:00 -04:00
|
|
|
if sub.IsAvailableCommand() && sub.HasSubCommands() {
|
|
|
|
cmds = append(cmds, sub)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmds
|
|
|
|
}
|
|
|
|
|
2018-12-19 06:29:01 -05:00
|
|
|
func invalidPlugins(cmd *cobra.Command) []*cobra.Command {
|
|
|
|
cmds := []*cobra.Command{}
|
|
|
|
for _, sub := range cmd.Commands() {
|
|
|
|
if !isPlugin(sub) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if invalidPluginReason(sub) != "" {
|
|
|
|
cmds = append(cmds, sub)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmds
|
|
|
|
}
|
|
|
|
|
|
|
|
func invalidPluginReason(cmd *cobra.Command) string {
|
|
|
|
return cmd.Annotations[pluginmanager.CommandAnnotationPluginInvalid]
|
|
|
|
}
|
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
var usageTemplate = `Usage:
|
2016-05-16 17:20:29 -04:00
|
|
|
|
2018-05-18 20:46:27 -04:00
|
|
|
{{- if not .HasSubCommands}} {{.UseLine}}{{end}}
|
2018-06-22 02:16:27 -04:00
|
|
|
{{- if .HasSubCommands}} {{ .CommandPath}}{{- if .HasAvailableFlags}} [OPTIONS]{{end}} COMMAND{{end}}
|
2016-09-12 11:37:00 -04:00
|
|
|
|
2018-05-31 11:09:51 -04:00
|
|
|
{{if ne .Long ""}}{{ .Long | trim }}{{ else }}{{ .Short | trim }}{{end}}
|
2016-09-12 11:37:00 -04:00
|
|
|
|
|
|
|
{{- if gt .Aliases 0}}
|
2016-04-19 12:59:48 -04:00
|
|
|
|
|
|
|
Aliases:
|
2016-09-12 11:37:00 -04:00
|
|
|
{{.NameAndAliases}}
|
|
|
|
|
|
|
|
{{- end}}
|
|
|
|
{{- if .HasExample}}
|
2016-04-19 12:59:48 -04:00
|
|
|
|
|
|
|
Examples:
|
2016-09-12 11:37:00 -04:00
|
|
|
{{ .Example }}
|
|
|
|
|
|
|
|
{{- end}}
|
2018-03-22 10:13:04 -04:00
|
|
|
{{- if .HasAvailableFlags}}
|
2016-04-19 12:59:48 -04:00
|
|
|
|
|
|
|
Options:
|
2017-02-01 11:20:51 -05:00
|
|
|
{{ wrappedFlagUsages . | trimRightSpace}}
|
2016-09-12 11:37:00 -04:00
|
|
|
|
|
|
|
{{- end}}
|
|
|
|
{{- if hasManagementSubCommands . }}
|
|
|
|
|
|
|
|
Management Commands:
|
|
|
|
|
|
|
|
{{- range managementSubCommands . }}
|
2018-12-11 09:50:04 -05:00
|
|
|
{{rpad .Name .NamePadding }} {{ if isFirstLevelCommand .}}{{commandVendor .}} {{ end}}{{.Short}}
|
2016-09-12 11:37:00 -04:00
|
|
|
{{- end}}
|
|
|
|
|
|
|
|
{{- end}}
|
|
|
|
{{- if hasSubCommands .}}
|
|
|
|
|
|
|
|
Commands:
|
|
|
|
|
|
|
|
{{- range operationSubCommands . }}
|
2018-12-11 09:50:04 -05:00
|
|
|
{{rpad .Name .NamePadding }} {{ if isFirstLevelCommand .}}{{commandVendor .}} {{ end}}{{.Short}}
|
2016-09-12 11:37:00 -04:00
|
|
|
{{- end}}
|
|
|
|
{{- end}}
|
2016-04-19 12:59:48 -04:00
|
|
|
|
2018-12-19 06:29:01 -05:00
|
|
|
{{- if hasInvalidPlugins . }}
|
|
|
|
|
|
|
|
Invalid Plugins:
|
|
|
|
|
|
|
|
{{- range invalidPlugins . }}
|
|
|
|
{{rpad .Name .NamePadding }} {{invalidPluginReason .}}
|
|
|
|
{{- end}}
|
|
|
|
|
|
|
|
{{- end}}
|
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
{{- if .HasSubCommands }}
|
2016-04-19 12:59:48 -04:00
|
|
|
|
2016-09-12 11:37:00 -04:00
|
|
|
Run '{{.CommandPath}} COMMAND --help' for more information on a command.
|
|
|
|
{{- end}}
|
2016-04-19 12:59:48 -04:00
|
|
|
`
|
2016-05-16 17:20:29 -04:00
|
|
|
|
|
|
|
var helpTemplate = `
|
|
|
|
{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|