diff --git a/cmd/tailscale/cli/serve_v2.go b/cmd/tailscale/cli/serve_v2.go index b7bbd3180..f4011280f 100644 --- a/cmd/tailscale/cli/serve_v2.go +++ b/cmd/tailscale/cli/serve_v2.go @@ -100,6 +100,12 @@ func buildShortUsage(subcmd string) string { }, "\n ") } +// errHelpFunc is standard error text that prompts users to +// run `$subcmd --help` for information on how to use serve. +var errHelpFunc = func(m serveMode) error { + return fmt.Errorf("try `tailscale %s --help` for usage info", infoMap[m].Name) +} + // newServeV2Command returns a new "serve" subcommand using e as its environment. func newServeV2Command(e *serveEnv, subcmd serveMode) *ffcli.Command { if subcmd != serve && subcmd != funnel { @@ -158,19 +164,19 @@ func validateArgs(subcmd serveMode, args []string) error { fmt.Fprintf(os.Stderr, "\t- %s\n", translation) } fmt.Fprint(os.Stderr, "\nPlease see https://tailscale.com/kb/1242/tailscale-serve for more information.\n") - return errHelp + return errHelpFunc(subcmd) } if len(args) == 0 { return flag.ErrHelp } if len(args) > 2 { fmt.Fprintf(os.Stderr, "Error: invalid number of arguments (%d)\n", len(args)) - return errHelp + return errHelpFunc(subcmd) } turnOff := args[len(args)-1] == "off" if len(args) == 2 && !turnOff { fmt.Fprintln(os.Stderr, "Error: invalid argument format") - return errHelp + return errHelpFunc(subcmd) } // Given the two checks above, we can assume there @@ -225,7 +231,7 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc { srvType, srvPort, err := srvTypeAndPortFromFlags(e) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n\n", err) - return errHelp + return errHelpFunc(subcmd) } sc, err := e.lc.GetServeConfig(ctx) @@ -295,7 +301,7 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc { } if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n\n", err) - return errHelp + return errHelpFunc(subcmd) } if err := e.lc.SetServeConfig(ctx, parentSC); err != nil { diff --git a/cmd/tailscale/cli/serve_v2_test.go b/cmd/tailscale/cli/serve_v2_test.go index 6abba1601..7b9ae4d32 100644 --- a/cmd/tailscale/cli/serve_v2_test.go +++ b/cmd/tailscale/cli/serve_v2_test.go @@ -370,15 +370,15 @@ func TestServeDevConfigMutations(t *testing.T) { add(step{reset: true}) add(step{ // !somehost, must be localhost or 127.0.0.1 command: cmd("serve --tls-terminated-tcp=443 --bg tcp://somehost:5432"), - wantErr: exactErr(errHelp, "errHelp"), + wantErr: exactErrMsg(errHelp), }) add(step{ // bad target port, too low command: cmd("serve --tls-terminated-tcp=443 --bg tcp://somehost:0"), - wantErr: exactErr(errHelp, "errHelp"), + wantErr: exactErrMsg(errHelp), }) add(step{ // bad target port, too high command: cmd("serve --tls-terminated-tcp=443 --bg tcp://somehost:65536"), - wantErr: exactErr(errHelp, "errHelp"), + wantErr: exactErrMsg(errHelp), }) add(step{ // support shorthand command: cmd("serve --tls-terminated-tcp=443 --bg 5432"), @@ -508,7 +508,7 @@ func TestServeDevConfigMutations(t *testing.T) { }) add(step{ // bad path command: cmd("serve --https=443 --bg bad/path"), - wantErr: exactErr(errHelp, "errHelp"), + wantErr: exactErrMsg(errHelp), }) add(step{reset: true}) add(step{ @@ -1283,3 +1283,14 @@ func TestIsLegacyInvocation(t *testing.T) { }) } } + +// exactErrMsg returns an error checker that wants exactly the provided want error. +// If optName is non-empty, it's used in the error message. +func exactErrMsg(want error) func(error) string { + return func(got error) string { + if got.Error() == want.Error() { + return "" + } + return fmt.Sprintf("got error %v, want %v", got, want) + } +}