diff options
| author | Matthew Holt <mholt@users.noreply.github.com> | 2019-10-01 11:02:13 -0600 | 
|---|---|---|
| committer | Matthew Holt <mholt@users.noreply.github.com> | 2019-10-01 11:02:13 -0600 | 
| commit | 2c3657bb8a37ce9c644dc1889223e37ca9988148 (patch) | |
| tree | c3b749791a4c928c230d540803741e38fbee0b23 /cmd | |
| parent | 5b36424cf056cc2063c708f0b68ba7cd68d23ac8 (diff) | |
cmd: CLI improvements; add --validate to adapt command
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/commandfuncs.go | 29 | ||||
| -rw-r--r-- | cmd/commands.go | 139 | 
2 files changed, 107 insertions, 61 deletions
| diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go index 715f064..5fba368 100644 --- a/cmd/commandfuncs.go +++ b/cmd/commandfuncs.go @@ -351,10 +351,11 @@ func cmdAdaptConfig(fl Flags) (int, error) {  	adaptCmdInputFlag := fl.String("config")  	adaptCmdAdapterFlag := fl.String("adapter")  	adaptCmdPrettyFlag := fl.Bool("pretty") +	adaptCmdValidateFlag := fl.Bool("validate")  	if adaptCmdAdapterFlag == "" || adaptCmdInputFlag == "" {  		return caddy.ExitCodeFailedStartup, -			fmt.Errorf("usage: caddy adapt --adapter <name> --input <file>") +			fmt.Errorf("--adapter and --config flags are required")  	}  	cfgAdapter := caddyconfig.GetAdapter(adaptCmdAdapterFlag) @@ -391,6 +392,19 @@ func cmdAdaptConfig(fl Flags) (int, error) {  	// print result to stdout  	fmt.Println(string(adaptedConfig)) +	// validate output if requested +	if adaptCmdValidateFlag { +		var cfg *caddy.Config +		err = json.Unmarshal(adaptedConfig, &cfg) +		if err != nil { +			return caddy.ExitCodeFailedStartup, fmt.Errorf("decoding config: %v", err) +		} +		err = caddy.Validate(cfg) +		if err != nil { +			return caddy.ExitCodeFailedStartup, fmt.Errorf("validation: %v", err) +		} +	} +  	return caddy.ExitCodeSuccess, nil  } @@ -457,7 +471,8 @@ usage:  commands:  `  		for _, cmd := range commands { -			s += fmt.Sprintf("  %-15s %s\n", cmd.Name, cmd.Short) +			short := strings.TrimSuffix(cmd.Short, ".") +			s += fmt.Sprintf("  %-15s %s\n", cmd.Name, short)  		}  		s += "\nUse 'caddy help <command>' for more information about a command.\n" @@ -475,8 +490,16 @@ commands:  		return caddy.ExitCodeFailedStartup, fmt.Errorf("unknown command: %s", args[0])  	} +	helpText := strings.TrimSpace(subcommand.Long) +	if helpText == "" { +		helpText = subcommand.Short +		if !strings.HasSuffix(helpText, ".") { +			helpText += "." +		} +	} +  	result := fmt.Sprintf("%s\n\nusage:\n  caddy %s %s\n", -		strings.TrimSpace(subcommand.Long), +		helpText,  		subcommand.Name,  		strings.TrimSpace(subcommand.Usage),  	) diff --git a/cmd/commands.go b/cmd/commands.go index 731e813..7473053 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -19,29 +19,40 @@ import (  	"regexp"  ) -// Command represents a subcommand. All fields -// are required to be set except for Flags if -// there are no flags and Usage if there are -// no flags or arguments. +// Command represents a subcommand. Name, Func, +// and Short are required.  type Command struct { +	// The name of the subcommand. Must conform to the +	// format described by the RegisterCommand() godoc. +	// Required.  	Name string -	// Run is a function that executes a subcommand. -	// It returns an exit code and any associated error. -	// Takes non-flag commandline arguments as args. -	// Flag must be parsed before Run is executed. +	// Run is a function that executes a subcommand using +	// the parsed flags. It returns an exit code and any +	// associated error. +	// Required.  	Func CommandFunc -	// Usage is the one-line message explaining args, flags. +	// Usage is a brief message describing the syntax of +	// the subcommand's flags and args. Use [] to indicate +	// optional parameters and <> to enclose literal values +	// intended to be replaced by the user. Do not prefix +	// the string with "caddy" or the name of the command +	// since these will be prepended for you; only include +	// the actual parameters for this command.  	Usage string -	// Short is the short description for command. +	// Short is a one-line message explaining what the +	// command does. Should not end with punctuation. +	// Required.  	Short string -	// Long is the message for 'caddy help <command>' +	// Long is the full help text shown to the user. +	// Will be trimmed of whitespace on both ends before +	// being printed.  	Long string -	// Flags is flagset for command. +	// Flags is the flagset for command.  	Flags *flag.FlagSet  } @@ -54,14 +65,15 @@ var commands = map[string]Command{  	"start": {  		Name:  "start",  		Func:  cmdStart, -		Usage: "[--config <path>] [--adapter <name>]", -		Short: "Starts the Caddy process and returns after server has started.", +		Usage: "[--config <path> [[--adapter <name>]]", +		Short: "Starts the Caddy process in the background and then returns",  		Long: ` -Starts the Caddy process, optionally bootstrapped with an initial -config file. Blocks until server is successfully running (or fails to run), -then returns. On Windows, the child process will remain attached to the -terminal, so closing the window will forcefully stop Caddy. See run for more -details.`, +Starts the Caddy process, optionally bootstrapped with an initial config file. +This command unblocks after the server starts running or fails to run. + +On Windows, the spawned child process will remain attached to the terminal, so +closing the window will forcefully stop Caddy; to avoid forgetting this, try +using 'caddy run' instead to keep it in the foreground.`,  		Flags: func() *flag.FlagSet {  			fs := flag.NewFlagSet("start", flag.ExitOnError)  			fs.String("config", "", "Configuration file") @@ -73,13 +85,12 @@ details.`,  	"run": {  		Name:  "run",  		Func:  cmdRun, -		Usage: "[--config <path>] [--adapter <name>] [--print-env]", -		Short: `Starts the Caddy process and blocks indefinitely.`, +		Usage: "[--config <path> [--adapter <name>]] [--environ]", +		Short: `Starts the Caddy process and blocks indefinitely`,  		Long: ` -Same as start, but blocks indefinitely; i.e. runs Caddy in "daemon" mode. On -Windows, this is recommended over caddy start when running Caddy manually since -it will be more obvious that Caddy is still running and bound to the terminal -window. +Starts the Caddy process, optionally bootstrapped with an initial config file, +and blocks indefinitely until the server is stopped; i.e. runs Caddy in +"daemon" mode (foreground).  If a config file is specified, it will be applied immediately after the process  is running. If the config file is not in Caddy's native JSON format, you can @@ -90,13 +101,13 @@ errors will immediately be used. If you want to review the results of the  adaptation first, use the 'adapt' subcommand.  As a special case, if the current working directory has a file called -"Caddyfile" and the caddyfile config adapter is plugged in (default), then that -file will be loaded and used to configure Caddy, even without any command line -flags. +"Caddyfile" and the caddyfile config adapter is plugged in (default), then +that file will be loaded and used to configure Caddy, even without any command +line flags.  If --environ is specified, the environment as seen by the Caddy process will  be printed before starting. This is the same as the environ command but does -not quit after printing.`, +not quit after printing, and can be useful for troubleshooting.`,  		Flags: func() *flag.FlagSet {  			fs := flag.NewFlagSet("run", flag.ExitOnError)  			fs.String("config", "", "Configuration file") @@ -111,26 +122,32 @@ not quit after printing.`,  		Name:  "stop",  		Func:  cmdStop,  		Short: "Gracefully stops the running Caddy process", -		Long: `Gracefully stops the running Caddy process. (Note: this will stop any process -named the same as the executable.) On Windows, this stop is forceful and Caddy -will not have an opportunity to clean up any active locks; for a graceful -shutdown on Windows, use Ctrl+C or the /stop endpoint.`, +		Long: ` +Stops the running Caddy process as gracefully as possible. + +On Windows, this stop is forceful and Caddy will not have an opportunity to +clean up any active locks; for a graceful shutdown on Windows, use Ctrl+C +or the /stop API endpoint. + +Note: this will stop any process named the same as the executable (os.Args[0]).`,  	},  	"reload": {  		Name:  "reload",  		Func:  cmdReload,  		Usage: "--config <path> [--adapter <name>] [--address <interface>]", -		Short: "Gives the running Caddy instance a new configuration", -		Long: `Gives the running Caddy instance a new configuration. This has the same effect -as POSTing a document to the /load endpoint, but is convenient for simple -workflows revolving around config files. Since the admin endpoint is -configurable, the endpoint configuration is loaded from the --address flag if -specified; otherwise it is loaded from the given config file; otherwise the -default is assumed.`, +		Short: "Changes the config of the running Caddy instance", +		Long: ` +Gives the running Caddy instance a new configuration. This has the same effect +as POSTing a document to the /load API endpoint, but is convenient for simple +workflows revolving around config files. + +Since the admin endpoint is configurable, the endpoint configuration is loaded +from the --address flag if specified; otherwise it is loaded from the given +config file; otherwise the default is assumed.`,  		Flags: func() *flag.FlagSet {  			fs := flag.NewFlagSet("reload", flag.ExitOnError) -			fs.String("config", "", "Configuration file") +			fs.String("config", "", "Configuration file (required)")  			fs.String("adapter", "", "Name of config adapter to apply")  			fs.String("address", "", "Address of the administration listener, if different from config")  			return fs @@ -140,15 +157,14 @@ default is assumed.`,  	"version": {  		Name:  "version",  		Func:  cmdVersion, -		Short: "Prints the version.", -		Long:  `Prints the version.`, +		Short: "Prints the version",  	},  	"list-modules": {  		Name:  "list-modules",  		Func:  cmdListModules, -		Short: "List installed Caddy modules.", -		Long:  `List installed Caddy modules.`, +		Usage: "[--versions]", +		Short: "Lists the installed Caddy modules",  		Flags: func() *flag.FlagSet {  			fs := flag.NewFlagSet("list-modules", flag.ExitOnError)  			fs.Bool("versions", false, "Print version information") @@ -159,24 +175,30 @@ default is assumed.`,  	"environ": {  		Name:  "environ",  		Func:  cmdEnviron, -		Short: "Prints the environment as seen by Caddy.", -		Long:  `Prints the environment as seen by Caddy.`, +		Short: "Prints the environment",  	},  	"adapt": {  		Name:  "adapt",  		Func:  cmdAdaptConfig, -		Usage: "--config <path> --adapter <name> [--pretty]", -		Short: "Adapts a configuration to Caddy's native JSON config structure", +		Usage: "--config <path> --adapter <name> [--pretty] [--validate]", +		Short: "Adapts a configuration to Caddy's native JSON",  		Long: ` -Adapts a configuration to Caddy's native JSON config structure and writes the -output to stdout, along with any warnings to stderr. If --pretty is specified, -the output will be formatted with indentation for human readability.`, +Adapts a configuration to Caddy's native JSON format and writes the +output to stdout, along with any warnings to stderr. + +If --pretty is specified, the output will be formatted with indentation +for human readability. + +If --validate is used, the adapted config will be checked for validity. +If the config is invalid, an error will be printed to stderr and a non- +zero exit status will be returned.`,  		Flags: func() *flag.FlagSet {  			fs := flag.NewFlagSet("adapt", flag.ExitOnError) -			fs.String("config", "", "Configuration file to adapt") -			fs.String("adapter", "", "Name of config adapter") +			fs.String("config", "", "Configuration file to adapt (required)") +			fs.String("adapter", "", "Name of config adapter (required)")  			fs.Bool("pretty", false, "Format the output for human readability") +			fs.Bool("validate", false, "Validate the output")  			return fs  		}(),  	}, @@ -185,10 +207,11 @@ the output will be formatted with indentation for human readability.`,  		Name:  "validate",  		Func:  cmdValidateConfig,  		Usage: "--config <path> [--adapter <name>]", -		Short: "Tests whether a configuration file is valid.", +		Short: "Tests whether a configuration file is valid",  		Long: ` -Loads and provisions the provided config, but does not start -running it.`, +Loads and provisions the provided config, but does not start running it. +This reveals any errors with the configuration through the loading and +provisioning stages.`,  		Flags: func() *flag.FlagSet {  			fs := flag.NewFlagSet("load", flag.ExitOnError)  			fs.String("config", "", "Input configuration file") @@ -208,7 +231,7 @@ func init() {  		Name:  "help",  		Func:  cmdHelp,  		Usage: "<command>", -		Short: "Shows help for a Caddy subcommand.", +		Short: "Shows help for a Caddy subcommand",  	}  } | 
