mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-22 12:58:37 +00:00
cmd/tailscale/cli: fix "subcommand required" errors when typod
Fixes #11672 Signed-off-by: Paul Scott <paul@tailscale.com>
This commit is contained in:
parent
3ff3445e9d
commit
d07ede461a
@ -99,15 +99,32 @@ func Run(args []string) (err error) {
|
|||||||
if errors.Is(err, flag.ErrHelp) {
|
if errors.Is(err, flag.ErrHelp) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if noexec := (ffcli.NoExecError{}); errors.As(err, &noexec) {
|
||||||
|
// When the user enters an unknown subcommand, ffcli tries to run
|
||||||
|
// the closest valid parent subcommand with everything else as args,
|
||||||
|
// returning NoExecError if it doesn't have an Exec function.
|
||||||
|
cmd := noexec.Command
|
||||||
|
args := cmd.FlagSet.Args()
|
||||||
|
if len(cmd.Subcommands) > 0 {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return fmt.Errorf("%s: unknown subcommand: %s", fullCmd(rootCmd, cmd), args[0])
|
||||||
|
}
|
||||||
|
subs := make([]string, 0, len(cmd.Subcommands))
|
||||||
|
for _, sub := range cmd.Subcommands {
|
||||||
|
subs = append(subs, sub.Name)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s: missing subcommand: %s", fullCmd(rootCmd, cmd), strings.Join(subs, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if envknob.Bool("TS_DUMP_HELP") {
|
if envknob.Bool("TS_DUMP_HELP") {
|
||||||
walkCommands(rootCmd, func(w cmdWalk) bool {
|
walkCommands(rootCmd, func(w cmdWalk) bool {
|
||||||
fmt.Println("===")
|
fmt.Println("===")
|
||||||
c := w.cmd
|
|
||||||
// UsageFuncs are typically called during Command.Run which ensures
|
// UsageFuncs are typically called during Command.Run which ensures
|
||||||
// FlagSet is not nil.
|
// FlagSet is not nil.
|
||||||
|
c := w.Command
|
||||||
if c.FlagSet == nil {
|
if c.FlagSet == nil {
|
||||||
c.FlagSet = flag.NewFlagSet(c.Name, flag.ContinueOnError)
|
c.FlagSet = flag.NewFlagSet(c.Name, flag.ContinueOnError)
|
||||||
}
|
}
|
||||||
@ -182,7 +199,12 @@ change in the future.
|
|||||||
driveCmd,
|
driveCmd,
|
||||||
},
|
},
|
||||||
FlagSet: rootfs,
|
FlagSet: rootfs,
|
||||||
Exec: func(context.Context, []string) error { return flag.ErrHelp },
|
Exec: func(ctx context.Context, args []string) error {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return fmt.Errorf("tailscale: unknown subcommand: %s", args[0])
|
||||||
|
}
|
||||||
|
return flag.ErrHelp
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if envknob.UseWIPCode() {
|
if envknob.UseWIPCode() {
|
||||||
rootCmd.Subcommands = append(rootCmd.Subcommands,
|
rootCmd.Subcommands = append(rootCmd.Subcommands,
|
||||||
@ -195,8 +217,8 @@ change in the future.
|
|||||||
}
|
}
|
||||||
|
|
||||||
walkCommands(rootCmd, func(w cmdWalk) bool {
|
walkCommands(rootCmd, func(w cmdWalk) bool {
|
||||||
if w.cmd.UsageFunc == nil {
|
if w.UsageFunc == nil {
|
||||||
w.cmd.UsageFunc = usageFunc
|
w.UsageFunc = usageFunc
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -220,10 +242,24 @@ var rootArgs struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type cmdWalk struct {
|
type cmdWalk struct {
|
||||||
cmd *ffcli.Command
|
*ffcli.Command
|
||||||
parents []*ffcli.Command
|
parents []*ffcli.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w cmdWalk) Path() string {
|
||||||
|
if len(w.parents) == 0 {
|
||||||
|
return w.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
for _, p := range w.parents {
|
||||||
|
sb.WriteString(p.Name)
|
||||||
|
sb.WriteString(" ")
|
||||||
|
}
|
||||||
|
sb.WriteString(w.Name)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
// walkCommands calls f for root and all of its nested subcommands until f
|
// walkCommands calls f for root and all of its nested subcommands until f
|
||||||
// returns false or all have been visited.
|
// returns false or all have been visited.
|
||||||
func walkCommands(root *ffcli.Command, f func(w cmdWalk) (more bool)) {
|
func walkCommands(root *ffcli.Command, f func(w cmdWalk) (more bool)) {
|
||||||
@ -243,6 +279,21 @@ func walkCommands(root *ffcli.Command, f func(w cmdWalk) (more bool)) {
|
|||||||
walk(root, nil, f)
|
walk(root, nil, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fullCmd returns the full "tailscale ... cmd" invocation for a subcommand.
|
||||||
|
func fullCmd(root, cmd *ffcli.Command) (full string) {
|
||||||
|
walkCommands(root, func(w cmdWalk) bool {
|
||||||
|
if w.Command == cmd {
|
||||||
|
full = w.Path()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if full == "" {
|
||||||
|
return cmd.Name
|
||||||
|
}
|
||||||
|
return full
|
||||||
|
}
|
||||||
|
|
||||||
// usageFuncNoDefaultValues is like usageFunc but doesn't print default values.
|
// usageFuncNoDefaultValues is like usageFunc but doesn't print default values.
|
||||||
func usageFuncNoDefaultValues(c *ffcli.Command) string {
|
func usageFuncNoDefaultValues(c *ffcli.Command) string {
|
||||||
return usageFuncOpt(c, false)
|
return usageFuncOpt(c, false)
|
||||||
|
@ -40,7 +40,7 @@ func TestShortUsage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
walkCommands(newRootCmd(), func(w cmdWalk) bool {
|
walkCommands(newRootCmd(), func(w cmdWalk) bool {
|
||||||
c, parents := w.cmd, w.parents
|
c, parents := w.Command, w.parents
|
||||||
|
|
||||||
// Words that we expect to be in the usage.
|
// Words that we expect to be in the usage.
|
||||||
words := make([]string, len(parents)+1)
|
words := make([]string, len(parents)+1)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"flag"
|
"flag"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -26,9 +25,6 @@ services on the host to use Tailscale in more ways.
|
|||||||
return fs
|
return fs
|
||||||
})(),
|
})(),
|
||||||
Subcommands: configureSubcommands(),
|
Subcommands: configureSubcommands(),
|
||||||
Exec: func(ctx context.Context, args []string) error {
|
|
||||||
return flag.ErrHelp
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureSubcommands() (out []*ffcli.Command) {
|
func configureSubcommands() (out []*ffcli.Command) {
|
||||||
|
@ -346,7 +346,7 @@ func outName(dst string) string {
|
|||||||
|
|
||||||
func runDebug(ctx context.Context, args []string) error {
|
func runDebug(ctx context.Context, args []string) error {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
return errors.New("unknown arguments")
|
return fmt.Errorf("tailscale debug: unknown subcommand: %s", args[0])
|
||||||
}
|
}
|
||||||
var usedFlag bool
|
var usedFlag bool
|
||||||
if out := debugArgs.cpuFile; out != "" {
|
if out := debugArgs.cpuFile; out != "" {
|
||||||
@ -401,7 +401,7 @@ func runDebug(ctx context.Context, args []string) error {
|
|||||||
// to subcommands.
|
// to subcommands.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("see 'tailscale debug --help")
|
return errors.New("tailscale debug: subcommand or flag required")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLocalCreds(ctx context.Context, args []string) error {
|
func runLocalCreds(ctx context.Context, args []string) error {
|
||||||
|
@ -5,7 +5,6 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -57,9 +56,6 @@ var driveCmd = &ffcli.Command{
|
|||||||
Exec: runDriveList,
|
Exec: runDriveList,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Exec: func(context.Context, []string) error {
|
|
||||||
return errors.New("drive subcommand required; run 'tailscale drive -h' for details")
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runDriveShare is the entry point for the "tailscale drive share" command.
|
// runDriveShare is the entry point for the "tailscale drive share" command.
|
||||||
|
@ -25,10 +25,6 @@ func exitNodeCmd() *ffcli.Command {
|
|||||||
Name: "exit-node",
|
Name: "exit-node",
|
||||||
ShortUsage: "tailscale exit-node [flags]",
|
ShortUsage: "tailscale exit-node [flags]",
|
||||||
ShortHelp: "Show machines on your tailnet configured as exit nodes",
|
ShortHelp: "Show machines on your tailnet configured as exit nodes",
|
||||||
LongHelp: "Show machines on your tailnet configured as exit nodes",
|
|
||||||
Exec: func(context.Context, []string) error {
|
|
||||||
return errors.New("exit-node subcommand required; run 'tailscale exit-node -h' for details")
|
|
||||||
},
|
|
||||||
Subcommands: append([]*ffcli.Command{
|
Subcommands: append([]*ffcli.Command{
|
||||||
{
|
{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
|
@ -44,12 +44,6 @@ var fileCmd = &ffcli.Command{
|
|||||||
fileCpCmd,
|
fileCpCmd,
|
||||||
fileGetCmd,
|
fileGetCmd,
|
||||||
},
|
},
|
||||||
Exec: func(context.Context, []string) error {
|
|
||||||
// TODO(bradfitz): is there a better ffcli way to
|
|
||||||
// annotate subcommand-required commands that don't
|
|
||||||
// have an exec body of their own?
|
|
||||||
return errors.New("file subcommand required; run 'tailscale file -h' for details")
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type countingReader struct {
|
type countingReader struct {
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
var netlockCmd = &ffcli.Command{
|
var netlockCmd = &ffcli.Command{
|
||||||
Name: "lock",
|
Name: "lock",
|
||||||
ShortUsage: "tailscale lock <sub-command> <arguments>",
|
ShortUsage: "tailscale lock <subcommand> [arguments...]",
|
||||||
ShortHelp: "Manage tailnet lock",
|
ShortHelp: "Manage tailnet lock",
|
||||||
LongHelp: "Manage tailnet lock",
|
LongHelp: "Manage tailnet lock",
|
||||||
Subcommands: []*ffcli.Command{
|
Subcommands: []*ffcli.Command{
|
||||||
@ -49,6 +49,9 @@ func runNetworkLockNoSubcommand(ctx context.Context, args []string) error {
|
|||||||
if len(args) >= 2 && args[0] == "tskey-wrap" {
|
if len(args) >= 2 && args[0] == "tskey-wrap" {
|
||||||
return runTskeyWrapCmd(ctx, args[1:])
|
return runTskeyWrapCmd(ctx, args[1:])
|
||||||
}
|
}
|
||||||
|
if len(args) > 0 {
|
||||||
|
return fmt.Errorf("tailscale lock: unknown subcommand: %s", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
return runNetworkLockStatus(ctx, args)
|
return runNetworkLockStatus(ctx, args)
|
||||||
}
|
}
|
||||||
@ -195,6 +198,10 @@ var nlStatusCmd = &ffcli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runNetworkLockStatus(ctx context.Context, args []string) error {
|
func runNetworkLockStatus(ctx context.Context, args []string) error {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return fmt.Errorf("tailscale lock status: unexpected argument")
|
||||||
|
}
|
||||||
|
|
||||||
st, err := localClient.NetworkLockStatus(ctx)
|
st, err := localClient.NetworkLockStatus(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fixTailscaledConnectError(err)
|
return fixTailscaledConnectError(err)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user