Rel v0.40.10 (#3206)
* [HOTFIX] Broke plugins ;( * add support for cust cols on alias+ns * rel notesmine
parent
78c7787798
commit
35361bb238
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.40.9
|
VERSION ?= v0.40.10
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.40.10
|
||||||
|
|
||||||
|
## 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!
|
||||||
|
|
||||||
|
Sounds like I did hose plugins after all... With feelings!
|
||||||
|
|
||||||
|
* Refactored plugins implementation, hopefully we didn't hose them 😳
|
||||||
|
* Updated plugins docs
|
||||||
|
* Apparently when it comes to icons, I've chosen... poorly 🙀
|
||||||
|
Updated `write` icon 🔓->✍️, hopefully for the better 👀??
|
||||||
|
|
||||||
|
## Videos Are In The Can!
|
||||||
|
|
||||||
|
Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
|
||||||
|
|
||||||
|
* [K9s v0.40.0 -Column Blow- Sneak peek](https://youtu.be/iy6RDozAM4A)
|
||||||
|
* [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
|
||||||
|
|
||||||
|
* [#3202](https://github.com/derailed/k9s/issues/3202) 0.40.8 breaks plugins loading
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<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)
|
||||||
|
|
@ -3,7 +3,24 @@
|
||||||
"title": "K9s plugin-multi schema",
|
"title": "K9s plugin-multi schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"$ref": "file://internal/config/json/schemas/plugin.json",
|
"properties": {
|
||||||
"additionalProperties": false
|
"shortCut": { "type": "string" },
|
||||||
|
"override": { "type": "boolean" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"confirm": { "type": "boolean" },
|
||||||
|
"dangerous": { "type": "boolean" },
|
||||||
|
"scopes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"command": { "type": "string" },
|
||||||
|
"background": { "type": "boolean" },
|
||||||
|
"overwriteOutput": { "type": "boolean" },
|
||||||
|
"args": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": ["string", "number"] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["shortCut", "description", "scopes", "command"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,25 @@
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"$ref": "file://internal/config/json/schemas/plugin.json",
|
"properties": {
|
||||||
"additionalProperties": false
|
"shortCut": { "type": "string" },
|
||||||
|
"override": { "type": "boolean" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"confirm": { "type": "boolean" },
|
||||||
|
"dangerous": { "type": "boolean" },
|
||||||
|
"scopes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"command": { "type": "string" },
|
||||||
|
"background": { "type": "boolean" },
|
||||||
|
"overwriteOutput": { "type": "boolean" },
|
||||||
|
"args": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": ["string", "number"] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["shortCut", "description", "scopes", "command"]
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func TestValidatePlugins(t *testing.T) {
|
||||||
"toast": {
|
"toast": {
|
||||||
path: "testdata/plugins/toast.yaml",
|
path: "testdata/plugins/toast.yaml",
|
||||||
schema: json.PluginsSchema,
|
schema: json.PluginsSchema,
|
||||||
err: "Additional property shortCuts is not allowed\nscopes is required\nshortCut is required",
|
err: "scopes is required\nshortCut is required",
|
||||||
},
|
},
|
||||||
"cool-snippet": {
|
"cool-snippet": {
|
||||||
path: "testdata/plugins/snippet.yaml",
|
path: "testdata/plugins/snippet.yaml",
|
||||||
|
|
@ -52,13 +52,10 @@ func TestValidatePlugins(t *testing.T) {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
bb, err := os.ReadFile(u.path)
|
bb, err := os.ReadFile(u.path)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
dir, _ := os.Getwd()
|
|
||||||
assert.NoError(t, os.Chdir("../../.."))
|
|
||||||
v := json.NewValidator()
|
v := json.NewValidator()
|
||||||
if err := v.Validate(u.schema, bb); err != nil {
|
if err := v.Validate(u.schema, bb); err != nil {
|
||||||
assert.Equal(t, u.err, err.Error())
|
assert.Equal(t, u.err, err.Error())
|
||||||
}
|
}
|
||||||
assert.NoError(t, os.Chdir(dir))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,11 +77,8 @@ func TestValidatePluginDir(t *testing.T) {
|
||||||
bb, err := os.ReadFile(filepath.Join(plugDir, e.Name()))
|
bb, err := os.ReadFile(filepath.Join(plugDir, e.Name()))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
assert.NoError(t, os.Chdir("../../.."))
|
|
||||||
p := json.NewValidator()
|
p := json.NewValidator()
|
||||||
assert.NoError(t, p.Validate(json.PluginsSchema, bb), e.Name())
|
assert.NoError(t, p.Validate(json.PluginsSchema, bb), e.Name())
|
||||||
assert.NoError(t, os.Chdir(dir))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -79,22 +76,16 @@ func TestPluginLoad(t *testing.T) {
|
||||||
"toast-invalid": {
|
"toast-invalid": {
|
||||||
path: "testdata/plugins/plugins-toast.yaml",
|
path: "testdata/plugins/plugins-toast.yaml",
|
||||||
ee: NewPlugins(),
|
ee: NewPlugins(),
|
||||||
err: "Additional property scoped is not allowed\nscopes is required\nAdditional property plugins is not allowed\ncommand is required\ndescription is required\nscopes is required\nshortCut is required\nAdditional property blah is not allowed\ncommand is required\ndescription is required\nscopes is required\nshortCut is required",
|
err: "plugin validation failed for testdata/plugins/plugins-toast.yaml: scopes is required\nAdditional property plugins is not allowed\ncommand is required\ndescription is required\nscopes is required\nshortCut is required\ncommand is required\ndescription is required\nscopes is required\nshortCut is required",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
assert.NoError(t, os.Chdir("../.."))
|
|
||||||
defer func() {
|
|
||||||
assert.NoError(t, os.Chdir(dir))
|
|
||||||
}()
|
|
||||||
for k, u := range uu {
|
for k, u := range uu {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
p := NewPlugins()
|
p := NewPlugins()
|
||||||
err := p.Load(path.Join(dir, u.path), false)
|
err := p.Load(u.path, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
idx := strings.Index(err.Error(), ":")
|
assert.Equal(t, u.err, err.Error())
|
||||||
assert.Equal(t, u.err, err.Error()[idx+2:])
|
|
||||||
}
|
}
|
||||||
assert.Equal(t, u.ee, p)
|
assert.Equal(t, u.ee, p)
|
||||||
})
|
})
|
||||||
|
|
@ -111,15 +102,9 @@ func TestSinglePluginFileLoad(t *testing.T) {
|
||||||
Confirm: true,
|
Confirm: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
assert.NoError(t, os.Chdir("../.."))
|
|
||||||
defer func() {
|
|
||||||
assert.NoError(t, os.Chdir(dir))
|
|
||||||
}()
|
|
||||||
|
|
||||||
p := NewPlugins()
|
p := NewPlugins()
|
||||||
assert.NoError(t, p.load(path.Join(dir, "testdata/plugins/plugins.yaml")))
|
assert.NoError(t, p.load("testdata/plugins/plugins.yaml"))
|
||||||
assert.NoError(t, p.loadDir(path.Join(dir, "/random/dir/not/exist")))
|
assert.NoError(t, p.loadDir("/random/dir/not/exist"))
|
||||||
|
|
||||||
assert.Equal(t, 1, len(p.Plugins))
|
assert.Equal(t, 1, len(p.Plugins))
|
||||||
v, ok := p.Plugins["blah"]
|
v, ok := p.Plugins["blah"]
|
||||||
|
|
@ -135,8 +120,8 @@ func TestMultiplePluginFilesLoad(t *testing.T) {
|
||||||
ee Plugins
|
ee Plugins
|
||||||
}{
|
}{
|
||||||
"empty": {
|
"empty": {
|
||||||
path: "internal/config/testdata/plugins/plugins.yaml",
|
path: "testdata/plugins/plugins.yaml",
|
||||||
dir: "internal/config/testdata/plugins/dir",
|
dir: "testdata/plugins/dir",
|
||||||
ee: Plugins{
|
ee: Plugins{
|
||||||
Plugins: plugins{
|
Plugins: plugins{
|
||||||
"blah": {
|
"blah": {
|
||||||
|
|
@ -181,11 +166,6 @@ func TestMultiplePluginFilesLoad(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
assert.NoError(t, os.Chdir("../.."))
|
|
||||||
defer func() {
|
|
||||||
assert.NoError(t, os.Chdir(dir))
|
|
||||||
}()
|
|
||||||
for k, u := range uu {
|
for k, u := range uu {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
p := NewPlugins()
|
p := NewPlugins()
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ func (v *CustomView) getVS(gvr, ns string) *ViewSetting {
|
||||||
})
|
})
|
||||||
slices.Reverse(kk)
|
slices.Reverse(kk)
|
||||||
for _, key := range kk {
|
for _, key := range kk {
|
||||||
if !strings.HasPrefix(key, gvr) {
|
if !strings.HasPrefix(key, gvr) && !strings.HasPrefix(gvr, key) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,10 +203,23 @@ func (v *CustomView) getVS(gvr, ns string) *ViewSetting {
|
||||||
vs := v.Views[key]
|
vs := v.Views[key]
|
||||||
return &vs
|
return &vs
|
||||||
}
|
}
|
||||||
|
case strings.HasPrefix(k, key):
|
||||||
|
kk := strings.Fields(k)
|
||||||
|
if len(kk) == 2 {
|
||||||
|
if v, ok := v.Views[kk[0]+"@"+kk[1]]; ok {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
if key == kk[0] {
|
||||||
|
vs := v.Views[key]
|
||||||
|
return &vs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
case key == k:
|
case key == k:
|
||||||
vs := v.Views[key]
|
vs := v.Views[key]
|
||||||
return &vs
|
return &vs
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ func hydrate(o runtime.Object, cc ColumnSpecs, parsers []*jsonpath.JSONPath, rh
|
||||||
Header: cc[idx].Header,
|
Header: cc[idx].Header,
|
||||||
Value: NAValue,
|
Value: NAValue,
|
||||||
}
|
}
|
||||||
slog.Warn("Unable to find column %s", slogs.Name, cc[idx].Header.Name)
|
slog.Warn("Unable to find custom column", slogs.Name, cc[idx].Header.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var v string
|
var v string
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core22
|
base: core22
|
||||||
version: 'v0.40.9'
|
version: 'v0.40.10'
|
||||||
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