diff --git a/Makefile b/Makefile
index ae69cd8b..92b3ec53 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.40.0
+VERSION ?= v0.40.1
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.40.1.md b/change_logs/release_v0.40.1.md
new file mode 100644
index 00000000..7660c687
--- /dev/null
+++ b/change_logs/release_v0.40.1.md
@@ -0,0 +1,64 @@
+
+
+# Release v0.40.1
+
+## 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!
+
+😳 Aye! Buzz kill on the 0.40.0 aftermath... 🙀 👻
+
+---
+
+## Videos Are In The Can!
+
+Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
+
+* [K9s v0.40.0 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
+
+* [#3113](https://github.com/derailed/k9s/issues/3113) 0.40.0 can't retain temporary view sort
+* [#3111](https://github.com/derailed/k9s/issues/3111) k9s can't describe or print YAML for HPAs in all namespaces view
+* [#2966](https://github.com/derailed/k9s/issues/2966) Go to the Contexts page and filter, contexts that are matched will be filtered ou
+* [#2962](https://github.com/derailed/k9s/issues/2962) Small colour/filtering related bug
+* [#2961](https://github.com/derailed/k9s/issues/2961) Drain node with the -disable-eviction
+* [#2958](https://github.com/derailed/k9s/issues/2958) Restart count in container view associated with the wrong container
+* [#2945](https://github.com/derailed/k9s/issues/2945) Could we add ServiceAccount Column in v1/POD view
+
+---
+
+## Contributed PRs
+
+Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
+
+* [#3094](https://github.com/derailed/k9s/pull/3094) Log in as root to the node.
+* [#3033](https://github.com/derailed/k9s/pull/3033) Skip cache invalidation on failed connection
+* [#2965](https://github.com/derailed/k9s/pull/2965) Make menu foreground style configurable through skins
+* [#2952](https://github.com/derailed/k9s/pull/2952) A modest attempt to improve the logo aesthetics
+* [#2833](https://github.com/derailed/k9s/pull/2833) allow scaling custom resource
+* [#2799](https://github.com/derailed/k9s/pull/2799) feat(app): add history navigation with [ and ], most recent command with -
+* [#2719](https://github.com/derailed/k9s/pull/2719) fix: stop table header cells from being selectable
+* [#2865](https://github.com/derailed/k9s/pull/2865) Feature/DisableAutoscroll
+
+---
+
+
© 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/dao/dynamic.go b/internal/dao/dynamic.go
index a1bd321f..4af90160 100644
--- a/internal/dao/dynamic.go
+++ b/internal/dao/dynamic.go
@@ -4,7 +4,6 @@ package dao
import (
"context"
- "errors"
"fmt"
"strings"
@@ -24,13 +23,30 @@ type Dynamic struct {
Generic
}
+// Get returns a given resource as a table object.
func (d *Dynamic) Get(ctx context.Context, path string) (runtime.Object, error) {
- return nil, errors.New("Not implemented")
+ oo, err := d.toTable(ctx, path)
+ if err != nil || len(oo) == 0 {
+ return nil, err
+ }
+
+ return oo[0], nil
}
+// List returns a collection of resources as one or more table objects.
func (d *Dynamic) List(ctx context.Context, ns string) ([]runtime.Object, error) {
+ return d.toTable(ctx, ns)
+}
+
+func (d *Dynamic) toTable(ctx context.Context, fqn string) ([]runtime.Object, error) {
strLabel, _ := ctx.Value(internal.KeyLabels).(string)
+ opts := []string{d.gvr.R()}
+ ns, n := client.Namespaced(fqn)
+ if n != "" {
+ opts = append(opts, n)
+ }
+
allNS := client.IsAllNamespaces(ns)
flags := cmdutil.NewMatchVersionFlags(d.getFactory().Client().Config().Flags())
f := cmdutil.NewFactory(flags)
@@ -40,7 +56,7 @@ func (d *Dynamic) List(ctx context.Context, ns string) ([]runtime.Object, error)
LabelSelectorParam(strLabel).
FieldSelectorParam("").
RequestChunksOf(0).
- ResourceTypeOrNameArgs(true, d.gvr.R()).
+ ResourceTypeOrNameArgs(true, opts...).
ContinueOnError().
Latest().
Flatten().
diff --git a/internal/dao/registry.go b/internal/dao/registry.go
index 0e707a8c..8c6c75ed 100644
--- a/internal/dao/registry.go
+++ b/internal/dao/registry.go
@@ -10,6 +10,7 @@ import (
"strings"
"sync"
+ "github.com/derailed/k9s/internal/client"
"github.com/rs/zerolog/log"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -17,8 +18,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
-
- "github.com/derailed/k9s/internal/client"
+ "k8s.io/apimachinery/pkg/util/sets"
)
const (
@@ -32,19 +32,19 @@ const (
// MetaAccess tracks resources metadata.
var MetaAccess = NewMeta()
-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": {},
-}
+var stdGroups = sets.New[string](
+ "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() {
for k := range m {
@@ -381,20 +381,15 @@ func loadPreferred(f Factory, m ResourceMetas) error {
}
func isStandardGroup(gv string) bool {
- if _, ok := stdGroups[gv]; ok {
- return true
- }
-
- return strings.Contains(gv, "k8s.io")
+ return stdGroups.Has(gv) || strings.Contains(gv, "k8s.io")
}
-var deprecatedGVRs = map[client.GVR]struct{}{
- client.NewGVR("extensions/v1beta1/ingresses"): {},
-}
+var deprecatedGVRs = sets.New[client.GVR](
+ client.NewGVR("extensions/v1beta1/ingresses"),
+)
func isDeprecated(gvr client.GVR) bool {
- _, ok := deprecatedGVRs[gvr]
- return ok
+ return deprecatedGVRs.Has(gvr)
}
// loadCRDs Wait for the cache to synced and then add some additional properties to CRD.
diff --git a/internal/model/registry.go b/internal/model/registry.go
index ab4bb774..97814706 100644
--- a/internal/model/registry.go
+++ b/internal/model/registry.go
@@ -84,9 +84,9 @@ var Registry = map[string]ResourceMeta{
},
// Core...
- // "v1/endpoints": {
- // Renderer: &render.Endpoints{},
- // },
+ "v1/endpoints": {
+ Renderer: &render.Endpoints{},
+ },
"v1/pods": {
DAO: &dao.Pod{},
Renderer: render.NewPod(),
diff --git a/internal/render/cust_cols.go b/internal/render/cust_cols.go
index acf541ed..7fde0e72 100644
--- a/internal/render/cust_cols.go
+++ b/internal/render/cust_cols.go
@@ -17,8 +17,10 @@ import (
"k8s.io/client-go/util/jsonpath"
)
+// ColsSpecs represents a collection of column specification ie NAME:spec|flags.
type ColsSpecs []string
+// NewColsSpecs returns a new instance.
func NewColsSpecs(cols ...string) ColsSpecs {
return ColsSpecs(cols)
}
@@ -40,6 +42,7 @@ func (cc ColsSpecs) parseSpecs() (ColumnSpecs, error) {
return specs, nil
}
+// RenderedCols tracks a collection of column header and cust column parse expression.
type RenderedCols []RenderedCol
func (rr RenderedCols) hydrateRow(row *model1.Row) {
@@ -50,6 +53,7 @@ func (rr RenderedCols) hydrateRow(row *model1.Row) {
row.Fields = ff
}
+// HasHeader checks if a given header is present in the collection.
func (rr RenderedCols) HasHeader(n string) bool {
for _, r := range rr {
if r.has(n) {
@@ -60,26 +64,31 @@ func (rr RenderedCols) HasHeader(n string) bool {
return false
}
+// RenderedCol represents a column header and a column spec.
type RenderedCol struct {
Header model1.HeaderColumn
Value string
}
+// Has checks if the header column match the given name.
func (r RenderedCol) has(n string) bool {
return r.Header.Name == n
}
+// ColumnSpec tracks a header column and an options cust column spec.
type ColumnSpec struct {
Header model1.HeaderColumn
Spec string
}
+// ColumnSpecs tracks a collection of column specs.
type ColumnSpecs []ColumnSpec
func (c ColumnSpecs) isEmpty() bool {
return len(c) == 0
}
+// Header builds a new header that is a super set of custom and/or default header.
func (cc ColumnSpecs) Header(rh model1.Header) model1.Header {
hh := make(model1.Header, 0, len(cc))
for _, h := range cc {
diff --git a/internal/render/table.go b/internal/render/table.go
index 2b8f52ed..0bc07e92 100644
--- a/internal/render/table.go
+++ b/internal/render/table.go
@@ -92,18 +92,26 @@ func (t *Table) Render(o any, ns string, r *model1.Row) error {
func (t *Table) defaultRow(row *metav1.TableRow, ns string, r *model1.Row) error {
th := t.defaultHeader()
ons, name := ns, UnknownValue
- if row.Object.Object != nil {
- m, _ := meta.Accessor(row.Object.Object)
- if m != nil {
+ switch {
+ case row.Object.Object != nil:
+ if m, _ := meta.Accessor(row.Object.Object); m != nil {
ons, name = m.GetNamespace(), m.GetName()
}
- } else if idx, ok := th.IndexOf("NAME", true); ok && idx >= 0 {
- name = row.Cells[idx].(string)
- if idx, ok := th.IndexOf("NAMESPACE", true); ok && idx >= 0 {
- ons = row.Cells[idx].(string)
+ case row.Object.Raw != nil:
+ var pm metav1.PartialObjectMetadata
+ if err := json.Unmarshal(row.Object.Raw, &pm); err != nil {
+ return err
+ }
+ ons, name = pm.Namespace, pm.Name
+ default:
+ if idx, ok := th.IndexOf("NAME", true); ok && idx >= 0 {
+ name = row.Cells[idx].(string)
+ if idx, ok := th.IndexOf("NAMESPACE", true); ok && idx >= 0 {
+ ons = row.Cells[idx].(string)
+ }
}
- } else {
}
+
if client.IsClusterWide(ons) {
ons = client.ClusterScope
}
diff --git a/internal/ui/table.go b/internal/ui/table.go
index 56d6bb20..c8ceafe9 100644
--- a/internal/ui/table.go
+++ b/internal/ui/table.go
@@ -157,9 +157,12 @@ func (t *Table) GVR() client.GVR { return t.gvr }
func (t *Table) ViewSettingsChanged(vs *config.ViewSetting) {
if t.setViewSetting(vs) {
if vs == nil {
- t.setSortCol(model1.SortColumn{})
+ if !t.getMSort() {
+ t.setSortCol(model1.SortColumn{})
+ }
+ } else {
+ t.setMSort(false)
}
- t.setMSort(false)
t.Refresh()
}
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 93e5755e..00423a23 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: k9s
base: core22
-version: 'v0.40.0'
+version: 'v0.40.1'
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.