From ecd33ff48d5b4c02998cddc12274b1f0afb4ff0c Mon Sep 17 00:00:00 2001 From: Fernand Galiana Date: Tue, 5 Mar 2024 17:11:31 -0700 Subject: [PATCH] K9s release v0.32.2 (#2598) * [Maint] cleaning up * [Bug] Fix #2593 * [Bug] Fix #2582 * Release v0.32.2 --- Makefile | 2 +- change_logs/release_v0.32.2.md | 43 ++++++++++++++ internal/client/client.go | 15 ++++- internal/client/types.go | 3 + internal/config/alias.go | 3 +- internal/config/alias_test.go | 7 ++- internal/config/testdata/aliases/plain.yaml | 4 +- internal/dao/dp.go | 4 +- internal/dao/ds.go | 4 +- internal/dao/node.go | 4 +- internal/dao/pod.go | 4 +- internal/dao/port_forwarder.go | 2 +- internal/dao/registry.go | 66 ++++++--------------- internal/dao/sts.go | 4 +- internal/model/table.go | 4 ++ internal/model1/table_data.go | 2 +- internal/view/browser.go | 2 +- snap/snapcraft.yaml | 2 +- 18 files changed, 105 insertions(+), 70 deletions(-) create mode 100644 change_logs/release_v0.32.2.md diff --git a/Makefile b/Makefile index bf7861e6ac..9a0824facf 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.32.1 +VERSION ?= v0.32.2 IMG_NAME := derailed/k9s IMAGE := ${IMG_NAME}:${VERSION} diff --git a/change_logs/release_v0.32.2.md b/change_logs/release_v0.32.2.md new file mode 100644 index 0000000000..96affe5958 --- /dev/null +++ b/change_logs/release_v0.32.2.md @@ -0,0 +1,43 @@ + + +# 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 + +--- + + © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) \ No newline at end of file diff --git a/internal/client/client.go b/internal/client/client.go index e12f900530..aaac43e8e4 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -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() diff --git a/internal/client/types.go b/internal/client/types.go index 8dad38a06a..b5a7d3cf76 100644 --- a/internal/client/types.go +++ b/internal/client/types.go @@ -55,6 +55,9 @@ const ( ) var ( + // PatchAccess patch a resource. + PatchAccess = []string{PatchVerb} + // GetAccess reads a resource. GetAccess = []string{GetVerb} diff --git a/internal/config/alias.go b/internal/config/alias.go index be53f4027f..426d41d76f 100644 --- a/internal/config/alias.go +++ b/internal/config/alias.go @@ -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") diff --git a/internal/config/alias_test.go b/internal/config/alias_test.go index d8551bfccf..c67f4f5824 100644 --- a/internal/config/alias_test.go +++ b/internal/config/alias_test.go @@ -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)) } diff --git a/internal/config/testdata/aliases/plain.yaml b/internal/config/testdata/aliases/plain.yaml index 185113e7da..4291a3c7b6 100644 --- a/internal/config/testdata/aliases/plain.yaml +++ b/internal/config/testdata/aliases/plain.yaml @@ -1,3 +1,3 @@ aliases: - dp: "apps.v1.deployments" - pe: ".v1.pods" + dp: "apps/v1/deployments" + pe: "v1/pods" diff --git a/internal/dao/dp.go b/internal/dao/dp.go index f3273399e9..5df3432113 100644 --- a/internal/dao/dp.go +++ b/internal/dao/dp.go @@ -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 } diff --git a/internal/dao/ds.go b/internal/dao/ds.go index b0bf8c0b69..3bbd7827c0 100644 --- a/internal/dao/ds.go +++ b/internal/dao/ds.go @@ -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 } diff --git a/internal/dao/node.go b/internal/dao/node.go index 1ee29ed143..e55f40c5de 100644 --- a/internal/dao/node.go +++ b/internal/dao/node.go @@ -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 } diff --git a/internal/dao/pod.go b/internal/dao/pod.go index 4b03d66ed7..d65854498b 100644 --- a/internal/dao/pod.go +++ b/internal/dao/pod.go @@ -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 } diff --git a/internal/dao/port_forwarder.go b/internal/dao/port_forwarder.go index 345ad3bb13..454af94285 100644 --- a/internal/dao/port_forwarder.go +++ b/internal/dao/port_forwarder.go @@ -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 } diff --git a/internal/dao/registry.go b/internal/dao/registry.go index fd19c6c4e9..e19ad58600 100644 --- a/internal/dao/registry.go +++ b/internal/dao/registry.go @@ -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") diff --git a/internal/dao/sts.go b/internal/dao/sts.go index 0137640957..9c111d523f 100644 --- a/internal/dao/sts.go +++ b/internal/dao/sts.go @@ -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 } diff --git a/internal/model/table.go b/internal/model/table.go index ce848724ab..be1157b587 100644 --- a/internal/model/table.go +++ b/internal/model/table.go @@ -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 diff --git a/internal/model1/table_data.go b/internal/model1/table_data.go index 10b0c3c654..13ef48ce81 100644 --- a/internal/model1/table_data.go +++ b/internal/model1/table_data.go @@ -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, } } diff --git a/internal/view/browser.go b/internal/view/browser.go index b6045a6de5..7f0876d1ab 100644 --- a/internal/view/browser.go +++ b/internal/view/browser.go @@ -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) } diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 5c2fdaa4ca..de4bd294a8 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -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.