@@ -2,10 +2,16 @@ package server
22
33import (
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+
1830type 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