diff --git a/Makefile b/Makefile
index a84db0a79f..7dc2db8daf 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
else
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
endif
-VERSION ?= v0.30.4
+VERSION ?= v0.30.5
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.30.5.md b/change_logs/release_v0.30.5.md
new file mode 100644
index 0000000000..b59cf29af1
--- /dev/null
+++ b/change_logs/release_v0.30.5.md
@@ -0,0 +1,52 @@
+
+
+# Release v0.30.5
+
+## 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)
+
+## 🎄 Maintenance Release! 🎄
+
+Thank you all for pitching in and helping flesh out issues!!
+
+---
+
+## Videos Are In The Can!
+
+Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
+
+* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
+* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
+
+---
+
+## Resolved Issues
+
+* [#2394](https://github.com/derailed/k9s/issues/2394) Allow setting custom log dir
+* [#2393](https://github.com/derailed/k9s/issues/2393) When switching contexts k9s does not switching to cluster's pod/namespaces/other k8s kinds view
+* [#2387](https://github.com/derailed/k9s/issues/2387) Invalid namespace xxx - with feelings!
+
+---
+
+## Contributed PRs
+
+Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
+
+* [#2396](https://github.com/derailed/k9s/pull/2396) feat: allow to customize logs dir through environment variable
+* [#2395](https://github.com/derailed/k9s/pull/2395) fix: create user tmp directory before the app one
+
+---
+
+
© 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/cmd/root.go b/cmd/root.go
index 703ddf289f..079ec37e45 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -91,7 +91,11 @@ func run(cmd *cobra.Command, args []string) error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
zerolog.SetGlobalLevel(parseLevel(*k9sFlags.LogLevel))
- app := view.NewApp(loadConfiguration())
+ cfg, err := loadConfiguration()
+ if err != nil {
+ return err
+ }
+ app := view.NewApp(cfg)
if err := app.Init(version, *k9sFlags.RefreshRate); err != nil {
return err
}
@@ -105,7 +109,7 @@ func run(cmd *cobra.Command, args []string) error {
return nil
}
-func loadConfiguration() *config.Config {
+func loadConfiguration() (*config.Config, error) {
log.Info().Msg("🐶 K9s starting up...")
k8sCfg := client.NewConfig(k8sFlags)
@@ -116,26 +120,29 @@ func loadConfiguration() *config.Config {
k9sCfg.K9s.Override(k9sFlags)
if err := k9sCfg.Refine(k8sFlags, k9sFlags, k8sCfg); err != nil {
log.Error().Err(err).Msgf("refine failed")
+ return nil, err
}
conn, err := client.InitConnection(k8sCfg)
- k9sCfg.SetConnection(conn)
if err != nil {
log.Error().Err(err).Msgf("failed to connect to context %q", k9sCfg.K9s.ActiveContextName())
- return k9sCfg
+ return nil, err
}
// Try to access server version if that fail. Connectivity issue?
- if !k9sCfg.GetConnection().CheckConnectivity() {
- log.Panic().Msgf("Cannot connect to context %s", k9sCfg.K9s.ActiveContextName())
+ if !conn.CheckConnectivity() {
+ return nil, fmt.Errorf("cannot connect to context: %s", k9sCfg.K9s.ActiveContextName())
}
- if !k9sCfg.GetConnection().ConnectionOK() {
- panic("No connectivity")
+ if !conn.ConnectionOK() {
+ return nil, fmt.Errorf("k8s connection failed for context: %s", k9sCfg.K9s.ActiveContextName())
}
+ k9sCfg.SetConnection(conn)
+
log.Info().Msg("✅ Kubernetes connectivity")
if err := k9sCfg.Save(); err != nil {
log.Error().Err(err).Msg("Config save")
+ return nil, err
}
- return k9sCfg
+ return k9sCfg, nil
}
func parseLevel(level string) zerolog.Level {
diff --git a/internal/config/config.go b/internal/config/config.go
index d4e8e81b7a..c4b781b27d 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -26,8 +26,8 @@ type Config struct {
// K9sHome returns k9s configs home directory.
func K9sHome() string {
- if env := os.Getenv(K9sConfigDir); env != "" {
- return env
+ if isEnvSet(K9sEnvConfigDir) {
+ return os.Getenv(K9sEnvConfigDir)
}
xdgK9sHome, err := xdg.ConfigFile(AppName)
@@ -84,7 +84,7 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
}
log.Debug().Msgf("Active Context %q", c.K9s.ActiveContextName())
- var ns = client.DefaultNamespace
+ var ns string
switch {
case k9sFlags != nil && IsBoolSet(k9sFlags.AllNamespaces):
ns = client.NamespaceAll
@@ -97,10 +97,12 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
}
ns = nss
}
+ if ns == "" {
+ ns = client.DefaultNamespace
+ }
if err := c.SetActiveNamespace(ns); err != nil {
return err
}
- flags.Namespace = &ns
return data.EnsureDirPath(c.K9s.GetScreenDumpDir(), data.DefaultDirMod)
}
@@ -139,10 +141,10 @@ func (c *Config) ActiveNamespace() string {
// ValidateFavorites ensure favorite ns are legit.
func (c *Config) ValidateFavorites() {
ct, err := c.K9s.ActiveContext()
- if err == nil {
- ct.Validate(c.conn, c.settings)
- ct.Namespace.Validate(c.conn, c.settings)
+ if err != nil {
+ return
}
+ ct.Validate(c.conn, c.settings)
}
// FavNamespaces returns fav namespaces in the current context.
@@ -197,8 +199,10 @@ func (c *Config) GetConnection() client.Connection {
// SetConnection set an api server connection.
func (c *Config) SetConnection(conn client.Connection) {
- c.conn, c.K9s.conn = conn, conn
- c.Validate()
+ c.conn = conn
+ if conn != nil {
+ c.K9s.resetConnection(conn)
+ }
}
func (c *Config) ActiveContextName() string {
diff --git a/internal/config/data/context.go b/internal/config/data/context.go
index e08de8ffa2..081b133822 100644
--- a/internal/config/data/context.go
+++ b/internal/config/data/context.go
@@ -47,7 +47,6 @@ func (c *Context) Validate(conn client.Connection, ks KubeSettings) {
if c.PortForwardAddress == "" {
c.PortForwardAddress = DefaultPFAddress
}
-
if cl, err := ks.CurrentClusterName(); err != nil {
c.ClusterName = cl
}
@@ -55,9 +54,6 @@ func (c *Context) Validate(conn client.Connection, ks KubeSettings) {
if c.Namespace == nil {
c.Namespace = NewNamespace()
}
- if c.Namespace.Active == client.BlankNamespace {
- c.Namespace.Active = client.DefaultNamespace
- }
c.Namespace.Validate(conn, ks)
if c.View == nil {
diff --git a/internal/config/data/dir.go b/internal/config/data/dir.go
index b8e5d88d07..d20e545652 100644
--- a/internal/config/data/dir.go
+++ b/internal/config/data/dir.go
@@ -8,31 +8,28 @@ import (
"os"
"path/filepath"
- "github.com/derailed/k9s/internal/client"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
"k8s.io/client-go/tools/clientcmd/api"
)
+// Dir tracks context configurations.
type Dir struct {
root string
- conn client.Connection
- ks KubeSettings
}
-func NewDir(root string, conn client.Connection, ks KubeSettings) *Dir {
+// NewDir returns a new instance.
+func NewDir(root string) *Dir {
return &Dir{
root: root,
- ks: ks,
- conn: conn,
}
}
-func (d Dir) Load(n string, ct *api.Context) (*Config, error) {
+// Load loads context configuration.
+func (d *Dir) Load(n string, ct *api.Context) (*Config, error) {
if ct == nil {
return nil, errors.New("api.Context must not be nil")
}
-
var (
path = filepath.Join(d.root, SanitizeContextSubpath(ct.Cluster, n), MainConfigFile)
cfg *Config
@@ -51,7 +48,6 @@ func (d Dir) Load(n string, ct *api.Context) (*Config, error) {
func (d *Dir) genConfig(path string, ct *api.Context) (*Config, error) {
cfg := NewConfig(ct)
- cfg.Validate(d.conn, d.ks)
if err := cfg.Save(path); err != nil {
return nil, err
}
@@ -68,7 +64,6 @@ func (d *Dir) loadConfig(path string) (*Config, error) {
if err := yaml.Unmarshal(bb, &cfg); err != nil {
return nil, err
}
- cfg.Validate(d.conn, d.ks)
return &cfg, nil
}
diff --git a/internal/config/data/dir_test.go b/internal/config/data/dir_test.go
index 56d0bc67ce..b780a585c6 100644
--- a/internal/config/data/dir_test.go
+++ b/internal/config/data/dir_test.go
@@ -71,7 +71,7 @@ func TestDirLoad(t *testing.T) {
assert.NoError(t, mock.EnsureDir(u.dir))
}
- d := data.NewDir(u.dir, mock.NewMockConnection(), ks)
+ d := data.NewDir(u.dir)
ct, err := ks.CurrentContext()
assert.NoError(t, err)
if err != nil {
diff --git a/internal/config/data/ns.go b/internal/config/data/ns.go
index 252b4f5750..e9e9379cb5 100644
--- a/internal/config/data/ns.go
+++ b/internal/config/data/ns.go
@@ -29,28 +29,30 @@ func NewNamespace() *Namespace {
}
func NewActiveNamespace(n string) *Namespace {
+ if n == client.BlankNamespace {
+ n = client.DefaultNamespace
+ }
return &Namespace{
Active: n,
Favorites: []string{client.DefaultNamespace},
}
}
-// Validate a namespace is setup correctly.
+// Validate validates a namespace is setup correctly.
func (n *Namespace) Validate(c client.Connection, ks KubeSettings) {
- if c == nil {
- n = NewActiveNamespace(client.DefaultNamespace)
+ if n.Active == client.BlankNamespace || c == nil {
+ n.Active = client.DefaultNamespace
}
if c == nil {
- log.Debug().Msgf("No connection found. Skipping ns validation")
return
}
if !n.isAllNamespaces() && !c.IsValidNamespace(n.Active) {
- log.Error().Msgf("[Config] Validation error active namespace %q does not exists", n.Active)
+ log.Error().Msgf("[Config] Validation failed active namespace %q does not exists. Resetting to default ns", n.Active)
+ n.Active = client.DefaultNamespace
}
-
for _, ns := range n.Favorites {
if ns != client.NamespaceAll && !c.IsValidNamespace(ns) {
- log.Debug().Msgf("[Config] Invalid favorite found '%s' - %t", ns, n.isAllNamespaces())
+ log.Debug().Msgf("[Namespace] Invalid favorite found '%s' - %t", ns, n.isAllNamespaces())
n.rmFavNS(ns)
}
}
diff --git a/internal/config/files.go b/internal/config/files.go
index 4913210f29..73ee51b6e1 100644
--- a/internal/config/files.go
+++ b/internal/config/files.go
@@ -6,7 +6,6 @@ package config
import (
_ "embed"
"os"
- "os/user"
"path/filepath"
"github.com/derailed/k9s/internal/config/data"
@@ -16,11 +15,11 @@ import (
)
const (
- // K9sConfigDir represents k9s configuration dir env var.
- K9sConfigDir = "K9S_CONFIG_DIR"
+ // K9sEnvConfigDir represents k9s configuration dir env var.
+ K9sEnvConfigDir = "K9S_CONFIG_DIR"
- // K9sLogsDir represents k9s logs dir env var.
- K9sLogsDir = "K9S_LOGS_DIR"
+ // K9sEnvLogsDir represents k9s logs dir env var.
+ K9sEnvLogsDir = "K9S_LOGS_DIR"
// AppName tracks k9s app name.
AppName = "k9s"
@@ -84,14 +83,21 @@ var (
// InitLogsLoc initializes K9s logs location.
func InitLogLoc() error {
var appLogDir string
- if envDir := os.Getenv(K9sLogsDir); envDir != "" {
- appLogDir = envDir
- } else {
- tmpDir, err := userTmpDir()
+ switch {
+ case isEnvSet(K9sEnvLogsDir):
+ appLogDir = os.Getenv(K9sEnvLogsDir)
+ case isEnvSet(K9sEnvConfigDir):
+ tmpDir, err := UserTmpDir()
if err != nil {
return err
}
appLogDir = tmpDir
+ default:
+ var err error
+ appLogDir, err = xdg.StateFile(AppName)
+ if err != nil {
+ return err
+ }
}
if err := data.EnsureFullPath(appLogDir, data.DefaultDirMod); err != nil {
return err
@@ -103,7 +109,7 @@ func InitLogLoc() error {
// InitLocs initializes k9s artifacts locations.
func InitLocs() error {
- if hasK9sConfigEnv() {
+ if isEnvSet(K9sEnvConfigDir) {
return initK9sEnvLocs()
}
@@ -111,7 +117,7 @@ func InitLocs() error {
}
func initK9sEnvLocs() error {
- AppConfigDir = os.Getenv(K9sConfigDir)
+ AppConfigDir = os.Getenv(K9sEnvConfigDir)
if err := data.EnsureFullPath(AppConfigDir, data.DefaultDirMod); err != nil {
return err
}
@@ -269,20 +275,3 @@ func EnsureHotkeysCfgFile() (string, error) {
func SkinFileFromName(n string) string {
return filepath.Join(AppSkinsDir, n+".yaml")
}
-
-// Helpers...
-
-func hasK9sConfigEnv() bool {
- return os.Getenv(K9sConfigDir) != ""
-}
-
-func userTmpDir() (string, error) {
- u, err := user.Current()
- if err != nil {
- return "", err
- }
-
- dir := filepath.Join(os.TempDir(), u.Username, AppName)
-
- return dir, nil
-}
diff --git a/internal/config/files_test.go b/internal/config/files_test.go
index ca204e09ee..fb946ff2ff 100644
--- a/internal/config/files_test.go
+++ b/internal/config/files_test.go
@@ -5,17 +5,63 @@ package config_test
import (
"os"
+ "path/filepath"
"testing"
+ "github.com/adrg/xdg"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/config/data"
"github.com/stretchr/testify/assert"
)
+func TestInitLogLoc(t *testing.T) {
+ tmp, err := config.UserTmpDir()
+ assert.NoError(t, err)
+
+ uu := map[string]struct {
+ dir string
+ e string
+ }{
+ "log-env": {
+ dir: "/tmp/test/k9s/logs",
+ e: "/tmp/test/k9s/logs/k9s.log",
+ },
+ "xdg-env": {
+ dir: "/tmp/test/xdg-state",
+ e: "/tmp/test/xdg-state/k9s/k9s.log",
+ },
+ "cfg-env": {
+ dir: "/tmp/test/k9s-test",
+ e: filepath.Join(tmp, "k9s.log"),
+ },
+ }
+
+ for k := range uu {
+ u := uu[k]
+ t.Run(k, func(t *testing.T) {
+ os.Unsetenv(config.K9sEnvLogsDir)
+ os.Unsetenv("XDG_STATE_HOME")
+ os.Unsetenv(config.K9sEnvConfigDir)
+ switch k {
+ case "log-env":
+ os.Setenv(config.K9sEnvLogsDir, u.dir)
+ case "xdg-env":
+ os.Setenv("XDG_STATE_HOME", u.dir)
+ xdg.Reload()
+ case "cfg-env":
+ os.Setenv(config.K9sEnvConfigDir, u.dir)
+ }
+ err := config.InitLogLoc()
+ assert.NoError(t, err)
+ assert.Equal(t, u.e, config.AppLogFile)
+ assert.NoError(t, os.RemoveAll(config.AppLogFile))
+ })
+ }
+}
func TestEnsureBenchmarkCfg(t *testing.T) {
- os.Setenv(config.K9sConfigDir, "/tmp/test-config")
+ os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config")
assert.NoError(t, config.InitLocs())
- defer assert.NoError(t, os.RemoveAll(config.K9sConfigDir))
+ defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))
assert.NoError(t, data.EnsureFullPath("/tmp/test-config/clusters/cl-1/ct-2", data.DefaultDirMod))
assert.NoError(t, os.WriteFile("/tmp/test-config/clusters/cl-1/ct-2/benchmarks.yaml", []byte{}, data.DefaultFileMod))
diff --git a/internal/config/flags.go b/internal/config/flags.go
index 8e7a78db45..a1fc7699c3 100644
--- a/internal/config/flags.go
+++ b/internal/config/flags.go
@@ -14,9 +14,6 @@ const (
DefaultCommand = ""
)
-// DefaultLogFile represents the default K9s log file.
-// var DefaultLogFile = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-%s.log", MustK9sUser()))
-
// Flags represents K9s configuration flags.
type Flags struct {
RefreshRate *int
diff --git a/internal/config/helpers.go b/internal/config/helpers.go
index 0e8d0c2d1d..752644d7d3 100644
--- a/internal/config/helpers.go
+++ b/internal/config/helpers.go
@@ -4,13 +4,32 @@
package config
import (
+ "os"
"os/user"
+ "path/filepath"
"github.com/derailed/k9s/internal/config/data"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
)
+// isEnvSet checks if env var is set.
+func isEnvSet(env string) bool {
+ return os.Getenv(env) != ""
+}
+
+// UserTmpDir returns the temp dir with the current user name.
+func UserTmpDir() (string, error) {
+ u, err := user.Current()
+ if err != nil {
+ return "", err
+ }
+
+ dir := filepath.Join(os.TempDir(), u.Username, AppName)
+
+ return dir, nil
+}
+
// InNSList check if ns is in an ns collection.
func InNSList(nn []interface{}, ns string) bool {
ss := make([]string, len(nn))
diff --git a/internal/config/k9s.go b/internal/config/k9s.go
index fd8bdae67b..56c8922368 100644
--- a/internal/config/k9s.go
+++ b/internal/config/k9s.go
@@ -50,12 +50,16 @@ func NewK9s(conn client.Connection, ks data.KubeSettings) *K9s {
Thresholds: NewThreshold(),
ShellPod: NewShellPod(),
ImageScans: NewImageScans(),
- dir: data.NewDir(AppContextsDir, conn, ks),
+ dir: data.NewDir(AppContextsDir),
conn: conn,
ks: ks,
}
}
+func (k *K9s) resetConnection(conn client.Connection) {
+ k.conn = conn
+}
+
// Save saves the k9s config to dis.
func (k *K9s) Save() error {
if k.activeConfig != nil {
@@ -177,19 +181,20 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
if err != nil {
return nil, err
}
- cfg, err := k.dir.Load(n, ct)
+ k.activeConfig, err = k.dir.Load(n, ct)
if err != nil {
return nil, err
}
- k.activeConfig = cfg
// If the context specifies a default namespace, use it!
if k.conn != nil {
if ns := k.conn.ActiveNamespace(); ns != client.BlankNamespace {
k.activeConfig.Context.Namespace.Active = ns
+ } else {
+ k.activeConfig.Context.Namespace.Active = client.DefaultNamespace
}
}
- return cfg.Context, nil
+ return k.activeConfig.Context, nil
}
// OverrideRefreshRate set the refresh rate manually.
@@ -319,4 +324,8 @@ func (k *K9s) Validate(c client.Connection, ks data.KubeSettings) {
k.Thresholds = NewThreshold()
}
k.Thresholds.Validate(c, ks)
+
+ if k.activeConfig != nil {
+ k.activeConfig.Validate(c, ks)
+ }
}
diff --git a/internal/ui/config.go b/internal/ui/config.go
index 6ca8a4951a..11539ea3d8 100644
--- a/internal/ui/config.go
+++ b/internal/ui/config.go
@@ -116,10 +116,6 @@ func (c *Configurator) StylesWatcher(ctx context.Context, s synchronizer) error
}
}()
- log.Debug().Msgf("SkinWatcher watching %q", config.K9sHome())
- if err := w.Add(config.K9sHome()); err != nil {
- return err
- }
log.Debug().Msgf("SkinWatcher watching %q", config.AppSkinsDir)
return w.Add(config.AppSkinsDir)
}
diff --git a/internal/ui/config_test.go b/internal/ui/config_test.go
index 9234131738..3a91554ccf 100644
--- a/internal/ui/config_test.go
+++ b/internal/ui/config_test.go
@@ -19,9 +19,9 @@ import (
)
func TestBenchConfig(t *testing.T) {
- os.Setenv(config.K9sConfigDir, "/tmp/test-config")
+ os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config")
assert.NoError(t, config.InitLocs())
- defer assert.NoError(t, os.RemoveAll(config.K9sConfigDir))
+ defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))
bc, error := config.EnsureBenchmarksCfgFile("cl-1", "ct-1")
assert.NoError(t, error)
@@ -29,9 +29,9 @@ func TestBenchConfig(t *testing.T) {
}
func TestSkinnedContext(t *testing.T) {
- os.Setenv(config.K9sConfigDir, "/tmp/test-config")
+ os.Setenv(config.K9sEnvConfigDir, "/tmp/test-config")
assert.NoError(t, config.InitLocs())
- defer assert.NoError(t, os.RemoveAll(config.K9sConfigDir))
+ defer assert.NoError(t, os.RemoveAll(config.K9sEnvConfigDir))
sf := filepath.Join("..", "config", "testdata", "black_and_wtf.yaml")
raw, err := os.ReadFile(sf)
diff --git a/internal/view/app.go b/internal/view/app.go
index bbba78a28d..ab9431e760 100644
--- a/internal/view/app.go
+++ b/internal/view/app.go
@@ -407,6 +407,10 @@ func (a *App) refreshCluster(context.Context) error {
}
func (a *App) switchNS(ns string) error {
+ if a.Config.ActiveNamespace() == ns {
+ return nil
+ }
+
if ns == client.ClusterScope {
ns = client.BlankNamespace
}
@@ -433,7 +437,7 @@ func (a *App) isValidNS(ns string) (bool, error) {
}
if !a.Conn().IsValidNamespace(ns) {
- return false, fmt.Errorf("isvalidns - invalid namespace: %q", ns)
+ return false, fmt.Errorf("invalid namespace: %q", ns)
}
return true, nil
@@ -445,16 +449,9 @@ func (a *App) switchContext(ci *cmd.Interpreter) error {
return nil
}
- log.Debug().Msgf("--> Switching Context %q--%q", name, a.Config.ActiveView())
a.Halt()
defer a.Resume()
{
- p := cmd.NewInterpreter(a.Config.ActiveView())
- if p.IsContextCmd() {
- a.Config.SetActiveView("pod")
- }
- p.ResetContextArg()
-
a.Config.Reset()
ct, err := a.Config.K9s.ActivateContext(name)
if err != nil {
@@ -466,14 +463,26 @@ func (a *App) switchContext(ci *cmd.Interpreter) error {
if cns, ok := ci.NSArg(); ok {
ct.Namespace.Active = cns
}
+
+ p := cmd.NewInterpreter(a.Config.ActiveView())
+ p.ResetContextArg()
+ if p.IsContextCmd() {
+ a.Config.SetActiveView("pod")
+ }
+ ns := a.Config.ActiveNamespace()
+ if !a.Conn().IsValidNamespace(ns) {
+ ns = client.DefaultNamespace
+ if err := a.Config.SetActiveNamespace(ns); err != nil {
+ return err
+ }
+ }
if err := a.Config.Save(); err != nil {
log.Error().Err(err).Msg("config save failed!")
}
-
- ns := a.Config.ActiveNamespace()
a.initFactory(ns)
- a.Flash().Infof("Switching context to %s", name)
+ log.Debug().Msgf("--> Switching Context %q -- %q -- %q", name, ns, a.Config.ActiveView())
+ a.Flash().Infof("Switching context to %q::%q", name, ns)
a.ReloadStyles(name)
a.gotoResource(a.Config.ActiveView(), "", true)
a.clusterModel.Reset(a.factory)
diff --git a/internal/view/browser.go b/internal/view/browser.go
index 429e3e94bb..21176d69ba 100644
--- a/internal/view/browser.go
+++ b/internal/view/browser.go
@@ -133,7 +133,6 @@ func (b *Browser) SetInstance(path string) {
// Start initializes browser updates.
func (b *Browser) Start() {
- b.app.Config.ValidateFavorites()
ns := b.app.Config.ActiveNamespace()
if n := b.GetModel().GetNamespace(); !client.IsClusterScoped(n) {
ns = n
diff --git a/internal/view/command.go b/internal/view/command.go
index 0fdb7f7a8f..3604c89aa1 100644
--- a/internal/view/command.go
+++ b/internal/view/command.go
@@ -131,10 +131,6 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool) error {
if c.specialCmd(p) {
return nil
}
- // if _, ok := c.alias.Check(p.Cmd()); !ok {
- // return fmt.Errorf("command not found %q", p.Cmd())
- // }
-
gvr, v, err := c.viewMetaFor(p)
if err != nil {
return err
diff --git a/internal/view/log.go b/internal/view/log.go
index b6120bc6b2..88b0cd8bfe 100644
--- a/internal/view/log.go
+++ b/internal/view/log.go
@@ -16,6 +16,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/color"
"github.com/derailed/k9s/internal/config"
+ "github.com/derailed/k9s/internal/config/data"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/ui"
@@ -420,19 +421,17 @@ func ensureDir(dir string) error {
return os.MkdirAll(dir, 0744)
}
-func saveData(dir, fqn, data string) (string, error) {
+func saveData(dir, fqn, logs string) (string, error) {
if err := ensureDir(dir); err != nil {
return "", err
}
- now := time.Now().UnixNano()
- fName := fmt.Sprintf("%s-%d.log", strings.Replace(fqn, "/", "-", 1), now)
-
- path := filepath.Join(dir, fName)
+ f := fmt.Sprintf("%s-%d.log", fqn, time.Now().UnixNano())
+ path := filepath.Join(dir, data.SanitizeFileName(f))
mod := os.O_CREATE | os.O_WRONLY
file, err := os.OpenFile(path, mod, 0600)
if err != nil {
- log.Error().Err(err).Msgf("LogFile create %s", path)
+ log.Error().Err(err).Msgf("Log file save failed: %q", path)
return "", nil
}
defer func() {
@@ -440,7 +439,7 @@ func saveData(dir, fqn, data string) (string, error) {
log.Error().Err(err).Msg("Closing Log file")
}
}()
- if _, err := file.Write([]byte(data)); err != nil {
+ if _, err := file.WriteString(logs); err != nil {
return "", err
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 342a0a8e1d..c83d786315 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: k9s
base: core20
-version: 'v0.30.4'
+version: 'v0.30.5'
summary: K9s is a CLI to view and manage your Kubernetes clusters.
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.