Skip to content

Commit 5935e4b

Browse files
authored
feat(Formatters + ListDetails): add instance list --details and -o json for better automations (#40)
## What - Adds `-o [json,table]` for controlling a uniform output - Adds `instance list --details --show-url` to gather hostnames from existing clusters
1 parent cf220f2 commit 5935e4b

14 files changed

Lines changed: 333 additions & 82 deletions

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ GO_LDFLAGS=-X cloudamqp-cli/cmd.Version=$(VERSION) \
1515
-X cloudamqp-cli/cmd.BuildDate=$(BUILD_DATE) \
1616
-X cloudamqp-cli/cmd.GitCommit=$(GIT_COMMIT)
1717

18+
19+
bin/cloudamqp:
20+
$(MAKE) build BINARY_NAME="bin/cloudamqp"
21+
1822
# Default target
1923
.PHONY: all
2024
all: build

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ Note: Dynamic completions (instance IDs, plans, regions) require a configured AP
9494

9595
## Commands
9696

97+
#### Output
98+
You can output either as JSON via `-o json` or Table format using `-o table`
99+
97100
### Instance Management
98101

99102
Manage CloudAMQP instances using your main API key.
@@ -115,6 +118,9 @@ cloudamqp instance create --name=my-instance --plan=bunny-1 --region=amazon-web-
115118
# List all instances
116119
cloudamqp instance list
117120

121+
# List all instances with more details
122+
cloudamqp instance list --details
123+
118124
# Get instance details
119125
cloudamqp instance get --id 1234
120126

cmd/instance_config.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,17 @@ var instanceConfigListCmd = &cobra.Command{
5050
return nil
5151
}
5252

53-
// Print table header
54-
fmt.Printf("%-40s %-30s\n", "KEY", "VALUE")
55-
fmt.Printf("%-40s %-30s\n", "---", "-----")
53+
p, err := getPrinter(cmd)
54+
if err != nil {
55+
return err
56+
}
5657

57-
// Print configuration data
58+
headers := []string{"KEY", "VALUE"}
59+
rows := make([][]string, 0, len(config))
5860
for key, value := range config {
59-
valueStr := fmt.Sprintf("%v", value)
60-
if len(valueStr) > 30 {
61-
valueStr = valueStr[:27] + "..."
62-
}
63-
fmt.Printf("%-40s %-30s\n", key, valueStr)
61+
rows = append(rows, []string{key, fmt.Sprintf("%v", value)})
6462
}
63+
p.PrintRecords(headers, rows)
6564

6665
return nil
6766
},

cmd/instance_get.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,35 @@ var instanceGetCmd = &cobra.Command{
5050
return err
5151
}
5252

53-
// Format output as "Name = Value"
54-
fmt.Printf("Name = %s\n", instance.Name)
55-
fmt.Printf("Plan = %s\n", instance.Plan)
56-
fmt.Printf("Region = %s\n", instance.Region)
57-
fmt.Printf("Tags = %s\n", strings.Join(instance.Tags, ","))
58-
59-
showURL, _ := cmd.Flags().GetBool("show-url")
60-
if showURL {
61-
fmt.Printf("URL = %s\n", instance.URL)
62-
} else {
63-
fmt.Printf("URL = %s\n", maskPassword(instance.URL))
53+
p, err := getPrinter(cmd)
54+
if err != nil {
55+
return err
6456
}
6557

66-
fmt.Printf("Hostname = %s\n", instance.HostnameExternal)
58+
showURL, _ := cmd.Flags().GetBool("show-url")
6759
ready := "No"
6860
if instance.Ready {
6961
ready = "Yes"
7062
}
71-
fmt.Printf("Ready = %s\n", ready)
63+
64+
urlVal := maskPassword(instance.URL)
65+
if showURL {
66+
urlVal = instance.URL
67+
}
68+
69+
p.PrintRecord(
70+
[]string{"ID", "NAME", "PLAN", "REGION", "TAGS", "URL", "HOSTNAME", "READY"},
71+
[]string{
72+
strconv.Itoa(instance.ID),
73+
instance.Name,
74+
instance.Plan,
75+
instance.Region,
76+
strings.Join(instance.Tags, ","),
77+
urlVal,
78+
instance.HostnameExternal,
79+
ready,
80+
},
81+
)
7282

7383
return nil
7484
},

