Skip to content

Commit

Permalink
refactor(server): group plans by type in server plans human output (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
kangasta authored Jul 3, 2024
1 parent e0fe8ae commit 127cf6e
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 10 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## Added
### Added

- Add `gateway plans` command for listing gateway plans.

### Changed

- In all outputs of `server plans`, sort plans by CPU count, memory amount, and storage size.
- In human readable output of `server plans`, group plans by type.

## [3.8.1] - 2024-05-24

### Changed
Expand Down
3 changes: 2 additions & 1 deletion internal/commands/server/firewall/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ func TestFirewallShowHumanOutput(t *testing.T) {
},
}

expected := ` Firewall rules
expected := `
Firewall rules
# Action Source Destination Dir Proto
─── ──────── ─────────────── ───────────── ───── ──────────
Expand Down
56 changes: 50 additions & 6 deletions internal/commands/server/plan_list.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package server

import (
"sort"
"strings"

"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
"github.com/UpCloudLtd/upcloud-go-api/v8/upcloud"
)

// PlanListCommand creates the "server plans" command
Expand All @@ -18,14 +22,28 @@ type planListCommand struct {

// ExecuteWithoutArguments implements commands.NoArgumentCommand
func (s *planListCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
plans, err := exec.All().GetPlans(exec.Context())
plansObj, err := exec.All().GetPlans(exec.Context())
if err != nil {
return nil, err
}

rows := []output.TableRow{}
for _, p := range plans.Plans {
rows = append(rows, output.TableRow{
plans := plansObj.Plans
sort.Slice(plans, func(i, j int) bool {
if plans[i].CoreNumber != plans[j].CoreNumber {
return plans[i].CoreNumber < plans[j].CoreNumber
}

if plans[i].MemoryAmount != plans[j].MemoryAmount {
return plans[i].MemoryAmount < plans[j].MemoryAmount
}

return plans[i].StorageSize < plans[j].StorageSize
})

rows := make(map[string][]output.TableRow)
for _, p := range plans {
key := planType(p)
rows[key] = append(rows[key], output.TableRow{
p.Name,
p.CoreNumber,
p.MemoryAmount,
Expand All @@ -37,7 +55,33 @@ func (s *planListCommand) ExecuteWithoutArguments(exec commands.Executor) (outpu

return output.MarshaledWithHumanOutput{
Value: plans,
Output: output.Table{
Output: output.Combined{
planSection("general_purpose", "General purpose", rows["general_purpose"]),
planSection("high_cpu", "High CPU", rows["high_cpu"]),
planSection("high_memory", "High memory", rows["high_memory"]),
planSection("developer", "Developer", rows["developer"]),
},
}, nil
}

func planType(p upcloud.Plan) string {
if strings.HasPrefix(p.Name, "DEV-") {
return "developer"
}
if strings.HasPrefix(p.Name, "HICPU-") {
return "high_cpu"
}
if strings.HasPrefix(p.Name, "HIMEM-") {
return "high_memory"
}
return "general_purpose"
}

func planSection(key, title string, rows []output.TableRow) output.CombinedSection {
return output.CombinedSection{
Key: key,
Title: title,
Contents: output.Table{
Columns: []output.TableColumn{
{Key: "name", Header: "Name"},
{Key: "cores", Header: "Cores"},
Expand All @@ -48,5 +92,5 @@ func (s *planListCommand) ExecuteWithoutArguments(exec commands.Executor) (outpu
},
Rows: rows,
},
}, nil
}
}
17 changes: 16 additions & 1 deletion internal/output/combined.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,30 @@ func (m Combined) MarshalHuman() ([]byte, error) {
marshaled = prefixLines(marshaled, " ")
}
out = append(out, marshaled...)

// ensure newline before first section
if i == 0 && firstNonSpaceChar(out) != '\n' {
out = append([]byte("\n"), out...)
}

// dont add newline after the last section
if i < len(m)-1 {
// dont add newline after the last section
out = append(out, []byte("\n")...)
}
}

return out, nil
}

func firstNonSpaceChar(bytes []byte) byte {
for _, b := range bytes {
if b != ' ' {
return b
}
}
return 0
}

func prefixLines(marshaled []byte, s string) (out []byte) {
padding := []byte(s)
for _, b := range marshaled {
Expand Down
3 changes: 2 additions & 1 deletion internal/output/combined_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func TestCombined(t *testing.T) {
},
}},
},
expectedHumanResult: ` MOCK
expectedHumanResult: `
MOCK
B D
─── ────
Expand Down

0 comments on commit 127cf6e

Please sign in to comment.