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: Add validate subcommand; list-modules --versions; some renaming
Renames --config-adapter flag to --adapter, adapt-config command to adapt, --print-env flag to --environ, and --input flag to --config.
Matthew Holt committed
Sep 30, 2019 at 23:43 UTC
5b36424cf056cc2063c708f0b68ba7cd68d23ac8
2 files changed
+154
-24
cmd/commandfuncs.go
+112
-10
@@ -26,6 +26,8 @@ import (
26
"net/http"
27
"os"
28
"os/exec"
29
+ "reflect"
30
+ "runtime/debug"
31
"strings"
32
33
"github.com/caddyserver/caddy/v2"
@@ -36,7 +38,7 @@ import (
38
39
func cmdStart(fl Flags) (int, error) {
40
startCmdConfigFlag := fl.String("config")
39
- startCmdConfigAdapterFlag := fl.String("config-adapter")
41
+ startCmdConfigAdapterFlag := fl.String("adapter")
42
43
// open a listener to which the child process will connect when
44
// it is ready to confirm that it has successfully started
@@ -61,7 +63,7 @@ func cmdStart(fl Flags) (int, error) {
63
cmd.Args = append(cmd.Args, "--config", startCmdConfigFlag)
64
}
65
if startCmdConfigAdapterFlag != "" {
64
- cmd.Args = append(cmd.Args, "--config-adapter", startCmdConfigAdapterFlag)
66
+ cmd.Args = append(cmd.Args, "--adapter", startCmdConfigAdapterFlag)
67
}
68
stdinpipe, err := cmd.StdinPipe()
69
if err != nil {
@@ -137,8 +139,8 @@ func cmdStart(fl Flags) (int, error) {
139
140
func cmdRun(fl Flags) (int, error) {
141
runCmdConfigFlag := fl.String("config")
140
- runCmdConfigAdapterFlag := fl.String("config-adapter")
141
- runCmdPrintEnvFlag := fl.Bool("print-env")
142
+ runCmdConfigAdapterFlag := fl.String("adapter")
143
+ runCmdPrintEnvFlag := fl.Bool("environ")
144
runCmdPingbackFlag := fl.String("pingback")
145
146
// if we are supposed to print the environment, do that first
@@ -216,7 +218,7 @@ func cmdStop(_ Flags) (int, error) {
218
219
func cmdReload(fl Flags) (int, error) {
220
reloadCmdConfigFlag := fl.String("config")
219
- reloadCmdConfigAdapterFlag := fl.String("config-adapter")
221
+ reloadCmdConfigAdapterFlag := fl.String("adapter")
222
reloadCmdAddrFlag := fl.String("address")
223
224
// a configuration is required
@@ -282,10 +284,61 @@ func cmdVersion(_ Flags) (int, error) {
284
return caddy.ExitCodeSuccess, nil
285
}
286
285
-func cmdListModules(_ Flags) (int, error) {
286
- for _, m := range caddy.Modules() {
287
- fmt.Println(m)
287
+func cmdListModules(fl Flags) (int, error) {
288
+ versions := fl.Bool("versions")
289
+
290
+ bi, ok := debug.ReadBuildInfo()
291
+ if !ok || !versions {
292
+ // if there's no build information,
293
+ // just print out the modules
294
+ for _, m := range caddy.Modules() {
295
+ fmt.Println(m)
296
+ }
297
+ return caddy.ExitCodeSuccess, nil
298
+ }
299
+
300
+ for _, modName := range caddy.Modules() {
301
+ modInfo, err := caddy.GetModule(modName)
302
+ if err != nil {
303
+ // that's weird
304
+ fmt.Println(modName)
305
+ continue
306
+ }
307
+
308
+ // to get the Caddy plugin's version info, we need to know
309
+ // the package that the Caddy module's value comes from; we
310
+ // can use reflection but we need a non-pointer value (I'm
311
+ // not sure why), and since New() should return a pointer
312
+ // value, we need to dereference it first
313
+ iface := interface{}(modInfo.New())
314
+ if rv := reflect.ValueOf(iface); rv.Kind() == reflect.Ptr {
315
+ iface = reflect.New(reflect.TypeOf(iface).Elem()).Elem().Interface()
316
+ }
317
+ modPkgPath := reflect.TypeOf(iface).PkgPath()
318
+
319
+ // now we find the Go module that the Caddy module's package
320
+ // belongs to; we assume the Caddy module package path will
321
+ // be prefixed by its Go module path, and we will choose the
322
+ // longest matching prefix in case there are nested modules
323
+ var matched *debug.Module
324
+ for _, dep := range bi.Deps {
325
+ if strings.HasPrefix(modPkgPath, dep.Path) {
326
+ if matched == nil || len(dep.Path) > len(matched.Path) {
327
+ matched = dep
328
+ }
329
+ }
330
+ }
331
+
332
+ // if we could find no matching module, just print out
333
+ // the module name instead
334
+ if matched == nil {
335
+ fmt.Println(modName)
336
+ continue
337
+ }
338
+
339
+ fmt.Printf("%s %s\n", modName, matched.Version)
340
}
341
+
342
return caddy.ExitCodeSuccess, nil
343
}
344
@@ -295,13 +348,13 @@ func cmdEnviron(_ Flags) (int, error) {
348
}
349
350
func cmdAdaptConfig(fl Flags) (int, error) {
351
+ adaptCmdInputFlag := fl.String("config")
352
adaptCmdAdapterFlag := fl.String("adapter")
299
- adaptCmdInputFlag := fl.String("input")
353
adaptCmdPrettyFlag := fl.Bool("pretty")
354
355
if adaptCmdAdapterFlag == "" || adaptCmdInputFlag == "" {
356
return caddy.ExitCodeFailedStartup,
304
- fmt.Errorf("usage: caddy adapt-config --adapter <name> --input <file>")
357
+ fmt.Errorf("usage: caddy adapt --adapter <name> --input <file>")
358
}
359
360
cfgAdapter := caddyconfig.GetAdapter(adaptCmdAdapterFlag)
@@ -341,6 +394,55 @@ func cmdAdaptConfig(fl Flags) (int, error) {
394
return caddy.ExitCodeSuccess, nil
395
}
396
397
+func cmdValidateConfig(fl Flags) (int, error) {
398
+ validateCmdConfigFlag := fl.String("config")
399
+ validateCmdAdapterFlag := fl.String("adapter")
400
+
401
+ input, err := ioutil.ReadFile(validateCmdConfigFlag)
402
+ if err != nil {
403
+ return caddy.ExitCodeFailedStartup,
404
+ fmt.Errorf("reading input file: %v", err)
405
+ }
406
+
407
+ if validateCmdAdapterFlag != "" {
408
+ cfgAdapter := caddyconfig.GetAdapter(validateCmdAdapterFlag)
409
+ if cfgAdapter == nil {
410
+ return caddy.ExitCodeFailedStartup,
411
+ fmt.Errorf("unrecognized config adapter: %s", validateCmdAdapterFlag)
412
+ }
413
+
414
+ adaptedConfig, warnings, err := cfgAdapter.Adapt(input, nil)
415
+ if err != nil {
416
+ return caddy.ExitCodeFailedStartup, err
417
+ }
418
+ // print warnings to stderr
419
+ for _, warn := range warnings {
420
+ msg := warn.Message
421
+ if warn.Directive != "" {
422
+ msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
423
+ }
424
+ log.Printf("[WARNING][%s] %s:%d: %s", validateCmdAdapterFlag, warn.File, warn.Line, msg)
425
+ }
426
+
427
+ input = adaptedConfig
428
+ }
429
+
430
+ var cfg *caddy.Config
431
+ err = json.Unmarshal(input, &cfg)
432
+ if err != nil {
433
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("decoding config: %v", err)
434
+ }
435
+
436
+ err = caddy.Validate(cfg)
437
+ if err != nil {
438
+ return caddy.ExitCodeFailedStartup, err
439
+ }
440
+
441
+ fmt.Println("Valid configuration")
442
+
443
+ return caddy.ExitCodeSuccess, nil
444
+}
445
+
446
func cmdHelp(fl Flags) (int, error) {
447
const fullDocs = `Full documentation is available at:
448
https://github.com/caddyserver/caddy/wiki/v2:-Documentation`
cmd/commands.go
+42
-14
@@ -54,7 +54,7 @@ var commands = map[string]Command{
54
"start": {
55
Name: "start",
56
Func: cmdStart,
57
- Usage: "[--config <path>] [--config-adapter <name>]",
57
+ Usage: "[--config <path>] [--adapter <name>]",
58
Short: "Starts the Caddy process and returns after server has started.",
59
Long: `
60
Starts the Caddy process, optionally bootstrapped with an initial
@@ -65,7 +65,7 @@ details.`,
65
Flags: func() *flag.FlagSet {
66
fs := flag.NewFlagSet("start", flag.ExitOnError)
67
fs.String("config", "", "Configuration file")
68
- fs.String("config-adapter", "", "Name of config adapter to apply")
68
+ fs.String("adapter", "", "Name of config adapter to apply")
69
return fs
70
}(),
71
},
@@ -73,7 +73,7 @@ details.`,
73
"run": {
74
Name: "run",
75
Func: cmdRun,
76
- Usage: "[--config <path>] [--config-adapter <name>] [--print-env]",
76
+ Usage: "[--config <path>] [--adapter <name>] [--print-env]",
77
Short: `Starts the Caddy process and blocks indefinitely.`,
78
Long: `
79
Same as start, but blocks indefinitely; i.e. runs Caddy in "daemon" mode. On
@@ -83,25 +83,25 @@ window.
83
84
If a config file is specified, it will be applied immediately after the process
85
is running. If the config file is not in Caddy's native JSON format, you can
86
-specify an adapter with --config-adapter to adapt the given config file to
86
+specify an adapter with --adapter to adapt the given config file to
87
Caddy's native format. The config adapter must be a registered module. Any
88
warnings will be printed to the log, but beware that any adaptation without
89
errors will immediately be used. If you want to review the results of the
90
-adaptation first, use adapt-config.
90
+adaptation first, use the 'adapt' subcommand.
91
92
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.
96
97
-If --print-env is specified, the environment as seen by the Caddy process will
97
+If --environ is specified, the environment as seen by the Caddy process will
98
be printed before starting. This is the same as the environ command but does
99
not quit after printing.`,
100
Flags: func() *flag.FlagSet {
101
fs := flag.NewFlagSet("run", flag.ExitOnError)
102
fs.String("config", "", "Configuration file")
103
- fs.String("config-adapter", "", "Name of config adapter to apply")
104
- fs.Bool("print-env", false, "Print environment")
103
+ fs.String("adapter", "", "Name of config adapter to apply")
104
+ fs.Bool("environ", false, "Print environment")
105
fs.String("pingback", "", "Echo confirmation bytes to this address on success")
106
return fs
107
}(),
@@ -120,7 +120,7 @@ shutdown on Windows, use Ctrl+C or the /stop endpoint.`,
120
"reload": {
121
Name: "reload",
122
Func: cmdReload,
123
- Usage: "--config <path> [--config-adapter <name>] [--address <interface>]",
123
+ 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
@@ -129,9 +129,9 @@ 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.`,
131
Flags: func() *flag.FlagSet {
132
- fs := flag.NewFlagSet("load", flag.ExitOnError)
132
+ fs := flag.NewFlagSet("reload", flag.ExitOnError)
133
fs.String("config", "", "Configuration file")
134
- fs.String("config-adapter", "", "Name of config adapter to apply")
134
+ fs.String("adapter", "", "Name of config adapter to apply")
135
fs.String("address", "", "Address of the administration listener, if different from config")
136
return fs
137
}(),
@@ -149,6 +149,11 @@ default is assumed.`,
149
Func: cmdListModules,
150
Short: "List installed Caddy modules.",
151
Long: `List installed Caddy modules.`,
152
+ Flags: func() *flag.FlagSet {
153
+ fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
154
+ fs.Bool("versions", false, "Print version information")
155
+ return fs
156
+ }(),
157
},
158
159
"environ": {
@@ -158,15 +163,38 @@ default is assumed.`,
163
Long: `Prints the environment as seen by Caddy.`,
164
},
165
161
- "adapt-config": {
162
- Name: "adapt-config",
166
+ "adapt": {
167
+ Name: "adapt",
168
Func: cmdAdaptConfig,
164
- Usage: "--input <path> --adapter <name> [--pretty]",
169
+ Usage: "--config <path> --adapter <name> [--pretty]",
170
Short: "Adapts a configuration to Caddy's native JSON config structure",
171
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.`,
175
+ Flags: func() *flag.FlagSet {
176
+ fs := flag.NewFlagSet("adapt", flag.ExitOnError)
177
+ fs.String("config", "", "Configuration file to adapt")
178
+ fs.String("adapter", "", "Name of config adapter")
179
+ fs.Bool("pretty", false, "Format the output for human readability")
180
+ return fs
181
+ }(),
182
+ },
183
+
184
+ "validate": {
185
+ Name: "validate",
186
+ Func: cmdValidateConfig,
187
+ Usage: "--config <path> [--adapter <name>]",
188
+ Short: "Tests whether a configuration file is valid.",
189
+ Long: `
190
+Loads and provisions the provided config, but does not start
191
+running it.`,
192
+ Flags: func() *flag.FlagSet {
193
+ fs := flag.NewFlagSet("load", flag.ExitOnError)
194
+ fs.String("config", "", "Input configuration file")
195
+ fs.String("adapter", "", "Name of config adapter")
196
+ return fs
197
+ }(),
198
},
199
}
200