additional tests + cleanup

mine
derailed 2022-06-30 11:26:06 -06:00
parent 373fd4587b
commit 27b0aa6135
13 changed files with 140 additions and 54 deletions

4
go.mod
View File

@ -59,7 +59,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
github.com/containerd/containerd v1.6.3 // indirect
github.com/containerd/containerd v1.6.6 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v20.10.11+incompatible // indirect
@ -147,7 +147,7 @@ require (
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect

9
go.sum
View File

@ -83,7 +83,7 @@ github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFP
github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE=
github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/hcsshim v0.9.2 h1:wB06W5aYFfUB3IvootYAY2WnOmIdgPGfqSI6tufQNnY=
github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@ -156,8 +156,8 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4=
github.com/containerd/containerd v1.6.3 h1:JfgUEIAH07xDWk6kqz0P3ArZt+KJ9YeihSC9uyFtSKg=
github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig=
github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0=
github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
@ -995,8 +995,9 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@ -23,15 +23,16 @@ var (
// Helm represents a helm chart.
type Helm struct {
NonResource
cfg *action.Configuration
ns string
}
// List returns a collection of resources.
func (c *Helm) List(ctx context.Context, ns string) ([]runtime.Object, error) {
cfg, err := c.EnsureHelmConfig(ns)
func (h *Helm) List(ctx context.Context, ns string) ([]runtime.Object, error) {
cfg, err := h.EnsureHelmConfig(ns)
if err != nil {
return nil, err
}
rr, err := action.NewList(cfg).Run()
if err != nil {
return nil, err
@ -46,9 +47,9 @@ func (c *Helm) List(ctx context.Context, ns string) ([]runtime.Object, error) {
}
// Get returns a resource.
func (c *Helm) Get(_ context.Context, path string) (runtime.Object, error) {
func (h *Helm) Get(_ context.Context, path string) (runtime.Object, error) {
ns, n := client.Namespaced(path)
cfg, err := c.EnsureHelmConfig(ns)
cfg, err := h.EnsureHelmConfig(ns)
if err != nil {
return nil, err
}
@ -61,9 +62,9 @@ func (c *Helm) Get(_ context.Context, path string) (runtime.Object, error) {
}
// GetValues returns values for a release
func (c *Helm) GetValues(path string, allValues bool) ([]byte, error) {
func (h *Helm) GetValues(path string, allValues bool) ([]byte, error) {
ns, n := client.Namespaced(path)
cfg, err := c.EnsureHelmConfig(ns)
cfg, err := h.EnsureHelmConfig(ns)
if err != nil {
return nil, err
}
@ -78,9 +79,9 @@ func (c *Helm) GetValues(path string, allValues bool) ([]byte, error) {
}
// Describe returns the chart notes.
func (c *Helm) Describe(path string) (string, error) {
func (h *Helm) Describe(path string) (string, error) {
ns, n := client.Namespaced(path)
cfg, err := c.EnsureHelmConfig(ns)
cfg, err := h.EnsureHelmConfig(ns)
if err != nil {
return "", err
}
@ -93,9 +94,9 @@ func (c *Helm) Describe(path string) (string, error) {
}
// ToYAML returns the chart manifest.
func (c *Helm) ToYAML(path string, showManaged bool) (string, error) {
func (h *Helm) ToYAML(path string, showManaged bool) (string, error) {
ns, n := client.Namespaced(path)
cfg, err := c.EnsureHelmConfig(ns)
cfg, err := h.EnsureHelmConfig(ns)
if err != nil {
return "", err
}
@ -108,9 +109,9 @@ func (c *Helm) ToYAML(path string, showManaged bool) (string, error) {
}
// Delete uninstall a Helm.
func (c *Helm) Delete(path string, _ *metav1.DeletionPropagation, force bool) error {
func (h *Helm) Delete(path string, _ *metav1.DeletionPropagation, force bool) error {
ns, n := client.Namespaced(path)
cfg, err := c.EnsureHelmConfig(ns)
cfg, err := h.EnsureHelmConfig(ns)
if err != nil {
return err
}
@ -128,12 +129,15 @@ func (c *Helm) Delete(path string, _ *metav1.DeletionPropagation, force bool) er
}
// EnsureHelmConfig return a new configuration.
func (c *Helm) EnsureHelmConfig(ns string) (*action.Configuration, error) {
cfg := new(action.Configuration)
if err := cfg.Init(c.Client().Config().Flags(), ns, os.Getenv("HELM_DRIVER"), helmLogger); err != nil {
func (h *Helm) EnsureHelmConfig(ns string) (*action.Configuration, error) {
if h.cfg != nil && h.ns == ns {
return h.cfg, nil
}
h.cfg = new(action.Configuration)
if err := h.cfg.Init(h.Client().Config().Flags(), ns, os.Getenv("HELM_DRIVER"), helmLogger); err != nil {
return nil, err
}
return cfg, nil
return h.cfg, nil
}
func helmLogger(s string, args ...interface{}) {

View File

@ -150,6 +150,7 @@ func (h Header) IsMetricsCol(col int) bool {
if col < 0 || col >= len(h) {
return false
}
return h[col].MX
}

View File

@ -96,10 +96,6 @@ func TestToAge(t *testing.T) {
t: time.Time{},
e: UnknownValue,
},
"good": {
t: testTime().Add(-10 * time.Second),
e: "3y196d",
},
}
for k := range uu {

View File

@ -177,14 +177,18 @@ 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
return Less(s.Asc, s.IsNumber, s.IsDuration, id1, id2, v1, v2)
less := Less(s.IsNumber, s.IsDuration, id1, id2, v1, v2)
if s.Asc {
return less
}
return !less
}
// ----------------------------------------------------------------------------
// Helpers...
// Less return true if c1 < c2.
func Less(asc, isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
func Less(isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
var less bool
switch {
case isNumber:
@ -196,12 +200,9 @@ func Less(asc, isNumber, isDuration bool, id1, id2, v1, v2 string) bool {
default:
less = sortorder.NaturalLess(v1, v2)
}
if v1 == v2 {
return sortorder.NaturalLess(id1, id2)
}
if asc {
return less
}
return !less
return less
}

View File

@ -239,7 +239,12 @@ 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
return Less(r.Asc, r.IsNumber, r.IsDuration, id1, id2, f1[r.Index], f2[r.Index])
less := Less(r.IsNumber, r.IsDuration, id1, id2, f1[r.Index], f2[r.Index])
if r.Asc {
return less
}
return !less
}
// ----------------------------------------------------------------------------

View File

@ -317,6 +317,18 @@ func TestRowsSortDuration(t *testing.T) {
asc bool
e render.Rows
}{
"years": {
rows: render.Rows{
{Fields: []string{testTime().Add(-365 * 24 * time.Hour).String(), "blee"}},
{Fields: []string{testTime().String(), "duh"}},
},
col: 0,
asc: true,
e: render.Rows{
{Fields: []string{testTime().String(), "duh"}},
{Fields: []string{testTime().Add(-365 * 24 * time.Hour).String(), "blee"}},
},
},
"durationAsc": {
rows: render.Rows{
{Fields: []string{testTime().Add(10 * time.Second).String(), "duh"}},
@ -392,3 +404,36 @@ func TestRowsSortMetrics(t *testing.T) {
})
}
}
func TestLess(t *testing.T) {
uu := map[string]struct {
isNumber, isDuration bool
id1, id2 string
v1, v2 string
e bool
}{
"years": {
isNumber: false,
isDuration: true,
id1: "id1",
id2: "id2",
v1: "2y263d",
v2: "1y179d",
},
"hours": {
isNumber: false,
isDuration: true,
id1: "id1",
id2: "id2",
v1: "2y263d",
v2: "19h",
},
}
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))
})
}
}

View File

@ -244,7 +244,7 @@ func (t *Table) doUpdate(data render.TableData) {
custData.Namespace,
colIndex,
custData.Header.IsTimeCol(colIndex),
data.Header.IsMetricsCol(colIndex),
custData.Header.IsMetricsCol(colIndex),
t.sortCol.asc,
)

View File

@ -184,7 +184,7 @@ func clearScreen() {
const (
k9sShell = "k9s-shell"
k9sShellRetryCount = 10
k9sShellRetryDelay = 1 * time.Second
k9sShellRetryDelay = 10 * time.Second
)
func ssh(a *App, node string) error {

View File

@ -108,10 +108,8 @@ func TestLogViewSave(t *testing.T) {
dir := filepath.Join(app.Config.K9s.GetScreenDumpDir(), app.Config.K9s.CurrentCluster)
c1, _ := os.ReadDir(dir)
fmt.Println("C1", c1)
v.SaveCmd(nil)
c2, _ := os.ReadDir(dir)
fmt.Println("C2", c2)
assert.Equal(t, len(c2), len(c1)+1)
}

View File

@ -1,8 +1,6 @@
package view
import (
"time"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/render"
@ -85,7 +83,7 @@ func (n *Namespace) decorate(data render.TableData) render.TableData {
Kind: render.EventUnchanged,
Row: render.Row{
ID: client.NamespaceAll,
Fields: render.Fields{client.NamespaceAll, "Active", "", "", time.Now().String()},
Fields: render.Fields{client.NamespaceAll, "Active", "", "", ""},
},
},
)

View File

@ -70,20 +70,49 @@ func TestTableViewFilter(t *testing.T) {
v.CmdBuff().SetActive(true)
v.CmdBuff().SetText("blee", "")
assert.Equal(t, 3, v.GetRowCount())
assert.Equal(t, 5, v.GetRowCount())
}
func TestTableViewSort(t *testing.T) {
v := NewTable(client.NewGVR("test"))
v.Init(makeContext())
v.SetModel(&mockTableModel{})
v.SortColCmd("NAME", true)(nil)
assert.Equal(t, 3, v.GetRowCount())
assert.Equal(t, "blee", v.GetCell(1, 0).Text)
v.SortInvertCmd(nil)
assert.Equal(t, 3, v.GetRowCount())
assert.Equal(t, "fred", v.GetCell(1, 0).Text)
uu := map[string]struct {
sortCol string
sorted []string
reversed []string
}{
"by_name": {
sortCol: "NAME",
sorted: []string{"r0", "r1", "r2", "r3"},
reversed: []string{"r3", "r2", "r1", "r0"},
},
"by_age": {
sortCol: "AGE",
sorted: []string{"r0", "r1", "r2", "r3"},
reversed: []string{"r3", "r2", "r1", "r0"},
},
"by_fred": {
sortCol: "FRED",
sorted: []string{"r3", "r2", "r0", "r1"},
reversed: []string{"r1", "r0", "r2", "r3"},
},
}
for k := range uu {
u := uu[k]
v.SortColCmd(u.sortCol, true)(nil)
assert.Equal(t, len(u.sorted)+1, v.GetRowCount())
for i, s := range u.sorted {
assert.Equal(t, s, v.GetCell(i+1, 0).Text)
}
v.SortInvertCmd(nil)
assert.Equal(t, len(u.reversed)+1, v.GetRowCount())
for i, s := range u.reversed {
assert.Equal(t, s, v.GetCell(i+1, 0).Text)
}
}
}
// ----------------------------------------------------------------------------
@ -128,27 +157,35 @@ func (t *mockTableModel) SetRefreshRate(time.Duration) {}
func makeTableData() render.TableData {
t := render.NewTableData()
t.Header = render.Header{
render.HeaderColumn{Name: "NAMESPACE"},
render.HeaderColumn{Name: "NAME", Align: tview.AlignRight},
render.HeaderColumn{Name: "FRED"},
render.HeaderColumn{Name: "AGE", Time: true, Decorator: render.AgeDecorator},
render.HeaderColumn{Name: "AGE", Time: true},
}
t.RowEvents = render.RowEvents{
render.RowEvent{
Row: render.Row{
Fields: render.Fields{"ns1", "blee", "10", "3m"},
Fields: render.Fields{"ns1", "r3", "10", "3y125d"},
},
},
render.RowEvent{
Row: render.Row{
Fields: render.Fields{"ns1", "fred", "15", "1m"},
Fields: render.Fields{"ns1", "r2", "15", "2y12d"},
},
Deltas: render.DeltaRow{"", "", "20", ""},
},
render.RowEvent{
Row: render.Row{
Fields: render.Fields{"ns1", "r1", "20", "19h"},
},
},
render.RowEvent{
Row: render.Row{
Fields: render.Fields{"ns1", "r0", "15", "10s"},
},
},
}
t.Namespace = ""
return *t
}