From 9e6919550be5689628d0020ec14e90ea6f527716 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Fri, 24 Feb 2023 18:09:12 -0500 Subject: 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 * Apply suggestions from code review Co-authored-by: Matt Holt --------- Co-authored-by: Emily Lange Co-authored-by: Matt Holt --- modules/caddyhttp/caddyauth/command.go | 16 +++++------ modules/caddyhttp/fileserver/command.go | 24 ++++++++-------- modules/caddyhttp/reverseproxy/command.go | 46 +++++++++++++++++-------------- modules/caddyhttp/staticresp.go | 31 ++++++++++----------- 4 files changed, 58 insertions(+), 59 deletions(-) (limited to 'modules/caddyhttp') diff --git a/modules/caddyhttp/caddyauth/command.go b/modules/caddyhttp/caddyauth/command.go index 609de4e..915bcd3 100644 --- a/modules/caddyhttp/caddyauth/command.go +++ b/modules/caddyhttp/caddyauth/command.go @@ -18,20 +18,19 @@ import ( "bufio" "bytes" "encoding/base64" - "flag" "fmt" "os" "os/signal" "github.com/caddyserver/caddy/v2" caddycmd "github.com/caddyserver/caddy/v2/cmd" + "github.com/spf13/cobra" "golang.org/x/term" ) func init() { caddycmd.RegisterCommand(caddycmd.Command{ Name: "hash-password", - Func: cmdHashPassword, Usage: "[--algorithm ] [--salt ] [--plaintext ]", Short: "Hashes a password and writes base64", Long: ` @@ -50,13 +49,12 @@ be provided (scrypt). Note that scrypt is deprecated. Please use 'bcrypt' instead. `, - Flags: func() *flag.FlagSet { - fs := flag.NewFlagSet("hash-password", flag.ExitOnError) - fs.String("algorithm", "bcrypt", "Name of the hash algorithm") - fs.String("plaintext", "", "The plaintext password") - fs.String("salt", "", "The password salt") - return fs - }(), + CobraFunc: func(cmd *cobra.Command) { + cmd.Flags().StringP("plaintext", "p", "", "The plaintext password") + cmd.Flags().StringP("salt", "s", "", "The password salt") + cmd.Flags().StringP("algorithm", "a", "bcrypt", "Name of the hash algorithm") + cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdHashPassword) + }, }) } diff --git a/modules/caddyhttp/fileserver/command.go b/modules/caddyhttp/fileserver/command.go index bc7f981..697ec34 100644 --- a/modules/caddyhttp/fileserver/command.go +++ b/modules/caddyhttp/fileserver/command.go @@ -16,7 +16,6 @@ package fileserver import ( "encoding/json" - "flag" "log" "strconv" "time" @@ -27,13 +26,13 @@ import ( "github.com/caddyserver/caddy/v2/modules/caddyhttp" caddytpl "github.com/caddyserver/caddy/v2/modules/caddyhttp/templates" "github.com/caddyserver/certmagic" + "github.com/spf13/cobra" "go.uber.org/zap" ) func init() { caddycmd.RegisterCommand(caddycmd.Command{ Name: "file-server", - Func: cmdFileServer, Usage: "[--domain ] [--root ] [--listen ] [--browse] [--access-log]", Short: "Spins up a production-ready file server", Long: ` @@ -49,17 +48,16 @@ using this option. If --browse is enabled, requests for folders without an index file will respond with a file listing.`, - Flags: func() *flag.FlagSet { - fs := flag.NewFlagSet("file-server", flag.ExitOnError) - fs.String("domain", "", "Domain name at which to serve the files") - fs.String("root", "", "The path to the root of the site") - fs.String("listen", "", "The address to which to bind the listener") - fs.Bool("browse", false, "Enable directory browsing") - fs.Bool("templates", false, "Enable template rendering") - fs.Bool("access-log", false, "Enable the access log") - fs.Bool("debug", false, "Enable verbose debug logs") - return fs - }(), + CobraFunc: func(cmd *cobra.Command) { + cmd.Flags().StringP("domain", "d", "", "Domain name at which to serve the files") + cmd.Flags().StringP("root", "r", "", "The path to the root of the site") + cmd.Flags().StringP("listen", "", "", "The address to which to bind the listener") + cmd.Flags().BoolP("browse", "b", false, "Enable directory browsing") + cmd.Flags().BoolP("templates", "t", false, "Enable template rendering") + cmd.Flags().BoolP("access-log", "", false, "Enable the access log") + cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs") + cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdFileServer) + }, }) } diff --git a/modules/caddyhttp/reverseproxy/command.go b/modules/caddyhttp/reverseproxy/command.go index 04fb9b4..8c171ec 100644 --- a/modules/caddyhttp/reverseproxy/command.go +++ b/modules/caddyhttp/reverseproxy/command.go @@ -16,7 +16,6 @@ package reverseproxy import ( "encoding/json" - "flag" "fmt" "net/http" "strconv" @@ -28,14 +27,14 @@ import ( "github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp/headers" "github.com/caddyserver/caddy/v2/modules/caddytls" + "github.com/spf13/cobra" "go.uber.org/zap" ) func init() { caddycmd.RegisterCommand(caddycmd.Command{ Name: "reverse-proxy", - Func: cmdReverseProxy, - Usage: "[--from ] [--to ] [--change-host-header] [--insecure] [--internal-certs] [--disable-redirects]", + Usage: "[--from ] [--to ] [--change-host-header] [--insecure] [--internal-certs] [--disable-redirects] [--access-log]", Short: "A quick and production-ready reverse proxy", Long: ` A simple but production-ready reverse proxy. Useful for quick deployments, @@ -63,17 +62,17 @@ For proxying: --insecure disables TLS verification with the upstream. WARNING: THIS DISABLES SECURITY BY NOT VERIFYING THE UPSTREAM'S CERTIFICATE. `, - Flags: func() *flag.FlagSet { - fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError) - fs.String("from", "localhost", "Address on which to receive traffic") - fs.Var(&reverseProxyCmdTo, "to", "Upstream address(es) to which traffic should be sent") - fs.Bool("change-host-header", false, "Set upstream Host header to address of upstream") - fs.Bool("insecure", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING TLS CERTIFICATES!)") - fs.Bool("internal-certs", false, "Use internal CA for issuing certs") - fs.Bool("debug", false, "Enable verbose debug logs") - fs.Bool("disable-redirects", false, "Disable HTTP->HTTPS redirects") - return fs - }(), + CobraFunc: func(cmd *cobra.Command) { + cmd.Flags().StringP("from", "f", "localhost", "Address on which to receive traffic") + cmd.Flags().StringSliceP("to", "t", []string{}, "Upstream address(es) to which traffic should be sent") + cmd.Flags().BoolP("change-host-header", "c", false, "Set upstream Host header to address of upstream") + cmd.Flags().BoolP("insecure", "", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING TLS CERTIFICATES!)") + cmd.Flags().BoolP("disable-redirects", "r", false, "Disable HTTP->HTTPS redirects") + cmd.Flags().BoolP("internal-certs", "i", false, "Use internal CA for issuing certs") + cmd.Flags().BoolP("access-log", "", false, "Enable the access log") + cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs") + cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdReverseProxy) + }, }) } @@ -83,14 +82,19 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { from := fs.String("from") changeHost := fs.Bool("change-host-header") insecure := fs.Bool("insecure") + disableRedir := fs.Bool("disable-redirects") internalCerts := fs.Bool("internal-certs") + accessLog := fs.Bool("access-log") debug := fs.Bool("debug") - disableRedir := fs.Bool("disable-redirects") httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort) httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort) - if len(reverseProxyCmdTo) == 0 { + to, err := fs.GetStringSlice("to") + if err != nil { + return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid to flag: %v", err) + } + if len(to) == 0 { return caddy.ExitCodeFailedStartup, fmt.Errorf("--to is required") } @@ -119,9 +123,9 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { // set up the upstream address; assume missing information from given parts // mixing schemes isn't supported, so use first defined (if available) - toAddresses := make([]string, len(reverseProxyCmdTo)) + toAddresses := make([]string, len(to)) var toScheme string - for i, toLoc := range reverseProxyCmdTo { + for i, toLoc := range to { addr, scheme, err := parseUpstreamDialAddress(toLoc) if err != nil { return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", toLoc, err) @@ -180,6 +184,9 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { Routes: caddyhttp.RouteList{route}, Listen: []string{":" + fromAddr.Port}, } + if accessLog { + server.Logs = &caddyhttp.ServerLogConfig{} + } if fromAddr.Scheme == "http" { server.AutoHTTPS = &caddyhttp.AutoHTTPSConfig{Disabled: true} @@ -238,6 +245,3 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { select {} } - -// reverseProxyCmdTo holds the parsed values from repeated use of the --to flag. -var reverseProxyCmdTo caddycmd.StringSlice diff --git a/modules/caddyhttp/staticresp.go b/modules/caddyhttp/staticresp.go index add5b12..67614c2 100644 --- a/modules/caddyhttp/staticresp.go +++ b/modules/caddyhttp/staticresp.go @@ -17,7 +17,6 @@ package caddyhttp import ( "bytes" "encoding/json" - "flag" "fmt" "io" "net/http" @@ -32,6 +31,7 @@ import ( "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" caddycmd "github.com/caddyserver/caddy/v2/cmd" + "github.com/spf13/cobra" "go.uber.org/zap" ) @@ -39,7 +39,6 @@ func init() { caddy.RegisterModule(StaticResponse{}) caddycmd.RegisterCommand(caddycmd.Command{ Name: "respond", - Func: cmdRespond, Usage: `[--status ] [--body ] [--listen ] [--access-log] [--debug] [--header "Field: value"] `, Short: "Simple, hard-coded HTTP responses for development and testing", Long: ` @@ -71,16 +70,15 @@ Access/request logging and more verbose debug logging can also be enabled. Response headers may be added using the --header flag for each header field. `, - Flags: func() *flag.FlagSet { - fs := flag.NewFlagSet("respond", flag.ExitOnError) - fs.String("listen", ":0", "The address to which to bind the listener") - fs.Int("status", http.StatusOK, "The response status code") - fs.String("body", "", "The body of the HTTP response") - fs.Bool("access-log", false, "Enable the access log") - fs.Bool("debug", false, "Enable more verbose debug-level logging") - fs.Var(&respondCmdHeaders, "header", "Set a header on the response (format: \"Field: value\"") - return fs - }(), + CobraFunc: func(cmd *cobra.Command) { + cmd.Flags().StringP("listen", "l", ":0", "The address to which to bind the listener") + cmd.Flags().IntP("status", "s", http.StatusOK, "The response status code") + cmd.Flags().StringP("body", "b", "", "The body of the HTTP response") + cmd.Flags().BoolP("access-log", "", false, "Enable the access log") + cmd.Flags().BoolP("debug", "v", false, "Enable more verbose debug-level logging") + cmd.Flags().StringSliceP("header", "H", []string{}, "Set a header on the response (format: \"Field: value\")") + cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdRespond) + }, }) } @@ -318,8 +316,12 @@ func cmdRespond(fl caddycmd.Flags) (int, error) { } // build headers map + headers, err := fl.GetStringSlice("header") + if err != nil { + return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid header flag: %v", err) + } hdr := make(http.Header) - for i, h := range respondCmdHeaders { + for i, h := range headers { key, val, found := strings.Cut(h, ":") key, val = strings.TrimSpace(key), strings.TrimSpace(val) if !found || key == "" || val == "" { @@ -432,9 +434,6 @@ func cmdRespond(fl caddycmd.Flags) (int, error) { select {} } -// respondCmdHeaders holds the parsed values from repeated use of the --header flag. -var respondCmdHeaders caddycmd.StringSlice - // Interface guards var ( _ MiddlewareHandler = (*StaticResponse)(nil) -- cgit v1.2.3