diff --git a/cmd/tailscale/cli/cli.go b/cmd/tailscale/cli/cli.go index fb63c318d..7cc4e2778 100644 --- a/cmd/tailscale/cli/cli.go +++ b/cmd/tailscale/cli/cli.go @@ -104,6 +104,26 @@ func newFlagSet(name string) *flag.FlagSet { return fs } +// CleanUpArgs rewrites command line arguments for simplicity and backwards compatibility. +// In particular, it rewrites --authkey to --auth-key. +func CleanUpArgs(args []string) []string { + out := make([]string, 0, len(args)) + for _, arg := range args { + // Rewrite --authkey to --auth-key, and --authkey=x to --auth-key=x, + // and the same for the -authkey variant. + switch { + case arg == "--authkey", arg == "-authkey": + arg = "--auth-key" + case strings.HasPrefix(arg, "--authkey="), strings.HasPrefix(arg, "-authkey="): + arg = strings.TrimLeft(arg, "-") + arg = strings.TrimPrefix(arg, "authkey=") + arg = "--auth-key=" + arg + } + out = append(out, arg) + } + return out +} + // Run runs the CLI. The args do not include the binary name. func Run(args []string) error { if len(args) == 1 && (args[0] == "-V" || args[0] == "--version") { diff --git a/cmd/tailscale/cli/cli_test.go b/cmd/tailscale/cli/cli_test.go index a7b2b5ce9..d87df2974 100644 --- a/cmd/tailscale/cli/cli_test.go +++ b/cmd/tailscale/cli/cli_test.go @@ -13,6 +13,7 @@ import ( "strings" "testing" + qt "github.com/frankban/quicktest" "github.com/google/go-cmp/cmp" "inet.af/netaddr" "tailscale.com/ipn" @@ -380,7 +381,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { Hostname: "foo", }, - want: accidentalUpPrefix + " --authkey=secretrand --force-reauth=false --reset --hostname=foo", + want: accidentalUpPrefix + " --auth-key=secretrand --force-reauth=false --reset --hostname=foo", }, { name: "error_exit_node_omit_with_ip_pref", @@ -487,7 +488,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) { } var upArgs upArgsT flagSet := newUpFlagSet(goos, &upArgs) - flagSet.Parse(tt.flags) + flags := CleanUpArgs(tt.flags) + flagSet.Parse(flags) newPrefs, err := prefsFromUpArgs(upArgs, t.Logf, new(ipnstate.Status), goos) if err != nil { t.Fatal(err) @@ -858,7 +860,8 @@ func TestUpdatePrefs(t *testing.T) { tt.env.goos = "linux" } tt.env.flagSet = newUpFlagSet(tt.env.goos, &tt.env.upArgs) - tt.env.flagSet.Parse(tt.flags) + flags := CleanUpArgs(tt.flags) + tt.env.flagSet.Parse(flags) newPrefs, err := prefsFromUpArgs(tt.env.upArgs, t.Logf, new(ipnstate.Status), tt.env.goos) if err != nil { @@ -1023,3 +1026,29 @@ func TestExitNodeIPOfArg(t *testing.T) { }) } } + +func TestCleanUpArgs(t *testing.T) { + c := qt.New(t) + tests := []struct { + in []string + want []string + }{ + {in: []string{"something"}, want: []string{"something"}}, + {in: []string{}, want: []string{}}, + {in: []string{"--authkey=0"}, want: []string{"--auth-key=0"}}, + {in: []string{"a", "--authkey=1", "b"}, want: []string{"a", "--auth-key=1", "b"}}, + {in: []string{"a", "--auth-key=2", "b"}, want: []string{"a", "--auth-key=2", "b"}}, + {in: []string{"a", "-authkey=3", "b"}, want: []string{"a", "--auth-key=3", "b"}}, + {in: []string{"a", "-auth-key=4", "b"}, want: []string{"a", "-auth-key=4", "b"}}, + {in: []string{"a", "--authkey", "5", "b"}, want: []string{"a", "--auth-key", "5", "b"}}, + {in: []string{"a", "-authkey", "6", "b"}, want: []string{"a", "--auth-key", "6", "b"}}, + {in: []string{"a", "authkey", "7", "b"}, want: []string{"a", "authkey", "7", "b"}}, + {in: []string{"--authkeyexpiry", "8"}, want: []string{"--authkeyexpiry", "8"}}, + {in: []string{"--auth-key-expiry", "9"}, want: []string{"--auth-key-expiry", "9"}}, + } + + for _, tt := range tests { + got := CleanUpArgs(tt.in) + c.Assert(got, qt.DeepEquals, tt.want) + } +} diff --git a/cmd/tailscale/cli/up.go b/cmd/tailscale/cli/up.go index c6e1296c9..5f7c543db 100644 --- a/cmd/tailscale/cli/up.go +++ b/cmd/tailscale/cli/up.go @@ -103,7 +103,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet { upf.BoolVar(&upArgs.runSSH, "ssh", false, "run an SSH server, permitting access per tailnet admin's declared policy") } upf.StringVar(&upArgs.advertiseTags, "advertise-tags", "", "comma-separated ACL tags to request; each must start with \"tag:\" (e.g. \"tag:eng,tag:montreal,tag:ssh\")") - upf.StringVar(&upArgs.authKeyOrFile, "authkey", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`) + upf.StringVar(&upArgs.authKeyOrFile, "auth-key", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`) upf.StringVar(&upArgs.hostname, "hostname", "", "hostname to use instead of the one provided by the OS") upf.StringVar(&upArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. \"10.0.0.0/8,192.168.0.0/24\") or empty string to not advertise routes") upf.BoolVar(&upArgs.advertiseDefaultRoute, "advertise-exit-node", false, "offer to be an exit node for internet traffic for the tailnet") @@ -743,7 +743,7 @@ func addPrefFlagMapping(flagName string, prefNames ...string) { // correspond to an ipn.Pref. func preflessFlag(flagName string) bool { switch flagName { - case "authkey", "force-reauth", "reset", "qr", "json": + case "auth-key", "force-reauth", "reset", "qr", "json": return true } return false diff --git a/cmd/tailscale/tailscale.go b/cmd/tailscale/tailscale.go index c69c86f64..f5b254fe9 100644 --- a/cmd/tailscale/tailscale.go +++ b/cmd/tailscale/tailscale.go @@ -17,6 +17,7 @@ import ( func main() { args := os.Args[1:] + args = cli.CleanUpArgs(args) if name, _ := os.Executable(); strings.HasSuffix(filepath.Base(name), ".cgi") { args = []string{"web", "-cgi"} }