diff --git a/commands/command.go b/commands/command.go index 23d399858..88d1fa3c7 100644 --- a/commands/command.go +++ b/commands/command.go @@ -34,6 +34,10 @@ type Command struct { fmtCols []string childCommands []*Command + + // overrideNS specifies a namespace to use in config. + // Set using the overrideCmdNS cmdOption when calling CmdBuilder + overrideNS string } // AddCommand adds child commands and adds child commands for cobra as well. @@ -72,19 +76,6 @@ func cmdBuilderWithInit(parent *Command, cr CmdRunner, cliText, shortdesc string Use: cliText, Short: shortdesc, Long: longdesc, - Run: func(cmd *cobra.Command, args []string) { - c, err := NewCmdConfig( - cmdNS(cmd), - &doctl.LiveConfig{}, - out, - args, - initCmd, - ) - checkErr(err) - - err = cr(c) - checkErr(err) - }, } c := &Command{Command: cc} @@ -97,6 +88,22 @@ func cmdBuilderWithInit(parent *Command, cr CmdRunner, cliText, shortdesc string co(c) } + // This must be defined after the options have been applied + // so that changes made by the options are accessible here. + c.Command.Run = func(cmd *cobra.Command, args []string) { + c, err := NewCmdConfig( + cmdNS(c), + &doctl.LiveConfig{}, + out, + args, + initCmd, + ) + checkErr(err) + + err = cr(c) + checkErr(err) + } + if cols := c.fmtCols; cols != nil { formatHelp := fmt.Sprintf("Columns for output in a comma-separated list. Possible values: `%s`.", strings.Join(cols, "`"+", "+"`")) diff --git a/commands/command_option.go b/commands/command_option.go index fca46dd8d..66ba75698 100644 --- a/commands/command_option.go +++ b/commands/command_option.go @@ -42,3 +42,13 @@ func hiddenCmd() cmdOption { c.Hidden = true } } + +// overrideCmdNS specifies a namespace to use in config overriding the +// normal usage of the parent command's name. This is useful in cases +// where deeply nested subcommands have conflicting names. See uptime_alerts.go +// for example usage. +func overrideCmdNS(ns string) cmdOption { + return func(c *Command) { + c.overrideNS = ns + } +} diff --git a/commands/doit.go b/commands/doit.go index fdda3360a..4a2d3ef01 100644 --- a/commands/doit.go +++ b/commands/doit.go @@ -326,16 +326,25 @@ func AddDurationFlag(cmd *Command, name, shorthand string, def time.Duration, de func flagName(cmd *Command, name string) string { if cmd.Parent() != nil { - return fmt.Sprintf("%s.%s.%s", cmd.Parent().Name(), cmd.Name(), name) + p := cmd.Parent().Name() + if cmd.overrideNS != "" { + p = cmd.overrideNS + } + return fmt.Sprintf("%s.%s.%s", p, cmd.Name(), name) } + return fmt.Sprintf("%s.%s", cmd.Name(), name) } -func cmdNS(cmd *cobra.Command) string { +func cmdNS(cmd *Command) string { if cmd.Parent() != nil { + if cmd.overrideNS != "" { + return fmt.Sprintf("%s.%s", cmd.overrideNS, cmd.Name()) + } + return fmt.Sprintf("%s.%s", cmd.Parent().Name(), cmd.Name()) } - return fmt.Sprintf("%s", cmd.Name()) + return cmd.Name() } func isTerminal(f *os.File) bool { diff --git a/commands/doit_test.go b/commands/doit_test.go new file mode 100644 index 000000000..5f5e04bd7 --- /dev/null +++ b/commands/doit_test.go @@ -0,0 +1,81 @@ +package commands + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func TestFlagName(t *testing.T) { + var flag = "thing" + testFn := func(c *CmdConfig) error { + return nil + } + parent := &Command{ + Command: &cobra.Command{ + Use: "doit", + Short: "Do the thing", + }, + } + + tests := []struct { + name string + cmd *Command + expected string + }{ + { + name: "default", + cmd: CmdBuilder(parent, testFn, "run", "Run it", "", Writer), + expected: "doit.run.thing", + }, + { + name: "overrideCmdNS", + cmd: CmdBuilder(parent, testFn, "run", "Run it", "", Writer, overrideCmdNS("doctl")), + expected: "doctl.run.thing", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + AddStringFlag(tt.cmd, flag, "", "", "the thing") + + assert.Equal(t, tt.expected, flagName(tt.cmd, flag)) + }) + } +} + +func TestCmdNS(t *testing.T) { + testFn := func(c *CmdConfig) error { + return nil + } + parent := &Command{ + Command: &cobra.Command{ + Use: "doit", + Short: "Do the thing", + }, + } + + tests := []struct { + name string + cmd *Command + expected string + }{ + { + name: "default", + cmd: CmdBuilder(parent, testFn, "run", "Run it", "", Writer), + expected: "doit.run", + }, + { + name: "overrideCmdNS", + cmd: CmdBuilder(parent, testFn, "run", "Run it", "", Writer, overrideCmdNS("doctl")), + expected: "doctl.run", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, cmdNS(tt.cmd)) + }) + } +} diff --git a/commands/monitoring.go b/commands/monitoring.go index e520fa23d..56a2195d0 100644 --- a/commands/monitoring.go +++ b/commands/monitoring.go @@ -110,6 +110,7 @@ func RunCmdAlertPolicyCreate(c *CmdConfig) error { if err != nil { return err } + err = validateAlertPolicyType(alertType) if err != nil { return err diff --git a/commands/uptime_alerts.go b/commands/uptime_alerts.go index 463ad5c17..0d156c171 100644 --- a/commands/uptime_alerts.go +++ b/commands/uptime_alerts.go @@ -42,7 +42,8 @@ and alert you when they're slow, unavailable, or SSL certificates are expiring.` cmdUptimeAlertsCreate := CmdBuilder(cmd, RunUptimeAlertsCreate, "create ", "Create an uptime alert", `Use this command to create an uptime alert on your account. You can use flags to specify the uptime alert, type, threshold, comparison, notifications, and period.`, Writer, - aliasOpt("c"), displayerType(&displayers.UptimeAlert{})) + aliasOpt("c"), displayerType(&displayers.UptimeAlert{}), overrideCmdNS("uptime-alert")) + AddStringFlag(cmdUptimeAlertsCreate, doctl.ArgUptimeAlertName, "", "", "Uptime alert name", requiredOpt()) AddStringFlag(cmdUptimeAlertsCreate, doctl.ArgUptimeAlertType, "", "", "Uptime alert type, must be one of latency, down, down_global or ssl_expiry", requiredOpt()) AddIntFlag(cmdUptimeAlertsCreate, doctl.ArgUptimeAlertThreshold, "", 0, "Uptime alert threshold at which the alert will enter a trigger state. The specific threshold is dependent on the alert type.") @@ -61,7 +62,7 @@ You can use flags to specify the uptime alert, type, threshold, comparison, noti cmdUptimeAlertsUpdate := CmdBuilder(cmd, RunUptimeAlertsUpdate, "update ", "Update an uptime alert", `Use this command to update an uptime alert on your account. You can use flags to specify the uptime alert, type, threshold, comparison, notifications, and period.`, Writer, - aliasOpt("u"), displayerType(&displayers.UptimeAlert{})) + aliasOpt("u"), displayerType(&displayers.UptimeAlert{}), overrideCmdNS("uptime-alert")) AddStringFlag(cmdUptimeAlertsUpdate, doctl.ArgUptimeAlertName, "", "", "Uptime alert name", requiredOpt()) AddStringFlag(cmdUptimeAlertsUpdate, doctl.ArgUptimeAlertType, "", "", "Uptime alert type, must be one of latency, down, down_global or ssl_expiry", requiredOpt()) AddIntFlag(cmdUptimeAlertsUpdate, doctl.ArgUptimeAlertThreshold, "", 0, "Uptime alert threshold at which the alert will enter a trigger state. The specific threshold is dependent on the alert type.")