parent
e55083ba27
commit
142282b584
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
||||||
else
|
else
|
||||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
endif
|
endif
|
||||||
VERSION ?= v0.50.0
|
VERSION ?= v0.50.1
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.51
|
||||||
|
|
||||||
|
## 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 on this repo!!
|
||||||
|
|
||||||
|
As you may know, K9s is not pimped out by corps with deep pockets, thus 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)
|
||||||
|
|
||||||
|
## 5-0, 5-0 HotFix!
|
||||||
|
|
||||||
|
It looks like we've broken a few things in the clean up process 😳
|
||||||
|
Apologizes for the `disruption in the farce`. Hopefully happier on v0.50.1...
|
||||||
|
Crossing fingers and toes!
|
||||||
|
|
||||||
|
☠️ Careful on this upgrade! 🏴☠️
|
||||||
|
We've gone thru lots of code revamp/refactor in the v0.50.0, so mileage may vary...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Issues
|
||||||
|
|
||||||
|
* [#3262](https://github.com/derailed/k9s/issues/3262) Crash when no shellPod is defined in config file
|
||||||
|
* [#3261](https://github.com/derailed/k9s/issues/3261) aliases with namespace and/or labels produce an error
|
||||||
|
* [#3258](https://github.com/derailed/k9s/issues/3258) mac silicon 0.50.0 runtime error
|
||||||
|
* [#3257](https://github.com/derailed/k9s/issues/3257) pods are reported to run on nodes they are not running on
|
||||||
|
* [#3256](https://github.com/derailed/k9s/issues/3256) Pods view seems broken in 0.50.0
|
||||||
|
* [#3255](https://github.com/derailed/k9s/issues/3255) Custom view does not work randomly
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
@ -71,6 +71,10 @@ func NewGVR(path string) *GVR {
|
||||||
return &gvr
|
return &gvr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GVR) IsCommand() bool {
|
||||||
|
return g != nil && strings.Contains(g.raw, " ")
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GVR) IsK8sRes() bool {
|
func (g *GVR) IsK8sRes() bool {
|
||||||
return strings.Contains(g.raw, "/")
|
return strings.Contains(g.raw, "/")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -15,6 +14,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
"github.com/derailed/k9s/internal/config/json"
|
"github.com/derailed/k9s/internal/config/json"
|
||||||
"github.com/derailed/k9s/internal/slogs"
|
"github.com/derailed/k9s/internal/slogs"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
@ -79,26 +79,35 @@ func (a *Aliases) Clear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) Resolve(command string) (*client.GVR, string, bool) {
|
||||||
|
agvr, ok := a.Get(command)
|
||||||
|
if !ok {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
if agvr.IsCommand() {
|
||||||
|
p := cmd.NewInterpreter(agvr.String())
|
||||||
|
gvr, ok := a.Get(p.Cmd())
|
||||||
|
if !ok {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
return gvr, p.Args(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return agvr, "", true
|
||||||
|
}
|
||||||
|
|
||||||
// Get retrieves an alias.
|
// Get retrieves an alias.
|
||||||
func (a *Aliases) Get(alias string) (*client.GVR, bool) {
|
func (a *Aliases) Get(alias string) (*client.GVR, bool) {
|
||||||
a.mx.RLock()
|
a.mx.RLock()
|
||||||
defer a.mx.RUnlock()
|
defer a.mx.RUnlock()
|
||||||
|
|
||||||
gvr, ok := a.Alias[alias]
|
gvr, ok := a.Alias[alias]
|
||||||
if ok && !gvr.IsK8sRes() {
|
|
||||||
if rgvr, found := a.Alias[gvr.String()]; found {
|
|
||||||
return rgvr, found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return gvr, ok
|
return gvr, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define declares a new alias.
|
// Define declares a new alias.
|
||||||
func (a *Aliases) Define(gvr *client.GVR, aliases ...string) {
|
func (a *Aliases) Define(gvr *client.GVR, aliases ...string) {
|
||||||
if gvr.String() == "deployment" {
|
|
||||||
fmt.Println("!!YO!!")
|
|
||||||
}
|
|
||||||
a.mx.Lock()
|
a.mx.Lock()
|
||||||
defer a.mx.Unlock()
|
defer a.mx.Unlock()
|
||||||
for _, alias := range aliases {
|
for _, alias := range aliases {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ func TestConfigSave(t *testing.T) {
|
||||||
xdg.Reload()
|
xdg.Reload()
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, err := c.K9s.ActivateContext(u.ct)
|
_, err := c.K9s.ActivateContext(u.ct)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if u.flags != nil {
|
if u.flags != nil {
|
||||||
|
|
@ -113,7 +113,7 @@ func TestSetActiveView(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, _ = c.K9s.ActivateContext(u.ct)
|
_, _ = c.K9s.ActivateContext(u.ct)
|
||||||
if u.flags != nil {
|
if u.flags != nil {
|
||||||
require.NoError(t, c.Refine(u.flags, nil, client.NewConfig(u.flags)))
|
require.NoError(t, c.Refine(u.flags, nil, client.NewConfig(u.flags)))
|
||||||
|
|
@ -156,7 +156,7 @@ func TestActiveContextName(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, _ = c.K9s.ActivateContext(u.ct)
|
_, _ = c.K9s.ActivateContext(u.ct)
|
||||||
if u.flags != nil {
|
if u.flags != nil {
|
||||||
require.NoError(t, c.Refine(u.flags, nil, client.NewConfig(u.flags)))
|
require.NoError(t, c.Refine(u.flags, nil, client.NewConfig(u.flags)))
|
||||||
|
|
@ -182,14 +182,22 @@ func TestActiveView(t *testing.T) {
|
||||||
"empty": {
|
"empty": {
|
||||||
e: data.DefaultView,
|
e: data.DefaultView,
|
||||||
},
|
},
|
||||||
|
|
||||||
"not-exists": {
|
"not-exists": {
|
||||||
ct: "fred",
|
ct: "fred",
|
||||||
e: data.DefaultView,
|
e: data.DefaultView,
|
||||||
},
|
},
|
||||||
|
|
||||||
"happy": {
|
"happy": {
|
||||||
ct: "ct-1-1",
|
ct: "ct-1-1",
|
||||||
e: data.DefaultView,
|
e: data.DefaultView,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"happy1": {
|
||||||
|
ct: "ct-1-2",
|
||||||
|
e: data.DefaultView,
|
||||||
|
},
|
||||||
|
|
||||||
"cli-override": {
|
"cli-override": {
|
||||||
flags: &genericclioptions.ConfigFlags{
|
flags: &genericclioptions.ConfigFlags{
|
||||||
KubeConfig: &cfgFile,
|
KubeConfig: &cfgFile,
|
||||||
|
|
@ -204,7 +212,7 @@ func TestActiveView(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, _ = c.K9s.ActivateContext(u.ct)
|
_, _ = c.K9s.ActivateContext(u.ct)
|
||||||
if u.flags != nil {
|
if u.flags != nil {
|
||||||
require.NoError(t, c.Refine(u.flags, nil, client.NewConfig(u.flags)))
|
require.NoError(t, c.Refine(u.flags, nil, client.NewConfig(u.flags)))
|
||||||
|
|
@ -233,7 +241,7 @@ func TestFavNamespaces(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, _ = c.K9s.ActivateContext(u.ct)
|
_, _ = c.K9s.ActivateContext(u.ct)
|
||||||
assert.Equal(t, u.e, c.FavNamespaces())
|
assert.Equal(t, u.e, c.FavNamespaces())
|
||||||
})
|
})
|
||||||
|
|
@ -258,7 +266,7 @@ func TestContextAliasesPath(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, _ = c.K9s.ActivateContext(u.ct)
|
_, _ = c.K9s.ActivateContext(u.ct)
|
||||||
assert.Equal(t, u.e, c.ContextAliasesPath())
|
assert.Equal(t, u.e, c.ContextAliasesPath())
|
||||||
})
|
})
|
||||||
|
|
@ -286,7 +294,7 @@ func TestContextPluginsPath(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
c := mock.NewMockConfig()
|
c := mock.NewMockConfig(t)
|
||||||
_, _ = c.K9s.ActivateContext(u.ct)
|
_, _ = c.K9s.ActivateContext(u.ct)
|
||||||
s, err := c.ContextPluginsPath()
|
s, err := c.ContextPluginsPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -344,7 +352,7 @@ func TestConfigActivateContext(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
ct, err := cfg.ActivateContext(u.ct)
|
ct, err := cfg.ActivateContext(u.ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.Equal(t, u.err, err.Error())
|
assert.Equal(t, u.err, err.Error())
|
||||||
|
|
@ -391,7 +399,7 @@ func TestConfigCurrentContext(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
|
|
||||||
err := cfg.Refine(u.flags, nil, client.NewConfig(u.flags))
|
err := cfg.Refine(u.flags, nil, client.NewConfig(u.flags))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -511,7 +519,7 @@ func TestConfigRefine(t *testing.T) {
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
|
|
||||||
err := cfg.Refine(u.flags, u.k9sFlags, client.NewConfig(u.flags))
|
err := cfg.Refine(u.flags, u.k9sFlags, client.NewConfig(u.flags))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -526,7 +534,7 @@ func TestConfigRefine(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigValidate(t *testing.T) {
|
func TestConfigValidate(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
cfg.SetConnection(mock.NewMockConnection())
|
cfg.SetConnection(mock.NewMockConnection())
|
||||||
|
|
||||||
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
||||||
|
|
@ -534,7 +542,7 @@ func TestConfigValidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigLoad(t *testing.T) {
|
func TestConfigLoad(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
|
|
||||||
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
||||||
assert.Equal(t, 2, cfg.K9s.RefreshRate)
|
assert.Equal(t, 2, cfg.K9s.RefreshRate)
|
||||||
|
|
@ -543,13 +551,13 @@ func TestConfigLoad(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigLoadCrap(t *testing.T) {
|
func TestConfigLoadCrap(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
|
|
||||||
assert.Error(t, cfg.Load("testdata/configs/k9s_not_there.yaml", true))
|
assert.Error(t, cfg.Load("testdata/configs/k9s_not_there.yaml", true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigSaveFile(t *testing.T) {
|
func TestConfigSaveFile(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
|
|
||||||
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
||||||
|
|
||||||
|
|
@ -570,7 +578,7 @@ func TestConfigSaveFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigReset(t *testing.T) {
|
func TestConfigReset(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
||||||
cfg.Reset()
|
cfg.Reset()
|
||||||
cfg.Validate("ct-1-1", "cl-1")
|
cfg.Validate("ct-1-1", "cl-1")
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ func TestClusterValidate(t *testing.T) {
|
||||||
c := data.NewContext()
|
c := data.NewContext()
|
||||||
c.Validate(mock.NewMockConnection(), "ct-1", "cl-1")
|
c.Validate(mock.NewMockConnection(), "ct-1", "cl-1")
|
||||||
|
|
||||||
assert.Equal(t, "po", c.View.Active)
|
assert.Equal(t, data.DefaultView, c.View.Active)
|
||||||
assert.Equal(t, "default", c.Namespace.Active)
|
assert.Equal(t, "default", c.Namespace.Active)
|
||||||
assert.Len(t, c.Namespace.Favorites, 1)
|
assert.Len(t, c.Namespace.Favorites, 1)
|
||||||
assert.Equal(t, []string{"default"}, c.Namespace.Favorites)
|
assert.Equal(t, []string{"default"}, c.Namespace.Favorites)
|
||||||
|
|
@ -25,7 +25,7 @@ func TestClusterValidateEmpty(t *testing.T) {
|
||||||
c := data.NewContext()
|
c := data.NewContext()
|
||||||
c.Validate(mock.NewMockConnection(), "ct-1", "cl-1")
|
c.Validate(mock.NewMockConnection(), "ct-1", "cl-1")
|
||||||
|
|
||||||
assert.Equal(t, "po", c.View.Active)
|
assert.Equal(t, data.DefaultView, c.View.Active)
|
||||||
assert.Equal(t, "default", c.Namespace.Active)
|
assert.Equal(t, "default", c.Namespace.Active)
|
||||||
assert.Len(t, c.Namespace.Favorites, 1)
|
assert.Len(t, c.Namespace.Favorites, 1)
|
||||||
assert.Equal(t, []string{"default"}, c.Namespace.Favorites)
|
assert.Equal(t, []string{"default"}, c.Namespace.Favorites)
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,9 @@ func (k *K9s) Validate(c client.Connection, contextName, clusterName string) {
|
||||||
if k.getActiveConfig() == nil {
|
if k.getActiveConfig() == nil {
|
||||||
_, _ = k.ActivateContext(contextName)
|
_, _ = k.ActivateContext(contextName)
|
||||||
}
|
}
|
||||||
|
if k.ShellPod != nil {
|
||||||
k.ShellPod.Validate()
|
k.ShellPod.Validate()
|
||||||
|
}
|
||||||
k.Logger = k.Logger.Validate()
|
k.Logger = k.Logger.Validate()
|
||||||
k.Thresholds = k.Thresholds.Validate()
|
k.Thresholds = k.Thresholds.Validate()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ func TestK9sMerge(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextScreenDumpDir(t *testing.T) {
|
func TestContextScreenDumpDir(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
_, err := cfg.K9s.ActivateContext("ct-1-1")
|
_, err := cfg.K9s.ActivateContext("ct-1-1")
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -142,7 +142,7 @@ func TestContextScreenDumpDir(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppScreenDumpDir(t *testing.T) {
|
func TestAppScreenDumpDir(t *testing.T) {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
|
|
||||||
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
require.NoError(t, cfg.Load("testdata/configs/k9s.yaml", true))
|
||||||
assert.Equal(t, "/tmp/k9s-test/screen-dumps", cfg.K9s.AppScreenDumpDir())
|
assert.Equal(t, "/tmp/k9s-test/screen-dumps", cfg.K9s.AppScreenDumpDir())
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,11 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
version "k8s.io/apimachinery/pkg/version"
|
version "k8s.io/apimachinery/pkg/version"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
disk "k8s.io/client-go/discovery/cached/disk"
|
disk "k8s.io/client-go/discovery/cached/disk"
|
||||||
|
|
@ -35,9 +37,11 @@ func EnsureDir(d string) error {
|
||||||
return os.MkdirAll(d, 0700)
|
return os.MkdirAll(d, 0700)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockConfig() *config.Config {
|
func NewMockConfig(t testing.TB) *config.Config {
|
||||||
if _, err := os.Stat("/tmp/test"); errors.Is(err, os.ErrExist) {
|
if _, err := os.Stat("/tmp/test"); err == nil {
|
||||||
_ = os.RemoveAll("/tmp/test")
|
if e := os.RemoveAll("/tmp/test"); e != nil {
|
||||||
|
require.NoError(t, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
config.AppContextsDir = "/tmp/test"
|
config.AppContextsDir = "/tmp/test"
|
||||||
cl, ct := "cl-1", "ct-1-1"
|
cl, ct := "cl-1", "ct-1-1"
|
||||||
|
|
|
||||||
|
|
@ -63,14 +63,7 @@ func (*Alias) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
||||||
|
|
||||||
// AsGVR returns a matching gvr if it exists.
|
// AsGVR returns a matching gvr if it exists.
|
||||||
func (a *Alias) AsGVR(alias string) (*client.GVR, string, bool) {
|
func (a *Alias) AsGVR(alias string) (*client.GVR, string, bool) {
|
||||||
gvr, ok := a.Aliases.Get(alias)
|
return a.Resolve(alias)
|
||||||
if ok {
|
|
||||||
if pgvr := MetaAccess.Lookup(alias); pgvr != client.NoGVR {
|
|
||||||
return pgvr, "", ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return gvr, "", ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get fetch a resource.
|
// Get fetch a resource.
|
||||||
|
|
@ -106,10 +99,9 @@ func (a *Alias) load(path string) error {
|
||||||
}
|
}
|
||||||
a.Define(gvr, gvr.AsResourceName())
|
a.Define(gvr, gvr.AsResourceName())
|
||||||
|
|
||||||
// Allow single shot commands for k8s resources only!
|
// Allow single shot commands for k8s resources only expect for metrics resource which override pods and nodes ;(!
|
||||||
if isStandardGroup(gvr.GVSub()) {
|
if isStandardGroup(gvr.GVSub()) && gvr.G() != "metrics.k8s.io" {
|
||||||
a.Define(gvr, meta.Name)
|
a.Define(gvr, meta.Name, meta.SingularName)
|
||||||
a.Define(gvr, meta.SingularName)
|
|
||||||
}
|
}
|
||||||
if len(meta.ShortNames) > 0 {
|
if len(meta.ShortNames) > 0 {
|
||||||
a.Define(gvr, meta.ShortNames...)
|
a.Define(gvr, meta.ShortNames...)
|
||||||
|
|
|
||||||
|
|
@ -20,39 +20,60 @@ func TestAsGVR(t *testing.T) {
|
||||||
a := dao.NewAlias(makeFactory())
|
a := dao.NewAlias(makeFactory())
|
||||||
a.Define(client.PodGVR, "po", "pod", "pods")
|
a.Define(client.PodGVR, "po", "pod", "pods")
|
||||||
a.Define(client.WkGVR, client.WkGVR.String(), "workload", "wkl")
|
a.Define(client.WkGVR, client.WkGVR.String(), "workload", "wkl")
|
||||||
|
a.Define(client.NewGVR("pod default"), "pp")
|
||||||
|
a.Define(client.NewGVR("pod default @fred"), "ppc")
|
||||||
|
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
cmd string
|
cmd string
|
||||||
ok bool
|
ok bool
|
||||||
gvr *client.GVR
|
gvr *client.GVR
|
||||||
|
exp string
|
||||||
}{
|
}{
|
||||||
"ok": {
|
"ok": {
|
||||||
cmd: "pods",
|
cmd: "pods",
|
||||||
ok: true,
|
ok: true,
|
||||||
gvr: client.PodGVR,
|
gvr: client.PodGVR,
|
||||||
},
|
},
|
||||||
|
|
||||||
"ok-short": {
|
"ok-short": {
|
||||||
cmd: "po",
|
cmd: "po",
|
||||||
ok: true,
|
ok: true,
|
||||||
gvr: client.PodGVR,
|
gvr: client.PodGVR,
|
||||||
},
|
},
|
||||||
|
|
||||||
"missing": {
|
"missing": {
|
||||||
cmd: "zorg",
|
cmd: "zorg",
|
||||||
},
|
},
|
||||||
|
|
||||||
"alias": {
|
"alias": {
|
||||||
cmd: "wkl",
|
cmd: "wkl",
|
||||||
ok: true,
|
ok: true,
|
||||||
gvr: client.WkGVR,
|
gvr: client.WkGVR,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ns-alias": {
|
||||||
|
cmd: "pp",
|
||||||
|
ok: true,
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
exp: "default",
|
||||||
|
},
|
||||||
|
|
||||||
|
"full-alias": {
|
||||||
|
cmd: "ppc",
|
||||||
|
ok: true,
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
exp: "default @fred",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range uu {
|
for k := range uu {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
gvr, _, ok := a.AsGVR(u.cmd)
|
gvr, exp, ok := a.AsGVR(u.cmd)
|
||||||
assert.Equal(t, u.ok, ok)
|
assert.Equal(t, u.ok, ok)
|
||||||
if u.ok {
|
if u.ok {
|
||||||
assert.Equal(t, u.gvr, gvr)
|
assert.Equal(t, u.gvr, gvr)
|
||||||
|
assert.Equal(t, u.exp, exp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/model1"
|
"github.com/derailed/k9s/internal/model1"
|
||||||
|
|
@ -19,7 +18,6 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/cache"
|
|
||||||
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -76,22 +74,15 @@ var defaultPodHeader = model1.Header{
|
||||||
model1.HeaderColumn{Name: "AGE", Attrs: model1.Attrs{Time: true}},
|
model1.HeaderColumn{Name: "AGE", Attrs: model1.Attrs{Time: true}},
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
cacheSize = 5_000
|
|
||||||
expiration = 5 * time.Minute
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pod renders a K8s Pod to screen.
|
// Pod renders a K8s Pod to screen.
|
||||||
type Pod struct {
|
type Pod struct {
|
||||||
*Base
|
*Base
|
||||||
cache *cache.LRUExpireCache
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPod returns a new instance.
|
// NewPod returns a new instance.
|
||||||
func NewPod() *Pod {
|
func NewPod() *Pod {
|
||||||
return &Pod{
|
return &Pod{
|
||||||
Base: new(Base),
|
Base: new(Base),
|
||||||
cache: cache.NewLRUExpireCache(cacheSize),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,23 +148,10 @@ func (p *Pod) defaultRow(pwm *PodWithMetrics, row *model1.Row) error {
|
||||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(pwm.Raw.Object["status"].(map[string]any), &st); err != nil {
|
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(pwm.Raw.Object["status"].(map[string]any), &st); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
key := pwm.Raw.GetUID()
|
|
||||||
for _, o := range pwm.Raw.GetOwnerReferences() {
|
|
||||||
if o.Controller != nil && *o.Controller {
|
|
||||||
key = o.UID
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spec := new(v1.PodSpec)
|
spec := new(v1.PodSpec)
|
||||||
if cspec, ok := p.cache.Get(key); ok {
|
|
||||||
spec = cspec.(*v1.PodSpec)
|
|
||||||
} else {
|
|
||||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(pwm.Raw.Object["spec"].(map[string]any), spec); err != nil {
|
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(pwm.Raw.Object["spec"].(map[string]any), spec); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.cache.Add(key, spec, expiration)
|
|
||||||
}
|
|
||||||
|
|
||||||
dt := pwm.Raw.GetDeletionTimestamp()
|
dt := pwm.Raw.GetDeletionTimestamp()
|
||||||
_, _, irc, _ := p.Statuses(st.InitContainerStatuses)
|
_, _, irc, _ := p.Statuses(st.InitContainerStatuses)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAppGetCmd(t *testing.T) {
|
func TestAppGetCmd(t *testing.T) {
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "")
|
a := ui.NewApp(mock.NewMockConfig(t), "")
|
||||||
a.Init()
|
a.Init()
|
||||||
a.CmdBuff().SetText("blee", "")
|
a.CmdBuff().SetText("blee", "")
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ func TestAppGetCmd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppInCmdMode(t *testing.T) {
|
func TestAppInCmdMode(t *testing.T) {
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "")
|
a := ui.NewApp(mock.NewMockConfig(t), "")
|
||||||
a.Init()
|
a.Init()
|
||||||
a.CmdBuff().SetText("blee", "")
|
a.CmdBuff().SetText("blee", "")
|
||||||
assert.False(t, a.InCmdMode())
|
assert.False(t, a.InCmdMode())
|
||||||
|
|
@ -30,7 +30,7 @@ func TestAppInCmdMode(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppResetCmd(t *testing.T) {
|
func TestAppResetCmd(t *testing.T) {
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "")
|
a := ui.NewApp(mock.NewMockConfig(t), "")
|
||||||
a.Init()
|
a.Init()
|
||||||
a.CmdBuff().SetText("blee", "")
|
a.CmdBuff().SetText("blee", "")
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ func TestAppResetCmd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppHasCmd(t *testing.T) {
|
func TestAppHasCmd(t *testing.T) {
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "")
|
a := ui.NewApp(mock.NewMockConfig(t), "")
|
||||||
a.Init()
|
a.Init()
|
||||||
|
|
||||||
a.ActivateCmd(true)
|
a.ActivateCmd(true)
|
||||||
|
|
@ -51,7 +51,7 @@ func TestAppHasCmd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppGetActions(t *testing.T) {
|
func TestAppGetActions(t *testing.T) {
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "")
|
a := ui.NewApp(mock.NewMockConfig(t), "")
|
||||||
a.Init()
|
a.Init()
|
||||||
|
|
||||||
a.GetActions().Add(ui.KeyZ, ui.KeyAction{Description: "zorg"})
|
a.GetActions().Add(ui.KeyZ, ui.KeyAction{Description: "zorg"})
|
||||||
|
|
@ -60,7 +60,7 @@ func TestAppGetActions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppViews(t *testing.T) {
|
func TestAppViews(t *testing.T) {
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "")
|
a := ui.NewApp(mock.NewMockConfig(t), "")
|
||||||
a.Init()
|
a.Init()
|
||||||
|
|
||||||
vv := []string{"crumbs", "logo", "prompt", "menu"}
|
vv := []string{"crumbs", "logo", "prompt", "menu"}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func TestSkinnedContext(t *testing.T) {
|
||||||
require.NoError(t, os.WriteFile(tf, raw, data.DefaultFileMod))
|
require.NoError(t, os.WriteFile(tf, raw, data.DefaultFileMod))
|
||||||
|
|
||||||
var cfg ui.Configurator
|
var cfg ui.Configurator
|
||||||
cfg.Config = mock.NewMockConfig()
|
cfg.Config = mock.NewMockConfig(t)
|
||||||
cl, ct := "cl-1", "ct-1"
|
cl, ct := "cl-1", "ct-1"
|
||||||
flags := genericclioptions.ConfigFlags{
|
flags := genericclioptions.ConfigFlags{
|
||||||
ClusterName: &cl,
|
ClusterName: &cl,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ func TestFlash(t *testing.T) {
|
||||||
"err": {l: model.FlashErr, i: "hello", e: "😡 hello\n"},
|
"err": {l: model.FlashErr, i: "hello", e: "😡 hello\n"},
|
||||||
}
|
}
|
||||||
|
|
||||||
a := ui.NewApp(mock.NewMockConfig(), "test")
|
a := ui.NewApp(mock.NewMockConfig(t), "test")
|
||||||
f := ui.NewFlash(a)
|
f := ui.NewFlash(a)
|
||||||
f.SetTestMode(true)
|
f.SetTestMode(true)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIndicatorReset(t *testing.T) {
|
func TestIndicatorReset(t *testing.T) {
|
||||||
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(), ""), config.NewStyles())
|
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(t), ""), config.NewStyles())
|
||||||
i.SetPermanent("Blee")
|
i.SetPermanent("Blee")
|
||||||
i.Info("duh")
|
i.Info("duh")
|
||||||
i.Reset()
|
i.Reset()
|
||||||
|
|
@ -22,21 +22,21 @@ func TestIndicatorReset(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIndicatorInfo(t *testing.T) {
|
func TestIndicatorInfo(t *testing.T) {
|
||||||
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(), ""), config.NewStyles())
|
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(t), ""), config.NewStyles())
|
||||||
i.Info("Blee")
|
i.Info("Blee")
|
||||||
|
|
||||||
assert.Equal(t, "[lawngreen::b] <Blee> \n", i.GetText(false))
|
assert.Equal(t, "[lawngreen::b] <Blee> \n", i.GetText(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIndicatorWarn(t *testing.T) {
|
func TestIndicatorWarn(t *testing.T) {
|
||||||
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(), ""), config.NewStyles())
|
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(t), ""), config.NewStyles())
|
||||||
i.Warn("Blee")
|
i.Warn("Blee")
|
||||||
|
|
||||||
assert.Equal(t, "[mediumvioletred::b] <Blee> \n", i.GetText(false))
|
assert.Equal(t, "[mediumvioletred::b] <Blee> \n", i.GetText(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIndicatorErr(t *testing.T) {
|
func TestIndicatorErr(t *testing.T) {
|
||||||
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(), ""), config.NewStyles())
|
i := ui.NewStatusIndicator(ui.NewApp(mock.NewMockConfig(t), ""), config.NewStyles())
|
||||||
i.Err("Blee")
|
i.Err("Blee")
|
||||||
|
|
||||||
assert.Equal(t, "[orangered::b] <Blee> \n", i.GetText(false))
|
assert.Equal(t, "[orangered::b] <Blee> \n", i.GetText(false))
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,14 @@ import (
|
||||||
func TestAliasNew(t *testing.T) {
|
func TestAliasNew(t *testing.T) {
|
||||||
v := view.NewAlias(client.AliGVR)
|
v := view.NewAlias(client.AliGVR)
|
||||||
|
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
assert.Equal(t, "Aliases", v.Name())
|
assert.Equal(t, "Aliases", v.Name())
|
||||||
assert.Len(t, v.Hints(), 6)
|
assert.Len(t, v.Hints(), 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAliasSearch(t *testing.T) {
|
func TestAliasSearch(t *testing.T) {
|
||||||
v := view.NewAlias(client.AliGVR)
|
v := view.NewAlias(client.AliGVR)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
v.GetTable().SetModel(&mockModel{})
|
v.GetTable().SetModel(&mockModel{})
|
||||||
v.GetTable().Refresh()
|
v.GetTable().Refresh()
|
||||||
v.App().Prompt().SetModel(v.GetTable().CmdBuff())
|
v.App().Prompt().SetModel(v.GetTable().CmdBuff())
|
||||||
|
|
@ -46,7 +46,7 @@ func TestAliasSearch(t *testing.T) {
|
||||||
|
|
||||||
func TestAliasGoto(t *testing.T) {
|
func TestAliasGoto(t *testing.T) {
|
||||||
v := view.NewAlias(client.AliGVR)
|
v := view.NewAlias(client.AliGVR)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
v.GetTable().Select(0, 0)
|
v.GetTable().Select(0, 0)
|
||||||
|
|
||||||
b := buffL{}
|
b := buffL{}
|
||||||
|
|
@ -74,8 +74,8 @@ func (b *buffL) BufferActive(bool, model.BufferKind) {
|
||||||
b.active++
|
b.active++
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeContext() context.Context {
|
func makeContext(t testing.TB) context.Context {
|
||||||
a := view.NewApp(mock.NewMockConfig())
|
a := view.NewApp(mock.NewMockConfig(t))
|
||||||
ctx := context.WithValue(context.Background(), internal.KeyApp, a)
|
ctx := context.WithValue(context.Background(), internal.KeyApp, a)
|
||||||
return context.WithValue(ctx, internal.KeyStyles, a.Styles)
|
return context.WithValue(ctx, internal.KeyStyles, a.Styles)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -466,7 +466,7 @@ func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
|
||||||
p := cmd.NewInterpreter(a.Config.ActiveView())
|
p := cmd.NewInterpreter(a.Config.ActiveView())
|
||||||
p.ResetContextArg()
|
p.ResetContextArg()
|
||||||
if p.IsContextCmd() {
|
if p.IsContextCmd() {
|
||||||
a.Config.SetActiveView("pod")
|
a.Config.SetActiveView(client.PodGVR.String())
|
||||||
}
|
}
|
||||||
ns := a.Config.ActiveNamespace()
|
ns := a.Config.ActiveNamespace()
|
||||||
if !a.Conn().IsValidNamespace(ns) {
|
if !a.Conn().IsValidNamespace(ns) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAppNew(t *testing.T) {
|
func TestAppNew(t *testing.T) {
|
||||||
a := view.NewApp(mock.NewMockConfig())
|
a := view.NewApp(mock.NewMockConfig(t))
|
||||||
_ = a.Init("blee", 10)
|
_ = a.Init("blee", 10)
|
||||||
|
|
||||||
assert.Equal(t, 15, a.GetActions().Len())
|
assert.Equal(t, 15, a.GetActions().Len())
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestConfigMapNew(t *testing.T) {
|
func TestConfigMapNew(t *testing.T) {
|
||||||
s := view.NewConfigMap(client.CmGVR)
|
s := view.NewConfigMap(client.CmGVR)
|
||||||
|
|
||||||
require.NoError(t, s.Init(makeCtx()))
|
require.NoError(t, s.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "ConfigMaps", s.Name())
|
assert.Equal(t, "ConfigMaps", s.Name())
|
||||||
assert.Len(t, s.Hints(), 7)
|
assert.Len(t, s.Hints(), 7)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ func (c *Interpreter) Cmd() string {
|
||||||
return c.cmd
|
return c.cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Interpreter) Args() string {
|
||||||
|
return strings.TrimSpace(strings.Replace(c.line, c.cmd, "", 1))
|
||||||
|
}
|
||||||
|
|
||||||
// IsBlank returns true if prompt is empty.
|
// IsBlank returns true if prompt is empty.
|
||||||
func (c *Interpreter) IsBlank() bool {
|
func (c *Interpreter) IsBlank() bool {
|
||||||
return c.line == ""
|
return c.line == ""
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
|
@ -21,7 +20,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
podCmd = "pod"
|
podCmd = "v1/pods"
|
||||||
ctxCmd = "ctx"
|
ctxCmd = "ctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -291,11 +290,7 @@ func (c *Command) viewMetaFor(p *cmd.Interpreter) (*client.GVR, *MetaViewer, err
|
||||||
return client.NoGVR, nil, fmt.Errorf("`%s` command not found", p.Cmd())
|
return client.NoGVR, nil, fmt.Errorf("`%s` command not found", p.Cmd())
|
||||||
}
|
}
|
||||||
if exp != "" {
|
if exp != "" {
|
||||||
ff := strings.Fields(exp)
|
p.Amend(cmd.NewInterpreter(gvr.String() + " " + exp))
|
||||||
ff[0] = gvr.String()
|
|
||||||
ap := cmd.NewInterpreter(strings.Join(ff, " "))
|
|
||||||
gvr = client.NewGVR(ap.Cmd())
|
|
||||||
p.Amend(ap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v := MetaViewer{
|
v := MetaViewer{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
"github.com/derailed/k9s/internal/dao"
|
||||||
|
"github.com/derailed/k9s/internal/view/cmd"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_viewMetaFor(t *testing.T) {
|
||||||
|
uu := map[string]struct {
|
||||||
|
cmd string
|
||||||
|
gvr *client.GVR
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
"empty": {
|
||||||
|
cmd: "",
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
err: errors.New("`` command not found"),
|
||||||
|
},
|
||||||
|
|
||||||
|
"toast-cmd": {
|
||||||
|
cmd: "v1/pd",
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
err: errors.New("`v1/pd` command not found"),
|
||||||
|
},
|
||||||
|
|
||||||
|
"gvr-cmd": {
|
||||||
|
cmd: "v1/pods",
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
err: errors.New("blah"),
|
||||||
|
},
|
||||||
|
|
||||||
|
"alias-cmd": {
|
||||||
|
cmd: "po",
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
err: errors.New("blee"),
|
||||||
|
},
|
||||||
|
|
||||||
|
"full-cmd": {
|
||||||
|
cmd: "pdl",
|
||||||
|
gvr: client.PodGVR,
|
||||||
|
err: errors.New("blee"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Command{
|
||||||
|
alias: &dao.Alias{
|
||||||
|
Aliases: config.NewAliases(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.alias.Define(client.PodGVR, "po", "pod", "pods", client.PodGVR.String())
|
||||||
|
c.alias.Define(client.NewGVR("pod default"), "pd")
|
||||||
|
c.alias.Define(client.NewGVR("pod default app=blee @fred"), "pdl")
|
||||||
|
|
||||||
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
p := cmd.NewInterpreter(u.cmd)
|
||||||
|
gvr, _, err := c.viewMetaFor(p)
|
||||||
|
if err != nil {
|
||||||
|
assert.Equal(t, u.err.Error(), err.Error())
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, u.gvr, gvr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestContainerNew(t *testing.T) {
|
func TestContainerNew(t *testing.T) {
|
||||||
c := view.NewContainer(client.CoGVR)
|
c := view.NewContainer(client.CoGVR)
|
||||||
|
|
||||||
require.NoError(t, c.Init(makeCtx()))
|
require.NoError(t, c.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Containers", c.Name())
|
assert.Equal(t, "Containers", c.Name())
|
||||||
assert.Len(t, c.Hints(), 19)
|
assert.Len(t, c.Hints(), 19)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestContext(t *testing.T) {
|
func TestContext(t *testing.T) {
|
||||||
ctx := view.NewContext(client.CtGVR)
|
ctx := view.NewContext(client.CtGVR)
|
||||||
|
|
||||||
require.NoError(t, ctx.Init(makeCtx()))
|
require.NoError(t, ctx.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Contexts", ctx.Name())
|
assert.Equal(t, "Contexts", ctx.Name())
|
||||||
assert.Len(t, ctx.Hints(), 5)
|
assert.Len(t, ctx.Hints(), 5)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func TestDir(t *testing.T) {
|
func TestDir(t *testing.T) {
|
||||||
v := view.NewDir("/fred")
|
v := view.NewDir("/fred")
|
||||||
|
|
||||||
require.NoError(t, v.Init(makeCtx()))
|
require.NoError(t, v.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Directory", v.Name())
|
assert.Equal(t, "Directory", v.Name())
|
||||||
assert.Len(t, v.Hints(), 7)
|
assert.Len(t, v.Hints(), 7)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
v := view.NewDeploy(client.DpGVR)
|
v := view.NewDeploy(client.DpGVR)
|
||||||
|
|
||||||
require.NoError(t, v.Init(makeCtx()))
|
require.NoError(t, v.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Deployments", v.Name())
|
assert.Equal(t, "Deployments", v.Name())
|
||||||
assert.Len(t, v.Hints(), 16)
|
assert.Len(t, v.Hints(), 16)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestDaemonSet(t *testing.T) {
|
func TestDaemonSet(t *testing.T) {
|
||||||
v := view.NewDaemonSet(client.DsGVR)
|
v := view.NewDaemonSet(client.DsGVR)
|
||||||
|
|
||||||
require.NoError(t, v.Init(makeCtx()))
|
require.NoError(t, v.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "DaemonSets", v.Name())
|
assert.Equal(t, "DaemonSets", v.Name())
|
||||||
assert.Len(t, v.Hints(), 17)
|
assert.Len(t, v.Hints(), 17)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,6 +321,11 @@ func launchNodeShell(v model.Igniter, a *App, node string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func launchPodShell(v model.Igniter, a *App) {
|
func launchPodShell(v model.Igniter, a *App) {
|
||||||
|
if a.Config.K9s.ShellPod == nil {
|
||||||
|
slog.Error("Shell pod not configured!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := nukeK9sShell(a); err != nil {
|
if err := nukeK9sShell(a); err != nil {
|
||||||
a.Flash().Errf("Launching node shell failed: %s", err)
|
a.Flash().Errf("Launching node shell failed: %s", err)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHelp(t *testing.T) {
|
func TestHelp(t *testing.T) {
|
||||||
ctx := makeCtx()
|
ctx := makeCtx(t)
|
||||||
|
|
||||||
app := ctx.Value(internal.KeyApp).(*view.App)
|
app := ctx.Value(internal.KeyApp).(*view.App)
|
||||||
po := view.NewPod(client.PodGVR)
|
po := view.NewPod(client.PodGVR)
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func TestParsePFAnn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractApp(t *testing.T) {
|
func TestExtractApp(t *testing.T) {
|
||||||
app := NewApp(mock.NewMockConfig())
|
app := NewApp(mock.NewMockConfig(t))
|
||||||
|
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
app *App
|
app *App
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ apiVersion: v1
|
||||||
the secret name you want to quote to use tls.","title":"secretName","type":"string"}},"required":["http","class","classInSpec"],"type":"object"}
|
the secret name you want to quote to use tls.","title":"secretName","type":"string"}},"required":["http","class","classInSpec"],"type":"object"}
|
||||||
`
|
`
|
||||||
|
|
||||||
v := NewLiveView(NewApp(mock.NewMockConfig()), "fred", nil)
|
v := NewLiveView(NewApp(mock.NewMockConfig(t)), "fred", nil)
|
||||||
require.NoError(t, v.Init(context.Background()))
|
require.NoError(t, v.Init(context.Background()))
|
||||||
v.text.SetText(colorizeYAML(config.Yaml{}, s))
|
v.text.SetText(colorizeYAML(config.Yaml{}, s))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ func TestLogAutoScroll(t *testing.T) {
|
||||||
SingleContainer: true,
|
SingleContainer: true,
|
||||||
}
|
}
|
||||||
v := NewLog(client.PodGVR, &opts)
|
v := NewLog(client.PodGVR, &opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
ii := dao.NewLogItems()
|
ii := dao.NewLogItems()
|
||||||
ii.Add(dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo"))
|
ii.Add(dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo"))
|
||||||
v.GetModel().Set(ii)
|
v.GetModel().Set(ii)
|
||||||
|
|
@ -39,7 +39,7 @@ func TestLogViewNav(t *testing.T) {
|
||||||
Container: "blee",
|
Container: "blee",
|
||||||
}
|
}
|
||||||
v := NewLog(client.PodGVR, &opts)
|
v := NewLog(client.PodGVR, &opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
|
|
||||||
buff := dao.NewLogItems()
|
buff := dao.NewLogItems()
|
||||||
for i := range 100 {
|
for i := range 100 {
|
||||||
|
|
@ -58,7 +58,7 @@ func TestLogViewClear(t *testing.T) {
|
||||||
Container: "blee",
|
Container: "blee",
|
||||||
}
|
}
|
||||||
v := NewLog(client.PodGVR, &opts)
|
v := NewLog(client.PodGVR, &opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
|
|
||||||
v.toggleAutoScrollCmd(nil)
|
v.toggleAutoScrollCmd(nil)
|
||||||
v.Logs().SetText("blee\nblah")
|
v.Logs().SetText("blee\nblah")
|
||||||
|
|
@ -73,7 +73,7 @@ func TestLogTimestamp(t *testing.T) {
|
||||||
Container: "c1",
|
Container: "c1",
|
||||||
}
|
}
|
||||||
l := NewLog(client.NewGVR("test"), &opts)
|
l := NewLog(client.NewGVR("test"), &opts)
|
||||||
require.NoError(t, l.Init(makeContext()))
|
require.NoError(t, l.Init(makeContext(t)))
|
||||||
ii := dao.NewLogItems()
|
ii := dao.NewLogItems()
|
||||||
ii.Add(
|
ii.Add(
|
||||||
&dao.LogItem{
|
&dao.LogItem{
|
||||||
|
|
@ -103,7 +103,7 @@ func TestLogFilter(t *testing.T) {
|
||||||
Container: "c1",
|
Container: "c1",
|
||||||
}
|
}
|
||||||
l := NewLog(client.NewGVR("test"), &opts)
|
l := NewLog(client.NewGVR("test"), &opts)
|
||||||
require.NoError(t, l.Init(makeContext()))
|
require.NoError(t, l.Init(makeContext(t)))
|
||||||
buff := dao.NewLogItems()
|
buff := dao.NewLogItems()
|
||||||
buff.Add(
|
buff.Add(
|
||||||
dao.NewLogItemFromString("duh"),
|
dao.NewLogItemFromString("duh"),
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func TestLog(t *testing.T) {
|
||||||
Container: "blee",
|
Container: "blee",
|
||||||
}
|
}
|
||||||
v := view.NewLog(client.PodGVR, &opts)
|
v := view.NewLog(client.PodGVR, &opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
|
|
||||||
ii := dao.NewLogItems()
|
ii := dao.NewLogItems()
|
||||||
ii.Add(dao.NewLogItemFromString("blee\n"), dao.NewLogItemFromString("bozo\n"))
|
ii.Add(dao.NewLogItemFromString("blee\n"), dao.NewLogItemFromString("bozo\n"))
|
||||||
|
|
@ -45,7 +45,7 @@ func TestLogFlush(t *testing.T) {
|
||||||
Container: "blee",
|
Container: "blee",
|
||||||
}
|
}
|
||||||
v := view.NewLog(client.PodGVR, &opts)
|
v := view.NewLog(client.PodGVR, &opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
|
|
||||||
items := dao.NewLogItems()
|
items := dao.NewLogItems()
|
||||||
items.Add(
|
items.Add(
|
||||||
|
|
@ -65,7 +65,7 @@ func BenchmarkLogFlush(b *testing.B) {
|
||||||
Container: "blee",
|
Container: "blee",
|
||||||
}
|
}
|
||||||
v := view.NewLog(client.PodGVR, &opts)
|
v := view.NewLog(client.PodGVR, &opts)
|
||||||
_ = v.Init(makeContext())
|
_ = v.Init(makeContext(b))
|
||||||
|
|
||||||
items := dao.NewLogItems()
|
items := dao.NewLogItems()
|
||||||
items.Add(
|
items.Add(
|
||||||
|
|
@ -103,9 +103,9 @@ func TestLogViewSave(t *testing.T) {
|
||||||
Container: "blee",
|
Container: "blee",
|
||||||
}
|
}
|
||||||
v := view.NewLog(client.PodGVR, &opts)
|
v := view.NewLog(client.PodGVR, &opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
|
|
||||||
app := makeApp()
|
app := makeApp(t)
|
||||||
ii := dao.NewLogItems()
|
ii := dao.NewLogItems()
|
||||||
ii.Add(dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo"))
|
ii.Add(dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo"))
|
||||||
ll := make([][]byte, ii.Len())
|
ll := make([][]byte, ii.Len())
|
||||||
|
|
@ -141,7 +141,7 @@ func TestAllContainerKeyBinding(t *testing.T) {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
v := view.NewLog(client.PodGVR, u.opts)
|
v := view.NewLog(client.PodGVR, u.opts)
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
_, got := v.Logs().Actions().Get(ui.KeyA)
|
_, got := v.Logs().Actions().Get(ui.KeyA)
|
||||||
assert.Equal(t, u.e, got)
|
assert.Equal(t, u.e, got)
|
||||||
})
|
})
|
||||||
|
|
@ -151,8 +151,8 @@ func TestAllContainerKeyBinding(t *testing.T) {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func makeApp() *view.App {
|
func makeApp(t *testing.T) *view.App {
|
||||||
return view.NewApp(mock.NewMockConfig())
|
return view.NewApp(mock.NewMockConfig(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureDumpDir(n string) error {
|
func ensureDumpDir(n string) error {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func (n *Namespace) bindKeys(aa *ui.KeyActions) {
|
||||||
|
|
||||||
func (n *Namespace) switchNs(app *App, _ ui.Tabular, _ *client.GVR, path string) {
|
func (n *Namespace) switchNs(app *App, _ ui.Tabular, _ *client.GVR, path string) {
|
||||||
n.useNamespace(path)
|
n.useNamespace(path)
|
||||||
app.gotoResource("pods", "", false, true)
|
app.gotoResource(client.PodGVR.String(), "", false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Namespace) useNsCmd(*tcell.EventKey) *tcell.EventKey {
|
func (n *Namespace) useNsCmd(*tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestNSCleanser(t *testing.T) {
|
func TestNSCleanser(t *testing.T) {
|
||||||
ns := view.NewNamespace(client.NsGVR)
|
ns := view.NewNamespace(client.NsGVR)
|
||||||
|
|
||||||
require.NoError(t, ns.Init(makeCtx()))
|
require.NoError(t, ns.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Namespaces", ns.Name())
|
assert.Equal(t, "Namespaces", ns.Name())
|
||||||
assert.Len(t, ns.Hints(), 7)
|
assert.Len(t, ns.Hints(), 7)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestPortForwardNew(t *testing.T) {
|
func TestPortForwardNew(t *testing.T) {
|
||||||
pf := view.NewPortForward(client.PfGVR)
|
pf := view.NewPortForward(client.PfGVR)
|
||||||
|
|
||||||
require.NoError(t, pf.Init(makeCtx()))
|
require.NoError(t, pf.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "PortForwards", pf.Name())
|
assert.Equal(t, "PortForwards", pf.Name())
|
||||||
assert.Len(t, pf.Hints(), 10)
|
assert.Len(t, pf.Hints(), 10)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,14 @@ import (
|
||||||
func TestPodNew(t *testing.T) {
|
func TestPodNew(t *testing.T) {
|
||||||
po := view.NewPod(client.PodGVR)
|
po := view.NewPod(client.PodGVR)
|
||||||
|
|
||||||
require.NoError(t, po.Init(makeCtx()))
|
require.NoError(t, po.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Pods", po.Name())
|
assert.Equal(t, "Pods", po.Name())
|
||||||
assert.Len(t, po.Hints(), 28)
|
assert.Len(t, po.Hints(), 28)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func makeCtx() context.Context {
|
func makeCtx(t testing.TB) context.Context {
|
||||||
cfg := mock.NewMockConfig()
|
cfg := mock.NewMockConfig(t)
|
||||||
return context.WithValue(context.Background(), internal.KeyApp, view.NewApp(cfg))
|
return context.WithValue(context.Background(), internal.KeyApp, view.NewApp(cfg))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestPriorityClassNew(t *testing.T) {
|
func TestPriorityClassNew(t *testing.T) {
|
||||||
s := view.NewPriorityClass(client.PcGVR)
|
s := view.NewPriorityClass(client.PcGVR)
|
||||||
|
|
||||||
require.NoError(t, s.Init(makeCtx()))
|
require.NoError(t, s.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "PriorityClass", s.Name())
|
assert.Equal(t, "PriorityClass", s.Name())
|
||||||
assert.Len(t, s.Hints(), 6)
|
assert.Len(t, s.Hints(), 6)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestPVCNew(t *testing.T) {
|
func TestPVCNew(t *testing.T) {
|
||||||
v := view.NewPersistentVolumeClaim(client.PvcGVR)
|
v := view.NewPersistentVolumeClaim(client.PvcGVR)
|
||||||
|
|
||||||
require.NoError(t, v.Init(makeCtx()))
|
require.NoError(t, v.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "PersistentVolumeClaims", v.Name())
|
assert.Equal(t, "PersistentVolumeClaims", v.Name())
|
||||||
assert.Len(t, v.Hints(), 11)
|
assert.Len(t, v.Hints(), 11)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestRbacNew(t *testing.T) {
|
func TestRbacNew(t *testing.T) {
|
||||||
v := view.NewRbac(client.RbacGVR)
|
v := view.NewRbac(client.RbacGVR)
|
||||||
|
|
||||||
require.NoError(t, v.Init(makeCtx()))
|
require.NoError(t, v.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Rbac", v.Name())
|
assert.Equal(t, "Rbac", v.Name())
|
||||||
assert.Len(t, v.Hints(), 5)
|
assert.Len(t, v.Hints(), 5)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func (r *Reference) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
path := r.GetTable().GetSelectedItem()
|
path := r.GetTable().GetSelectedItem()
|
||||||
ns, _ := client.Namespaced(path)
|
ns, _ := client.Namespaced(path)
|
||||||
gvr := ui.TrimCell(r.GetTable().SelectTable, row, 2)
|
gvr := ui.TrimCell(r.GetTable().SelectTable, row, 2)
|
||||||
r.App().gotoResource(client.NewGVR(gvr).R()+" "+ns, path, false, true)
|
r.App().gotoResource(client.NewGVR(gvr).String()+" "+ns, path, false, true)
|
||||||
|
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestReferenceNew(t *testing.T) {
|
func TestReferenceNew(t *testing.T) {
|
||||||
s := view.NewReference(client.RefGVR)
|
s := view.NewReference(client.RefGVR)
|
||||||
|
|
||||||
require.NoError(t, s.Init(makeCtx()))
|
require.NoError(t, s.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "References", s.Name())
|
assert.Equal(t, "References", s.Name())
|
||||||
assert.Len(t, s.Hints(), 4)
|
assert.Len(t, s.Hints(), 4)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestScreenDumpNew(t *testing.T) {
|
func TestScreenDumpNew(t *testing.T) {
|
||||||
po := view.NewScreenDump(client.SdGVR)
|
po := view.NewScreenDump(client.SdGVR)
|
||||||
|
|
||||||
require.NoError(t, po.Init(makeCtx()))
|
require.NoError(t, po.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "ScreenDumps", po.Name())
|
assert.Equal(t, "ScreenDumps", po.Name())
|
||||||
assert.Len(t, po.Hints(), 5)
|
assert.Len(t, po.Hints(), 5)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestSecretNew(t *testing.T) {
|
func TestSecretNew(t *testing.T) {
|
||||||
s := view.NewSecret(client.SecGVR)
|
s := view.NewSecret(client.SecGVR)
|
||||||
|
|
||||||
require.NoError(t, s.Init(makeCtx()))
|
require.NoError(t, s.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Secrets", s.Name())
|
assert.Equal(t, "Secrets", s.Name())
|
||||||
assert.Len(t, s.Hints(), 8)
|
assert.Len(t, s.Hints(), 8)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func TestStatefulSetNew(t *testing.T) {
|
func TestStatefulSetNew(t *testing.T) {
|
||||||
s := view.NewStatefulSet(client.StsGVR)
|
s := view.NewStatefulSet(client.StsGVR)
|
||||||
|
|
||||||
require.NoError(t, s.Init(makeCtx()))
|
require.NoError(t, s.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "StatefulSets", s.Name())
|
assert.Equal(t, "StatefulSets", s.Name())
|
||||||
assert.Len(t, s.Hints(), 14)
|
assert.Len(t, s.Hints(), 14)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ func init() {
|
||||||
func TestServiceNew(t *testing.T) {
|
func TestServiceNew(t *testing.T) {
|
||||||
s := view.NewService(client.SvcGVR)
|
s := view.NewService(client.SvcGVR)
|
||||||
|
|
||||||
require.NoError(t, s.Init(makeCtx()))
|
require.NoError(t, s.Init(makeCtx(t)))
|
||||||
assert.Equal(t, "Services", s.Name())
|
assert.Equal(t, "Services", s.Name())
|
||||||
assert.Len(t, s.Hints(), 12)
|
assert.Len(t, s.Hints(), 12)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
|
|
||||||
func TestTableSave(t *testing.T) {
|
func TestTableSave(t *testing.T) {
|
||||||
v := NewTable(client.NewGVR("test"))
|
v := NewTable(client.NewGVR("test"))
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
v.SetTitle("k9s-test")
|
v.SetTitle("k9s-test")
|
||||||
|
|
||||||
require.NoError(t, ensureDumpDir("/tmp/test-dumps"))
|
require.NoError(t, ensureDumpDir("/tmp/test-dumps"))
|
||||||
|
|
@ -43,7 +43,7 @@ func TestTableSave(t *testing.T) {
|
||||||
|
|
||||||
func TestTableNew(t *testing.T) {
|
func TestTableNew(t *testing.T) {
|
||||||
v := NewTable(client.NewGVR("test"))
|
v := NewTable(client.NewGVR("test"))
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
|
|
||||||
data := model1.NewTableDataWithRows(
|
data := model1.NewTableDataWithRows(
|
||||||
client.NewGVR("test"),
|
client.NewGVR("test"),
|
||||||
|
|
@ -74,7 +74,7 @@ func TestTableNew(t *testing.T) {
|
||||||
|
|
||||||
func TestTableViewFilter(t *testing.T) {
|
func TestTableViewFilter(t *testing.T) {
|
||||||
v := NewTable(client.NewGVR("test"))
|
v := NewTable(client.NewGVR("test"))
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
v.SetModel(&mockTableModel{})
|
v.SetModel(&mockTableModel{})
|
||||||
v.Refresh()
|
v.Refresh()
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ func TestTableViewFilter(t *testing.T) {
|
||||||
|
|
||||||
func TestTableViewSort(t *testing.T) {
|
func TestTableViewSort(t *testing.T) {
|
||||||
v := NewTable(client.NewGVR("test"))
|
v := NewTable(client.NewGVR("test"))
|
||||||
require.NoError(t, v.Init(makeContext()))
|
require.NoError(t, v.Init(makeContext(t)))
|
||||||
v.SetModel(new(mockTableModel))
|
v.SetModel(new(mockTableModel))
|
||||||
|
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
|
|
@ -199,8 +199,8 @@ func makeTableData() *model1.TableData {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeContext() context.Context {
|
func makeContext(t *testing.T) context.Context {
|
||||||
a := NewApp(mock.NewMockConfig())
|
a := NewApp(mock.NewMockConfig(t))
|
||||||
ctx := context.WithValue(context.Background(), internal.KeyApp, a)
|
ctx := context.WithValue(context.Background(), internal.KeyApp, a)
|
||||||
return context.WithValue(ctx, internal.KeyStyles, a.Styles)
|
return context.WithValue(ctx, internal.KeyStyles, a.Styles)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func (*Workload) showRes(app *App, _ ui.Tabular, _ *client.GVR, path string) {
|
||||||
app.Flash().Err(fmt.Errorf("unable to parse path: %q", path))
|
app.Flash().Err(fmt.Errorf("unable to parse path: %q", path))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app.gotoResource(gvr.R(), fqn, false, true)
|
app.gotoResource(gvr.String(), fqn, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Workload) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (w *Workload) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core22
|
base: core22
|
||||||
version: 'v0.50.0'
|
version: 'v0.50.1'
|
||||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||||
description: |
|
description: |
|
||||||
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue