K9s release v0.32.2 (#2598)
* [Maint] cleaning up * [Bug] Fix #2593 * [Bug] Fix #2582 * Release v0.32.2mine
parent
69cd0cd707
commit
ecd33ff48d
2
Makefile
2
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.32.1
|
||||
VERSION ?= v0.32.2
|
||||
IMG_NAME := derailed/k9s
|
||||
IMAGE := ${IMG_NAME}:${VERSION}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||
|
||||
# Release v0.32.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!
|
||||
|
||||
Mo aftermath ;(
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
* [#2582](https://github.com/derailed/k9s/issues/2582) Slowness due to client-side throttling in v0.32.0 (Maybe??)
|
||||
* [#2593](https://github.com/derailed/k9s/issues/2593) Popeye not working in 0.32.X
|
||||
|
||||
---
|
||||
|
||||
<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)
|
||||
|
|
@ -246,7 +246,7 @@ func (a *APIClient) ValidNamespaceNames() (NamespaceNames, error) {
|
|||
}
|
||||
}
|
||||
|
||||
ok, err := a.CanI(ClusterScope, "v1/namespaces", "", []string{ListVerb})
|
||||
ok, err := a.CanI(ClusterScope, "v1/namespaces", "", ListAccess)
|
||||
if !ok || err != nil {
|
||||
return nil, fmt.Errorf("user not authorized to list all namespaces")
|
||||
}
|
||||
|
|
@ -524,12 +524,25 @@ func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
|||
return a.getMxsClient(), err
|
||||
}
|
||||
|
||||
func (a *APIClient) invalidateCache() error {
|
||||
dial, err := a.CachedDiscovery()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dial.Invalidate()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwitchContext handles kubeconfig context switches.
|
||||
func (a *APIClient) SwitchContext(name string) error {
|
||||
log.Debug().Msgf("Switching context %q", name)
|
||||
if err := a.config.SwitchContext(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.invalidateCache(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.reset()
|
||||
ResetMetrics()
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
// PatchAccess patch a resource.
|
||||
PatchAccess = []string{PatchVerb}
|
||||
|
||||
// GetAccess reads a resource.
|
||||
GetAccess = []string{GetVerb}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,8 @@ func (a *Aliases) loadDefaultAliases() {
|
|||
a.declare("help", "h", "?")
|
||||
a.declare("quit", "q", "q!", "qa", "Q")
|
||||
a.declare("aliases", "alias", "a")
|
||||
a.declare("popeye", "pop")
|
||||
// !!BOZO!!
|
||||
// a.declare("popeye", "pop")
|
||||
a.declare("helm", "charts", "chart", "hm")
|
||||
a.declare("dir", "d")
|
||||
a.declare("contexts", "context", "ctx")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package config_test
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
|
|
@ -109,8 +110,8 @@ func TestAliasesLoad(t *testing.T) {
|
|||
config.AppConfigDir = "testdata/aliases"
|
||||
a := config.NewAliases()
|
||||
|
||||
assert.Nil(t, a.Load("testdata/aliases/plain.yaml"))
|
||||
assert.Equal(t, 56, len(a.Alias))
|
||||
assert.Nil(t, a.Load(path.Join(config.AppConfigDir, "plain.yaml")))
|
||||
assert.Equal(t, 54, len(a.Alias))
|
||||
}
|
||||
|
||||
func TestAliasesSave(t *testing.T) {
|
||||
|
|
@ -123,7 +124,7 @@ func TestAliasesSave(t *testing.T) {
|
|||
|
||||
assert.Equal(t, c, len(a.Alias))
|
||||
assert.Nil(t, a.Save())
|
||||
assert.Nil(t, a.LoadFile("/tmp/test-aliases/aliases.yaml"))
|
||||
assert.Nil(t, a.LoadFile(config.AppAliasesFile))
|
||||
assert.Equal(t, c, len(a.Alias))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
aliases:
|
||||
dp: "apps.v1.deployments"
|
||||
pe: ".v1.pods"
|
||||
dp: "apps/v1/deployments"
|
||||
pe: "v1/pods"
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (d *Deployment) Restart(ctx context.Context, path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
auth, err := d.Client().CanI(dp.Namespace, "apps/v1/deployments", dp.Name, []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(dp.Namespace, "apps/v1/deployments", dp.Name, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -261,7 +261,7 @@ func (d *Deployment) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (d *Deployment) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", n, []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", n, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func (d *DaemonSet) Restart(ctx context.Context, path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", ds.Name, []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", ds.Name, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ func (d *DaemonSet) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (d *DaemonSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/daemonset", n, []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/daemonset", n, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ func (n *Node) ensureCordoned(path string) (bool, error) {
|
|||
// FetchNode retrieves a node.
|
||||
func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
||||
_, n := client.Namespaced(path)
|
||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", n, []string{"get"})
|
||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", n, client.GetAccess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -272,7 +272,7 @@ func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
|||
|
||||
// FetchNodes retrieves all nodes.
|
||||
func FetchNodes(ctx context.Context, f Factory, labelsSel string) (*v1.NodeList, error) {
|
||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", "", []string{client.ListVerb})
|
||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", "", client.ListAccess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ func (p *Pod) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
|||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:log", n, []string{client.GetVerb})
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:log", n, client.GetAccess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -426,7 +426,7 @@ func (p *Pod) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (p *Pod) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pod", n, []string{client.PatchVerb})
|
||||
auth, err := p.Client().CanI(ns, "v1/pod", n, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func (p *PortForwarder) Start(path string, tt port.PortTunnel) (*portforward.Por
|
|||
p.path, p.tunnel, p.age = path, tt, time.Now()
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods", n, []string{client.GetVerb})
|
||||
auth, err := p.Client().CanI(ns, "v1/pods", n, client.GetAccess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,50 +21,24 @@ const (
|
|||
crdCat = "crd"
|
||||
k9sCat = "k9s"
|
||||
helmCat = "helm"
|
||||
crdGVR = "apiextensions.k8s.io/v1/customresourcedefinitions"
|
||||
)
|
||||
|
||||
// MetaAccess tracks resources metadata.
|
||||
var MetaAccess = NewMeta()
|
||||
|
||||
var stdGroups = []string{
|
||||
"admissionregistration.k8s.io/v1",
|
||||
"admissionregistration.k8s.io/v1beta1",
|
||||
"apiextensions.k8s.io/v1",
|
||||
"apiextensions.k8s.io/v1beta1",
|
||||
"apiregistration.k8s.io/v1",
|
||||
"apiregistration.k8s.io/v1beta1",
|
||||
"apps/v1",
|
||||
"authentication.k8s.io/v1",
|
||||
"authentication.k8s.io/v1beta1",
|
||||
"authorization.k8s.io/v1",
|
||||
"authorization.k8s.io/v1beta1",
|
||||
"autoscaling/v1",
|
||||
"autoscaling/v2beta1",
|
||||
"autoscaling/v2beta2",
|
||||
"batch/v1",
|
||||
"batch/v1beta1",
|
||||
"certificates.k8s.io/v1",
|
||||
"certificates.k8s.io/v1beta1",
|
||||
"coordination.k8s.io/v1",
|
||||
"coordination.k8s.io/v1beta1",
|
||||
"discovery.k8s.io/v1beta1",
|
||||
"dynatrace.com/v1alpha1",
|
||||
"events.k8s.io/v1",
|
||||
"extensions/v1beta1",
|
||||
"flowcontrol.apiserver.k8s.io/v1beta1",
|
||||
"metrics.k8s.io/v1beta1",
|
||||
"networking.k8s.io/v1",
|
||||
"networking.k8s.io/v1beta1",
|
||||
"node.k8s.io/v1",
|
||||
"node.k8s.io/v1beta1",
|
||||
"policy/v1beta1",
|
||||
"rbac.authorization.k8s.io/v1",
|
||||
"rbac.authorization.k8s.io/v1beta1",
|
||||
"scheduling.k8s.io/v1",
|
||||
"scheduling.k8s.io/v1beta1",
|
||||
"storage.k8s.io/v1",
|
||||
"storage.k8s.io/v1beta1",
|
||||
"v1",
|
||||
var stdGroups = map[string]struct{}{
|
||||
"apps/v1": {},
|
||||
"autoscaling/v1": {},
|
||||
"autoscaling/v2": {},
|
||||
"autoscaling/v2beta1": {},
|
||||
"autoscaling/v2beta2": {},
|
||||
"batch/v1": {},
|
||||
"batch/v1beta1": {},
|
||||
"extensions/v1beta1": {},
|
||||
"policy/v1beta1": {},
|
||||
"policy/v1": {},
|
||||
"v1": {},
|
||||
}
|
||||
|
||||
func (m ResourceMetas) clear() {
|
||||
|
|
@ -372,7 +346,6 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dial.Invalidate()
|
||||
rr, err := dial.ServerPreferredResources()
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msgf("Failed to load preferred resources")
|
||||
|
|
@ -387,7 +360,7 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
|||
if res.SingularName == "" {
|
||||
res.SingularName = strings.ToLower(res.Kind)
|
||||
}
|
||||
if !isStandardGroup(res.Group) {
|
||||
if !isStandardGroup(r.GroupVersion) {
|
||||
res.Categories = append(res.Categories, crdCat)
|
||||
}
|
||||
m[gvr] = res
|
||||
|
|
@ -397,14 +370,12 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func isStandardGroup(r string) bool {
|
||||
for _, res := range stdGroups {
|
||||
if strings.Index(res, r) == 0 {
|
||||
return true
|
||||
}
|
||||
func isStandardGroup(gv string) bool {
|
||||
if _, ok := stdGroups[gv]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return strings.Contains(gv, "k8s.io")
|
||||
}
|
||||
|
||||
var deprecatedGVRs = map[client.GVR]struct{}{
|
||||
|
|
@ -420,7 +391,6 @@ func loadCRDs(f Factory, m ResourceMetas) {
|
|||
if f.Client() == nil || !f.Client().ConnectionOK() {
|
||||
return
|
||||
}
|
||||
const crdGVR = "apiextensions.k8s.io/v1/customresourcedefinitions"
|
||||
oo, err := f.List(crdGVR, client.ClusterScope, false, labels.Everything())
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("Fail CRDs load")
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func (s *StatefulSet) Restart(ctx context.Context, path string) error {
|
|||
s.Forwarders().Kill(client.FQN(p.Namespace, p.Name))
|
||||
}
|
||||
|
||||
auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", n, []string{client.PatchVerb})
|
||||
auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", n, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -291,7 +291,7 @@ func (s *StatefulSet) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (s *StatefulSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulset", n, []string{client.PatchVerb})
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulset", n, client.PatchAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,10 @@ func (t *Table) updater(ctx context.Context) {
|
|||
}
|
||||
|
||||
func (t *Table) refresh(ctx context.Context) error {
|
||||
defer func(ti time.Time) {
|
||||
log.Trace().Msgf("Refresh [%s](%d) %s ", t.gvr, t.data.RowCount(), time.Since(ti))
|
||||
}(time.Now())
|
||||
|
||||
if !atomic.CompareAndSwapInt32(&t.inUpdate, 0, 1) {
|
||||
log.Debug().Msgf("Dropping update...")
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -244,7 +244,6 @@ func (t *TableData) Reset(ns string) {
|
|||
|
||||
func (t *TableData) Reconcile(ctx context.Context, r Renderer, oo []runtime.Object) error {
|
||||
var rows Rows
|
||||
|
||||
if len(oo) > 0 {
|
||||
if r.IsGeneric() {
|
||||
table, ok := oo[0].(*metav1.Table)
|
||||
|
|
@ -399,6 +398,7 @@ func (t *TableData) Clone() *TableData {
|
|||
header: t.header.Clone(),
|
||||
rowEvents: t.rowEvents.Clone(),
|
||||
namespace: t.namespace,
|
||||
gvr: t.gvr,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ func editRes(app *App, gvr client.GVR, path string) error {
|
|||
if gvr.String() == "v1/namespaces" {
|
||||
ns = n
|
||||
}
|
||||
if ok, err := app.Conn().CanI(ns, gvr.String(), n, []string{"patch"}); !ok || err != nil {
|
||||
if ok, err := app.Conn().CanI(ns, gvr.String(), n, client.PatchAccess); !ok || err != nil {
|
||||
return fmt.Errorf("current user can't edit resource %s", gvr)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name: k9s
|
||||
base: core20
|
||||
version: 'v0.32.1'
|
||||
version: 'v0.32.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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue