K9s rel v0.25.21 (#1635)

* additional tests + cleanup

* update docs

* update docs
mine
Fernand Galiana 2022-06-30 12:03:33 -06:00 committed by GitHub
parent 7c5ba99cc1
commit 66c22f1c6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 174 additions and 55 deletions

View File

@ -5,7 +5,7 @@ PACKAGE := github.com/derailed/$(NAME)
GIT_REV ?= $(shell git rev-parse --short HEAD)
SOURCE_DATE_EPOCH ?= $(shell date +%s)
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
VERSION ?= v0.25.20
VERSION ?= v0.25.21
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -0,0 +1,33 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# Release v0.25.21
## Notes
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better are as ever very much noted and appreciated! Also big thanks to all that have allocated their own time to help others on both slack and this repo!!
If you feel K9s is helping your Kubernetes journey, please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
---
## Maintenance Release
---
## Resolved Issues
* [Issue #1634](https://github.com/derailed/k9s/issues/1634) Namespace view all has the age field in strange format
* [Issue #1633](https://github.com/derailed/k9s/issues/1633) Nodes sort by age has wrong order
## Resolved PR
* [PR #1632](https://github.com/derailed/k9s/pull/1632) Fix delete dialog dropdown styling
* [PR #1629](https://github.com/derailed/k9s/pull/1629) Fix reference to base image in dockerfile
* [PR #1627](https://github.com/derailed/k9s/pull/1627) Fix TestToAge
* [PR #1624](https://github.com/derailed/k9s/pull/1624) Change makefile version
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2021 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

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=
@ -999,8 +999,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: "3y197d",
},
}
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
}