cmd/instance_list.go

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65
"strconv"
6+
"strings"
7+
"sync"
78

89
"cloudamqp-cli/client"
9-
"cloudamqp-cli/internal/table"
1010
"github.com/spf13/cobra"
1111
)
1212

@@ -35,18 +35,85 @@ var instanceListCmd = &cobra.Command{
3535
return nil
3636
}
3737

38-
// Create table and populate data
39-
t := table.New(os.Stdout, "ID", "NAME", "PLAN", "REGION")
40-
for _, instance := range instances {
41-
t.AddRow(
38+
p, err := getPrinter(cmd)
39+
if err != nil {
40+
return err
41+
}
42+
43+
details, _ := cmd.Flags().GetBool("details")
44+
45+
if details {
46+
showURL, _ := cmd.Flags().GetBool("show-url")
47+
detailed := make([]*client.Instance, len(instances))
48+
headers := []string{"ID", "NAME", "PLAN", "REGION", "TAGS", "URL", "HOSTNAME", "READY"}
49+
rows := make([][]string, len(instances))
50+
var (
51+
mu sync.Mutex
52+
firstErr error
53+
wg sync.WaitGroup
54+
)
55+
for i, instance := range instances {
56+
wg.Add(1)
57+
go func(idx, id int) {
58+
defer wg.Done()
59+
det, err := c.GetInstance(id)
60+
mu.Lock()
61+
defer mu.Unlock()
62+
if err != nil {
63+
if firstErr == nil {
64+
firstErr = fmt.Errorf("error fetching instance %d: %w", id, err)
65+
}
66+
return
67+
}
68+
detailed[idx] = det
69+
}(i, instance.ID)
70+
}
71+
wg.Wait()
72+
if firstErr != nil {
73+
return firstErr
74+
}
75+
76+
for i, inst := range detailed {
77+
ready := "No"
78+
if inst.Ready {
79+
ready = "Yes"
80+
}
81+
urlVal := maskPassword(inst.URL)
82+
if showURL {
83+
urlVal = inst.URL
84+
}
85+
rows[i] = []string{
86+
strconv.Itoa(inst.ID),
87+
inst.Name,
88+
inst.Plan,
89+
inst.Region,
90+
strings.Join(inst.Tags, ","),
91+
urlVal,
92+
inst.HostnameExternal,
93+
ready,
94+
}
95+
}
96+
p.PrintRecords(headers, rows)
97+
return nil
98+
}
99+
100+
headers := []string{"ID", "NAME", "PLAN", "REGION"}
101+
rows := make([][]string, len(instances))
102+
for i, instance := range instances {
103+
rows[i] = []string{
42104
strconv.Itoa(instance.ID),
43105
instance.Name,
44106
instance.Plan,
45107
instance.Region,
46-
)
108+
}
47109
}
48-
t.Print()
110+
p.PrintRecords(headers, rows)
49111

50112
return nil
51113
},
52114
}
115+
116+
func init() {
117+
instanceListCmd.Flags().BoolP("details", "", false, "Fetch full details for each instance (one GET request per instance)")
118+
instanceListCmd.Flags().BoolP("show-url", "", false, "Show full connection URL with credentials (requires --details)")
119+
}

cmd/instance_nodes.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65

76
"cloudamqp-cli/client"
8-
"cloudamqp-cli/internal/table"
97
"github.com/spf13/cobra"
108
)
119

@@ -50,9 +48,14 @@ var instanceNodesListCmd = &cobra.Command{
5048
return nil
5149
}
5250

