Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
cmd: CLI improvements; add --validate to adapt command
Matthew Holt committed
Oct 1, 2019 at 11:02 UTC
2c3657bb8a37ce9c644dc1889223e37ca9988148
2 files changed
+107
-61
cmd/commandfuncs.go
+26
-3
@@ -351,10 +351,11 @@ func cmdAdaptConfig(fl Flags) (int, error) {
351
adaptCmdInputFlag := fl.String("config")
352
adaptCmdAdapterFlag := fl.String("adapter")
353
adaptCmdPrettyFlag := fl.Bool("pretty")
354
+ adaptCmdValidateFlag := fl.Bool("validate")
355
356
if adaptCmdAdapterFlag == "" || adaptCmdInputFlag == "" {
357
return caddy.ExitCodeFailedStartup,
357
- fmt.Errorf("usage: caddy adapt --adapter <name> --input <file>")
358
+ fmt.Errorf("--adapter and --config flags are required")
359
}
360
361
cfgAdapter := caddyconfig.GetAdapter(adaptCmdAdapterFlag)
@@ -391,6 +392,19 @@ func cmdAdaptConfig(fl Flags) (int, error) {
392
// print result to stdout
393
fmt.Println(string(adaptedConfig))
394
395
+ // validate output if requested
396
+ if adaptCmdValidateFlag {
397
+ var cfg *caddy.Config
398
+ err = json.Unmarshal(adaptedConfig, &cfg)
399
+ if err != nil {
400
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("decoding config: %v", err)
401
+ }
402
+ err = caddy.Validate(cfg)
403
+ if err != nil {
404
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("validation: %v", err)
405
+ }
406
+ }
407
+
408
return caddy.ExitCodeSuccess, nil
409
}
410
@@ -457,7 +471,8 @@ usage:
471
commands:
472
`
473
for _, cmd := range commands {
460
- s += fmt.Sprintf(" %-15s %s\n", cmd.Name, cmd.Short)
474
+ short := strings.TrimSuffix(cmd.Short, ".")
475
+ s += fmt.Sprintf(" %-15s %s\n", cmd.Name, short)
476
}
477
478
s += "\nUse 'caddy help <command>' for more information about a command.\n"
@@ -475,8 +490,16 @@ commands:
490
return caddy.ExitCodeFailedStartup, fmt.Errorf("unknown command: %s", args[0])
491
}
492
493
+ helpText := strings.TrimSpace(subcommand.Long)
494
+ if helpText == "" {
495
+ helpText = subcommand.Short
496
+ if !strings.HasSuffix(helpText, ".") {
497
+ helpText += "."
498
+ }
499
+ }
500
+
501
result := fmt.Sprintf("%s\n\nusage:\n caddy %s %s\n",
479
- strings.TrimSpace(subcommand.Long),
502
+ helpText,
503
subcommand.Name,
504
strings.TrimSpace(subcommand.Usage),
505
)
cmd/commands.go
+81
-58
@@ -19,29 +19,40 @@ import (
19
"regexp"
20
)
21
22
-// Command represents a subcommand. All fields
23
-// are required to be set except for Flags if
24
-// there are no flags and Usage if there are
25
-// no flags or arguments.
22
+// Command represents a subcommand. Name, Func,
23
+// and Short are required.
24
type Command struct {
25
+ // The name of the subcommand. Must conform to the
26
+ // format described by the RegisterCommand() godoc.
27
+ // Required.
28
Name string
29
29
- // Run is a function that executes a subcommand.
30
- // It returns an exit code and any associated error.
31
- // Takes non-flag commandline arguments as args.
32
- // Flag must be parsed before Run is executed.
30
+ // Run is a function that executes a subcommand using
31
+ // the parsed flags. It returns an exit code and any
32
+ // associated error.
33
+ // Required.
34
Func CommandFunc
35
35
- // Usage is the one-line message explaining args, flags.
36
+ // Usage is a brief message describing the syntax of
37
+ // the subcommand's flags and args. Use [] to indicate
38
+ // optional parameters and <> to enclose literal values
39
+ // intended to be replaced by the user. Do not prefix
40
+ // the string with "caddy" or the name of the command
41
+ // since these will be prepended for you; only include
42
+ // the actual parameters for this command.
43
Usage string
44
38
- // Short is the short description for command.
45
+ // Short is a one-line message explaining what the
46
+ // command does. Should not end with punctuation.
47
+ // Required.
48
Short string
49
41
- // Long is the message for 'caddy help <command>'
50
+ // Long is the full help text shown to the user.
51
+ // Will be trimmed of whitespace on both ends before
52
+ // being printed.
53
Long string
54
44
- // Flags is flagset for command.
55
+ // Flags is the flagset for command.
56
Flags *flag.FlagSet
57
}
58
@@ -54,14 +65,15 @@ var commands = map[string]Command{
65
"start": {
66
Name: "start",
67
Func: cmdStart,
57
- Usage: "[--config <path>] [--adapter <name>]",
58
- Short: "Starts the Caddy process and returns after server has started.",
68
+ Usage: "[--config <path> [[--adapter <name>]]",
69
+ Short: "Starts the Caddy process in the background and then returns",
70
Long: `
60
-Starts the Caddy process, optionally bootstrapped with an initial
61
-config file. Blocks until server is successfully running (or fails to run),
62
-then returns. On Windows, the child process will remain attached to the
63
-terminal, so closing the window will forcefully stop Caddy. See run for more
64
-details.`,
71
+Starts the Caddy process, optionally bootstrapped with an initial config file.
72
+This command unblocks after the server starts running or fails to run.
73
+
74
+On Windows, the spawned child process will remain attached to the terminal, so
75
+closing the window will forcefully stop Caddy; to avoid forgetting this, try
76
+using 'caddy run' instead to keep it in the foreground.`,
77
Flags: func() *flag.FlagSet {
78
fs := flag.NewFlagSet("start", flag.ExitOnError)
79
fs.String("config", "", "Configuration file")
@@ -73,13 +85,12 @@ details.`,
85
"run": {
86
Name: "run",
87
Func: cmdRun,
76
- Usage: "[--config <path>] [--adapter <name>] [--print-env]",
77
- Short: `Starts the Caddy process and blocks indefinitely.`,
88
+ Usage: "[--config <path> [--adapter <name>]] [--environ]",
89
+ Short: `Starts the Caddy process and blocks indefinitely`,
90
Long: `
79
-Same as start, but blocks indefinitely; i.e. runs Caddy in "daemon" mode. On
80
-Windows, this is recommended over caddy start when running Caddy manually since
81
-it will be more obvious that Caddy is still running and bound to the terminal
82
-window.
91
+Starts the Caddy process, optionally bootstrapped with an initial config file,
92
+and blocks indefinitely until the server is stopped; i.e. runs Caddy in
93
+"daemon" mode (foreground).
94
95
If a config file is specified, it will be applied immediately after the process
96
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
101
adaptation first, use the 'adapt' subcommand.
102
103
As a special case, if the current working directory has a file called
93
-"Caddyfile" and the caddyfile config adapter is plugged in (default), then that
94
-file will be loaded and used to configure Caddy, even without any command line
95
-flags.
104
+"Caddyfile" and the caddyfile config adapter is plugged in (default), then
105
+that file will be loaded and used to configure Caddy, even without any command
106
+line flags.
107
108
If --environ is specified, the environment as seen by the Caddy process will
109
be printed before starting. This is the same as the environ command but does
99
-not quit after printing.`,
110
+not quit after printing, and can be useful for troubleshooting.`,
111
Flags: func() *flag.FlagSet {
112
fs := flag.NewFlagSet("run", flag.ExitOnError)
113
fs.String("config", "", "Configuration file")
@@ -111,26 +122,32 @@ not quit after printing.`,
122
Name: "stop",
123
Func: cmdStop,
124
Short: "Gracefully stops the running Caddy process",
114
- Long: `Gracefully stops the running Caddy process. (Note: this will stop any process
115
-named the same as the executable.) On Windows, this stop is forceful and Caddy
116
-will not have an opportunity to clean up any active locks; for a graceful
117
-shutdown on Windows, use Ctrl+C or the /stop endpoint.`,
125
+ Long: `
126
+Stops the running Caddy process as gracefully as possible.
127
+
128
+On Windows, this stop is forceful and Caddy will not have an opportunity to
129
+clean up any active locks; for a graceful shutdown on Windows, use Ctrl+C
130
+or the /stop API endpoint.
131
+
132
+Note: this will stop any process named the same as the executable (os.Args[0]).`,
133
},
134
135
"reload": {
136
Name: "reload",
137
Func: cmdReload,
138
Usage: "--config <path> [--adapter <name>] [--address <interface>]",
124
- Short: "Gives the running Caddy instance a new configuration",
125
- Long: `Gives the running Caddy instance a new configuration. This has the same effect
126
-as POSTing a document to the /load endpoint, but is convenient for simple
127
-workflows revolving around config files. Since the admin endpoint is
128
-configurable, the endpoint configuration is loaded from the --address flag if
129
-specified; otherwise it is loaded from the given config file; otherwise the
130
-default is assumed.`,
139
+ Short: "Changes the config of the running Caddy instance",
140
+ Long: `
141
+Gives the running Caddy instance a new configuration. This has the same effect
142
+as POSTing a document to the /load API endpoint, but is convenient for simple
143
+workflows revolving around config files.
144
+
145
+Since the admin endpoint is configurable, the endpoint configuration is loaded
146
+from the --address flag if specified; otherwise it is loaded from the given
147
+config file; otherwise the default is assumed.`,
148
Flags: func() *flag.FlagSet {
149
fs := flag.NewFlagSet("reload", flag.ExitOnError)
133
- fs.String("config", "", "Configuration file")
150
+ fs.String("config", "", "Configuration file (required)")
151
fs.String("adapter", "", "Name of config adapter to apply")
152
fs.String("address", "", "Address of the administration listener, if different from config")
153
return fs
@@ -140,15 +157,14 @@ default is assumed.`,
157
"version": {
158
Name: "version",
159
Func: cmdVersion,
143
- Short: "Prints the version.",
144
- Long: `Prints the version.`,
160
+ Short: "Prints the version",
161
},
162
163
"list-modules": {
164
Name: "list-modules",
165
Func: cmdListModules,
150
- Short: "List installed Caddy modules.",
151
- Long: `List installed Caddy modules.`,
166
+ Usage: "[--versions]",
167
+ Short: "Lists the installed Caddy modules",
168
Flags: func() *flag.FlagSet {
169
fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
170
fs.Bool("versions", false, "Print version information")
@@ -159,24 +175,30 @@ default is assumed.`,
175
"environ": {
176
Name: "environ",
177
Func: cmdEnviron,
162
- Short: "Prints the environment as seen by Caddy.",
163
- Long: `Prints the environment as seen by Caddy.`,
178
+ Short: "Prints the environment",
179
},
180
181
"adapt": {
182
Name: "adapt",
183
Func: cmdAdaptConfig,
169
- Usage: "--config <path> --adapter <name> [--pretty]",
170
- Short: "Adapts a configuration to Caddy's native JSON config structure",
184
+ Usage: "--config <path> --adapter <name> [--pretty] [--validate]",
185
+ Short: "Adapts a configuration to Caddy's native JSON",
186
Long: `
172
-Adapts a configuration to Caddy's native JSON config structure and writes the
173
-output to stdout, along with any warnings to stderr. If --pretty is specified,
174
-the output will be formatted with indentation for human readability.`,
187
+Adapts a configuration to Caddy's native JSON format and writes the
188
+output to stdout, along with any warnings to stderr.
189
+
190
+If --pretty is specified, the output will be formatted with indentation
191
+for human readability.
192
+
193
+If --validate is used, the adapted config will be checked for validity.
194
+If the config is invalid, an error will be printed to stderr and a non-
195
+zero exit status will be returned.`,
196
Flags: func() *flag.FlagSet {
197
fs := flag.NewFlagSet("adapt", flag.ExitOnError)
177
- fs.String("config", "", "Configuration file to adapt")
178
- fs.String("adapter", "", "Name of config adapter")
198
+ fs.String("config", "", "Configuration file to adapt (required)")
199
+ fs.String("adapter", "", "Name of config adapter (required)")
200
fs.Bool("pretty", false, "Format the output for human readability")
201
+ fs.Bool("validate", false, "Validate the output")
202
return fs
203
}(),
204
},
@@ -185,10 +207,11 @@ the output will be formatted with indentation for human readability.`,
207
Name: "validate",
208
Func: cmdValidateConfig,
209
Usage: "--config <path> [--adapter <name>]",
188
- Short: "Tests whether a configuration file is valid.",
210
+ Short: "Tests whether a configuration file is valid",
211
Long: `
190
-Loads and provisions the provided config, but does not start
191
-running it.`,
212
+Loads and provisions the provided config, but does not start running it.
213
+This reveals any errors with the configuration through the loading and
214
+provisioning stages.`,
215
Flags: func() *flag.FlagSet {
216
fs := flag.NewFlagSet("load", flag.ExitOnError)
217
fs.String("config", "", "Input configuration file")
@@ -208,7 +231,7 @@ func init() {
231
Name: "help",
232
Func: cmdHelp,
233
Usage: "<command>",
211
- Short: "Shows help for a Caddy subcommand.",
234
+ Short: "Shows help for a Caddy subcommand",
235
}
236
}
237