Skip to content

Commit

Permalink
consider units when sorting capacity (#2252)
Browse files Browse the repository at this point in the history
  • Loading branch information
wjiec authored Oct 18, 2023
1 parent e906fa6 commit b2be433
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 20 deletions.
10 changes: 10 additions & 0 deletions internal/render/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type HeaderColumn struct {
Wide bool
MX bool
Time bool
Capacity bool
}

// Clone copies a header.
Expand Down Expand Up @@ -163,6 +164,15 @@ func (h Header) IsTimeCol(col int) bool {
return h[col].Time
}

// IsCapacityCol checks if given column index represents a capacity.
func (h Header) IsCapacityCol(col int) bool {
if col < 0 || col >= len(h) {
return false
}

return h[col].Capacity
}

// ValidColIndex returns the valid col index or -1 if none.
func (h Header) ValidColIndex() int {
return h.IndexOf("VALID", true)
Expand Down
6 changes: 6 additions & 0 deletions internal/render/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/rs/zerolog/log"
"golang.org/x/text/language"
"golang.org/x/text/message"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/duration"
)
Expand Down Expand Up @@ -57,6 +58,11 @@ func durationToSeconds(duration string) int64 {
return n
}

func capacityToNumber(capacity string) int64 {
quantity := resource.MustParse(capacity)
return quantity.Value()
}

// AsThousands prints a number with thousand separator.
func AsThousands(n int64) string {
p := message.NewPrinter(language.English)
Expand Down
2 changes: 1 addition & 1 deletion internal/render/pv.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (p PersistentVolume) ColorerFunc() ColorerFunc {
func (PersistentVolume) Header(string) Header {
return Header{
HeaderColumn{Name: "NAME"},
HeaderColumn{Name: "CAPACITY"},
HeaderColumn{Name: "CAPACITY", Capacity: true},
HeaderColumn{Name: "ACCESS MODES"},
HeaderColumn{Name: "RECLAIM POLICY"},
HeaderColumn{Name: "STATUS"},
Expand Down
2 changes: 1 addition & 1 deletion internal/render/pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (PersistentVolumeClaim) Header(ns string) Header {
HeaderColumn{Name: "NAME"},
HeaderColumn{Name: "STATUS"},
HeaderColumn{Name: "VOLUME"},
HeaderColumn{Name: "CAPACITY"},
HeaderColumn{Name: "CAPACITY", Capacity: true},
HeaderColumn{Name: "ACCESS MODES"},
HeaderColumn{Name: "STORAGECLASS"},
HeaderColumn{Name: "LABELS", Wide: true},
Expand Down
20 changes: 13 additions & 7 deletions internal/render/row.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,13 @@ func (rr Rows) Find(id string) (int, bool) {
}

// Sort rows based on column index and order.
func (rr Rows) Sort(col int, asc, isNum, isDur bool) {
func (rr Rows) Sort(col int, asc, isNum, isDur, isCapacity bool) {
t := RowSorter{
Rows: rr,
Index: col,
IsNumber: isNum,
IsDuration: isDur,
IsCapacity: isCapacity,
Asc: asc,
}
sort.Sort(t)
Expand All @@ -160,10 +161,12 @@ func (rr Rows) Sort(col int, asc, isNum, isDur bool) {

// RowSorter sorts rows.
type RowSorter struct {
Rows Rows
Index int
IsNumber, IsDuration bool
Asc bool
Rows Rows
Index int
IsNumber bool
IsDuration bool
IsCapacity bool
Asc bool
}

func (s RowSorter) Len() int {
Expand All @@ -177,7 +180,7 @@ func (s RowSorter) Swap(i, j int) {
func (s RowSorter) Less(i, j int) bool {
v1, v2 := s.Rows[i].Fields[s.Index], s.Rows[j].Fields[s.Index]
id1, id2 := s.Rows[i].ID, s.Rows[j].ID
less := Less(s.IsNumber, s.IsDuration, id1, id2, v1, v2)
less := Less(s.IsNumber, s.IsDuration, s.IsCapacity, id1, id2, v1, v2)
if s.Asc {
return less
}
Expand All @@ -188,7 +191,7 @@ func (s RowSorter) Less(i, j int) bool {
// Helpers...

// Less return true if c1 < c2.
func Less(isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
func Less(isNumber, isDuration, isCapacity bool, id1, id2, v1, v2 string) bool {
var less bool
switch {
case isNumber:
Expand All @@ -197,6 +200,9 @@ func Less(isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
case isDuration:
d1, d2 := durationToSeconds(v1), durationToSeconds(v2)
less = d1 <= d2
case isCapacity:
c1, c2 := capacityToNumber(v1), capacityToNumber(v2)
less = c1 <= c2
default:
less = sortorder.NaturalLess(v1, v2)
}
Expand Down
6 changes: 4 additions & 2 deletions internal/render/row_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (r RowEvents) FindIndex(id string) (int, bool) {
}

// Sort rows based on column index and order.
func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) {
func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, isCapacity, asc bool) {
if sortCol == -1 {
return
}
Expand All @@ -205,6 +205,7 @@ func (r RowEvents) Sort(ns string, sortCol int, isDuration, numCol, asc bool) {
Asc: asc,
IsNumber: numCol,
IsDuration: isDuration,
IsCapacity: isCapacity,
}
sort.Sort(t)
}
Expand All @@ -218,6 +219,7 @@ type RowEventSorter struct {
NS string
IsNumber bool
IsDuration bool
IsCapacity bool
Asc bool
}

Expand All @@ -232,7 +234,7 @@ func (r RowEventSorter) Swap(i, j int) {
func (r RowEventSorter) Less(i, j int) bool {
f1, f2 := r.Events[i].Row.Fields, r.Events[j].Row.Fields
id1, id2 := r.Events[i].Row.ID, r.Events[j].Row.ID
less := Less(r.IsNumber, r.IsDuration, id1, id2, f1[r.Index], f2[r.Index])
less := Less(r.IsNumber, r.IsDuration, r.IsCapacity, id1, id2, f1[r.Index], f2[r.Index])
if r.Asc {
return less
}
Expand Down
24 changes: 23 additions & 1 deletion internal/render/row_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ func TestRowEventsSort(t *testing.T) {
re render.RowEvents
col int
duration, num, asc bool
capacity bool
e render.RowEvents
}{
"age_time": {
Expand Down Expand Up @@ -464,12 +465,33 @@ func TestRowEventsSort(t *testing.T) {
{Row: render.Row{ID: "ns2/C", Fields: render.Fields{"C", "2", "3"}}},
},
},
"capacity": {
re: render.RowEvents{
{Row: render.Row{ID: "ns1/B", Fields: render.Fields{"B", "2", "3", "1Gi"}}},
{Row: render.Row{ID: "ns1/A", Fields: render.Fields{"A", "2", "3", "1.1G"}}},
{Row: render.Row{ID: "ns1/C", Fields: render.Fields{"C", "2", "3", "0.5Ti"}}},
{Row: render.Row{ID: "ns2/B", Fields: render.Fields{"B", "2", "3", "12e6"}}},
{Row: render.Row{ID: "ns2/A", Fields: render.Fields{"A", "2", "3", "1234"}}},
{Row: render.Row{ID: "ns2/C", Fields: render.Fields{"C", "2", "3", "0.1Ei"}}},
},
col: 3,
asc: true,
capacity: true,
e: render.RowEvents{
{Row: render.Row{ID: "ns2/A", Fields: render.Fields{"A", "2", "3", "1234"}}},
{Row: render.Row{ID: "ns2/B", Fields: render.Fields{"B", "2", "3", "12e6"}}},
{Row: render.Row{ID: "ns1/B", Fields: render.Fields{"B", "2", "3", "1Gi"}}},
{Row: render.Row{ID: "ns1/A", Fields: render.Fields{"A", "2", "3", "1.1G"}}},
{Row: render.Row{ID: "ns1/C", Fields: render.Fields{"C", "2", "3", "0.5Ti"}}},
{Row: render.Row{ID: "ns2/C", Fields: render.Fields{"C", "2", "3", "0.1Ei"}}},
},
},
}

for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.re.Sort("", u.col, u.duration, u.num, u.asc)
u.re.Sort("", u.col, u.duration, u.num, u.capacity, u.asc)
assert.Equal(t, u.e, u.re)
})
}
Expand Down
82 changes: 74 additions & 8 deletions internal/render/row_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func TestRowsSortText(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.rows.Sort(u.col, u.asc, u.num, false)
u.rows.Sort(u.col, u.asc, u.num, false, false)
assert.Equal(t, u.e, u.rows)
})
}
Expand Down Expand Up @@ -369,7 +369,7 @@ func TestRowsSortDuration(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.rows.Sort(u.col, u.asc, false, true)
u.rows.Sort(u.col, u.asc, false, true, false)
assert.Equal(t, u.e, u.rows)
})
}
Expand Down Expand Up @@ -411,22 +411,67 @@ func TestRowsSortMetrics(t *testing.T) {
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.rows.Sort(u.col, u.asc, true, false)
u.rows.Sort(u.col, u.asc, true, false, false)
assert.Equal(t, u.e, u.rows)
})
}
}

func TestRowsSortCapacity(t *testing.T) {
uu := map[string]struct {
rows render.Rows
col int
asc bool
e render.Rows
}{
"capacityAsc": {
rows: render.Rows{
{Fields: []string{"10Gi", "duh"}},
{Fields: []string{"10G", "blee"}},
},
col: 0,
asc: true,
e: render.Rows{
{Fields: []string{"10G", "blee"}},
{Fields: []string{"10Gi", "duh"}},
},
},
"capacityDesc": {
rows: render.Rows{
{Fields: []string{"10000m", "1000Mi"}},
{Fields: []string{"1m", "50Mi"}},
},
col: 1,
asc: false,
e: render.Rows{
{Fields: []string{"10000m", "1000Mi"}},
{Fields: []string{"1m", "50Mi"}},
},
},
}

for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.rows.Sort(u.col, u.asc, false, false, true)
assert.Equal(t, u.e, u.rows)
})
}
}

func TestLess(t *testing.T) {
uu := map[string]struct {
isNumber, isDuration bool
id1, id2 string
v1, v2 string
e bool
isNumber bool
isDuration bool
isCapacity bool
id1, id2 string
v1, v2 string
e bool
}{
"years": {
isNumber: false,
isDuration: true,
isCapacity: false,
id1: "id1",
id2: "id2",
v1: "2y263d",
Expand All @@ -435,17 +480,38 @@ func TestLess(t *testing.T) {
"hours": {
isNumber: false,
isDuration: true,
isCapacity: false,
id1: "id1",
id2: "id2",
v1: "2y263d",
v2: "19h",
},
"capacity1": {
isNumber: false,
isDuration: false,
isCapacity: true,
id1: "id1",
id2: "id2",
v1: "1Gi",
v2: "1G",
e: false,
},
"capacity2": {
isNumber: false,
isDuration: false,
isCapacity: true,
id1: "id1",
id2: "id2",
v1: "1Gi",
v2: "1Ti",
e: true,
},
}

for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
assert.Equal(t, u.e, render.Less(u.isNumber, u.isDuration, u.id1, u.id2, u.v1, u.v2))
assert.Equal(t, u.e, render.Less(u.isNumber, u.isDuration, u.isCapacity, u.id1, u.id2, u.v1, u.v2))
})
}
}
1 change: 1 addition & 0 deletions internal/ui/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ func (t *Table) doUpdate(data *render.TableData) {
colIndex,
custData.Header.IsTimeCol(colIndex),
custData.Header.IsMetricsCol(colIndex),
custData.Header.IsCapacityCol(colIndex),
t.sortCol.asc,
)

Expand Down

0 comments on commit b2be433

Please sign in to comment.