Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 05:30 UTC
cmd: Expand cobra support, add short flags (#5379)
* cmd: Expand cobra support * Convert commands to cobra, add short flags * Fix version command typo Co-authored-by: Emily Lange <git@indeednotjames.com> * Apply suggestions from code review Co-authored-by: Matt Holt <mholt@users.noreply.github.com> --------- Co-authored-by: Emily Lange <git@indeednotjames.com> Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
Francis Lavoie committed
Feb 24, 2023 at 18:09 UTC
9e6919550be5689628d0020ec14e90ea6f527716
7 files changed
+198
-209
cmd/cobra.go
+15
-6
@@ -109,12 +109,21 @@ func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
109
Use: caddyCmd.Name,
110
Short: caddyCmd.Short,
111
Long: caddyCmd.Long,
112
- RunE: func(cmd *cobra.Command, _ []string) error {
113
- fls := cmd.Flags()
114
- _, err := caddyCmd.Func(Flags{fls})
115
- return err
116
- },
112
}
118
- cmd.Flags().AddGoFlagSet(caddyCmd.Flags)
113
+ if caddyCmd.CobraFunc != nil {
114
+ caddyCmd.CobraFunc(cmd)
115
+ } else {
116
+ cmd.RunE = WrapCommandFuncForCobra(caddyCmd.Func)
117
+ cmd.Flags().AddGoFlagSet(caddyCmd.Flags)
118
+ }
119
return cmd
120
}
121
+
122
+// WrapCommandFuncForCobra wraps a Caddy CommandFunc for use
123
+// in a cobra command's RunE field.
124
+func WrapCommandFuncForCobra(f CommandFunc) func(cmd *cobra.Command, _ []string) error {
125
+ return func(cmd *cobra.Command, _ []string) error {
126
+ _, err := f(Flags{cmd.Flags()})
127
+ return err
128
+ }
129
+}
cmd/commands.go
+109
-124
@@ -34,12 +34,6 @@ type Command struct {
34
// Required.
35
Name string
36
37
- // Func is a function that executes a subcommand using
38
- // the parsed flags. It returns an exit code and any
39
- // associated error.
40
- // Required.
41
- Func CommandFunc
42
-
37
// Usage is a brief message describing the syntax of
38
// the subcommand's flags and args. Use [] to indicate
39
// optional parameters and <> to enclose literal values
@@ -60,7 +54,21 @@ type Command struct {
54
Long string
55
56
// Flags is the flagset for command.
57
+ // This is ignored if CobraFunc is set.
58
Flags *flag.FlagSet
59
+
60
+ // Func is a function that executes a subcommand using
61
+ // the parsed flags. It returns an exit code and any
62
+ // associated error.
63
+ // Required if CobraFunc is not set.
64
+ Func CommandFunc
65
+
66
+ // CobraFunc allows further configuration of the command
67
+ // via cobra's APIs. If this is set, then Func and Flags
68
+ // are ignored, with the assumption that they are set in
69
+ // this function. A caddycmd.WrapCommandFuncForCobra helper
70
+ // exists to simplify porting CommandFunc to Cobra's RunE.
71
+ CobraFunc func(*cobra.Command)
72
}
73
74
// CommandFunc is a command's function. It runs the
@@ -79,7 +87,6 @@ var commands = make(map[string]Command)
87
func init() {
88
RegisterCommand(Command{
89
Name: "start",
82
- Func: cmdStart,
90
Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--watch] [--pidfile <file>]",
91
Short: "Starts the Caddy process in the background and then returns",
92
Long: `
@@ -93,21 +100,19 @@ On Windows, the spawned child process will remain attached to the terminal, so
100
closing the window will forcefully stop Caddy; to avoid forgetting this, try
101
using 'caddy run' instead to keep it in the foreground.
102
`,
96
- Flags: func() *flag.FlagSet {
97
- fs := flag.NewFlagSet("start", flag.ExitOnError)
98
- fs.String("config", "", "Configuration file")
99
- fs.String("adapter", "", "Name of config adapter to apply")
100
- fs.String("envfile", "", "Environment file to load")
101
- fs.Bool("watch", false, "Reload changed config file automatically")
102
- fs.String("pidfile", "", "Path of file to which to write process ID")
103
- return fs
104
- }(),
103
+ CobraFunc: func(cmd *cobra.Command) {
104
+ cmd.Flags().StringP("config", "c", "", "Configuration file")
105
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply")
106
+ cmd.Flags().StringP("envfile", "", "", "Environment file to load")
107
+ cmd.Flags().BoolP("watch", "w", false, "Reload changed config file automatically")
108
+ cmd.Flags().StringP("pidfile", "", "", "Path of file to which to write process ID")
109
+ cmd.RunE = WrapCommandFuncForCobra(cmdStart)
110
+ },
111
})
112
113
RegisterCommand(Command{
114
Name: "run",
109
- Func: cmdRun,
110
- Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--environ] [--resume] [--watch] [--pidfile <fil>]",
115
+ Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--environ] [--resume] [--watch] [--pidfile <file>]",
116
Short: `Starts the Caddy process and blocks indefinitely`,
117
Long: `
118
Starts the Caddy process, optionally bootstrapped with an initial config file,
@@ -141,24 +146,22 @@ If --watch is specified, the config file will be loaded automatically after
146
changes. ⚠️ This can make unintentional config changes easier; only use this
147
option in a local development environment.
148
`,
144
- Flags: func() *flag.FlagSet {
145
- fs := flag.NewFlagSet("run", flag.ExitOnError)
146
- fs.String("config", "", "Configuration file")
147
- fs.String("adapter", "", "Name of config adapter to apply")
148
- fs.String("envfile", "", "Environment file to load")
149
- fs.Bool("environ", false, "Print environment")
150
- fs.Bool("resume", false, "Use saved config, if any (and prefer over --config file)")
151
- fs.Bool("watch", false, "Watch config file for changes and reload it automatically")
152
- fs.String("pidfile", "", "Path of file to which to write process ID")
153
- fs.String("pingback", "", "Echo confirmation bytes to this address on success")
154
- return fs
155
- }(),
149
+ CobraFunc: func(cmd *cobra.Command) {
150
+ cmd.Flags().StringP("config", "c", "", "Configuration file")
151
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply")
152
+ cmd.Flags().StringP("envfile", "", "", "Environment file to load")
153
+ cmd.Flags().BoolP("environ", "e", false, "Print environment")
154
+ cmd.Flags().BoolP("resume", "r", false, "Use saved config, if any (and prefer over --config file)")
155
+ cmd.Flags().BoolP("watch", "w", false, "Watch config file for changes and reload it automatically")
156
+ cmd.Flags().StringP("pidfile", "", "", "Path of file to which to write process ID")
157
+ cmd.Flags().StringP("pingback", "", "", "Echo confirmation bytes to this address on success")
158
+ cmd.RunE = WrapCommandFuncForCobra(cmdRun)
159
+ },
160
})
161
162
RegisterCommand(Command{
163
Name: "stop",
160
- Func: cmdStop,
161
- Usage: "[--address <interface>] [--config <path> [--adapter <name>]]",
164
+ Usage: "[--config <path> [--adapter <name>]] [--address <interface>]",
165
Short: "Gracefully stops a started Caddy process",
166
Long: `
167
Stops the background Caddy process as gracefully as possible.
@@ -167,18 +170,16 @@ It requires that the admin API is enabled and accessible, since it will
170
use the API's /stop endpoint. The address of this request can be customized
171
using the --address flag, or from the given --config, if not the default.
172
`,
170
- Flags: func() *flag.FlagSet {
171
- fs := flag.NewFlagSet("stop", flag.ExitOnError)
172
- fs.String("address", "", "The address to use to reach the admin API endpoint, if not the default")
173
- fs.String("config", "", "Configuration file to use to parse the admin address, if --address is not used")
174
- fs.String("adapter", "", "Name of config adapter to apply (when --config is used)")
175
- return fs
176
- }(),
173
+ CobraFunc: func(cmd *cobra.Command) {
174
+ cmd.Flags().StringP("config", "c", "", "Configuration file to use to parse the admin address, if --address is not used")
175
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply (when --config is used)")
176
+ cmd.Flags().StringP("address", "", "", "The address to use to reach the admin API endpoint, if not the default")
177
+ cmd.RunE = WrapCommandFuncForCobra(cmdStop)
178
+ },
179
})
180
181
RegisterCommand(Command{
182
Name: "reload",
181
- Func: cmdReload,
183
Usage: "--config <path> [--adapter <name>] [--address <interface>]",
184
Short: "Changes the config of the running Caddy instance",
185
Long: `
@@ -190,19 +191,17 @@ Since the admin endpoint is configurable, the endpoint configuration is loaded
191
from the --address flag if specified; otherwise it is loaded from the given
192
config file; otherwise the default is assumed.
193
`,
193
- Flags: func() *flag.FlagSet {
194
- fs := flag.NewFlagSet("reload", flag.ExitOnError)
195
- fs.String("config", "", "Configuration file (required)")
196
- fs.String("adapter", "", "Name of config adapter to apply")
197
- fs.String("address", "", "Address of the administration listener, if different from config")
198
- fs.Bool("force", false, "Force config reload, even if it is the same")
199
- return fs
200
- }(),
194
+ CobraFunc: func(cmd *cobra.Command) {
195
+ cmd.Flags().StringP("config", "c", "", "Configuration file (required)")
196
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply")
197
+ cmd.Flags().StringP("address", "", "", "Address of the administration listener, if different from config")
198
+ cmd.Flags().BoolP("force", "f", false, "Force config reload, even if it is the same")
199
+ cmd.RunE = WrapCommandFuncForCobra(cmdReload)
200
+ },
201
})
202
203
RegisterCommand(Command{
204
Name: "version",
205
- Func: cmdVersion,
205
Short: "Prints the version",
206
Long: `
207
Prints the version of this Caddy binary.
@@ -217,31 +216,29 @@ detailed version information is printed as given by Go modules.
216
For more details about the full version string, see the Go module
217
documentation: https://go.dev/doc/modules/version-numbers
218
`,
219
+ Func: cmdVersion,
220
})
221
222
RegisterCommand(Command{
223
Name: "list-modules",
224
- Func: cmdListModules,
225
- Usage: "[--packages] [--versions]",
224
+ Usage: "[--packages] [--versions] [--skip-standard]",
225
Short: "Lists the installed Caddy modules",
227
- Flags: func() *flag.FlagSet {
228
- fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
229
- fs.Bool("packages", false, "Print package paths")
230
- fs.Bool("versions", false, "Print version information")
231
- fs.Bool("skip-standard", false, "Skip printing standard modules")
232
- return fs
233
- }(),
226
+ CobraFunc: func(cmd *cobra.Command) {
227
+ cmd.Flags().BoolP("packages", "", false, "Print package paths")
228
+ cmd.Flags().BoolP("versions", "", false, "Print version information")
229
+ cmd.Flags().BoolP("skip-standard", "s", false, "Skip printing standard modules")
230
+ cmd.RunE = WrapCommandFuncForCobra(cmdListModules)
231
+ },
232
})
233
234
RegisterCommand(Command{
235
Name: "build-info",
238
- Func: cmdBuildInfo,
236
Short: "Prints information about this build",
237
+ Func: cmdBuildInfo,
238
})
239
240
RegisterCommand(Command{
241
Name: "environ",
244
- Func: cmdEnviron,
242
Short: "Prints the environment",
243
Long: `
244
Prints the environment as seen by this Caddy process.
@@ -261,11 +258,11 @@ by adding the "--environ" flag.
258
259
Environments may contain sensitive data.
260
`,
261
+ Func: cmdEnviron,
262
})
263
264
RegisterCommand(Command{
265
Name: "adapt",
268
- Func: cmdAdaptConfig,
266
Usage: "--config <path> [--adapter <name>] [--pretty] [--validate]",
267
Short: "Adapts a configuration to Caddy's native JSON",
268
Long: `
@@ -279,19 +276,17 @@ If --validate is used, the adapted config will be checked for validity.
276
If the config is invalid, an error will be printed to stderr and a non-
277
zero exit status will be returned.
278
`,
282
- Flags: func() *flag.FlagSet {
283
- fs := flag.NewFlagSet("adapt", flag.ExitOnError)
284
- fs.String("config", "", "Configuration file to adapt (required)")
285
- fs.String("adapter", "caddyfile", "Name of config adapter")
286
- fs.Bool("pretty", false, "Format the output for human readability")
287
- fs.Bool("validate", false, "Validate the output")
288
- return fs
289
- }(),
279
+ CobraFunc: func(cmd *cobra.Command) {
280
+ cmd.Flags().StringP("config", "c", "", "Configuration file to adapt (required)")
281
+ cmd.Flags().StringP("adapter", "a", "caddyfile", "Name of config adapter")
282
+ cmd.Flags().BoolP("pretty", "p", false, "Format the output for human readability")
283
+ cmd.Flags().BoolP("validate", "", false, "Validate the output")
284
+ cmd.RunE = WrapCommandFuncForCobra(cmdAdaptConfig)
285
+ },
286
})
287
288
RegisterCommand(Command{
289
Name: "validate",
294
- Func: cmdValidateConfig,
290
Usage: "--config <path> [--adapter <name>] [--envfile <path>]",
291
Short: "Tests whether a configuration file is valid",
292
Long: `
@@ -302,18 +297,16 @@ provisioning stages.
297
If --envfile is specified, an environment file with environment variables in
298
the KEY=VALUE format will be loaded into the Caddy process.
299
`,
305
- Flags: func() *flag.FlagSet {
306
- fs := flag.NewFlagSet("validate", flag.ExitOnError)
307
- fs.String("config", "", "Input configuration file")
308
- fs.String("adapter", "", "Name of config adapter")
309
- fs.String("envfile", "", "Environment file to load")
310
- return fs
311
- }(),
300
+ CobraFunc: func(cmd *cobra.Command) {
301
+ cmd.Flags().StringP("config", "c", "", "Input configuration file")
302
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter")
303
+ cmd.Flags().StringP("envfile", "", "", "Environment file to load")
304
+ cmd.RunE = WrapCommandFuncForCobra(cmdValidateConfig)
305
+ },
306
})
307
308
RegisterCommand(Command{
309
Name: "fmt",
316
- Func: cmdFmt,
310
Usage: "[--overwrite] [--diff] [<path>]",
311
Short: "Formats a Caddyfile",
312
Long: `
@@ -332,32 +325,28 @@ If you wish you use stdin instead of a regular file, use - as the path.
325
When reading from stdin, the --overwrite flag has no effect: the result
326
is always printed to stdout.
327
`,
335
- Flags: func() *flag.FlagSet {
336
- fs := flag.NewFlagSet("fmt", flag.ExitOnError)
337
- fs.Bool("overwrite", false, "Overwrite the input file with the results")
338
- fs.Bool("diff", false, "Print the differences between the input file and the formatted output")
339
- return fs
340
- }(),
328
+ CobraFunc: func(cmd *cobra.Command) {
329
+ cmd.Flags().BoolP("overwrite", "w", false, "Overwrite the input file with the results")
330
+ cmd.Flags().BoolP("diff", "d", false, "Print the differences between the input file and the formatted output")
331
+ cmd.RunE = WrapCommandFuncForCobra(cmdFmt)
332
+ },
333
})
334
335
RegisterCommand(Command{
336
Name: "upgrade",
345
- Func: cmdUpgrade,
337
Short: "Upgrade Caddy (EXPERIMENTAL)",
338
Long: `
339
Downloads an updated Caddy binary with the same modules/plugins at the
340
latest versions. EXPERIMENTAL: May be changed or removed.
341
`,
351
- Flags: func() *flag.FlagSet {
352
- fs := flag.NewFlagSet("upgrade", flag.ExitOnError)
353
- fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
354
- return fs
355
- }(),
342
+ CobraFunc: func(cmd *cobra.Command) {
343
+ cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")
344
+ cmd.RunE = WrapCommandFuncForCobra(cmdUpgrade)
345
+ },
346
})
347
348
RegisterCommand(Command{
349
Name: "add-package",
360
- Func: cmdAddPackage,
350
Usage: "<packages...>",
351
Short: "Adds Caddy packages (EXPERIMENTAL)",
352
Long: `
@@ -365,11 +354,10 @@ Downloads an updated Caddy binary with the specified packages (module/plugin)
354
added. Retains existing packages. Returns an error if the any of packages are
355
already included. EXPERIMENTAL: May be changed or removed.
356
`,
368
- Flags: func() *flag.FlagSet {
369
- fs := flag.NewFlagSet("add-package", flag.ExitOnError)
370
- fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
371
- return fs
372
- }(),
357
+ CobraFunc: func(cmd *cobra.Command) {
358
+ cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")
359
+ cmd.RunE = WrapCommandFuncForCobra(cmdAddPackage)
360
+ },
361
})
362
363
RegisterCommand(Command{
@@ -382,31 +370,14 @@ Downloads an updated Caddy binaries without the specified packages (module/plugi
370
Returns an error if any of the packages are not included.
371
EXPERIMENTAL: May be changed or removed.
372
`,
385
- Flags: func() *flag.FlagSet {
386
- fs := flag.NewFlagSet("remove-package", flag.ExitOnError)
387
- fs.Bool("keep-backup", false, "Keep the backed up binary, instead of deleting it")
388
- return fs
389
- }(),
373
+ CobraFunc: func(cmd *cobra.Command) {
374
+ cmd.Flags().BoolP("keep-backup", "k", false, "Keep the backed up binary, instead of deleting it")
375
+ cmd.RunE = WrapCommandFuncForCobra(cmdRemovePackage)
376
+ },
377
})
378
379
RegisterCommand(Command{
393
- Name: "manpage",
394
- Func: func(fl Flags) (int, error) {
395
- dir := strings.TrimSpace(fl.String("directory"))
396
- if dir == "" {
397
- return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
398
- }
399
- if err := os.MkdirAll(dir, 0755); err != nil {
400
- return caddy.ExitCodeFailedQuit, err
401
- }
402
- if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
403
- Title: "Caddy",
404
- Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
405
- }, dir); err != nil {
406
- return caddy.ExitCodeFailedQuit, err
407
- }
408
- return caddy.ExitCodeSuccess, nil
409
- },
380
+ Name: "manpage",
381
Usage: "--directory <path>",
382
Short: "Generates the manual pages for Caddy commands",
383
Long: `
@@ -416,11 +387,25 @@ tagged into section 8 (System Administration).
387
The manual page files are generated into the directory specified by the
388
argument of --directory. If the directory does not exist, it will be created.
389
`,
419
- Flags: func() *flag.FlagSet {
420
- fs := flag.NewFlagSet("manpage", flag.ExitOnError)
421
- fs.String("directory", "", "The output directory where the manpages are generated")
422
- return fs
423
- }(),
390
+ CobraFunc: func(cmd *cobra.Command) {
391
+ cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
392
+ cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) {
393
+ dir := strings.TrimSpace(fl.String("directory"))
394
+ if dir == "" {
395
+ return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
396
+ }
397
+ if err := os.MkdirAll(dir, 0755); err != nil {
398
+ return caddy.ExitCodeFailedQuit, err
399
+ }
400
+ if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
401
+ Title: "Caddy",
402
+ Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
403
+ }, dir); err != nil {
404
+ return caddy.ExitCodeFailedQuit, err
405
+ }
406
+ return caddy.ExitCodeSuccess, nil
407
+ })
408
+ },
409
})
410
411
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
@@ -504,7 +489,7 @@ func RegisterCommand(cmd Command) {
489
if cmd.Name == "" {
490
panic("command name is required")
491
}
507
- if cmd.Func == nil {
492
+ if cmd.Func == nil && cmd.CobraFunc == nil {
493
panic("command function missing")
494
}
495
if cmd.Short == "" {
modules/caddyhttp/caddyauth/command.go
+7
-9
@@ -18,20 +18,19 @@ import (
18
"bufio"
19
"bytes"
20
"encoding/base64"
21
- "flag"
21
"fmt"
22
"os"
23
"os/signal"
24
25
"github.com/caddyserver/caddy/v2"
26
caddycmd "github.com/caddyserver/caddy/v2/cmd"
27
+ "github.com/spf13/cobra"
28
"golang.org/x/term"
29
)
30
31
func init() {
32
caddycmd.RegisterCommand(caddycmd.Command{
33
Name: "hash-password",
34
- Func: cmdHashPassword,
34
Usage: "[--algorithm <name>] [--salt <string>] [--plaintext <password>]",
35
Short: "Hashes a password and writes base64",
36
Long: `
@@ -50,13 +49,12 @@ be provided (scrypt).
49
50
Note that scrypt is deprecated. Please use 'bcrypt' instead.
51
`,
53
- Flags: func() *flag.FlagSet {
54
- fs := flag.NewFlagSet("hash-password", flag.ExitOnError)
55
- fs.String("algorithm", "bcrypt", "Name of the hash algorithm")
56
- fs.String("plaintext", "", "The plaintext password")
57
- fs.String("salt", "", "The password salt")
58
- return fs
59
- }(),
52
+ CobraFunc: func(cmd *cobra.Command) {
53
+ cmd.Flags().StringP("plaintext", "p", "", "The plaintext password")
54
+ cmd.Flags().StringP("salt", "s", "", "The password salt")
55
+ cmd.Flags().StringP("algorithm", "a", "bcrypt", "Name of the hash algorithm")
56
+ cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdHashPassword)
57
+ },
58
})
59
}
60
modules/caddyhttp/fileserver/command.go
+11
-13
@@ -16,7 +16,6 @@ package fileserver
16
17
import (
18
"encoding/json"
19
- "flag"
19
"log"
20
"strconv"
21
"time"
@@ -27,13 +26,13 @@ import (
26
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
27
caddytpl "github.com/caddyserver/caddy/v2/modules/caddyhttp/templates"
28
"github.com/caddyserver/certmagic"
29
+ "github.com/spf13/cobra"
30
"go.uber.org/zap"
31
)
32
33
func init() {
34
caddycmd.RegisterCommand(caddycmd.Command{
35
Name: "file-server",
36
- Func: cmdFileServer,
36
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--browse] [--access-log]",
37
Short: "Spins up a production-ready file server",
38
Long: `
@@ -49,17 +48,16 @@ using this option.
48
49
If --browse is enabled, requests for folders without an index file will
50
respond with a file listing.`,
52
- Flags: func() *flag.FlagSet {
53
- fs := flag.NewFlagSet("file-server", flag.ExitOnError)
54
- fs.String("domain", "", "Domain name at which to serve the files")
55
- fs.String("root", "", "The path to the root of the site")
56
- fs.String("listen", "", "The address to which to bind the listener")
57
- fs.Bool("browse", false, "Enable directory browsing")
58
- fs.Bool("templates", false, "Enable template rendering")
59
- fs.Bool("access-log", false, "Enable the access log")
60
- fs.Bool("debug", false, "Enable verbose debug logs")
61
- return fs
62
- }(),
51
+ CobraFunc: func(cmd *cobra.Command) {
52
+ cmd.Flags().StringP("domain", "d", "", "Domain name at which to serve the files")
53
+ cmd.Flags().StringP("root", "r", "", "The path to the root of the site")
54
+ cmd.Flags().StringP("listen", "", "", "The address to which to bind the listener")
55
+ cmd.Flags().BoolP("browse", "b", false, "Enable directory browsing")
56
+ cmd.Flags().BoolP("templates", "t", false, "Enable template rendering")
57
+ cmd.Flags().BoolP("access-log", "", false, "Enable the access log")
58
+ cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
59
+ cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdFileServer)
60
+ },
61
})
62
}
63
modules/caddyhttp/reverseproxy/command.go
+25
-21
@@ -16,7 +16,6 @@ package reverseproxy
16
17
import (
18
"encoding/json"
19
- "flag"
19
"fmt"
20
"net/http"
21
"strconv"
@@ -28,14 +27,14 @@ import (
27
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
28
"github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
29
"github.com/caddyserver/caddy/v2/modules/caddytls"
30
+ "github.com/spf13/cobra"
31
"go.uber.org/zap"
32
)
33
34
func init() {
35
caddycmd.RegisterCommand(caddycmd.Command{
36
Name: "reverse-proxy",
37
- Func: cmdReverseProxy,
38
- Usage: "[--from <addr>] [--to <addr>] [--change-host-header] [--insecure] [--internal-certs] [--disable-redirects]",
37
+ Usage: "[--from <addr>] [--to <addr>] [--change-host-header] [--insecure] [--internal-certs] [--disable-redirects] [--access-log]",
38
Short: "A quick and production-ready reverse proxy",
39
Long: `
40
A simple but production-ready reverse proxy. Useful for quick deployments,
@@ -63,17 +62,17 @@ For proxying:
62
--insecure disables TLS verification with the upstream. WARNING: THIS
63
DISABLES SECURITY BY NOT VERIFYING THE UPSTREAM'S CERTIFICATE.
64
`,
66
- Flags: func() *flag.FlagSet {
67
- fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError)
68
- fs.String("from", "localhost", "Address on which to receive traffic")
69
- fs.Var(&reverseProxyCmdTo, "to", "Upstream address(es) to which traffic should be sent")
70
- fs.Bool("change-host-header", false, "Set upstream Host header to address of upstream")
71
- fs.Bool("insecure", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING TLS CERTIFICATES!)")
72
- fs.Bool("internal-certs", false, "Use internal CA for issuing certs")
73
- fs.Bool("debug", false, "Enable verbose debug logs")
74
- fs.Bool("disable-redirects", false, "Disable HTTP->HTTPS redirects")
75
- return fs
76
- }(),
65
+ CobraFunc: func(cmd *cobra.Command) {
66
+ cmd.Flags().StringP("from", "f", "localhost", "Address on which to receive traffic")
67
+ cmd.Flags().StringSliceP("to", "t", []string{}, "Upstream address(es) to which traffic should be sent")
68
+ cmd.Flags().BoolP("change-host-header", "c", false, "Set upstream Host header to address of upstream")
69
+ cmd.Flags().BoolP("insecure", "", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING TLS CERTIFICATES!)")
70
+ cmd.Flags().BoolP("disable-redirects", "r", false, "Disable HTTP->HTTPS redirects")
71
+ cmd.Flags().BoolP("internal-certs", "i", false, "Use internal CA for issuing certs")
72
+ cmd.Flags().BoolP("access-log", "", false, "Enable the access log")
73
+ cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
74
+ cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdReverseProxy)
75
+ },
76
})
77
}
78
@@ -83,14 +82,19 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
82
from := fs.String("from")
83
changeHost := fs.Bool("change-host-header")
84
insecure := fs.Bool("insecure")
85
+ disableRedir := fs.Bool("disable-redirects")
86
internalCerts := fs.Bool("internal-certs")
87
+ accessLog := fs.Bool("access-log")
88
debug := fs.Bool("debug")
88
- disableRedir := fs.Bool("disable-redirects")
89
90
httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort)
91
httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort)
92
93
- if len(reverseProxyCmdTo) == 0 {
93
+ to, err := fs.GetStringSlice("to")
94
+ if err != nil {
95
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid to flag: %v", err)
96
+ }
97
+ if len(to) == 0 {
98
return caddy.ExitCodeFailedStartup, fmt.Errorf("--to is required")
99
}
100
@@ -119,9 +123,9 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
123
124
// set up the upstream address; assume missing information from given parts
125
// mixing schemes isn't supported, so use first defined (if available)
122
- toAddresses := make([]string, len(reverseProxyCmdTo))
126
+ toAddresses := make([]string, len(to))
127
var toScheme string
124
- for i, toLoc := range reverseProxyCmdTo {
128
+ for i, toLoc := range to {
129
addr, scheme, err := parseUpstreamDialAddress(toLoc)
130
if err != nil {
131
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", toLoc, err)
@@ -180,6 +184,9 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
184
Routes: caddyhttp.RouteList{route},
185
Listen: []string{":" + fromAddr.Port},
186
}
187
+ if accessLog {
188
+ server.Logs = &caddyhttp.ServerLogConfig{}
189
+ }
190
191
if fromAddr.Scheme == "http" {
192
server.AutoHTTPS = &caddyhttp.AutoHTTPSConfig{Disabled: true}
@@ -238,6 +245,3 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
245
246
select {}
247
}
241
-
242
-// reverseProxyCmdTo holds the parsed values from repeated use of the --to flag.
243
-var reverseProxyCmdTo caddycmd.StringSlice
modules/caddyhttp/staticresp.go
+15
-16
@@ -17,7 +17,6 @@ package caddyhttp
17
import (
18
"bytes"
19
"encoding/json"
20
- "flag"
20
"fmt"
21
"io"
22
"net/http"
@@ -32,6 +31,7 @@ import (
31
"github.com/caddyserver/caddy/v2/caddyconfig"
32
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
33
caddycmd "github.com/caddyserver/caddy/v2/cmd"
34
+ "github.com/spf13/cobra"
35
"go.uber.org/zap"
36
)
37
@@ -39,7 +39,6 @@ func init() {
39
caddy.RegisterModule(StaticResponse{})
40
caddycmd.RegisterCommand(caddycmd.Command{
41
Name: "respond",
42
- Func: cmdRespond,
42
Usage: `[--status <code>] [--body <content>] [--listen <addr>] [--access-log] [--debug] [--header "Field: value"] <body|status>`,
43
Short: "Simple, hard-coded HTTP responses for development and testing",
44
Long: `
@@ -71,16 +70,15 @@ Access/request logging and more verbose debug logging can also be enabled.
70
71
Response headers may be added using the --header flag for each header field.
72
`,
74
- Flags: func() *flag.FlagSet {
75
- fs := flag.NewFlagSet("respond", flag.ExitOnError)
76
- fs.String("listen", ":0", "The address to which to bind the listener")
77
- fs.Int("status", http.StatusOK, "The response status code")
78
- fs.String("body", "", "The body of the HTTP response")
79
- fs.Bool("access-log", false, "Enable the access log")
80
- fs.Bool("debug", false, "Enable more verbose debug-level logging")
81
- fs.Var(&respondCmdHeaders, "header", "Set a header on the response (format: \"Field: value\"")
82
- return fs
83
- }(),
73
+ CobraFunc: func(cmd *cobra.Command) {
74
+ cmd.Flags().StringP("listen", "l", ":0", "The address to which to bind the listener")
75
+ cmd.Flags().IntP("status", "s", http.StatusOK, "The response status code")
76
+ cmd.Flags().StringP("body", "b", "", "The body of the HTTP response")
77
+ cmd.Flags().BoolP("access-log", "", false, "Enable the access log")
78
+ cmd.Flags().BoolP("debug", "v", false, "Enable more verbose debug-level logging")
79
+ cmd.Flags().StringSliceP("header", "H", []string{}, "Set a header on the response (format: \"Field: value\")")
80
+ cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdRespond)
81
+ },
82
})
83
}
84
@@ -318,8 +316,12 @@ func cmdRespond(fl caddycmd.Flags) (int, error) {
316
}
317
318
// build headers map
319
+ headers, err := fl.GetStringSlice("header")
320
+ if err != nil {
321
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid header flag: %v", err)
322
+ }
323
hdr := make(http.Header)
322
- for i, h := range respondCmdHeaders {
324
+ for i, h := range headers {
325
key, val, found := strings.Cut(h, ":")
326
key, val = strings.TrimSpace(key), strings.TrimSpace(val)
327
if !found || key == "" || val == "" {
@@ -432,9 +434,6 @@ func cmdRespond(fl caddycmd.Flags) (int, error) {
434
select {}
435
}
436
435
-// respondCmdHeaders holds the parsed values from repeated use of the --header flag.
436
-var respondCmdHeaders caddycmd.StringSlice
437
-
437
// Interface guards
438
var (
439
_ MiddlewareHandler = (*StaticResponse)(nil)
modules/caddypki/command.go
+16
-20
@@ -18,7 +18,6 @@ import (
18
"crypto/x509"
19
"encoding/json"
20
"encoding/pem"
21
- "flag"
21
"fmt"
22
"net/http"
23
"os"
@@ -27,12 +26,12 @@ import (
26
"github.com/caddyserver/caddy/v2"
27
caddycmd "github.com/caddyserver/caddy/v2/cmd"
28
"github.com/smallstep/truststore"
29
+ "github.com/spf13/cobra"
30
)
31
32
func init() {
33
caddycmd.RegisterCommand(caddycmd.Command{
34
Name: "trust",
35
- Func: cmdTrust,
35
Usage: "[--ca <id>] [--address <listen>] [--config <path> [--adapter <name>]]",
36
Short: "Installs a CA certificate into local trust stores",
37
Long: `
@@ -53,19 +52,17 @@ This command will attempt to connect to Caddy's admin API running at
52
'` + caddy.DefaultAdminListen + `' to fetch the root certificate. You may
53
explicitly specify the --address, or use the --config flag to load
54
the admin address from your config, if not using the default.`,
56
- Flags: func() *flag.FlagSet {
57
- fs := flag.NewFlagSet("trust", flag.ExitOnError)
58
- fs.String("ca", "", "The ID of the CA to trust (defaults to 'local')")
59
- fs.String("address", "", "Address of the administration API listener (if --config is not used)")
60
- fs.String("config", "", "Configuration file (if --address is not used)")
61
- fs.String("adapter", "", "Name of config adapter to apply (if --config is used)")
62
- return fs
63
- }(),
55
+ CobraFunc: func(cmd *cobra.Command) {
56
+ cmd.Flags().StringP("ca", "", "", "The ID of the CA to trust (defaults to 'local')")
57
+ cmd.Flags().StringP("address", "", "", "Address of the administration API listener (if --config is not used)")
58
+ cmd.Flags().StringP("config", "c", "", "Configuration file (if --address is not used)")
59
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply (if --config is used)")
60
+ cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdTrust)
61
+ },
62
})
63
64
caddycmd.RegisterCommand(caddycmd.Command{
65
Name: "untrust",
68
- Func: cmdUntrust,
66
Usage: "[--cert <path>] | [[--ca <id>] [--address <listen>] [--config <path> [--adapter <name>]]]",
67
Short: "Untrusts a locally-trusted CA certificate",
68
Long: `
@@ -89,15 +86,14 @@ will attempt to connect to the Caddy's admin API running at
86
'` + caddy.DefaultAdminListen + `' to fetch the root certificate.
87
You may explicitly specify the --address, or use the --config flag
88
to load the admin address from your config, if not using the default.`,
92
- Flags: func() *flag.FlagSet {
93
- fs := flag.NewFlagSet("untrust", flag.ExitOnError)
94
- fs.String("cert", "", "The path to the CA certificate to untrust")
95
- fs.String("ca", "", "The ID of the CA to untrust (defaults to 'local')")
96
- fs.String("address", "", "Address of the administration API listener (if --config is not used)")
97
- fs.String("config", "", "Configuration file (if --address is not used)")
98
- fs.String("adapter", "", "Name of config adapter to apply (if --config is used)")
99
- return fs
100
- }(),
89
+ CobraFunc: func(cmd *cobra.Command) {
90
+ cmd.Flags().StringP("cert", "p", "", "The path to the CA certificate to untrust")
91
+ cmd.Flags().StringP("ca", "", "", "The ID of the CA to untrust (defaults to 'local')")
92
+ cmd.Flags().StringP("address", "", "", "Address of the administration API listener (if --config is not used)")
93
+ cmd.Flags().StringP("config", "c", "", "Configuration file (if --address is not used)")
94
+ cmd.Flags().StringP("adapter", "a", "", "Name of config adapter to apply (if --config is used)")
95
+ cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdUntrust)
96
+ },
97
})
98
}
99