@marcel91200 / caddy Mirror · read-only / commits / 9e691955
Fork 0

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