Rel v0.50.12 (#3572)

* fix #3570 - broken display when no resources

* fix #3547 - surface error

* fix #3562 - rbac del ns

* rel notes
mine
Fernand Galiana 2025-09-19 08:36:53 -06:00 committed by GitHub
parent ab839ed11e
commit 09c1c07950
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 71 additions and 21 deletions

View File

@ -1,5 +1,5 @@
NAME := k9s NAME := k9s
VERSION ?= v0.50.11 VERSION ?= v0.50.12
PACKAGE := github.com/derailed/$(NAME) PACKAGE := github.com/derailed/$(NAME)
OUTPUT_BIN ?= execs/${NAME} OUTPUT_BIN ?= execs/${NAME}
GO_FLAGS ?= GO_FLAGS ?=

View File

@ -0,0 +1,29 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
# Release v0.50.12
## 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/zt-3360a389v-ElLHrb0Dp1kAXqYUItSAFA)
## Maintenance Release!
## Resolved Issues
* [#3570](https://github.com/derailed/k9s/issues/3570) 0.50.11 could not display any resources
* [#3562](https://github.com/derailed/k9s/issues/3562) Can't delete namespace
* [#3547](https://github.com/derailed/k9s/issues/3547) Error message from admission controller
---
<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)#

View File

@ -184,7 +184,7 @@ func (a *APIClient) CanI(ns string, gvr *GVR, name string, verbs []string) (auth
slogs.Verb, verbs, slogs.Verb, verbs,
) )
if resp != nil { if resp != nil {
clog.Debug("[CAN] reps", clog.Debug("[CAN] response",
slogs.AuthStatus, resp.Status.Allowed, slogs.AuthStatus, resp.Status.Allowed,
slogs.AuthReason, resp.Status.Reason, slogs.AuthReason, resp.Status.Reason,
) )
@ -196,7 +196,7 @@ func (a *APIClient) CanI(ns string, gvr *GVR, name string, verbs []string) (auth
} }
if !resp.Status.Allowed { if !resp.Status.Allowed {
a.cache.Add(key, false, cacheExpiry) a.cache.Add(key, false, cacheExpiry)
return auth, fmt.Errorf("`%s access denied for user on %q:%s", v, ns, gvr) return auth, fmt.Errorf("(%s) access denied for user on resource %q:%s in namespace %q", v, name, gvr, ns)
} }
} }
auth = true auth = true

View File

@ -14,7 +14,7 @@ import (
const ( const (
// DefaultFlashDelay sets the flash clear delay. // DefaultFlashDelay sets the flash clear delay.
DefaultFlashDelay = 3 * time.Second DefaultFlashDelay = 6 * time.Second
// FlashInfo represents an info message. // FlashInfo represents an info message.
FlashInfo FlashLevel = iota FlashInfo FlashLevel = iota

View File

@ -302,8 +302,8 @@ func (b *Browser) TableNoData(mdata *model1.TableData) {
if !b.app.ConOK() || cancel == nil || !b.app.IsRunning() { if !b.app.ConOK() || cancel == nil || !b.app.IsRunning() {
return return
} }
// Skip warning on first view or if table data is empty (likely during initialization) // Skip warning on first view (likely during initialization)
if b.firstView.Load() == 0 || mdata.Empty() { if b.firstView.Load() == 0 || mdata.HeaderCount() == 0 {
b.firstView.Add(1) b.firstView.Add(1)
return return
} }
@ -316,7 +316,7 @@ func (b *Browser) TableNoData(mdata *model1.TableData) {
b.setUpdating(true) b.setUpdating(true)
defer b.setUpdating(false) defer b.setUpdating(false)
if b.GetColumnCount() == 0 { if b.GetColumnCount() == 0 {
b.app.Flash().Warnf("No resources found for %s in namespace %s", b.GVR(), client.PrintNamespace(b.GetNamespace())) b.app.Flash().Warnf("No resources found for %s in %q namespace", b.GVR(), client.PrintNamespace(b.GetNamespace()))
} }
b.refreshActions() b.refreshActions()
b.UpdateUI(cdata, mdata) b.UpdateUI(cdata, mdata)

View File

@ -164,9 +164,21 @@ func (c *Container) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
return evt return evt
} }
var err error
c.Stop() c.Stop()
defer c.Start() defer func() {
shellIn(c.App(), c.GetTable().Path, path) c.Start()
if err != nil {
c.App().QueueUpdate(func() {
if err != nil {
c.App().Flash().Errf("Shell exec failed: %s", err)
}
})
c.App().Flash().Err(err)
}
}()
err = shellIn(c.App(), c.GetTable().Path, path)
return nil return nil
} }

View File

@ -587,7 +587,7 @@ func pipe(_ context.Context, opts *shellOpts, statusChan chan<- string, w, e *by
close(statusChan) close(statusChan)
if err != nil { if err != nil {
err = fmt.Errorf("command failed. Check logs: %w", err) err = fmt.Errorf("command failed. Check k9s logs: %w", err)
} }
return err return err

View File

@ -393,28 +393,33 @@ func containerShellIn(a *App, comp model.Component, path, co string) error {
} }
func resumeShellIn(a *App, c model.Component, path, co string) { func resumeShellIn(a *App, c model.Component, path, co string) {
var err error
c.Stop() c.Stop()
defer c.Start() defer func() {
c.Start()
a.QueueUpdate(func() {
if err != nil {
a.Flash().Errf("Shell exec failed: %s", err)
}
})
}()
shellIn(a, path, co) err = shellIn(a, path, co)
} }
func shellIn(a *App, fqn, co string) { func shellIn(a *App, fqn, co string) error {
platform, err := getPodOS(a.factory, fqn) platform, err := getPodOS(a.factory, fqn)
if err != nil { if err != nil {
slog.Warn("OS detect failed", slogs.Error, err) return err
} }
args := computeShellArgs(fqn, co, a.Conn().Config().Flags(), platform)
args := computeShellArgs(fqn, co, a.Conn().Config().Flags(), platform)
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold) c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
err = runK(a, &shellOpts{ return runK(a, &shellOpts{
clear: true, clear: true,
banner: c.Sprintf(bannerFmt, fqn, co), banner: c.Sprintf(bannerFmt, fqn, co),
args: args}, args: args},
) )
if err != nil {
a.Flash().Errf("Shell exec failed: %s", err)
}
} }
func containerAttachIn(a *App, comp model.Component, path, co string) error { func containerAttachIn(a *App, comp model.Component, path, co string) error {

View File

@ -201,7 +201,11 @@ func (f *Factory) isClusterWide() bool {
// CanForResource return an informer is user has access. // CanForResource return an informer is user has access.
func (f *Factory) CanForResource(ns string, gvr *client.GVR, verbs []string) (informers.GenericInformer, error) { func (f *Factory) CanForResource(ns string, gvr *client.GVR, verbs []string) (informers.GenericInformer, error) {
auth, err := f.Client().CanI(ns, gvr, "", verbs) var resName string
if gvr == client.NsGVR {
resName = ns
}
auth, err := f.Client().CanI(ns, gvr, resName, verbs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,6 @@
name: k9s name: k9s
base: core22 base: core22
version: 'v0.50.11' version: 'v0.50.12'
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.