From 197d65659a4c925a3ab402a52512806f016c3513 Mon Sep 17 00:00:00 2001 From: cthuang Date: Wed, 16 Sep 2020 13:15:49 +0100 Subject: [PATCH] TUN-3291: cloudflared tunnel run -h explains how to use flags from parent command --- cmd/cloudflared/tunnel/cmd.go | 137 +++++++++++++++----------- cmd/cloudflared/tunnel/subcommands.go | 43 ++++++-- 2 files changed, 115 insertions(+), 65 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 6bf8a3b3..8b60eb4a 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -182,7 +182,13 @@ func Commands() []*cli.Command { subcommands = append(subcommands, buildCleanupCommand()) subcommands = append(subcommands, buildRouteCommand()) - cmds = append(cmds, &cli.Command{ + cmds = append(cmds, buildTunnelCommand(subcommands)) + + return cmds +} + +func buildTunnelCommand(subcommands []*cli.Command) *cli.Command { + return &cli.Command{ Name: "tunnel", Action: cliutil.ErrorHandler(TunnelCommand), Before: Before, @@ -198,20 +204,18 @@ func Commands() []*cli.Command { To use, begin by calling login to download a certificate: - cloudflared tunnel login + $ cloudflared tunnel login With your certificate installed you can then launch your first tunnel, replacing my.site.com with a subdomain of your site: - cloudflared tunnel --hostname my.site.com --url http://localhost:8080 + $ cloudflared tunnel --hostname my.site.com --url http://localhost:8080 If you have a web server running on port 8080 (in this example), it will be available on the internet!`, Subcommands: subcommands, Flags: tunnelFlags(false), - }) - - return cmds + } } func TunnelCommand(c *cli.Context) error { @@ -748,7 +752,7 @@ func appendFlags(flags []cli.Flag, extraFlags ...cli.Flag) []cli.Flag { } func tunnelFlags(shouldHide bool) []cli.Flag { - return []cli.Flag{ + flags := []cli.Flag{ &cli.StringFlag{ Name: "config", Usage: "Specifies a config file in YAML format.", @@ -805,13 +809,6 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_ORIGIN_CA_POOL"}, Hidden: shouldHide, }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "url", - Value: "http://localhost:8080", - Usage: "Connect to the local webserver at `URL`.", - EnvVars: []string{"TUNNEL_URL"}, - Hidden: shouldHide, - }), altsrc.NewStringFlag(&cli.StringFlag{ Name: "unix-socket", Usage: "Path to unix socket to use instead of --url", @@ -927,20 +924,6 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_RETRIES"}, Hidden: shouldHide, }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "hello-world", - Value: false, - Usage: "Run Hello World Server", - EnvVars: []string{"TUNNEL_HELLO_WORLD"}, - Hidden: shouldHide, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: sshServerFlag, - Value: false, - Usage: "Run an SSH Server", - EnvVars: []string{"TUNNEL_SSH_SERVER"}, - Hidden: true, // TODO: remove when feature is complete - }), altsrc.NewStringFlag(&cli.StringFlag{ Name: "pidfile", Usage: "Write the application's PID to this file after first successful connection.", @@ -1085,6 +1068,54 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"DIAL_EDGE_TIMEOUT"}, Hidden: true, }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "stdin-control", + Usage: "Control the process using commands sent through stdin", + EnvVars: []string{"STDIN-CONTROL"}, + Hidden: true, + Value: false, + }), + altsrc.NewStringFlag(&cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + EnvVars: []string{"TUNNEL_NAME"}, + Usage: "Stable name to identify the tunnel. Using this flag will create, route and run a tunnel. For production usage, execute each command separately", + }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: uiFlag, + Usage: "Launch tunnel UI. Tunnel logs are scrollable via 'j', 'k', or arrow keys.", + Value: false, + Hidden: shouldHide, + }), + urlFlag(shouldHide), + helloWorldFlag(shouldHide), + createSocks5Flag(shouldHide), + } + return append(flags, sshFlags(shouldHide)...) +} + +func urlFlag(shouldHide bool) cli.Flag { + return altsrc.NewStringFlag(&cli.StringFlag{ + Name: "url", + Value: "http://localhost:8080", + Usage: "Connect to the local webserver at `URL`.", + EnvVars: []string{"TUNNEL_URL"}, + Hidden: shouldHide, + }) +} + +func helloWorldFlag(shouldHide bool) cli.Flag { + return altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "hello-world", + Value: false, + Usage: "Run Hello World Server", + EnvVars: []string{"TUNNEL_HELLO_WORLD"}, + Hidden: shouldHide, + }) +} + +func sshFlags(shouldHide bool) []cli.Flag { + return []cli.Flag{ altsrc.NewStringFlag(&cli.StringFlag{ Name: sshPortFlag, Usage: "Localhost port that cloudflared SSH server will run on", @@ -1116,18 +1147,18 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"REGION_ID"}, Hidden: true, }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: accessKeyIDFlag, - Usage: "Access Key ID of where to upload SSH logs", - EnvVars: []string{"ACCESS_CLIENT_ID"}, - Hidden: true, - }), altsrc.NewStringFlag(&cli.StringFlag{ Name: secretIDFlag, Usage: "Secret ID of where to upload SSH logs", EnvVars: []string{"SECRET_ID"}, Hidden: true, }), + altsrc.NewStringFlag(&cli.StringFlag{ + Name: accessKeyIDFlag, + Usage: "Access Key ID of where to upload SSH logs", + EnvVars: []string{"ACCESS_CLIENT_ID"}, + Hidden: true, + }), altsrc.NewStringFlag(&cli.StringFlag{ Name: sessionTokenIDFlag, Usage: "Session Token to use in the configuration of SSH logs uploading", @@ -1147,18 +1178,11 @@ func tunnelFlags(shouldHide bool) []cli.Flag { Hidden: true, }), altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "stdin-control", - Usage: "Control the process using commands sent through stdin", - EnvVars: []string{"STDIN-CONTROL"}, - Hidden: true, + Name: sshServerFlag, Value: false, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: socks5Flag, - Usage: "specify if this tunnel is running as a SOCK5 Server", - EnvVars: []string{"TUNNEL_SOCKS"}, - Value: false, - Hidden: shouldHide, + Usage: "Run an SSH Server", + EnvVars: []string{"TUNNEL_SSH_SERVER"}, + Hidden: true, // TODO: remove when feature is complete }), altsrc.NewBoolFlag(&cli.BoolFlag{ Name: bastionFlag, @@ -1167,22 +1191,19 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_BASTION"}, Hidden: shouldHide, }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - EnvVars: []string{"TUNNEL_NAME"}, - Usage: "Stable name to identify the tunnel. Using this flag will create, route and run a tunnel. For production usage, execute each command separately", - Hidden: true, - }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: uiFlag, - Usage: "Launch tunnel UI. Tunnel logs are scrollable via 'j', 'k', or arrow keys.", - Value: false, - Hidden: shouldHide, - }), } } +func createSocks5Flag(shouldHide bool) cli.Flag { + return altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: socks5Flag, + Usage: "specify if this tunnel is running as a SOCK5 Server", + EnvVars: []string{"TUNNEL_SOCKS"}, + Value: false, + Hidden: shouldHide, + }) +} + func stdinControl(reconnectCh chan origin.ReconnectSignal, logger logger.Service) { for { scanner := bufio.NewScanner(os.Stdin) diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index b2262f19..d02a6b91 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -42,10 +42,10 @@ var ( Usage: "List tunnels with the given `NAME`", } listExistedAtFlag = &cli.TimestampFlag{ - Name: "when", - Aliases: []string{"w"}, - Usage: "List tunnels that are active at the given `TIME` in RFC3339 format", - Layout: tunnelstore.TimeLayout, + Name: "when", + Aliases: []string{"w"}, + Usage: "List tunnels that are active at the given `TIME` in RFC3339 format", + Layout: tunnelstore.TimeLayout, DefaultText: fmt.Sprintf("current time, %s", time.Now().Format(tunnelstore.TimeLayout)), } listIDFlag = &cli.StringFlag{ @@ -293,18 +293,48 @@ func renderOutput(format string, v interface{}) error { } func buildRunCommand() *cli.Command { + flags := []cli.Flag{ + forceFlag, + credentialsFileFlag, + urlFlag(false), + helloWorldFlag(false), + createSocks5Flag(false), + } + flags = append(flags, sshFlags(false)...) + var runCommandHelpTemplate = `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.UsageText}} + +DESCRIPTION: + {{.Description}} + +TUNNEL COMMAND OPTIONS: + See cloudflared tunnel -h + +RUN COMMAND OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}} +` return &cli.Command{ Name: "run", Action: cliutil.ErrorHandler(runCommand), Usage: "Proxy a local web server by running the given tunnel", + UsageText: "cloudflared tunnel [tunnel command options] run [run command options]", ArgsUsage: "TUNNEL", Description: `Runs the tunnel identified by name or UUUD, creating a highly available connection between your server and the Cloudflare edge. This command requires the tunnel credentials file created when "cloudflared tunnel create" was run, however it does not need access to cert.pem from "cloudflared login". If you experience problems running - the tunnel, "cloudflared tunnel cleanup" may help by removing any old connection records.`, - Flags: []cli.Flag{forceFlag, credentialsFileFlag}, + the tunnel, "cloudflared tunnel cleanup" may help by removing any old connection records. + + All the flags from the tunnel command are available, note that they have to be specified before the run command. There are flags defined both in tunnel and run command. The one in run command will take precedence. + For example cloudflared tunnel --url localhost:3000 run --url localhost:5000 will proxy requests to localhost:5000. +`, + Flags: flags, + CustomHelpTemplate: runCommandHelpTemplate, } } @@ -471,4 +501,3 @@ func routeCommand(c *cli.Context) error { sc.logger.Infof(res.SuccessSummary()) return nil } -