diff --git a/Makefile b/Makefile
index 143ff123..74fffb26 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.31.1
+VERSION ?= v0.31.2
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.31.2.md b/change_logs/release_v0.31.2.md
new file mode 100644
index 00000000..68734f52
--- /dev/null
+++ b/change_logs/release_v0.31.2.md
@@ -0,0 +1,51 @@
+
+
+# Release v0.31.2
+
+## 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!
+
+Yikes! The aftermath...
+
+Thank you all for pitching in and helping flesh out issues!!
+
+Please make sure to add gory details to issues ie relevant configs, debug logs, etc...
+
+Comments like: `same here!` doesn't really help us zero in. Everyone has slightly different settings/platforms so every little bits of info helps with the resolves.
+Thank you!!
+
+---
+
+## Videos Are In The Can!
+
+Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
+
+* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
+* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
+* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
+
+---
+
+## Resolved Issues
+
+* [#2449](https://github.com/derailed/k9s/issues/2449) [Bug]: views.yaml columns not respected on startup
+* [#2448](https://github.com/derailed/k9s/issues/2448) Missing '.thresholds' in config.yaml result in 'assignment to entry in nil map'
+* [#2446](https://github.com/derailed/k9s/issues/2446) Context Switch unreliable/not working
+
+---
+
+
© 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/internal/client/config.go b/internal/client/config.go
index 66990488..0a3253e5 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -118,7 +118,7 @@ func (c *Config) CurrentContextName() (string, error) {
}
cfg, err := c.RawConfig()
if err != nil {
- return "", err
+ return "", fmt.Errorf("fail to load rawConfig: %w", err)
}
return cfg.CurrentContext, nil
diff --git a/internal/config/config.go b/internal/config/config.go
index 89adde32..ea78be80 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -68,16 +68,16 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
}
if isStringSet(flags.Context) {
if _, err := c.K9s.ActivateContext(*flags.Context); err != nil {
- return err
+ return fmt.Errorf("k8sflags. unable to activate context %q: %w", *flags.Context, err)
}
} else {
n, err := cfg.CurrentContextName()
if err != nil {
- return err
+ return fmt.Errorf("unable to retrieve kubeconfig current context %q: %w", n, err)
}
_, err = c.K9s.ActivateContext(n)
if err != nil {
- return err
+ return fmt.Errorf("unable to activate context %q: %w", *flags.Context, err)
}
}
log.Debug().Msgf("Active Context %q", c.K9s.ActiveContextName())
@@ -220,7 +220,7 @@ func (c *Config) Load(path string) error {
var cfg Config
if err := yaml.Unmarshal(bb, &cfg); err != nil {
- return err
+ return fmt.Errorf("main config yaml load failed: %w", err)
}
c.Merge(&cfg)
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 21bdd564..509bd5af 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -4,7 +4,6 @@
package config_test
import (
- "errors"
"fmt"
"os"
"path/filepath"
@@ -407,7 +406,7 @@ func TestConfigRefine(t *testing.T) {
uu := map[string]struct {
flags *genericclioptions.ConfigFlags
k9sFlags *config.Flags
- err error
+ err string
context string
cluster string
namespace string
@@ -488,7 +487,7 @@ func TestConfigRefine(t *testing.T) {
KubeConfig: &cfgFile,
Context: &ns1,
},
- err: errors.New(`no context found for: "ns-1"`),
+ err: `k8sflags. unable to activate context "ns-1": no context found for: "ns-1"`,
},
"use-current-context": {
flags: &genericclioptions.ConfigFlags{
@@ -507,7 +506,7 @@ func TestConfigRefine(t *testing.T) {
err := cfg.Refine(u.flags, u.k9sFlags, client.NewConfig(u.flags))
if err != nil {
- assert.Equal(t, u.err, err)
+ assert.Equal(t, u.err, err.Error())
} else {
assert.Nil(t, err)
assert.Equal(t, u.context, cfg.K9s.ActiveContextName())
diff --git a/internal/config/data/dir.go b/internal/config/data/dir.go
index 8e6c8b17..0eb3f5f4 100644
--- a/internal/config/data/dir.go
+++ b/internal/config/data/dir.go
@@ -68,7 +68,7 @@ func (d *Dir) loadConfig(path string) (*Config, error) {
var cfg Config
if err := yaml.Unmarshal(bb, &cfg); err != nil {
- return nil, err
+ return nil, fmt.Errorf("context-config yaml load failed: %w\n%s", err, string(bb))
}
return &cfg, nil
diff --git a/internal/config/json/schemas/k9s.json b/internal/config/json/schemas/k9s.json
index 42d2cb39..6b17e24f 100644
--- a/internal/config/json/schemas/k9s.json
+++ b/internal/config/json/schemas/k9s.json
@@ -30,9 +30,17 @@
},
"shellPod": {
"type": "object",
- "additionalProperties": false,
+ "additionalProperties": true,
"properties": {
"image": { "type": "string" },
+ "command": {
+ "type": "array",
+ "items": { "type": "string"}
+ },
+ "args": {
+ "type": "array",
+ "items": { "type": "string"}
+ },
"namespace": { "type": "string" },
"limits": {
"type": "object",
@@ -41,7 +49,13 @@
"memory": { "type": "string" }
},
"required": ["cpu", "memory"]
- }
+ },
+ "labels": {
+ "type": "object",
+ "additionalProperties": { "type": "string" },
+ "required": []
+ },
+ "tty": { "type": "boolean" }
},
"required": ["image", "namespace", "limits"]
},
diff --git a/internal/config/k9s.go b/internal/config/k9s.go
index 09cee753..95f52e1b 100644
--- a/internal/config/k9s.go
+++ b/internal/config/k9s.go
@@ -10,6 +10,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config/data"
+ "github.com/rs/zerolog/log"
)
// K9s tracks K9s configuration options.
@@ -65,10 +66,11 @@ func (k *K9s) resetConnection(conn client.Connection) {
k.conn = conn
}
-// Save saves the k9s config to dis.
+// Save saves the k9s config to disk.
func (k *K9s) Save() error {
if k.activeConfig == nil {
- return fmt.Errorf("save failed. no active config detected")
+ log.Warn().Msgf("Save failed. no active config detected")
+ return nil
}
path := filepath.Join(
AppContextsDir,
@@ -97,7 +99,9 @@ func (k *K9s) Merge(k1 *K9s) {
k.ShellPod = k1.ShellPod
k.Logger = k1.Logger
k.ImageScans = k1.ImageScans
- k.Thresholds = k1.Thresholds
+ if k1.Thresholds != nil {
+ k.Thresholds = k1.Thresholds
+ }
}
// AppScreenDumpDir fetch screen dumps dir.
@@ -193,6 +197,9 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
if k.activeConfig.Context == nil {
return nil, fmt.Errorf("context activation failed for: %s", n)
}
+ if k.activeConfig.Context == nil {
+ return nil, fmt.Errorf("context activation failed for: %s", n)
+ }
return k.activeConfig.Context, nil
}
diff --git a/internal/view/app.go b/internal/view/app.go
index 4c4c1959..52e5bee7 100644
--- a/internal/view/app.go
+++ b/internal/view/app.go
@@ -453,10 +453,12 @@ func (a *App) isValidNS(ns string) (bool, error) {
return true, nil
}
-func (a *App) switchContext(ci *cmd.Interpreter) error {
+func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
name, ok := ci.HasContext()
if !ok || a.Config.ActiveContextName() == name {
- return nil
+ if !force {
+ return nil
+ }
}
a.Halt()
diff --git a/internal/view/browser.go b/internal/view/browser.go
index 9589e99b..36503bee 100644
--- a/internal/view/browser.go
+++ b/internal/view/browser.go
@@ -249,13 +249,6 @@ func (b *Browser) TableDataChanged(data *render.TableData) {
b.app.QueueUpdateDraw(func() {
b.refreshActions()
- if !b.app.Config.K9s.UI.Reactive {
- if err := b.app.RefreshCustomViews(); err != nil {
- log.Warn().Err(err).Msg("CustomViews load failed")
- b.app.Logo().Warn("Views load failed!")
- }
- }
-
b.Update(data, b.app.Conn().HasMetrics())
})
}
diff --git a/internal/view/command.go b/internal/view/command.go
index 97676176..92ea9428 100644
--- a/internal/view/command.go
+++ b/internal/view/command.go
@@ -167,7 +167,7 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool) error {
return err
}
- if err := c.app.switchContext(p); err != nil {
+ if err := c.app.switchContext(p, false); err != nil {
return err
}
}
diff --git a/internal/view/context.go b/internal/view/context.go
index 66d4c45e..22266194 100644
--- a/internal/view/context.go
+++ b/internal/view/context.go
@@ -129,5 +129,5 @@ func useContext(app *App, name string) error {
return err
}
- return app.switchContext(cmd.NewInterpreter("ctx " + name))
+ return app.switchContext(cmd.NewInterpreter("ctx "+name), true)
}
diff --git a/internal/view/table.go b/internal/view/table.go
index f1e28b90..f0297d80 100644
--- a/internal/view/table.go
+++ b/internal/view/table.go
@@ -15,6 +15,7 @@ import (
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tcell/v2"
+ "github.com/rs/zerolog/log"
)
// Table represents a table viewer.
@@ -46,6 +47,13 @@ func (t *Table) Init(ctx context.Context) (err error) {
ctx = context.WithValue(ctx, internal.KeyHasMetrics, t.app.Conn().HasMetrics())
}
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
+ if !t.app.Config.K9s.UI.Reactive {
+ if err := t.app.RefreshCustomViews(); err != nil {
+ log.Warn().Err(err).Msg("CustomViews load failed")
+ t.app.Logo().Warn("Views load failed!")
+ }
+ }
+
ctx = context.WithValue(ctx, internal.KeyViewConfig, t.app.CustomView)
t.Table.Init(ctx)
t.SetInputCapture(t.keyboard)
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 0c9f329f..98655392 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: k9s
base: core20
-version: 'v0.31.1'
+version: 'v0.31.2'
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.