From 82372a776ee459d82a5111094fa167837433d188 Mon Sep 17 00:00:00 2001 From: Lucas Polesello Date: Thu, 19 Feb 2026 17:17:39 -0300 Subject: [PATCH 1/2] feat(Formatters + ListDetails): add `instance list --details` and `-o json` for better automations --- Makefile | 4 ++ cmd/instance_config.go | 17 +++--- cmd/instance_get.go | 36 +++++++---- cmd/instance_list.go | 83 +++++++++++++++++++++++--- cmd/instance_nodes.go | 19 +++--- cmd/instance_plugins.go | 17 +++--- cmd/plans.go | 23 ++++--- cmd/regions.go | 16 +++-- cmd/root.go | 11 ++++ cmd/team_list.go | 17 +++--- cmd/vpc_get.go | 25 ++++++-- cmd/vpc_list.go | 19 +++--- internal/output/output.go | 122 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 327 insertions(+), 82 deletions(-) create mode 100644 internal/output/output.go diff --git a/Makefile b/Makefile index 5503b0c..24c0a7c 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,10 @@ GO_LDFLAGS=-X cloudamqp-cli/cmd.Version=$(VERSION) \ -X cloudamqp-cli/cmd.BuildDate=$(BUILD_DATE) \ -X cloudamqp-cli/cmd.GitCommit=$(GIT_COMMIT) + +bin/cloudamqp: + $(MAKE) build BINARY_NAME="bin/cloudamqp" + # Default target .PHONY: all all: build diff --git a/cmd/instance_config.go b/cmd/instance_config.go index 6cb4836..9070c1a 100644 --- a/cmd/instance_config.go +++ b/cmd/instance_config.go @@ -50,18 +50,17 @@ var instanceConfigListCmd = &cobra.Command{ return nil } - // Print table header - fmt.Printf("%-40s %-30s\n", "KEY", "VALUE") - fmt.Printf("%-40s %-30s\n", "---", "-----") + p, err := getPrinter(cmd) + if err != nil { + return err + } - // Print configuration data + headers := []string{"KEY", "VALUE"} + rows := make([][]string, 0, len(config)) for key, value := range config { - valueStr := fmt.Sprintf("%v", value) - if len(valueStr) > 30 { - valueStr = valueStr[:27] + "..." - } - fmt.Printf("%-40s %-30s\n", key, valueStr) + rows = append(rows, []string{key, fmt.Sprintf("%v", value)}) } + p.PrintRecords(headers, rows) return nil }, diff --git a/cmd/instance_get.go b/cmd/instance_get.go index faf160f..86f141c 100644 --- a/cmd/instance_get.go +++ b/cmd/instance_get.go @@ -50,25 +50,35 @@ var instanceGetCmd = &cobra.Command{ return err } - // Format output as "Name = Value" - fmt.Printf("Name = %s\n", instance.Name) - fmt.Printf("Plan = %s\n", instance.Plan) - fmt.Printf("Region = %s\n", instance.Region) - fmt.Printf("Tags = %s\n", strings.Join(instance.Tags, ",")) - - showURL, _ := cmd.Flags().GetBool("show-url") - if showURL { - fmt.Printf("URL = %s\n", instance.URL) - } else { - fmt.Printf("URL = %s\n", maskPassword(instance.URL)) + p, err := getPrinter(cmd) + if err != nil { + return err } - fmt.Printf("Hostname = %s\n", instance.HostnameExternal) + showURL, _ := cmd.Flags().GetBool("show-url") ready := "No" if instance.Ready { ready = "Yes" } - fmt.Printf("Ready = %s\n", ready) + + urlVal := maskPassword(instance.URL) + if showURL { + urlVal = instance.URL + } + + p.PrintRecord( + []string{"ID", "NAME", "PLAN", "REGION", "TAGS", "URL", "HOSTNAME", "READY"}, + []string{ + strconv.Itoa(instance.ID), + instance.Name, + instance.Plan, + instance.Region, + strings.Join(instance.Tags, ","), + urlVal, + instance.HostnameExternal, + ready, + }, + ) return nil }, diff --git a/cmd/instance_list.go b/cmd/instance_list.go index 998ad96..2dba881 100644 --- a/cmd/instance_list.go +++ b/cmd/instance_list.go @@ -2,11 +2,11 @@ package cmd import ( "fmt" - "os" "strconv" + "strings" + "sync" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -35,18 +35,85 @@ var instanceListCmd = &cobra.Command{ return nil } - // Create table and populate data - t := table.New(os.Stdout, "ID", "NAME", "PLAN", "REGION") - for _, instance := range instances { - t.AddRow( + p, err := getPrinter(cmd) + if err != nil { + return err + } + + details, _ := cmd.Flags().GetBool("details") + + if details { + showURL, _ := cmd.Flags().GetBool("show-url") + detailed := make([]*client.Instance, len(instances)) + headers := []string{"ID", "NAME", "PLAN", "REGION", "TAGS", "URL", "HOSTNAME", "READY"} + rows := make([][]string, len(instances)) + var ( + mu sync.Mutex + firstErr error + wg sync.WaitGroup + ) + for i, instance := range instances { + wg.Add(1) + go func(idx, id int) { + defer wg.Done() + det, err := c.GetInstance(id) + mu.Lock() + defer mu.Unlock() + if err != nil { + if firstErr == nil { + firstErr = fmt.Errorf("error fetching instance %d: %w", id, err) + } + return + } + detailed[idx] = det + }(i, instance.ID) + } + wg.Wait() + if firstErr != nil { + return firstErr + } + + for i, inst := range detailed { + ready := "No" + if inst.Ready { + ready = "Yes" + } + urlVal := maskPassword(inst.URL) + if showURL { + urlVal = inst.URL + } + rows[i] = []string{ + strconv.Itoa(inst.ID), + inst.Name, + inst.Plan, + inst.Region, + strings.Join(inst.Tags, ","), + urlVal, + inst.HostnameExternal, + ready, + } + } + p.PrintRecords(headers, rows) + return nil + } + + headers := []string{"ID", "NAME", "PLAN", "REGION"} + rows := make([][]string, len(instances)) + for i, instance := range instances { + rows[i] = []string{ strconv.Itoa(instance.ID), instance.Name, instance.Plan, instance.Region, - ) + } } - t.Print() + p.PrintRecords(headers, rows) return nil }, } + +func init() { + instanceListCmd.Flags().BoolP("details", "", false, "Fetch full details for each instance (one GET request per instance)") + instanceListCmd.Flags().BoolP("show-url", "", false, "Show full connection URL with credentials (requires --details)") +} diff --git a/cmd/instance_nodes.go b/cmd/instance_nodes.go index 6d90878..5e7086c 100644 --- a/cmd/instance_nodes.go +++ b/cmd/instance_nodes.go @@ -2,10 +2,8 @@ package cmd import ( "fmt" - "os" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -50,9 +48,14 @@ var instanceNodesListCmd = &cobra.Command{ return nil } - // Create table and populate data - t := table.New(os.Stdout, "NAME", "CONFIGURED", "RUNNING", "DISK_SIZE", "RABBITMQ_VERSION") - for _, node := range nodes { + p, err := getPrinter(cmd) + if err != nil { + return err + } + + headers := []string{"NAME", "CONFIGURED", "RUNNING", "DISK_SIZE", "RABBITMQ_VERSION"} + rows := make([][]string, len(nodes)) + for i, node := range nodes { configured := "No" if node.Configured { configured = "Yes" @@ -62,15 +65,15 @@ var instanceNodesListCmd = &cobra.Command{ running = "Yes" } totalDisk := node.DiskSize + node.AdditionalDiskSize - t.AddRow( + rows[i] = []string{ node.Name, configured, running, fmt.Sprintf("%d GB", totalDisk), node.RabbitMQVersion, - ) + } } - t.Print() + p.PrintRecords(headers, rows) return nil }, diff --git a/cmd/instance_plugins.go b/cmd/instance_plugins.go index b0360f4..6ee91cd 100644 --- a/cmd/instance_plugins.go +++ b/cmd/instance_plugins.go @@ -2,10 +2,8 @@ package cmd import ( "fmt" - "os" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -50,16 +48,21 @@ var instancePluginsListCmd = &cobra.Command{ return nil } - // Create table and populate data - t := table.New(os.Stdout, "NAME", "ENABLED") - for _, plugin := range plugins { + p, err := getPrinter(cmd) + if err != nil { + return err + } + + headers := []string{"NAME", "ENABLED"} + rows := make([][]string, len(plugins)) + for i, plugin := range plugins { enabled := "No" if plugin.Enabled { enabled = "Yes" } - t.AddRow(plugin.Name, enabled) + rows[i] = []string{plugin.Name, enabled} } - t.Print() + p.PrintRecords(headers, rows) return nil }, diff --git a/cmd/plans.go b/cmd/plans.go index 4d760b2..e80b806 100644 --- a/cmd/plans.go +++ b/cmd/plans.go @@ -2,10 +2,8 @@ package cmd import ( "fmt" - "os" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -37,9 +35,14 @@ var plansCmd = &cobra.Command{ return nil } - // Create table and populate data - t := table.New(os.Stdout, "NAME", "PRICE", "BACKEND", "SHARED") - for _, plan := range plans { + p, err := getPrinter(cmd) + if err != nil { + return err + } + + headers := []string{"NAME", "PRICE", "BACKEND", "SHARED"} + rows := make([][]string, len(plans)) + for i, plan := range plans { shared := "No" if plan.Shared { shared = "Yes" @@ -48,14 +51,10 @@ var plansCmd = &cobra.Command{ if plan.Price == 0 { price = "Free" } - t.AddRow( - plan.Name, - price, - plan.Backend, - shared, - ) + rows[i] = []string{plan.Name, price, plan.Backend, shared} } - t.Print() + p.PrintRecords(headers, rows) + return nil }, } diff --git a/cmd/regions.go b/cmd/regions.go index 4d25438..87f0d18 100644 --- a/cmd/regions.go +++ b/cmd/regions.go @@ -2,10 +2,8 @@ package cmd import ( "fmt" - "os" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -37,11 +35,17 @@ var regionsCmd = &cobra.Command{ return nil } - t := table.New(os.Stdout, "PROVIDER", "REGION", "NAME") - for _, region := range regions { - t.AddRow(region.Provider, region.Region, region.Name) + p, err := getPrinter(cmd) + if err != nil { + return err + } + + headers := []string{"PROVIDER", "REGION", "NAME"} + rows := make([][]string, len(regions)) + for i, region := range regions { + rows[i] = []string{region.Provider, region.Region, region.Name} } - t.Print() + p.PrintRecords(headers, rows) return nil }, diff --git a/cmd/root.go b/cmd/root.go index e4fe996..a3bc6b3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,11 +2,19 @@ package cmd import ( "fmt" + "os" "strings" + "cloudamqp-cli/internal/output" "github.com/spf13/cobra" ) +func getPrinter(cmd *cobra.Command) (*output.Printer, error) { + format, _ := cmd.Flags().GetString("output") + fields, _ := cmd.Flags().GetStringSlice("fields") + return output.New(os.Stdout, output.Format(format), fields) +} + var apiKey string func getVersionString() string { @@ -42,6 +50,9 @@ func init() { // Set custom version template to match gh style rootCmd.SetVersionTemplate("cloudamqp version {{.Version}}\n") + rootCmd.PersistentFlags().StringP("output", "o", "table", "Output format: table or json") + rootCmd.PersistentFlags().StringSlice("fields", nil, "Fields to include in output (comma-separated)") + rootCmd.AddCommand(instanceCmd) rootCmd.AddCommand(vpcCmd) rootCmd.AddCommand(regionsCmd) diff --git a/cmd/team_list.go b/cmd/team_list.go index 56a112e..565fcf8 100644 --- a/cmd/team_list.go +++ b/cmd/team_list.go @@ -2,11 +2,9 @@ package cmd import ( "fmt" - "os" "strings" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -35,9 +33,14 @@ var teamListCmd = &cobra.Command{ return nil } - // Create table and populate data - t := table.New(os.Stdout, "EMAIL", "ROLES", "2FA") - for _, member := range members { + p, err := getPrinter(cmd) + if err != nil { + return err + } + + headers := []string{"EMAIL", "ROLES", "2FA"} + rows := make([][]string, len(members)) + for i, member := range members { roles := strings.Join(member.Roles, ", ") if roles == "" { roles = "-" @@ -46,9 +49,9 @@ var teamListCmd = &cobra.Command{ if member.TFAAuthEnabled { tfa = "Yes" } - t.AddRow(member.Email, roles, tfa) + rows[i] = []string{member.Email, roles, tfa} } - t.Print() + p.PrintRecords(headers, rows) return nil }, diff --git a/cmd/vpc_get.go b/cmd/vpc_get.go index e0f8f29..b4ec0f2 100644 --- a/cmd/vpc_get.go +++ b/cmd/vpc_get.go @@ -1,9 +1,9 @@ package cmd import ( - "encoding/json" "fmt" "strconv" + "strings" "cloudamqp-cli/client" "github.com/spf13/cobra" @@ -39,12 +39,29 @@ var vpcGetCmd = &cobra.Command{ return err } - output, err := json.MarshalIndent(vpc, "", " ") + p, err := getPrinter(cmd) if err != nil { - return fmt.Errorf("failed to format response: %v", err) + return err + } + + instanceIDs := make([]string, len(vpc.Instances)) + for i, id := range vpc.Instances { + instanceIDs[i] = strconv.Itoa(id) } - fmt.Printf("VPC details:\n%s\n", string(output)) + p.PrintRecord( + []string{"ID", "NAME", "REGION", "SUBNET", "PLAN", "TAGS", "INSTANCES"}, + []string{ + strconv.Itoa(vpc.ID), + vpc.Name, + vpc.Region, + vpc.Subnet, + vpc.Plan, + strings.Join(vpc.Tags, ","), + strings.Join(instanceIDs, ","), + }, + ) + return nil }, } diff --git a/cmd/vpc_list.go b/cmd/vpc_list.go index fa5b2c9..1c2a312 100644 --- a/cmd/vpc_list.go +++ b/cmd/vpc_list.go @@ -2,11 +2,9 @@ package cmd import ( "fmt" - "os" "strconv" "cloudamqp-cli/client" - "cloudamqp-cli/internal/table" "github.com/spf13/cobra" ) @@ -35,17 +33,22 @@ var vpcListCmd = &cobra.Command{ return nil } - // Create table and populate data - t := table.New(os.Stdout, "ID", "NAME", "SUBNET", "REGION") - for _, vpc := range vpcs { - t.AddRow( + p, err := getPrinter(cmd) + if err != nil { + return err + } + + headers := []string{"ID", "NAME", "SUBNET", "REGION"} + rows := make([][]string, len(vpcs)) + for i, vpc := range vpcs { + rows[i] = []string{ strconv.Itoa(vpc.ID), vpc.Name, vpc.Subnet, vpc.Region, - ) + } } - t.Print() + p.PrintRecords(headers, rows) return nil }, diff --git a/internal/output/output.go b/internal/output/output.go new file mode 100644 index 0000000..cac2878 --- /dev/null +++ b/internal/output/output.go @@ -0,0 +1,122 @@ +package output + +import ( + "encoding/json" + "fmt" + "io" + "strings" + + "cloudamqp-cli/internal/table" +) + +type Format string + +const ( + FormatTable Format = "table" + FormatJSON Format = "json" +) + +type Printer struct { + format Format + fields []string + writer io.Writer +} + +func New(writer io.Writer, format Format, fields []string) (*Printer, error) { + switch format { + case FormatTable, FormatJSON, "": + if format == "" { + format = FormatTable + } + default: + return nil, fmt.Errorf("unknown output format %q: use \"table\" or \"json\"", format) + } + return &Printer{format: format, fields: fields, writer: writer}, nil +} + +func (p *Printer) filterColumns(headers []string, rows [][]string) ([]string, [][]string) { + if len(p.fields) == 0 { + return headers, rows + } + + var filteredHeaders []string + var indices []int + filteredRows := make([][]string, len(rows)) + fieldSet := make(map[string]bool, len(p.fields)) + for _, f := range p.fields { + fieldSet[strings.ToUpper(strings.TrimSpace(f))] = true + } + + for i, h := range headers { + if fieldSet[strings.ToUpper(h)] { + filteredHeaders = append(filteredHeaders, h) + indices = append(indices, i) + } + } + + for i, row := range rows { + filteredRow := make([]string, len(indices)) + for j, idx := range indices { + if idx < len(row) { + filteredRow[j] = row[idx] + } + } + filteredRows[i] = filteredRow + } + + return filteredHeaders, filteredRows +} + +func (p *Printer) PrintRecords(headers []string, rows [][]string) { + headers, rows = p.filterColumns(headers, rows) + + switch p.format { + case FormatJSON: + records := make([]map[string]string, len(rows)) + for i, row := range rows { + record := make(map[string]string, len(headers)) + for j, h := range headers { + if j < len(row) { + record[strings.ToLower(h)] = row[j] + } + } + records[i] = record + } + data, _ := json.MarshalIndent(records, "", " ") + fmt.Fprintln(p.writer, string(data)) + default: + t := table.New(p.writer, headers...) + for _, row := range rows { + t.AddRow(row...) + } + t.Print() + } +} + +func (p *Printer) PrintRecord(headers []string, values []string) { + headers, rows := p.filterColumns(headers, [][]string{values}) + var row []string + if len(rows) > 0 { + row = rows[0] + } + + switch p.format { + case FormatJSON: + record := make(map[string]string, len(headers)) + for i, h := range headers { + if i < len(row) { + record[strings.ToLower(h)] = row[i] + } + } + data, _ := json.MarshalIndent(record, "", " ") + fmt.Fprintln(p.writer, string(data)) + default: + for i, h := range headers { + val := "" + if i < len(row) { + val = row[i] + } + fmt.Fprintf(p.writer, "%s = %s\n", h, val) + } + } +} From 6ac553698a12b099379e3b356bbd6552826983e3 Mon Sep 17 00:00:00 2001 From: Lucas Polesello Date: Thu, 19 Feb 2026 19:04:46 -0300 Subject: [PATCH 2/2] feat!(flags): removed `--id` from commands --- cmd/command_test.go | 38 ++++----- cmd/instance_account.go | 36 +++------ cmd/instance_actions.go | 171 ++++++++++++++++++---------------------- cmd/instance_config.go | 58 +++++--------- cmd/instance_delete.go | 23 ++---- cmd/instance_get.go | 16 ++-- cmd/instance_nodes.go | 32 +++----- cmd/instance_plugins.go | 54 +++++-------- cmd/instance_resize.go | 23 ++---- cmd/instance_update.go | 21 ++--- cmd/vpc_delete.go | 23 ++---- cmd/vpc_get.go | 16 ++-- cmd/vpc_update.go | 19 ++--- 13 files changed, 202 insertions(+), 328 deletions(-) diff --git a/cmd/command_test.go b/cmd/command_test.go index ec2c00a..ad1a1f2 100644 --- a/cmd/command_test.go +++ b/cmd/command_test.go @@ -32,10 +32,10 @@ func TestInstanceCommand(t *testing.T) { assert.Contains(t, commandNames, "create") assert.Contains(t, commandNames, "list") - assert.Contains(t, commandNames, "get --id ") - assert.Contains(t, commandNames, "update --id ") - assert.Contains(t, commandNames, "delete --id ") - assert.Contains(t, commandNames, "resize-disk --id ") + assert.Contains(t, commandNames, "get ") + assert.Contains(t, commandNames, "update ") + assert.Contains(t, commandNames, "delete ") + assert.Contains(t, commandNames, "resize-disk ") } func TestVPCCommand(t *testing.T) { @@ -53,9 +53,9 @@ func TestVPCCommand(t *testing.T) { assert.Contains(t, commandNames, "create") assert.Contains(t, commandNames, "list") - assert.Contains(t, commandNames, "get --id ") - assert.Contains(t, commandNames, "update --id ") - assert.Contains(t, commandNames, "delete --id ") + assert.Contains(t, commandNames, "get ") + assert.Contains(t, commandNames, "update ") + assert.Contains(t, commandNames, "delete ") } func TestInstanceCreateCommand_Validation(t *testing.T) { @@ -221,18 +221,18 @@ func TestInstanceActionsCommand(t *testing.T) { } expectedActions := []string{ - "restart-rabbitmq --id ", - "restart-cluster --id ", - "restart-management --id ", - "stop --id ", - "start --id ", - "reboot --id ", - "stop-cluster --id ", - "start-cluster --id ", - "upgrade-erlang --id ", - "upgrade-rabbitmq --id ", - "upgrade-all --id ", - "upgrade-versions --id ", + "restart-rabbitmq ", + "restart-cluster ", + "restart-management ", + "stop ", + "start ", + "reboot ", + "stop-cluster ", + "start-cluster ", + "upgrade-erlang ", + "upgrade-rabbitmq ", + "upgrade-all ", + "upgrade-versions ", } for _, action := range expectedActions { diff --git a/cmd/instance_account.go b/cmd/instance_account.go index 6496db1..a0b7ed3 100644 --- a/cmd/instance_account.go +++ b/cmd/instance_account.go @@ -19,16 +19,12 @@ var instanceAccountCmd = &cobra.Command{ } var rotatePasswordCmd = &cobra.Command{ - Use: "rotate-password --id ", + Use: "rotate-password ", Short: "Rotate password", Long: `Initiate rotation of the user password on your instance.`, - Example: ` cloudamqp instance account rotate-password --id 1234`, + Example: ` cloudamqp instance account rotate-password 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -37,7 +33,7 @@ var rotatePasswordCmd = &cobra.Command{ c := client.New(apiKey, Version) - err = c.RotatePassword(idFlag) + err = c.RotatePassword(args[0]) if err != nil { fmt.Printf("Error rotating password: %v\n", err) return err @@ -49,16 +45,12 @@ var rotatePasswordCmd = &cobra.Command{ } var rotateInstanceAPIKeyCmd = &cobra.Command{ - Use: "rotate-apikey --id ", + Use: "rotate-apikey ", Short: "Rotate Instance API key", Long: `Rotate the Instance API key.`, - Example: ` cloudamqp instance account rotate-apikey --id 1234`, + Example: ` cloudamqp instance account rotate-apikey 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -67,26 +59,22 @@ var rotateInstanceAPIKeyCmd = &cobra.Command{ c := client.New(apiKey, Version) - err = c.RotateInstanceAPIKey(idFlag) + err = c.RotateInstanceAPIKey(args[0]) if err != nil { fmt.Printf("Error rotating instance API key: %v\n", err) return err } fmt.Println("Instance API key rotation initiated successfully.") - fmt.Printf("Warning: The local config for instance %s will need to be updated.\n", idFlag) - fmt.Printf("Run 'cloudamqp instance get --id %s' to retrieve and save the new API key.\n", idFlag) + fmt.Printf("Warning: The local config for instance %s will need to be updated.\n", args[0]) + fmt.Printf("Run 'cloudamqp instance get %s' to retrieve and save the new API key.\n", args[0]) return nil }, } func init() { - // Add --id flag to both account commands - rotatePasswordCmd.Flags().StringP("id", "", "", "Instance ID (required)") - rotatePasswordCmd.MarkFlagRequired("id") - - rotateInstanceAPIKeyCmd.Flags().StringP("id", "", "", "Instance ID (required)") - rotateInstanceAPIKeyCmd.MarkFlagRequired("id") + rotatePasswordCmd.ValidArgsFunction = completeInstances + rotateInstanceAPIKeyCmd.ValidArgsFunction = completeInstances instanceAccountCmd.AddCommand(rotatePasswordCmd) instanceAccountCmd.AddCommand(rotateInstanceAPIKeyCmd) diff --git a/cmd/instance_actions.go b/cmd/instance_actions.go index 0b7c0aa..018d2e8 100644 --- a/cmd/instance_actions.go +++ b/cmd/instance_actions.go @@ -22,161 +22,170 @@ var instanceActionsCmd = &cobra.Command{ // Restart commands var restartRabbitMQCmd = &cobra.Command{ - Use: "restart-rabbitmq --id ", + Use: "restart-rabbitmq ", Short: "Restart RabbitMQ", Long: `Restart RabbitMQ on specified nodes or all nodes.`, - Example: ` cloudamqp instance restart-rabbitmq --id 1234 - cloudamqp instance restart-rabbitmq --id 1234 --nodes=node1,node2`, + Example: ` cloudamqp instance restart-rabbitmq 1234 + cloudamqp instance restart-rabbitmq 1234 --nodes=node1,node2`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performNodeAction(cmd, "restart-rabbitmq") + return performNodeAction(cmd, args[0], "restart-rabbitmq") }, } var restartClusterCmd = &cobra.Command{ - Use: "restart-cluster --id ", + Use: "restart-cluster ", Short: "Restart cluster", Long: `Restart the entire cluster.`, - Example: ` cloudamqp instance restart-cluster --id 1234`, + Example: ` cloudamqp instance restart-cluster 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performClusterAction(cmd, "restart-cluster") + return performClusterAction(cmd, args[0], "restart-cluster") }, } var restartManagementCmd = &cobra.Command{ - Use: "restart-management --id ", + Use: "restart-management ", Short: "Restart management interface", Long: `Restart the RabbitMQ management interface.`, - Example: ` cloudamqp instance restart-management --id 1234`, + Example: ` cloudamqp instance restart-management 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performNodeAction(cmd, "restart-management") + return performNodeAction(cmd, args[0], "restart-management") }, } // Stop/Start commands var stopCmd = &cobra.Command{ - Use: "stop --id ", + Use: "stop ", Short: "Stop instance", Long: `Stop specified nodes or all nodes.`, - Example: ` cloudamqp instance stop --id 1234`, + Example: ` cloudamqp instance stop 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performNodeAction(cmd, "stop") + return performNodeAction(cmd, args[0], "stop") }, } var startCmd = &cobra.Command{ - Use: "start --id ", + Use: "start ", Short: "Start instance", Long: `Start specified nodes or all nodes.`, - Example: ` cloudamqp instance start --id 1234`, + Example: ` cloudamqp instance start 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performNodeAction(cmd, "start") + return performNodeAction(cmd, args[0], "start") }, } var rebootCmd = &cobra.Command{ - Use: "reboot --id ", + Use: "reboot ", Short: "Reboot instance", Long: `Reboot specified nodes or all nodes.`, - Example: ` cloudamqp instance reboot --id 1234`, + Example: ` cloudamqp instance reboot 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performNodeAction(cmd, "reboot") + return performNodeAction(cmd, args[0], "reboot") }, } // Cluster commands var stopClusterCmd = &cobra.Command{ - Use: "stop-cluster --id ", + Use: "stop-cluster ", Short: "Stop cluster", Long: `Stop the entire cluster.`, - Example: ` cloudamqp instance stop-cluster --id 1234`, + Example: ` cloudamqp instance stop-cluster 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performClusterAction(cmd, "stop-cluster") + return performClusterAction(cmd, args[0], "stop-cluster") }, } var startClusterCmd = &cobra.Command{ - Use: "start-cluster --id ", + Use: "start-cluster ", Short: "Start cluster", Long: `Start the entire cluster.`, - Example: ` cloudamqp instance start-cluster --id 1234`, + Example: ` cloudamqp instance start-cluster 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performClusterAction(cmd, "start-cluster") + return performClusterAction(cmd, args[0], "start-cluster") }, } // Upgrade commands var upgradeErlangCmd = &cobra.Command{ - Use: "upgrade-erlang --id ", + Use: "upgrade-erlang ", Short: "Upgrade Erlang", Long: `Always updates to latest compatible version. Note: This action is asynchronous. The request will return immediately, the process runs in the background.`, - Example: ` cloudamqp instance upgrade-erlang --id 1234`, + Example: ` cloudamqp instance upgrade-erlang 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performUpgradeAction(cmd, "upgrade-erlang", "") + return performUpgradeAction(cmd, args[0], "upgrade-erlang", "") }, } var upgradeRabbitMQCmd = &cobra.Command{ - Use: "upgrade-rabbitmq --id ", + Use: "upgrade-rabbitmq ", Short: "Upgrade RabbitMQ", Long: `Upgrade RabbitMQ to specified version. Note: This action is asynchronous. The request will return immediately, the process runs in the background.`, - Example: ` cloudamqp instance upgrade-rabbitmq --id 1234 --version=3.10.7`, + Example: ` cloudamqp instance upgrade-rabbitmq 1234 --version=3.10.7`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { version, _ := cmd.Flags().GetString("version") if version == "" { return fmt.Errorf("version flag is required") } - return performUpgradeAction(cmd, "upgrade-rabbitmq", version) + return performUpgradeAction(cmd, args[0], "upgrade-rabbitmq", version) }, } var upgradeRabbitMQErlangCmd = &cobra.Command{ - Use: "upgrade-all --id ", + Use: "upgrade-all ", Short: "Upgrade RabbitMQ and Erlang", Long: `Always updates to latest possible version of both RabbitMQ and Erlang. Note: This action is asynchronous. The request will return immediately, the process runs in the background.`, - Example: ` cloudamqp instance upgrade-all --id 1234`, + Example: ` cloudamqp instance upgrade-all 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performUpgradeAction(cmd, "upgrade-all", "") + return performUpgradeAction(cmd, args[0], "upgrade-all", "") }, } // HiPE and Firehose commands var toggleHiPECmd = &cobra.Command{ - Use: "toggle-hipe --id ", + Use: "toggle-hipe ", Short: "Enable/disable HiPE", Long: `Enable or disable HiPE (High Performance Erlang) compilation.`, - Example: ` cloudamqp instance toggle-hipe --id 1234 --enable=true`, + Example: ` cloudamqp instance toggle-hipe 1234 --enable=true`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performToggleAction(cmd, "hipe") + return performToggleAction(cmd, args[0], "hipe") }, } var toggleFirehoseCmd = &cobra.Command{ - Use: "toggle-firehose --id ", + Use: "toggle-firehose ", Short: "Enable/disable Firehose", Long: `Enable or disable RabbitMQ Firehose tracing (not recommended in production).`, - Example: ` cloudamqp instance toggle-firehose --id 1234 --enable=true --vhost=/`, + Example: ` cloudamqp instance toggle-firehose 1234 --enable=true --vhost=/`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return performToggleAction(cmd, "firehose") + return performToggleAction(cmd, args[0], "firehose") }, } var upgradeVersionsCmd = &cobra.Command{ - Use: "upgrade-versions --id ", + Use: "upgrade-versions ", Short: "Fetch upgrade versions", Long: `Returns what version of Erlang and RabbitMQ the cluster will update to.`, - Example: ` cloudamqp instance upgrade-versions --id 1234`, + Example: ` cloudamqp instance upgrade-versions 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -185,7 +194,7 @@ var upgradeVersionsCmd = &cobra.Command{ c := client.New(apiKey, Version) - versions, err := c.GetUpgradeVersions(idFlag) + versions, err := c.GetUpgradeVersions(args[0]) if err != nil { fmt.Printf("Error getting upgrade versions: %v\n", err) return err @@ -202,12 +211,7 @@ var upgradeVersionsCmd = &cobra.Command{ } // Helper functions -func performNodeAction(cmd *cobra.Command, action string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - +func performNodeAction(cmd *cobra.Command, instanceID, action string) error { var err error apiKey, err := getAPIKey() if err != nil { @@ -224,15 +228,15 @@ func performNodeAction(cmd *cobra.Command, action string) error { switch action { case "restart-rabbitmq": - err = c.RestartRabbitMQ(idFlag, nodes) + err = c.RestartRabbitMQ(instanceID, nodes) case "restart-management": - err = c.RestartManagement(idFlag, nodes) + err = c.RestartManagement(instanceID, nodes) case "stop": - err = c.StopInstance(idFlag, nodes) + err = c.StopInstance(instanceID, nodes) case "start": - err = c.StartInstance(idFlag, nodes) + err = c.StartInstance(instanceID, nodes) case "reboot": - err = c.RebootInstance(idFlag, nodes) + err = c.RebootInstance(instanceID, nodes) default: return fmt.Errorf("unknown action: %s", action) } @@ -246,12 +250,7 @@ func performNodeAction(cmd *cobra.Command, action string) error { return nil } -func performClusterAction(cmd *cobra.Command, action string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - +func performClusterAction(cmd *cobra.Command, instanceID, action string) error { var err error apiKey, err := getAPIKey() if err != nil { @@ -262,11 +261,11 @@ func performClusterAction(cmd *cobra.Command, action string) error { switch action { case "restart-cluster": - err = c.RestartCluster(idFlag) + err = c.RestartCluster(instanceID) case "stop-cluster": - err = c.StopCluster(idFlag) + err = c.StopCluster(instanceID) case "start-cluster": - err = c.StartCluster(idFlag) + err = c.StartCluster(instanceID) default: return fmt.Errorf("unknown action: %s", action) } @@ -280,12 +279,7 @@ func performClusterAction(cmd *cobra.Command, action string) error { return nil } -func performUpgradeAction(cmd *cobra.Command, action, version string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - +func performUpgradeAction(cmd *cobra.Command, instanceID, action, version string) error { var err error apiKey, err := getAPIKey() if err != nil { @@ -296,11 +290,11 @@ func performUpgradeAction(cmd *cobra.Command, action, version string) error { switch action { case "upgrade-erlang": - err = c.UpgradeErlang(idFlag) + err = c.UpgradeErlang(instanceID) case "upgrade-rabbitmq": - err = c.UpgradeRabbitMQ(idFlag, version) + err = c.UpgradeRabbitMQ(instanceID, version) case "upgrade-all": - err = c.UpgradeRabbitMQErlang(idFlag) + err = c.UpgradeRabbitMQErlang(instanceID) default: return fmt.Errorf("unknown action: %s", action) } @@ -314,12 +308,7 @@ func performUpgradeAction(cmd *cobra.Command, action, version string) error { return nil } -func performToggleAction(cmd *cobra.Command, action string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - +func performToggleAction(cmd *cobra.Command, instanceID, action string) error { var err error apiKey, err := getAPIKey() if err != nil { @@ -342,7 +331,7 @@ func performToggleAction(cmd *cobra.Command, action string) error { Enable: enable, Nodes: nodes, } - err = c.ToggleHiPE(idFlag, req) + err = c.ToggleHiPE(instanceID, req) case "firehose": vhost, _ := cmd.Flags().GetString("vhost") @@ -354,7 +343,7 @@ func performToggleAction(cmd *cobra.Command, action string) error { Enable: enable, VHost: vhost, } - err = c.ToggleFirehose(idFlag, req) + err = c.ToggleFirehose(instanceID, req) default: return fmt.Errorf("unknown action: %s", action) @@ -374,19 +363,15 @@ func performToggleAction(cmd *cobra.Command, action string) error { } func init() { - // Add --id flag to all action commands - commands := []*cobra.Command{ + // Add completion for all action commands + for _, cmd := range []*cobra.Command{ restartRabbitMQCmd, restartClusterCmd, restartManagementCmd, stopCmd, startCmd, rebootCmd, stopClusterCmd, startClusterCmd, upgradeErlangCmd, upgradeRabbitMQCmd, upgradeRabbitMQErlangCmd, toggleHiPECmd, toggleFirehoseCmd, upgradeVersionsCmd, - } - - for _, cmd := range commands { - cmd.Flags().StringP("id", "", "", "Instance ID (required)") - cmd.MarkFlagRequired("id") - cmd.RegisterFlagCompletionFunc("id", completeInstanceIDFlag) + } { + cmd.ValidArgsFunction = completeInstances } // Add node flags where applicable diff --git a/cmd/instance_config.go b/cmd/instance_config.go index 9070c1a..8b07dea 100644 --- a/cmd/instance_config.go +++ b/cmd/instance_config.go @@ -21,16 +21,12 @@ var instanceConfigCmd = &cobra.Command{ } var instanceConfigListCmd = &cobra.Command{ - Use: "list --id ", + Use: "list ", Short: "List all configuration settings", Long: `Retrieve and display all current RabbitMQ configuration settings.`, - Example: ` cloudamqp instance config list --id 1234`, + Example: ` cloudamqp instance config list 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -39,7 +35,7 @@ var instanceConfigListCmd = &cobra.Command{ c := client.New(apiKey, Version) - config, err := c.GetRabbitMQConfig(idFlag) + config, err := c.GetRabbitMQConfig(args[0]) if err != nil { fmt.Printf("Error getting configuration: %v\n", err) return err @@ -67,18 +63,13 @@ var instanceConfigListCmd = &cobra.Command{ } var instanceConfigGetCmd = &cobra.Command{ - Use: "get --id ", + Use: "get ", Short: "Get a specific configuration setting", Long: `Retrieve a specific RabbitMQ configuration setting by name.`, - Example: ` cloudamqp instance config get --id 1234 rabbit.heartbeat`, - Args: cobra.ExactArgs(1), + Example: ` cloudamqp instance config get 1234 rabbit.heartbeat`, + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - - settingName := args[0] + settingName := args[1] var err error apiKey, err := getAPIKey() @@ -88,7 +79,7 @@ var instanceConfigGetCmd = &cobra.Command{ c := client.New(apiKey, Version) - config, err := c.GetRabbitMQConfig(idFlag) + config, err := c.GetRabbitMQConfig(args[0]) if err != nil { fmt.Printf("Error getting configuration: %v\n", err) return err @@ -105,20 +96,15 @@ var instanceConfigGetCmd = &cobra.Command{ } var instanceConfigSetCmd = &cobra.Command{ - Use: "set --id ", + Use: "set ", Short: "Set a configuration setting", Long: `Update a RabbitMQ configuration setting. The value will be automatically converted to the appropriate type.`, - Example: ` cloudamqp instance config set --id 1234 rabbit.heartbeat 120 - cloudamqp instance config set --id 1234 rabbit.vm_memory_high_watermark 0.8`, - Args: cobra.ExactArgs(2), + Example: ` cloudamqp instance config set 1234 rabbit.heartbeat 120 + cloudamqp instance config set 1234 rabbit.vm_memory_high_watermark 0.8`, + Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - - settingName := args[0] - settingValue := args[1] + settingName := args[1] + settingValue := args[2] var err error apiKey, err := getAPIKey() @@ -148,7 +134,7 @@ var instanceConfigSetCmd = &cobra.Command{ settingName: value, } - err = c.UpdateRabbitMQConfig(idFlag, config) + err = c.UpdateRabbitMQConfig(args[0], config) if err != nil { fmt.Printf("Error updating configuration: %v\n", err) return err @@ -160,15 +146,9 @@ var instanceConfigSetCmd = &cobra.Command{ } func init() { - // Add --id flag to all subcommands - instanceConfigListCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instanceConfigListCmd.MarkFlagRequired("id") - - instanceConfigGetCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instanceConfigGetCmd.MarkFlagRequired("id") - - instanceConfigSetCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instanceConfigSetCmd.MarkFlagRequired("id") + instanceConfigListCmd.ValidArgsFunction = completeInstances + instanceConfigGetCmd.ValidArgsFunction = completeInstances + instanceConfigSetCmd.ValidArgsFunction = completeInstances instanceConfigCmd.AddCommand(instanceConfigListCmd) instanceConfigCmd.AddCommand(instanceConfigGetCmd) diff --git a/cmd/instance_delete.go b/cmd/instance_delete.go index 9137113..0da1527 100644 --- a/cmd/instance_delete.go +++ b/cmd/instance_delete.go @@ -11,20 +11,17 @@ import ( "github.com/spf13/cobra" ) -var ( - deleteInstanceID string - forceDelete bool -) +var forceDelete bool var instanceDeleteCmd = &cobra.Command{ - Use: "delete --id ", + Use: "delete ", Short: "Delete a CloudAMQP instance", Long: `Delete a CloudAMQP instance permanently. WARNING: This action cannot be undone. All data will be lost.`, - Example: ` cloudamqp instance delete --id 1234 - cloudamqp instance delete --id 1234 --force`, - Args: cobra.NoArgs, + Example: ` cloudamqp instance delete 1234 + cloudamqp instance delete 1234 --force`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var err error apiKey, err = getAPIKey() @@ -32,11 +29,7 @@ WARNING: This action cannot be undone. All data will be lost.`, return fmt.Errorf("failed to get API key: %w", err) } - if deleteInstanceID == "" { - return fmt.Errorf("--id is required") - } - - instanceID, err := strconv.Atoi(deleteInstanceID) + instanceID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid instance ID: %v", err) } @@ -70,8 +63,6 @@ WARNING: This action cannot be undone. All data will be lost.`, } func init() { - instanceDeleteCmd.Flags().StringVar(&deleteInstanceID, "id", "", "Instance ID (required)") instanceDeleteCmd.Flags().BoolVar(&forceDelete, "force", false, "Skip confirmation prompt") - instanceDeleteCmd.MarkFlagRequired("id") - instanceDeleteCmd.RegisterFlagCompletionFunc("id", completeInstances) + instanceDeleteCmd.ValidArgsFunction = completeInstances } diff --git a/cmd/instance_get.go b/cmd/instance_get.go index 86f141c..725d551 100644 --- a/cmd/instance_get.go +++ b/cmd/instance_get.go @@ -21,23 +21,19 @@ func maskPassword(urlStr string) string { } var instanceGetCmd = &cobra.Command{ - Use: "get --id ", + Use: "get ", Short: "Get details of a specific CloudAMQP instance", Long: `Retrieves and displays detailed information about a specific CloudAMQP instance.`, - Example: ` cloudamqp instance get --id 1234`, + Example: ` cloudamqp instance get 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err = getAPIKey() if err != nil { return fmt.Errorf("failed to get API key: %w", err) } - instanceID, err := strconv.Atoi(idFlag) + instanceID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid instance ID: %v", err) } @@ -85,8 +81,6 @@ var instanceGetCmd = &cobra.Command{ } func init() { - instanceGetCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instanceGetCmd.MarkFlagRequired("id") instanceGetCmd.Flags().BoolP("show-url", "", false, "Show full connection URL with credentials") - instanceGetCmd.RegisterFlagCompletionFunc("id", completeInstanceIDFlag) + instanceGetCmd.ValidArgsFunction = completeInstances } diff --git a/cmd/instance_nodes.go b/cmd/instance_nodes.go index 5e7086c..63b246a 100644 --- a/cmd/instance_nodes.go +++ b/cmd/instance_nodes.go @@ -19,16 +19,12 @@ var instanceNodesCmd = &cobra.Command{ } var instanceNodesListCmd = &cobra.Command{ - Use: "list --id ", + Use: "list ", Short: "List nodes in the instance", Long: `Retrieves all nodes in the instance.`, - Example: ` cloudamqp instance nodes list --id 1234`, + Example: ` cloudamqp instance nodes list 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -37,7 +33,7 @@ var instanceNodesListCmd = &cobra.Command{ c := client.New(apiKey, Version) - nodes, err := c.ListNodes(idFlag) + nodes, err := c.ListNodes(args[0]) if err != nil { fmt.Printf("Error listing nodes: %v\n", err) return err @@ -80,16 +76,12 @@ var instanceNodesListCmd = &cobra.Command{ } var instanceNodesVersionsCmd = &cobra.Command{ - Use: "versions --id ", + Use: "versions ", Short: "Get available versions", Long: `Lists available versions to which the instance can be upgraded. For RabbitMQ instances, shows RabbitMQ and Erlang versions. For LavinMQ instances, shows LavinMQ versions.`, - Example: ` cloudamqp instance nodes versions --id 1234`, + Example: ` cloudamqp instance nodes versions 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -98,7 +90,7 @@ var instanceNodesVersionsCmd = &cobra.Command{ c := client.New(apiKey, Version) - versions, err := c.GetAvailableVersions(idFlag) + versions, err := c.GetAvailableVersions(args[0]) if err != nil { fmt.Printf("Error getting available versions: %v\n", err) return err @@ -116,12 +108,8 @@ var instanceNodesVersionsCmd = &cobra.Command{ } func init() { - // Add --id flag to all subcommands - instanceNodesListCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instanceNodesListCmd.MarkFlagRequired("id") - - instanceNodesVersionsCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instanceNodesVersionsCmd.MarkFlagRequired("id") + instanceNodesListCmd.ValidArgsFunction = completeInstances + instanceNodesVersionsCmd.ValidArgsFunction = completeInstances instanceNodesCmd.AddCommand(instanceNodesListCmd) instanceNodesCmd.AddCommand(instanceNodesVersionsCmd) diff --git a/cmd/instance_plugins.go b/cmd/instance_plugins.go index 6ee91cd..3442d0f 100644 --- a/cmd/instance_plugins.go +++ b/cmd/instance_plugins.go @@ -19,16 +19,12 @@ var instancePluginsCmd = &cobra.Command{ } var instancePluginsListCmd = &cobra.Command{ - Use: "list --id ", + Use: "list ", Short: "List plugins", Long: `Retrieves all available RabbitMQ plugins.`, - Example: ` cloudamqp instance plugins list --id 1234`, + Example: ` cloudamqp instance plugins list 1234`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } - var err error apiKey, err := getAPIKey() if err != nil { @@ -37,7 +33,7 @@ var instancePluginsListCmd = &cobra.Command{ c := client.New(apiKey, Version) - plugins, err := c.ListPlugins(idFlag) + plugins, err := c.ListPlugins(args[0]) if err != nil { fmt.Printf("Error listing plugins: %v\n", err) return err @@ -69,17 +65,14 @@ var instancePluginsListCmd = &cobra.Command{ } var instancePluginsEnableCmd = &cobra.Command{ - Use: "enable --id ", + Use: "enable ", Short: "Enable a plugin", Long: `Enables a RabbitMQ plugin on the instance.`, - Args: cobra.ExactArgs(1), - Example: ` cloudamqp instance plugins enable rabbitmq_top --id 1234`, + Args: cobra.ExactArgs(2), + Example: ` cloudamqp instance plugins enable 1234 rabbitmq_top`, RunE: func(cmd *cobra.Command, args []string) error { - pluginName := args[0] - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } + instanceID := args[0] + pluginName := args[1] var err error apiKey, err := getAPIKey() @@ -89,7 +82,7 @@ var instancePluginsEnableCmd = &cobra.Command{ c := client.New(apiKey, Version) - err = c.EnablePlugin(idFlag, pluginName) + err = c.EnablePlugin(instanceID, pluginName) if err != nil { fmt.Printf("Error enabling plugin '%s': %v\n", pluginName, err) return err @@ -101,17 +94,14 @@ var instancePluginsEnableCmd = &cobra.Command{ } var instancePluginsDisableCmd = &cobra.Command{ - Use: "disable --id ", + Use: "disable ", Short: "Disable a plugin", Long: `Disables a RabbitMQ plugin on the instance.`, - Args: cobra.ExactArgs(1), - Example: ` cloudamqp instance plugins disable rabbitmq_top --id 1234`, + Args: cobra.ExactArgs(2), + Example: ` cloudamqp instance plugins disable 1234 rabbitmq_top`, RunE: func(cmd *cobra.Command, args []string) error { - pluginName := args[0] - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("instance ID is required. Use --id flag") - } + instanceID := args[0] + pluginName := args[1] var err error apiKey, err := getAPIKey() @@ -121,7 +111,7 @@ var instancePluginsDisableCmd = &cobra.Command{ c := client.New(apiKey, Version) - err = c.DisablePlugin(idFlag, pluginName) + err = c.DisablePlugin(instanceID, pluginName) if err != nil { fmt.Printf("Error disabling plugin '%s': %v\n", pluginName, err) return err @@ -133,15 +123,9 @@ var instancePluginsDisableCmd = &cobra.Command{ } func init() { - // Add --id flag to all plugins commands - instancePluginsListCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instancePluginsListCmd.MarkFlagRequired("id") - - instancePluginsEnableCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instancePluginsEnableCmd.MarkFlagRequired("id") - - instancePluginsDisableCmd.Flags().StringP("id", "", "", "Instance ID (required)") - instancePluginsDisableCmd.MarkFlagRequired("id") + instancePluginsListCmd.ValidArgsFunction = completeInstances + instancePluginsEnableCmd.ValidArgsFunction = completeInstances + instancePluginsDisableCmd.ValidArgsFunction = completeInstances // Add all commands to plugins instancePluginsCmd.AddCommand(instancePluginsListCmd) diff --git a/cmd/instance_resize.go b/cmd/instance_resize.go index f57b311..be6cd7d 100644 --- a/cmd/instance_resize.go +++ b/cmd/instance_resize.go @@ -9,13 +9,12 @@ import ( ) var ( - resizeInstanceID string - diskSize int - allowDowntime bool + diskSize int + allowDowntime bool ) var instanceResizeCmd = &cobra.Command{ - Use: "resize-disk --id ", + Use: "resize-disk ", Short: "Resize instance disk", Long: `Resize the disk size of an instance. Default behavior is to expand the disk without any downtime. Currently limited to instances in Amazon Web Services (AWS) and Google Compute Engine (GCE). @@ -25,9 +24,9 @@ Note: This action is asynchronous. The request will return almost immediately. T Note: Due to restrictions from cloud providers, it's only possible to resize the disk every 8 hours unless --allow-downtime is set. Available disk sizes: 0, 25, 50, 100, 250, 500, 1000, 2000 GB`, - Example: ` cloudamqp instance resize-disk --id 1234 --disk-size=100 - cloudamqp instance resize-disk --id 1234 --disk-size=250 --allow-downtime`, - Args: cobra.NoArgs, + Example: ` cloudamqp instance resize-disk 1234 --disk-size=100 + cloudamqp instance resize-disk 1234 --disk-size=250 --allow-downtime`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var err error apiKey, err = getAPIKey() @@ -35,11 +34,7 @@ Available disk sizes: 0, 25, 50, 100, 250, 500, 1000, 2000 GB`, return fmt.Errorf("failed to get API key: %w", err) } - if resizeInstanceID == "" { - return fmt.Errorf("--id is required") - } - - instanceID, err := strconv.Atoi(resizeInstanceID) + instanceID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid instance ID: %v", err) } @@ -79,10 +74,8 @@ Available disk sizes: 0, 25, 50, 100, 250, 500, 1000, 2000 GB`, } func init() { - instanceResizeCmd.Flags().StringVar(&resizeInstanceID, "id", "", "Instance ID (required)") instanceResizeCmd.Flags().IntVar(&diskSize, "disk-size", 0, "Disk size to add in gigabytes (0, 25, 50, 100, 250, 500, 1000, 2000)") instanceResizeCmd.Flags().BoolVar(&allowDowntime, "allow-downtime", false, "Allow cluster downtime if needed when resizing disk") - instanceResizeCmd.MarkFlagRequired("id") instanceResizeCmd.MarkFlagRequired("disk-size") - instanceResizeCmd.RegisterFlagCompletionFunc("id", completeInstances) + instanceResizeCmd.ValidArgsFunction = completeInstances } diff --git a/cmd/instance_update.go b/cmd/instance_update.go index 13e4463..74c2d1f 100644 --- a/cmd/instance_update.go +++ b/cmd/instance_update.go @@ -9,14 +9,13 @@ import ( ) var ( - updateInstanceID string updateInstanceName string updateInstancePlan string updateInstanceTags []string ) var instanceUpdateCmd = &cobra.Command{ - Use: "update --id ", + Use: "update ", Short: "Update a CloudAMQP instance", Long: `Update an existing CloudAMQP instance with new configuration. @@ -24,10 +23,10 @@ You can update the following fields: --name: Instance name --plan: Subscription plan --tags: Instance tags (replaces existing tags)`, - Example: ` cloudamqp instance update --id 1234 --name=new-name - cloudamqp instance update --id 1234 --plan=rabbit-1 - cloudamqp instance update --id 1234 --tags=production --tags=updated`, - Args: cobra.NoArgs, + Example: ` cloudamqp instance update 1234 --name=new-name + cloudamqp instance update 1234 --plan=rabbit-1 + cloudamqp instance update 1234 --tags=production --tags=updated`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var err error apiKey, err = getAPIKey() @@ -35,11 +34,7 @@ You can update the following fields: return fmt.Errorf("failed to get API key: %w", err) } - if updateInstanceID == "" { - return fmt.Errorf("--id is required") - } - - instanceID, err := strconv.Atoi(updateInstanceID) + instanceID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid instance ID: %v", err) } @@ -68,11 +63,9 @@ You can update the following fields: } func init() { - instanceUpdateCmd.Flags().StringVar(&updateInstanceID, "id", "", "Instance ID (required)") instanceUpdateCmd.Flags().StringVar(&updateInstanceName, "name", "", "New instance name") instanceUpdateCmd.Flags().StringVar(&updateInstancePlan, "plan", "", "New subscription plan") instanceUpdateCmd.Flags().StringSliceVar(&updateInstanceTags, "tags", []string{}, "New instance tags") - instanceUpdateCmd.MarkFlagRequired("id") - instanceUpdateCmd.RegisterFlagCompletionFunc("id", completeInstances) + instanceUpdateCmd.ValidArgsFunction = completeInstances instanceUpdateCmd.RegisterFlagCompletionFunc("plan", completePlans) } diff --git a/cmd/vpc_delete.go b/cmd/vpc_delete.go index 03719a0..1814730 100644 --- a/cmd/vpc_delete.go +++ b/cmd/vpc_delete.go @@ -11,20 +11,17 @@ import ( "github.com/spf13/cobra" ) -var ( - deleteVPCID string - forceDeleteVPC bool -) +var forceDeleteVPC bool var vpcDeleteCmd = &cobra.Command{ - Use: "delete --id ", + Use: "delete ", Short: "Delete a CloudAMQP VPC", Long: `Delete a CloudAMQP VPC permanently. WARNING: This action cannot be undone. All instances in the VPC must be deleted first.`, - Example: ` cloudamqp vpc delete --id 5678 - cloudamqp vpc delete --id 5678 --force`, - Args: cobra.NoArgs, + Example: ` cloudamqp vpc delete 5678 + cloudamqp vpc delete 5678 --force`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var err error apiKey, err = getAPIKey() @@ -32,11 +29,7 @@ WARNING: This action cannot be undone. All instances in the VPC must be deleted return fmt.Errorf("failed to get API key: %w", err) } - if deleteVPCID == "" { - return fmt.Errorf("--id is required") - } - - vpcID, err := strconv.Atoi(deleteVPCID) + vpcID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid VPC ID: %v", err) } @@ -70,8 +63,6 @@ WARNING: This action cannot be undone. All instances in the VPC must be deleted } func init() { - vpcDeleteCmd.Flags().StringVar(&deleteVPCID, "id", "", "VPC ID (required)") vpcDeleteCmd.Flags().BoolVar(&forceDeleteVPC, "force", false, "Skip confirmation prompt") - vpcDeleteCmd.MarkFlagRequired("id") - vpcDeleteCmd.RegisterFlagCompletionFunc("id", completeVPCArgs) + vpcDeleteCmd.ValidArgsFunction = completeVPCArgs } diff --git a/cmd/vpc_get.go b/cmd/vpc_get.go index b4ec0f2..1353567 100644 --- a/cmd/vpc_get.go +++ b/cmd/vpc_get.go @@ -10,23 +10,19 @@ import ( ) var vpcGetCmd = &cobra.Command{ - Use: "get --id ", + Use: "get ", Short: "Get details of a specific CloudAMQP VPC", Long: `Retrieves and displays detailed information about a specific CloudAMQP VPC.`, - Example: ` cloudamqp vpc get --id 5678`, + Example: ` cloudamqp vpc get 5678`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - idFlag, _ := cmd.Flags().GetString("id") - if idFlag == "" { - return fmt.Errorf("VPC ID is required. Use --id flag") - } - var err error apiKey, err = getAPIKey() if err != nil { return fmt.Errorf("failed to get API key: %w", err) } - vpcID, err := strconv.Atoi(idFlag) + vpcID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid VPC ID: %v", err) } @@ -67,7 +63,5 @@ var vpcGetCmd = &cobra.Command{ } func init() { - vpcGetCmd.Flags().StringP("id", "", "", "VPC ID (required)") - vpcGetCmd.MarkFlagRequired("id") - vpcGetCmd.RegisterFlagCompletionFunc("id", completeVPCIDFlag) + vpcGetCmd.ValidArgsFunction = completeVPCArgs } diff --git a/cmd/vpc_update.go b/cmd/vpc_update.go index 7db9d18..4c5b8b7 100644 --- a/cmd/vpc_update.go +++ b/cmd/vpc_update.go @@ -9,22 +9,21 @@ import ( ) var ( - updateVPCID string updateVPCName string updateVPCTags []string ) var vpcUpdateCmd = &cobra.Command{ - Use: "update --id ", + Use: "update ", Short: "Update a CloudAMQP VPC", Long: `Update an existing CloudAMQP VPC with new configuration. You can update the following fields: --name: VPC name --tags: VPC tags (replaces existing tags)`, - Example: ` cloudamqp vpc update --id 5678 --name=new-vpc-name - cloudamqp vpc update --id 5678 --tags=production --tags=updated`, - Args: cobra.NoArgs, + Example: ` cloudamqp vpc update 5678 --name=new-vpc-name + cloudamqp vpc update 5678 --tags=production --tags=updated`, + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var err error apiKey, err = getAPIKey() @@ -32,11 +31,7 @@ You can update the following fields: return fmt.Errorf("failed to get API key: %w", err) } - if updateVPCID == "" { - return fmt.Errorf("--id is required") - } - - vpcID, err := strconv.Atoi(updateVPCID) + vpcID, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("invalid VPC ID: %v", err) } @@ -64,9 +59,7 @@ You can update the following fields: } func init() { - vpcUpdateCmd.Flags().StringVar(&updateVPCID, "id", "", "VPC ID (required)") vpcUpdateCmd.Flags().StringVar(&updateVPCName, "name", "", "New VPC name") vpcUpdateCmd.Flags().StringSliceVar(&updateVPCTags, "tags", []string{}, "New VPC tags") - vpcUpdateCmd.MarkFlagRequired("id") - vpcUpdateCmd.RegisterFlagCompletionFunc("id", completeVPCArgs) + vpcUpdateCmd.ValidArgsFunction = completeVPCArgs }