Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/tailscale/cli: fix typod subcommand error messages #11757

Merged
merged 6 commits into from
Apr 17, 2024

Conversation

icio
Copy link
Contributor

@icio icio commented Apr 16, 2024

Fixes #11672
Updates #11626

This PR changes the error when a user typos a subcommand or fails to provide one for the following commands:

  • tailscale
  • tailscale configure
  • tailscale debug
  • tailscale exit-node
  • tailscale drive
  • tailscale file
  • tailscale lock

The lesson about ffcli to take away from this is that the closest valid parent's Exec function gets run if the subcommand is not recognised. So those Exec: func(...) { if len(args) > 1 { return errors.New("unexpected arg") } } and Exec: func(...) { return errors.New("subcmd required") } were catching all of the typos.

❓ Do we want to default to printing --help when a subcommand is required buy omitted? Sometimes that help text can be quite long. I've opted for writing a short "tailscale file: subcommand required: cp, get"-style error message at the moment, but it's easy for us to change this.

There's a few other tidy-ups in here because I wanted the walkCommands change from #11336 so that we could generate better errors (which itself came with some unit test improvements and fixes). The commits are granular, but I can split up the PR further if needed!

Examples:

tsdiff() { diff -L "tailscale $@" -L "go run ./cmd/tailscale $@" --color=always -u <( tailscale "$@" 2>&1 ) <( go run ./cmd/tailscale "$@" 2>&1 ) && echo "(unchanged)"; }
$ tsdiff configure
--- tailscale configure
+++ go run ./cmd/tailscale configure
@@ -1,10 +1,2 @@
-[ALPHA] Configure the host to enable more Tailscale features
-
-USAGE
-  tailscale configure <subcommand>
-
-The 'configure' set of commands are intended to provide a way to enable different
-services on the host to use Tailscale in more ways.
-
-SUBCOMMANDS
-  kubeconfig  [ALPHA] Connect to a Kubernetes cluster using a Tailscale Auth Proxy
+tailscale configure: missing subcommand: kubeconfig
+exit status 1

$ tsdiff exit-node
--- tailscale exit-node
+++ go run ./cmd/tailscale exit-node
@@ -1 +1,2 @@
-exit-node subcommand required; run 'tailscale exit-node -h' for details
+tailscale exit-node: missing subcommand: list, suggest
+exit status 1

$ tsdiff exit-node ls
--- tailscale exit-node ls
+++ go run ./cmd/tailscale exit-node ls
@@ -1 +1,2 @@
-exit-node subcommand required; run 'tailscale exit-node -h' for details
+tailscale exit-node: unknown subcommand: ls
+exit status 1

$ tsdiff exit-node ls
--- tailscale exit-node ls
+++ go run ./cmd/tailscale exit-node ls
@@ -1 +1,2 @@
-exit-node subcommand required; run 'tailscale exit-node -h' for details
+tailscale exit-node: unknown subcommand: ls
+exit status 1

$ tsdiff debug
--- tailscale debug
+++ go run ./cmd/tailscale debug
@@ -1 +1,2 @@
-see 'tailscale debug --help
+tailscale debug: subcommand or flag required
+exit status 1

$ tsdiff debug adsf
--- tailscale debug adsf
+++ go run ./cmd/tailscale debug adsf
@@ -1 +1,2 @@
-unknown arguments
+tailscale debug: unknown subcommand: adsf
+exit status 1

$ tsdiff drive
--- tailscale drive
+++ go run ./cmd/tailscale drive
@@ -1 +1,2 @@
-drive subcommand required; run 'tailscale drive -h' for details
+tailscale drive: missing subcommand: share, rename, unshare, list
+exit status 1

$ tsdiff drive asdf
--- tailscale drive asdf
+++ go run ./cmd/tailscale drive asdf
@@ -1 +1,2 @@
-drive subcommand required; run 'tailscale drive -h' for details
+tailscale drive: unknown subcommand: asdf
+exit status 1

$ tsdiff file
--- tailscale file
+++ go run ./cmd/tailscale file
@@ -1 +1,2 @@
-file subcommand required; run 'tailscale file -h' for details
+tailscale file: missing subcommand: cp, get
+exit status 1

$ tsdiff file adsf
--- tailscale file adsf
+++ go run ./cmd/tailscale file adsf
@@ -1 +1,2 @@
-file subcommand required; run 'tailscale file -h' for details
+tailscale file: unknown subcommand: adsf
+exit status 1

