Skip to content

Commit a28ede9

Browse files
committed
feat: add command to list available zones
1 parent c080a00 commit a28ede9

7 files changed

Lines changed: 123 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ 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 `zone list` command that lists available zones.
11+
912
### Fixed
1013
- Do not display usage if execution fails because of missing credentials
1114

internal/commands/all/all.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/UpCloudLtd/upcloud-cli/internal/commands/serverfirewall"
1313
"github.com/UpCloudLtd/upcloud-cli/internal/commands/serverstorage"
1414
"github.com/UpCloudLtd/upcloud-cli/internal/commands/storage"
15+
"github.com/UpCloudLtd/upcloud-cli/internal/commands/zone"
1516
"github.com/UpCloudLtd/upcloud-cli/internal/config"
1617

1718
"github.com/spf13/cobra"
@@ -93,6 +94,10 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) {
9394
accountCommand := commands.BuildCommand(account.BaseAccountCommand(), rootCmd, conf)
9495
commands.BuildCommand(account.ShowCommand(), accountCommand.Cobra(), conf)
9596

97+
// Zone
98+
zoneCommand := commands.BuildCommand(zone.BaseZoneCommand(), rootCmd, conf)
99+
commands.BuildCommand(zone.ListCommand(), zoneCommand.Cobra(), conf)
100+
96101
// Misc
97102
commands.BuildCommand(
98103
&root.CompletionCommand{

internal/commands/zone/list.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package zone
2+
3+
import (
4+
"github.com/UpCloudLtd/upcloud-cli/internal/commands"
5+
"github.com/UpCloudLtd/upcloud-cli/internal/output"
6+
)
7+
8+
// ListCommand creates the "zone list" command
9+
func ListCommand() commands.Command {
10+
return &listCommand{
11+
BaseCommand: commands.New("list", "List available zones", "upctl zone list"),
12+
}
13+
}
14+
15+
type listCommand struct {
16+
*commands.BaseCommand
17+
}
18+
19+
// ExecuteWithoutArguments implements commands.NoArgumentCommand
20+
func (s *listCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
21+
svc := exec.All()
22+
zones, err := svc.GetZones()
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
rows := []output.TableRow{}
28+
for _, z := range zones.Zones {
29+
rows = append(rows, output.TableRow{
30+
z.ID,
31+
z.Description,
32+
z.Public,
33+
})
34+
}
35+
36+
return output.Table{
37+
Columns: []output.TableColumn{
38+
{Key: "id", Header: "ID"},
39+
{Key: "description", Header: "Description"},
40+
{Key: "public", Header: "Public", Format: output.BoolFormat},
41+
},
42+
Rows: rows,
43+
}, nil
44+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package zone
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/UpCloudLtd/upcloud-cli/internal/commands"
8+
"github.com/UpCloudLtd/upcloud-cli/internal/config"
9+
smock "github.com/UpCloudLtd/upcloud-cli/internal/mock"
10+
"github.com/UpCloudLtd/upcloud-cli/internal/output"
11+
12+
"github.com/UpCloudLtd/upcloud-go-api/v4/upcloud"
13+
"github.com/gemalto/flume"
14+
"github.com/jedib0t/go-pretty/v6/text"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestZoneListHumanOutput(t *testing.T) {
19+
text.DisableColors()
20+
zones := upcloud.Zones{
21+
Zones: []upcloud.Zone{
22+
{ID: "fi-hel1", Description: "Helsinki #1", Public: 1},
23+
{ID: "de-fra1", Description: "Frankfurt #1", Public: 1},
24+
},
25+
}
26+
27+
mService := smock.Service{}
28+
mService.On("GetZones").Return(&zones, nil)
29+
30+
conf := config.New()
31+
// force human output
32+
conf.Viper().Set(config.KeyOutput, config.ValueOutputHuman)
33+
34+
command := commands.BuildCommand(ListCommand(), nil, conf)
35+
36+
res, err := command.(commands.NoArgumentCommand).ExecuteWithoutArguments(commands.NewExecutor(conf, &mService, flume.New("test")))
37+
38+
assert.Nil(t, err)
39+
40+
buf := bytes.NewBuffer(nil)
41+
err = output.Render(buf, conf, res)
42+
assert.NoError(t, err)
43+
assert.Regexp(t, "ID\\s+Description\\s+Public", buf.String())
44+
assert.Regexp(t, "fi-hel1\\s+Helsinki #1\\s+yes", buf.String())
45+
}

internal/commands/zone/zone.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package zone
2+
3+
import (
4+
"github.com/UpCloudLtd/upcloud-cli/internal/commands"
5+
)
6+
7+
// BaseZoneCommand creates the base "zone" command
8+
func BaseZoneCommand() commands.Command {
9+
return &zoneCommand{
10+
commands.New("zone", "Display zone information"),
11+
}
12+
}
13+
14+
type zoneCommand struct {
15+
*commands.BaseCommand
16+
}

internal/mock/mock.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ func (m *Service) GetAccount() (*upcloud.Account, error) {
2121
return args[0].(*upcloud.Account), args.Error(1)
2222
}
2323

24+
// GetZones implements service.Zones.GetZones
25+
func (m *Service) GetZones() (*upcloud.Zones, error) {
26+
args := m.Called()
27+
if args[0] == nil {
28+
return nil, args.Error(1)
29+
}
30+
return args[0].(*upcloud.Zones), args.Error(1)
31+
}
32+
2433
// GetPlans implements service.Plan.GetPlans
2534
func (m *Service) GetPlans() (*upcloud.Plans, error) {
2635
args := m.Called()

internal/service/service.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type AllServices interface {
1313
service.IpAddress
1414
service.Plans
1515
service.Account
16+
service.Zones
1617
}
1718

1819
// Wrapper is a temporary reimplementation fo upcloud-go-api which prevents

0 commit comments

Comments
 (0)