From 9e0a4a20cc4f7bac4fb2daa00945c1ab303e526a Mon Sep 17 00:00:00 2001 From: Toni Kangas Date: Wed, 3 Jul 2024 13:51:55 +0300 Subject: [PATCH] feat(objectstorage): add command to list available regions --- CHANGELOG.md | 1 + internal/commands/all/all.go | 1 + internal/commands/objectstorage/regions.go | 60 ++++++++++++++++++++++ internal/format/stringslice.go | 16 +++--- 4 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 internal/commands/objectstorage/regions.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6389ef..142e62ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `gateway plans` command for listing gateway plans. +- Add `objectstorage regions` command for listing managed object storage regions. ### Changed diff --git a/internal/commands/all/all.go b/internal/commands/all/all.go index b146bfb8..2c653bc8 100644 --- a/internal/commands/all/all.go +++ b/internal/commands/all/all.go @@ -197,6 +197,7 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) { commands.BuildCommand(objectstorage.DeleteCommand(), objectStorageCommand.Cobra(), conf) commands.BuildCommand(objectstorage.ListCommand(), objectStorageCommand.Cobra(), conf) commands.BuildCommand(objectstorage.ShowCommand(), objectStorageCommand.Cobra(), conf) + commands.BuildCommand(objectstorage.RegionsCommand(), objectStorageCommand.Cobra(), conf) // Network Gateway operations gatewayCommand := commands.BuildCommand(gateway.BaseGatewayCommand(), rootCmd, conf) diff --git a/internal/commands/objectstorage/regions.go b/internal/commands/objectstorage/regions.go new file mode 100644 index 00000000..59923f29 --- /dev/null +++ b/internal/commands/objectstorage/regions.go @@ -0,0 +1,60 @@ +package objectstorage + +import ( + "sort" + + "github.com/UpCloudLtd/upcloud-cli/v3/internal/commands" + "github.com/UpCloudLtd/upcloud-cli/v3/internal/format" + "github.com/UpCloudLtd/upcloud-cli/v3/internal/output" + "github.com/UpCloudLtd/upcloud-go-api/v8/upcloud/request" +) + +// RegionsCommand creates the "objectstorage regions" command +func RegionsCommand() commands.Command { + return ®ionsCommand{ + BaseCommand: commands.New("regions", "List objectstorage regions", "upctl objectstorage regions"), + } +} + +type regionsCommand struct { + *commands.BaseCommand +} + +// ExecuteWithoutArguments implements commands.NoArgumentCommand +func (s *regionsCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) { + regions, err := exec.All().GetManagedObjectStorageRegions(exec.Context(), &request.GetManagedObjectStorageRegionsRequest{}) + if err != nil { + return nil, err + } + + sort.Slice(regions, func(i, j int) bool { + return regions[i].Name < regions[j].Name + }) + + rows := []output.TableRow{} + for _, r := range regions { + zones := []string{} + for _, z := range r.Zones { + zones = append(zones, z.Name) + } + sort.Strings(zones) + + rows = append(rows, output.TableRow{ + r.Name, + r.PrimaryZone, + zones, + }) + } + + return output.MarshaledWithHumanOutput{ + Value: regions, + Output: output.Table{ + Columns: []output.TableColumn{ + {Key: "name", Header: "Name"}, + {Key: "primary_zone", Header: "Primary zone"}, + {Key: "zones", Header: "Zones", Format: format.StringSliceSingleLineAnd}, + }, + Rows: rows, + }, + }, nil +} diff --git a/internal/format/stringslice.go b/internal/format/stringslice.go index b0deed0b..75da0cbf 100644 --- a/internal/format/stringslice.go +++ b/internal/format/stringslice.go @@ -9,14 +9,18 @@ import ( ) func StringSliceOr(val interface{}) (text.Colors, string, error) { - return formatStringSlice(val, "or") + return formatStringSlice(val, "or", false) } func StringSliceAnd(val interface{}) (text.Colors, string, error) { - return formatStringSlice(val, "and") + return formatStringSlice(val, "and", false) } -func formatStringSlice(val interface{}, andOrOr string) (text.Colors, string, error) { +func StringSliceSingleLineAnd(val interface{}) (text.Colors, string, error) { + return formatStringSlice(val, "and", true) +} + +func formatStringSlice(val interface{}, andOrOr string, singleLine bool) (text.Colors, string, error) { if val == nil { return nil, "", nil } @@ -26,7 +30,7 @@ func formatStringSlice(val interface{}, andOrOr string) (text.Colors, string, er } if ifaceSliceVal, ok := toIfaceSlice(val); ok { - return nil, stringSliceString(ifaceSliceVal, andOrOr), nil + return nil, stringSliceString(ifaceSliceVal, andOrOr, singleLine), nil } return nil, fmt.Sprintf("%+v", val), nil @@ -54,7 +58,7 @@ func maxStringLen(strings []string) int { return max } -func stringSliceString(values []interface{}, andOrOr string) string { +func stringSliceString(values []interface{}, andOrOr string, singleLine bool) string { if len(values) == 0 { return "" } @@ -69,7 +73,7 @@ func stringSliceString(values []interface{}, andOrOr string) string { } whitespace := " " - if maxStringLen(strs) > 15 || len(strs) > 3 { + if !singleLine && (maxStringLen(strs) > 15 || len(strs) > 3) { whitespace = "\n" }