$ tsdiff lock
--- tailscale lock
+++ go run ./cmd/tailscale lock
@@ -1,4 +1,4 @@
-Warning: client version "1.65.30-t8e07deb67" != tailscaled server version "1.63.73-t1535d0fec-g0f72471bf"
+Warning: client version "1.65.30-tefcc016d9" != tailscaled server version "1.63.73-t1535d0fec-g0f72471bf"
 Tailnet lock is NOT enabled.

 This node's tailnet-lock key: tlpub:1c6a52676a6e3e126ab6078ab683823a4c2fa0f9fb0ac8adb4ff835b53fa29d0

$ tsdiff lock asdf
--- tailscale lock asdf
+++ go run ./cmd/tailscale lock asdf
@@ -1,5 +1,2 @@
-Warning: client version "1.65.30-t8e07deb67" != tailscaled server version "1.63.73-t1535d0fec-g0f72471bf"
-Tailnet lock is NOT enabled.
-
-This node's tailnet-lock key: tlpub:1c6a52676a6e3e126ab6078ab683823a4c2fa0f9fb0ac8adb4ff835b53fa29d0
-
+tailscale lock: unknown subcommand: asdf
+exit status 1

$ tsdiff lock status
--- tailscale lock status
+++ go run ./cmd/tailscale lock status
@@ -1,4 +1,4 @@
-Warning: client version "1.65.30-t8e07deb67" != tailscaled server version "1.63.73-t1535d0fec-g0f72471bf"
+Warning: client version "1.65.30-tefcc016d9" != tailscaled server version "1.63.73-t1535d0fec-g0f72471bf"
 Tailnet lock is NOT enabled.

 This node's tailnet-lock key: tlpub:1c6a52676a6e3e126ab6078ab683823a4c2fa0f9fb0ac8adb4ff835b53fa29d0

$ tsdiff lock status asdf
--- tailscale lock status asdf
+++ go run ./cmd/tailscale lock status asdf
@@ -1,5 +1,2 @@
-Warning: client version "1.65.30-t8e07deb67" != tailscaled server version "1.63.73-t1535d0fec-g0f72471bf"
-Tailnet lock is NOT enabled.
-
-This node's tailnet-lock key: tlpub:1c6a52676a6e3e126ab6078ab683823a4c2fa0f9fb0ac8adb4ff835b53fa29d0
-
+tailscale lock status: unexpected argument
+exit status 1

Changes to --help:

--- help-main.txt	2024-04-16 14:51:45
+++ help-upd.txt	2024-04-16 15:20:20
@@ -585,21 +585,21 @@
   --cert-file string
     	output cert file or "-" for stdout; defaults to DOMAIN.crt if --cert-file and --key-file are both unset
   --key-file string
     	output key file or "-" for stdout; defaults to DOMAIN.key if --cert-file and --key-file are both unset
   --serve-demo, --serve-demo=false
     	if true, serve on port :443 using the cert as a demo, instead of writing out the files to disk (default false)
 ===
 Manage tailnet lock
 
 USAGE
-  tailscale lock <sub-command> <arguments>
+  tailscale lock <subcommand> [arguments...]
 
 Manage tailnet lock
 
 SUBCOMMANDS
   init             Initialize tailnet lock
   status           Outputs the state of tailnet lock
   add              Adds one or more trusted signing keys to tailnet lock
   remove           Removes one or more trusted signing keys from tailnet lock
   sign             Signs a node or pre-approved auth key
   disable          Consumes a disablement secret to shut down tailnet lock for the tailnet
@@ -754,22 +754,20 @@
 
 USAGE
   tailscale licenses
 
 Get open source license information
 ===
 Show machines on your tailnet configured as exit nodes
 
 USAGE
   tailscale exit-node [flags]
-
-Show machines on your tailnet configured as exit nodes
 
 SUBCOMMANDS
   list     Show exit nodes
   suggest  Suggests the best available exit node
 ===
 Show exit nodes
 
 USAGE
   tailscale exit-node list [flags]
 
@@ -994,22 +992,22 @@
 USAGE
   tailscale debug netmap
 
 FLAGS
   --show-private-key, --show-private-key=false
     	include node private key in printed netmap (default false)
 ===
 Convert between site-specific IPv4 CIDRs and IPv6 'via' routes
 
 USAGE
