K9s/release v0.31.2 (#2452)
* [Bug] Fix #2428 * fix #2446 * fix #2449 * schemas updates * Add debug info * v0.31.2 rel notesmine
parent
98a7f3f1d6
commit
65100b05d9
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.31.1
|
VERSION ?= v0.31.2
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
@ -118,7 +118,7 @@ func (c *Config) CurrentContextName() (string, error) {
|
||||||
}
|
}
|
||||||
cfg, err := c.RawConfig()
|
cfg, err := c.RawConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", fmt.Errorf("fail to load rawConfig: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg.CurrentContext, nil
|
return cfg.CurrentContext, nil
|
||||||
|
|
|
||||||
|
|
@ -68,16 +68,16 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
|
||||||
}
|
}
|
||||||
if isStringSet(flags.Context) {
|
if isStringSet(flags.Context) {
|
||||||
if _, err := c.K9s.ActivateContext(*flags.Context); err != nil {
|
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 {
|
} else {
|
||||||
n, err := cfg.CurrentContextName()
|
n, err := cfg.CurrentContextName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to retrieve kubeconfig current context %q: %w", n, err)
|
||||||
}
|
}
|
||||||
_, err = c.K9s.ActivateContext(n)
|
_, err = c.K9s.ActivateContext(n)
|
||||||
if err != nil {
|
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())
|
log.Debug().Msgf("Active Context %q", c.K9s.ActiveContextName())
|
||||||
|
|
@ -220,7 +220,7 @@ func (c *Config) Load(path string) error {
|
||||||
|
|
||||||
var cfg Config
|
var cfg Config
|
||||||
if err := yaml.Unmarshal(bb, &cfg); err != nil {
|
if err := yaml.Unmarshal(bb, &cfg); err != nil {
|
||||||
return err
|
return fmt.Errorf("main config yaml load failed: %w", err)
|
||||||
}
|
}
|
||||||
c.Merge(&cfg)
|
c.Merge(&cfg)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
package config_test
|
package config_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -407,7 +406,7 @@ func TestConfigRefine(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
flags *genericclioptions.ConfigFlags
|
flags *genericclioptions.ConfigFlags
|
||||||
k9sFlags *config.Flags
|
k9sFlags *config.Flags
|
||||||
err error
|
err string
|
||||||
context string
|
context string
|
||||||
cluster string
|
cluster string
|
||||||
namespace string
|
namespace string
|
||||||
|
|
@ -488,7 +487,7 @@ func TestConfigRefine(t *testing.T) {
|
||||||
KubeConfig: &cfgFile,
|
KubeConfig: &cfgFile,
|
||||||
Context: &ns1,
|
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": {
|
"use-current-context": {
|
||||||
flags: &genericclioptions.ConfigFlags{
|
flags: &genericclioptions.ConfigFlags{
|
||||||
|
|
@ -507,7 +506,7 @@ func TestConfigRefine(t *testing.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 {
|
||||||
assert.Equal(t, u.err, err)
|
assert.Equal(t, u.err, err.Error())
|
||||||
} else {
|
} else {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, u.context, cfg.K9s.ActiveContextName())
|
assert.Equal(t, u.context, cfg.K9s.ActiveContextName())
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func (d *Dir) loadConfig(path string) (*Config, error) {
|
||||||
|
|
||||||
var cfg Config
|
var cfg Config
|
||||||
if err := yaml.Unmarshal(bb, &cfg); err != nil {
|
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
|
return &cfg, nil
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,17 @@
|
||||||
},
|
},
|
||||||
"shellPod": {
|
"shellPod": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": true,
|
||||||
"properties": {
|
"properties": {
|
||||||
"image": { "type": "string" },
|
"image": { "type": "string" },
|
||||||
|
"command": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string"}
|
||||||
|
},
|
||||||
|
"args": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string"}
|
||||||
|
},
|
||||||
"namespace": { "type": "string" },
|
"namespace": { "type": "string" },
|
||||||
"limits": {
|
"limits": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -41,7 +49,13 @@
|
||||||
"memory": { "type": "string" }
|
"memory": { "type": "string" }
|
||||||
},
|
},
|
||||||
"required": ["cpu", "memory"]
|
"required": ["cpu", "memory"]
|
||||||
}
|
},
|
||||||
|
"labels": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": { "type": "string" },
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
|
"tty": { "type": "boolean" }
|
||||||
},
|
},
|
||||||
"required": ["image", "namespace", "limits"]
|
"required": ["image", "namespace", "limits"]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
"github.com/derailed/k9s/internal/config/data"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// K9s tracks K9s configuration options.
|
// K9s tracks K9s configuration options.
|
||||||
|
|
@ -65,10 +66,11 @@ func (k *K9s) resetConnection(conn client.Connection) {
|
||||||
k.conn = conn
|
k.conn = conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save saves the k9s config to dis.
|
// Save saves the k9s config to disk.
|
||||||
func (k *K9s) Save() error {
|
func (k *K9s) Save() error {
|
||||||
if k.activeConfig == nil {
|
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(
|
path := filepath.Join(
|
||||||
AppContextsDir,
|
AppContextsDir,
|
||||||
|
|
@ -97,7 +99,9 @@ func (k *K9s) Merge(k1 *K9s) {
|
||||||
k.ShellPod = k1.ShellPod
|
k.ShellPod = k1.ShellPod
|
||||||
k.Logger = k1.Logger
|
k.Logger = k1.Logger
|
||||||
k.ImageScans = k1.ImageScans
|
k.ImageScans = k1.ImageScans
|
||||||
k.Thresholds = k1.Thresholds
|
if k1.Thresholds != nil {
|
||||||
|
k.Thresholds = k1.Thresholds
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppScreenDumpDir fetch screen dumps dir.
|
// AppScreenDumpDir fetch screen dumps dir.
|
||||||
|
|
@ -193,6 +197,9 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
|
||||||
if k.activeConfig.Context == nil {
|
if k.activeConfig.Context == nil {
|
||||||
return nil, fmt.Errorf("context activation failed for: %s", n)
|
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
|
return k.activeConfig.Context, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,10 +453,12 @@ func (a *App) isValidNS(ns string) (bool, error) {
|
||||||
return true, nil
|
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()
|
name, ok := ci.HasContext()
|
||||||
if !ok || a.Config.ActiveContextName() == name {
|
if !ok || a.Config.ActiveContextName() == name {
|
||||||
return nil
|
if !force {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Halt()
|
a.Halt()
|
||||||
|
|
|
||||||
|
|
@ -249,13 +249,6 @@ func (b *Browser) TableDataChanged(data *render.TableData) {
|
||||||
|
|
||||||
b.app.QueueUpdateDraw(func() {
|
b.app.QueueUpdateDraw(func() {
|
||||||
b.refreshActions()
|
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())
|
b.Update(data, b.app.Conn().HasMetrics())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.app.switchContext(p); err != nil {
|
if err := c.app.switchContext(p, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,5 +129,5 @@ func useContext(app *App, name string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.switchContext(cmd.NewInterpreter("ctx " + name))
|
return app.switchContext(cmd.NewInterpreter("ctx "+name), true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/tcell/v2"
|
"github.com/derailed/tcell/v2"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Table represents a table viewer.
|
// 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.KeyHasMetrics, t.app.Conn().HasMetrics())
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, internal.KeyStyles, t.app.Styles)
|
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)
|
ctx = context.WithValue(ctx, internal.KeyViewConfig, t.app.CustomView)
|
||||||
t.Table.Init(ctx)
|
t.Table.Init(ctx)
|
||||||
t.SetInputCapture(t.keyboard)
|
t.SetInputCapture(t.keyboard)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core20
|
base: core20
|
||||||
version: 'v0.31.1'
|
version: 'v0.31.2'
|
||||||
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