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.