-  tailscale via <site-id> <v4-cidr>
-  tailscale via <v6-route>
+  tailscale debug via <site-id> <v4-cidr>
+  tailscale debug via <v6-route>
 ===
 Debug ts2021 protocol connectivity
 
 USAGE
   tailscale debug ts2021
 
 FLAGS
   --host string
     	hostname of control plane (default controlplane.tailscale.com)
   --verbose, --verbose=false
@@ -1077,23 +1075,23 @@
   tailscale debug dial-types <hostname-or-IP> <port>
 
 FLAGS
   --network string
     	network type to dial ("tcp", "udp", etc.) (default tcp)
 ===
 Share a directory with your tailnet
 
 USAGE
   tailscale drive share <name> <path>
-    tailscale drive rename <oldname> <newname>
-    tailscale drive unshare <name>
-    tailscale drive list
+  tailscale drive rename <oldname> <newname>
+  tailscale drive unshare <name>
+  tailscale drive list
 
 Taildrive allows you to share directories with other machines on your tailnet.
 
 In order to share folders, your node needs to have the node attribute "drive:share".
 
 In order to access shares, your node needs to have the node attribute "drive:access".
 
 For example, to enable sharing and accessing shares for all member nodes:
 
   "nodeAttrs": [
@@ -1171,34 +1169,34 @@
 
 You can remove shares by name, for example you could remove the above share by running:
 
   $ tailscale drive unshare newdocs
 
 You can get a list of currently published shares by running:
 
   $ tailscale drive list
 
 SUBCOMMANDS
-  share    [ALPHA] create or modify a share
-  rename   [ALPHA] rename a share
-  unshare  [ALPHA] remove a share
-  list     [ALPHA] list current shares
+  share    [ALPHA] Create or modify a share
+  rename   [ALPHA] Rename a share
+  unshare  [ALPHA] Remove a share
+  list     [ALPHA] List current shares
 ===
-[ALPHA] create or modify a share
+[ALPHA] Create or modify a share
 
 USAGE
   tailscale drive share <name> <path>
 ===
-[ALPHA] rename a share
+[ALPHA] Rename a share
 
 USAGE
   tailscale drive rename <oldname> <newname>
 ===
-[ALPHA] remove a share
+[ALPHA] Remove a share
 
 USAGE
   tailscale drive unshare <name>
 ===
-[ALPHA] list current shares
+[ALPHA] List current shares
 
 USAGE
   tailscale drive list

@icio icio requested a review from bradfitz April 16, 2024 13:53
@icio icio marked this pull request as ready for review April 16, 2024 14:24
@@ -216,11 +238,29 @@ var rootArgs struct {
socket string
}

func walkCommands(cmd *ffcli.Command, f func(*ffcli.Command)) {
f(cmd)
func walkCommands(cmd *ffcli.Command, parents []*ffcli.Command, f func(c *ffcli.Command, parents []*ffcli.Command)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the old signature was readable. this new signature probably warrants some docs on what's going on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make the recursive part be a closure func inside this func and keep the outer func exposed to callers not taking the parents second argument that is always nil?

And maybe the func can take a new struct type containing the command (embedded?) and parents, rather than taking two things, especially because the second thing is often ignored by callers?

optional

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rewritten this function to use iteration because it was upsetting me that I couldn't reference the recursive closure from its own declaration: https://go.dev/play/p/JZcwMW7ttD0

Copy link
Member

@bradfitz bradfitz Apr 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way to write self-referential func literals is https://go.dev/play/p/18WqmO49FTF for better or worse

cmd/tailscale/cli/cli.go Outdated Show resolved Hide resolved
cmd/tailscale/cli/cli.go Outdated Show resolved Hide resolved
Updates #cleanup

Signed-off-by: Paul Scott <paul@tailscale.com>
Updates #cleanup

Signed-off-by: Paul Scott <paul@tailscale.com>
Updates #cleanup

Signed-off-by: Paul Scott <paul@tailscale.com>
…rors

Updates #11364

Signed-off-by: Paul Scott <paul@tailscale.com>
Fixes #11672

Signed-off-by: Paul Scott <paul@tailscale.com>
Updates #11626

Signed-off-by: Paul Scott <paul@tailscale.com>
@icio icio merged commit 454a03a into main Apr 17, 2024
48 checks passed
@icio icio deleted the icio/precomplete-changes branch April 17, 2024 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cmd/tailscale: confusing "subcommand required" error message
2 participants