Skip to content

Commit 0cfa308

Browse files
committed
feat(server): add --show-ip-addresses flag to server list command
1 parent b8f7c37 commit 0cfa308

2 files changed

Lines changed: 109 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
9+
### Added
10+
- Add `--show-ip-addresses` flag to `server list` command to optionally include IP addresses in command output.
911

1012
## [1.4.0] - 2022-06-15
1113
### Added

internal/commands/server/list.go

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ package server
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/UpCloudLtd/upcloud-cli/internal/commands"
8+
"github.com/UpCloudLtd/upcloud-cli/internal/config"
79
"github.com/UpCloudLtd/upcloud-cli/internal/output"
10+
"github.com/UpCloudLtd/upcloud-cli/internal/service"
811
"github.com/UpCloudLtd/upcloud-cli/internal/ui"
12+
"github.com/UpCloudLtd/upcloud-go-api/v4/upcloud"
13+
"github.com/jedib0t/go-pretty/v6/text"
14+
"github.com/spf13/pflag"
915
)
1016

1117
// ListCommand creates the "server list" command
@@ -15,13 +21,27 @@ func ListCommand() commands.Command {
1521
}
1622
}
1723

24+
type listIPAddress struct {
25+
Access string `json:"access"`
26+
Address string `json:"address"`
27+
Floating bool `json:"floating"`
28+
}
29+
1830
type listCommand struct {
1931
*commands.BaseCommand
32+
showIPAddresses config.OptionalBoolean
33+
}
34+
35+
// InitCommand implements Command.InitCommand
36+
func (ls *listCommand) InitCommand() {
37+
flags := &pflag.FlagSet{}
38+
config.AddToggleFlag(flags, &ls.showIPAddresses, "show-ip-addresses", false, "Show IP addresses of the servers in the output.")
39+
ls.AddFlags(flags)
2040
}
2141

2242
// ExecuteWithoutArguments implements commands.NoArgumentCommand
23-
func (s *listCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
24-
svc := exec.Server()
43+
func (ls *listCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
44+
svc := exec.All()
2545
servers, err := svc.GetServers()
2646
if err != nil {
2747
return nil, err
@@ -46,14 +66,91 @@ func (s *listCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Ou
4666
})
4767
}
4868

69+
columns := []output.TableColumn{
70+
{Key: "uuid", Header: "UUID", Colour: ui.DefaultUUUIDColours},
71+
{Key: "hostname", Header: "Hostname"},
72+
{Key: "plan", Header: "Plan"},
73+
{Key: "zone", Header: "Zone"},
74+
{Key: "state", Header: "State"},
75+
}
76+
77+
if ls.showIPAddresses.Value() {
78+
ipaddressMap, err := getIPAddressesByServerUUID(svc)
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
for i, row := range rows {
84+
uuid := row[0].(string)
85+
86+
var listIpaddresses []listIPAddress
87+
if apiIpaddresses, ok := ipaddressMap[uuid]; ok {
88+
for _, ipa := range apiIpaddresses {
89+
listIpaddresses = append(listIpaddresses, listIPAddress{
90+
Access: ipa.Access,
91+
Address: ipa.Address,
92+
Floating: ipa.Floating.Bool(),
93+
})
94+
}
95+
}
96+
row = append(row[:3], row[2:]...)
97+
row[2] = listIpaddresses
98+
rows[i] = row
99+
}
100+
columns = append(columns[:3], columns[2:]...)
101+
columns[2] = output.TableColumn{
102+
Key: "ip_addresses",
103+
Header: "IP addresses",
104+
Format: formatListIPAddresses,
105+
}
106+
}
107+
49108
return output.Table{
50-
Columns: []output.TableColumn{
51-
{Key: "uuid", Header: "UUID", Colour: ui.DefaultUUUIDColours},
52-
{Key: "hostname", Header: "Hostname"},
53-
{Key: "plan", Header: "Plan"},
54-
{Key: "zone", Header: "Zone"},
55-
{Key: "state", Header: "State"},
56-
},
57-
Rows: rows,
109+
Columns: columns,
110+
Rows: rows,
58111
}, nil
59112
}
113+
114+
// getIPAddressesByServerUUID returns IP addresses grouped by server UUID. This function will be removed when server end-point response includes IP addresses.
115+
func getIPAddressesByServerUUID(svc service.AllServices) (map[string][]upcloud.IPAddress, error) {
116+
ipaddresses, err := svc.GetIPAddresses()
117+
if err != nil {
118+
return nil, err
119+
}
120+
121+
ipaddressMap := make(map[string][]upcloud.IPAddress)
122+
for _, ipaddress := range ipaddresses.IPAddresses {
123+
current, ok := ipaddressMap[ipaddress.ServerUUID]
124+
if ok {
125+
ipaddressMap[ipaddress.ServerUUID] = append(current, ipaddress)
126+
} else {
127+
ipaddressMap[ipaddress.ServerUUID] = []upcloud.IPAddress{ipaddress}
128+
}
129+
}
130+
131+
return ipaddressMap, nil
132+
}
133+
134+
func formatListIPAddresses(val interface{}) (text.Colors, string, error) {
135+
ipaddresses, ok := val.([]listIPAddress)
136+
if !ok {
137+
return nil, "", fmt.Errorf("cannot parse IP addresses from %T, expected []listIPAddress", val)
138+
}
139+
140+
var rows []string
141+
for _, ipa := range ipaddresses {
142+
var floating string
143+
if ipa.Floating {
144+
floating = " (f)"
145+
}
146+
147+
rows = append(rows, fmt.Sprintf(
148+
"%s: %s%s",
149+
ipa.Access,
150+
ui.DefaultAddressColours.Sprint(ipa.Address),
151+
floating,
152+
))
153+
}
154+
155+
return nil, strings.Join(rows, ",\n"), nil
156+
}

0 commit comments

Comments
 (0)