53-
// Create table and populate data
54-
t := table.New(os.Stdout, "NAME", "CONFIGURED", "RUNNING", "DISK_SIZE", "RABBITMQ_VERSION")
55-
for _, node := range nodes {
51+
p, err := getPrinter(cmd)
52+
if err != nil {
53+
return err
54+
}
55+
56+
headers := []string{"NAME", "CONFIGURED", "RUNNING", "DISK_SIZE", "RABBITMQ_VERSION"}
57+
rows := make([][]string, len(nodes))
58+
for i, node := range nodes {
5659
configured := "No"
5760
if node.Configured {
5861
configured = "Yes"
@@ -62,15 +65,15 @@ var instanceNodesListCmd = &cobra.Command{
6265
running = "Yes"
6366
}
6467
totalDisk := node.DiskSize + node.AdditionalDiskSize
65-
t.AddRow(
68+
rows[i] = []string{
6669
node.Name,
6770
configured,
6871
running,
6972
fmt.Sprintf("%d GB", totalDisk),
7073
node.RabbitMQVersion,
71-
)
74+
}
7275
}
73-
t.Print()
76+
p.PrintRecords(headers, rows)
7477

7578
return nil
7679
},

cmd/instance_plugins.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65

76
"cloudamqp-cli/client"
8-
"cloudamqp-cli/internal/table"
97
"github.com/spf13/cobra"
108
)
119

@@ -50,16 +48,21 @@ var instancePluginsListCmd = &cobra.Command{
5048
return nil
5149
}
5250

53-
// Create table and populate data
54-
t := table.New(os.Stdout, "NAME", "ENABLED")
55-
for _, plugin := range plugins {
51+
p, err := getPrinter(cmd)
52+
if err != nil {
53+
return err
54+
}
55+
56+
headers := []string{"NAME", "ENABLED"}
57+
rows := make([][]string, len(plugins))
58+
for i, plugin := range plugins {
5659
enabled := "No"
5760
if plugin.Enabled {
5861
enabled = "Yes"
5962
}
60-
t.AddRow(plugin.Name, enabled)
63+
rows[i] = []string{plugin.Name, enabled}
6164
}
62-
t.Print()
65+
p.PrintRecords(headers, rows)
6366

6467
return nil
6568
},

cmd/plans.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65

76
"cloudamqp-cli/client"
8-
"cloudamqp-cli/internal/table"
97
"github.com/spf13/cobra"
108
)
119

@@ -37,9 +35,14 @@ var plansCmd = &cobra.Command{
3735
return nil
3836
}
3937

40-
// Create table and populate data
41-
t := table.New(os.Stdout, "NAME", "PRICE", "BACKEND", "SHARED")
42-
for _, plan := range plans {
38+
p, err := getPrinter(cmd)
39+
if err != nil {
40+
return err
41+
}
42+
43+
headers := []string{"NAME", "PRICE", "BACKEND", "SHARED"}
44+
rows := make([][]string, len(plans))
45+
for i, plan := range plans {
4346
shared := "No"
4447
if plan.Shared {
4548
shared = "Yes"
@@ -48,14 +51,10 @@ var plansCmd = &cobra.Command{
4851
if plan.Price == 0 {
4952
price = "Free"
5053
}
51-
t.AddRow(
52-
plan.Name,
53-
price,
54-
plan.Backend,
55-
shared,
56-
)
54+
rows[i] = []string{plan.Name, price, plan.Backend, shared}
5755
}
58-
t.Print()
56+
p.PrintRecords(headers, rows)
57+
5958
return nil
6059
},
6160
}

cmd/regions.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65

76
"cloudamqp-cli/client"
8-
"cloudamqp-cli/internal/table"
97
"github.com/spf13/cobra"
108
)
119

@@ -37,11 +35,17 @@ var regionsCmd = &cobra.Command{
3735
return nil
3836
}
3937

40-
t := table.New(os.Stdout, "PROVIDER", "REGION", "NAME")
41-
for _, region := range regions {
42-
t.AddRow(region.Provider, region.Region, region.Name)
38+
p, err := getPrinter(cmd)
39+
if err != nil {
40+
return err
41+
}
42+
43+
headers := []string{"PROVIDER", "REGION", "NAME"}
44+
rows := make([][]string, len(regions))
45+
for i, region := range regions {
46+
rows[i] = []string{region.Provider, region.Region, region.Name}
4347
}
44-
t.Print()
48+
p.PrintRecords(headers, rows)
4549

4650
return nil
4751
},

0 commit comments

Comments
 (0)