diff --git a/Makefile b/Makefile
index 49aa168b..d11c7a09 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ PACKAGE := github.com/derailed/$(NAME)
GIT_REV ?= $(shell git rev-parse --short HEAD)
SOURCE_DATE_EPOCH ?= $(shell date +%s)
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
-VERSION ?= v0.25.10
+VERSION ?= v0.25.11
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}
diff --git a/change_logs/release_v0.25.11.md b/change_logs/release_v0.25.11.md
new file mode 100644
index 00000000..d2f86b64
--- /dev/null
+++ b/change_logs/release_v0.25.11.md
@@ -0,0 +1,42 @@
+
+
+# Release v0.25.11
+
+## 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!
+
+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)
+
+### A Word From Our Sponsors...
+
+I want to recognize the following folks that have been kind enough to join our sponsorship program and opted to `pay it forward`!
+
+* [Joshua Kapellen](https://github.com/joshuakapellen)
+* [Qdentity](https://github.com/qdentity)
+* [Maxim](https://github.com/bsod90)
+* [Sönke Schau](https://github.com/xgcssch)
+
+So if you feel K9s is helping with your productivity while administering your Kubernetes clusters, please consider pitching in as it will go a long way in ensuring a thriving environment for this repo and our k9ers community at large.
+
+Also please take some time and give a huge shoot out to all the good folks below that have spent time plowing thru the code to help improve K9s for all of us!
+
+Thank you!!
+
+---
+
+## Maintenance Release!
+
+Hoy! end of year suck... Feeling the burn ;( Apologize for the disruptions...
+
+---
+
+## Resolved Issues
+
+* [Issue #1374](https://github.com/derailed/k9s/issues/1374) --all-namespaces does not work v0.25.10
+* [Issue #1376](https://github.com/derailed/k9s/issues/1376) Events not sorted correctly by dates
+* [Issue #1373](https://github.com/derailed/k9s/issues/1373) change namespace not possible
+
+
© 2021 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/internal/client/config.go b/internal/client/config.go
index c5b7017b..a5640f5e 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -23,9 +23,8 @@ const (
// Config tracks a kubernetes configuration.
type Config struct {
- flags *genericclioptions.ConfigFlags
- mutex *sync.RWMutex
- OverrideNS bool
+ flags *genericclioptions.ConfigFlags
+ mutex *sync.RWMutex
}
// NewConfig returns a new k8s config or an error if the flags are invalid.
@@ -183,15 +182,15 @@ func (c *Config) CurrentClusterName() (string, error) {
}
// ClusterNames fetch all kubeconfig defined clusters.
-func (c *Config) ClusterNames() ([]string, error) {
+func (c *Config) ClusterNames() (map[string]struct{}, error) {
cfg, err := c.RawConfig()
if err != nil {
return nil, err
}
- cc := make([]string, 0, len(cfg.Clusters))
+ cc := make(map[string]struct{}, len(cfg.Clusters))
for name := range cfg.Clusters {
- cc = append(cc, name)
+ cc[name] = struct{}{}
}
return cc, nil
diff --git a/internal/config/config.go b/internal/config/config.go
index 8c0e556c..f94cf4e3 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -36,15 +36,14 @@ type (
CurrentNamespaceName() (string, error)
// ClusterNames() returns all available cluster names.
- ClusterNames() ([]string, error)
+ ClusterNames() (map[string]struct{}, error)
}
// Config tracks K9s configuration options.
Config struct {
- K9s *K9s `yaml:"k9s"`
- client client.Connection
- settings KubeSettings
- overrideNS bool
+ K9s *K9s `yaml:"k9s"`
+ client client.Connection
+ settings KubeSettings
}
)
@@ -94,11 +93,10 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
c.K9s.ActivateCluster()
var ns string
- var override bool
if k9sFlags != nil && IsBoolSet(k9sFlags.AllNamespaces) {
- ns, override = client.NamespaceAll, true
+ ns = client.NamespaceAll
} else if isSet(flags.Namespace) {
- ns, override = *flags.Namespace, true
+ ns = *flags.Namespace
} else if context.Namespace != "" {
ns = context.Namespace
} else if cl := c.K9s.ActiveCluster(); cl != nil {
@@ -109,7 +107,7 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
if err := c.SetActiveNamespace(ns); err != nil {
return err
}
- flags.Namespace, c.overrideNS = &ns, override
+ flags.Namespace = &ns
}
if isSet(flags.ClusterName) {
@@ -142,6 +140,9 @@ func (c *Config) ActiveNamespace() string {
return "default"
}
cl := c.CurrentCluster()
+ if cl != nil && cl.Namespace != nil {
+ return cl.Namespace.Active
+ }
if cl == nil {
cl = NewCluster()
c.K9s.Clusters[c.K9s.CurrentCluster] = cl
@@ -154,10 +155,6 @@ func (c *Config) ActiveNamespace() string {
return ns
}
- if cl.Namespace != nil {
- return cl.Namespace.Active
- }
-
return "default"
}
@@ -221,9 +218,6 @@ func (c *Config) GetConnection() client.Connection {
// SetConnection set an api server connection.
func (c *Config) SetConnection(conn client.Connection) {
c.client = conn
- if c.client != nil && c.client.Config() != nil {
- c.client.Config().OverrideNS = c.overrideNS
- }
}
// Load K9s configuration from file.
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index f8b6dae3..f7883241 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -198,7 +198,7 @@ func TestConfigSaveFile(t *testing.T) {
m.When(mk.CurrentContextName()).ThenReturn("minikube", nil)
m.When(mk.CurrentClusterName()).ThenReturn("minikube", nil)
m.When(mk.CurrentNamespaceName()).ThenReturn("default", nil)
- m.When(mk.ClusterNames()).ThenReturn([]string{"minikube", "fred", "blee"}, nil)
+ m.When(mk.ClusterNames()).ThenReturn(map[string]struct{}{"minikube": {}, "fred": {}, "blee": {}}, nil)
m.When(mk.NamespaceNames(namespaces())).ThenReturn([]string{"default"})
cfg := config.NewConfig(mk)
@@ -228,7 +228,7 @@ func TestConfigReset(t *testing.T) {
m.When(mk.CurrentContextName()).ThenReturn("blee", nil)
m.When(mk.CurrentClusterName()).ThenReturn("blee", nil)
m.When(mk.CurrentNamespaceName()).ThenReturn("default", nil)
- m.When(mk.ClusterNames()).ThenReturn([]string{"blee"}, nil)
+ m.When(mk.ClusterNames()).ThenReturn(map[string]struct{}{"blee": {}}, nil)
m.When(mk.NamespaceNames(namespaces())).ThenReturn([]string{"default"})
cfg := config.NewConfig(mk)
@@ -271,7 +271,7 @@ func (m *mockSettings) CurrentClusterName() (string, error) { return "", nil }
func (m *mockSettings) CurrentNamespaceName() (string, error) {
return *m.flags.Namespace, nil
}
-func (m *mockSettings) ClusterNames() ([]string, error) { return nil, nil }
+func (m *mockSettings) ClusterNames() (map[string]struct{}, error) { return nil, nil }
// ----------------------------------------------------------------------------
// Test Data...
diff --git a/internal/config/k9s.go b/internal/config/k9s.go
index 53265e71..621943a5 100644
--- a/internal/config/k9s.go
+++ b/internal/config/k9s.go
@@ -194,9 +194,9 @@ func (k *K9s) validateClusters(c client.Connection, ks KubeSettings) {
if err != nil {
return
}
- for key := range k.Clusters {
- k.Clusters[key].Validate(c, ks)
- if InList(cc, key) {
+ for key, cluster := range k.Clusters {
+ cluster.Validate(c, ks)
+ if _, ok := cc[key]; ok {
continue
}
if k.CurrentCluster == key {
@@ -224,8 +224,8 @@ func (k *K9s) Validate(c client.Connection, ks KubeSettings) {
}
k.Thresholds.Validate(c, ks)
- if ctx, err := ks.CurrentContextName(); err == nil && len(k.CurrentContext) == 0 {
- k.CurrentContext = ctx
+ if context, err := ks.CurrentContextName(); err == nil && len(k.CurrentContext) == 0 {
+ k.CurrentContext = context
k.CurrentCluster = ""
}
diff --git a/internal/config/k9s_test.go b/internal/config/k9s_test.go
index ca1fabc9..631af222 100644
--- a/internal/config/k9s_test.go
+++ b/internal/config/k9s_test.go
@@ -66,7 +66,7 @@ func TestK9sValidate(t *testing.T) {
mk := NewMockKubeSettings()
m.When(mk.CurrentContextName()).ThenReturn("ctx1", nil)
m.When(mk.CurrentClusterName()).ThenReturn("c1", nil)
- m.When(mk.ClusterNames()).ThenReturn([]string{"c1", "c2"}, nil)
+ m.When(mk.ClusterNames()).ThenReturn(map[string]struct{}{"c1": {}, "c2": {}}, nil)
m.When(mk.NamespaceNames(namespaces())).ThenReturn([]string{"default"})
c := config.NewK9s()
@@ -90,7 +90,7 @@ func TestK9sValidateBlank(t *testing.T) {
mk := NewMockKubeSettings()
m.When(mk.CurrentContextName()).ThenReturn("ctx1", nil)
m.When(mk.CurrentClusterName()).ThenReturn("c1", nil)
- m.When(mk.ClusterNames()).ThenReturn([]string{"c1", "c2"}, nil)
+ m.When(mk.ClusterNames()).ThenReturn(map[string]struct{}{"c1": {}, "c2": {}}, nil)
m.When(mk.NamespaceNames(namespaces())).ThenReturn([]string{"default"})
var c config.K9s
diff --git a/internal/config/mock_kubesettings_test.go b/internal/config/mock_kubesettings_test.go
index b8a41f0b..9269469f 100644
--- a/internal/config/mock_kubesettings_test.go
+++ b/internal/config/mock_kubesettings_test.go
@@ -25,17 +25,17 @@ func NewMockKubeSettings(options ...pegomock.Option) *MockKubeSettings {
func (mock *MockKubeSettings) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }
func (mock *MockKubeSettings) FailHandler() pegomock.FailHandler { return mock.fail }
-func (mock *MockKubeSettings) ClusterNames() ([]string, error) {
+func (mock *MockKubeSettings) ClusterNames() (map[string]struct{}, error) {
if mock == nil {
panic("mock must not be nil. Use myMock := NewMockKubeSettings().")
}
params := []pegomock.Param{}
- result := pegomock.GetGenericMockFrom(mock).Invoke("ClusterNames", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
- var ret0 []string
+ result := pegomock.GetGenericMockFrom(mock).Invoke("ClusterNames", params, []reflect.Type{reflect.TypeOf((*map[string]struct{})(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
+ var ret0 map[string]struct{}
var ret1 error
if len(result) != 0 {
if result[0] != nil {
- ret0 = result[0].([]string)
+ ret0 = result[0].(map[string]struct{})
}
if result[1] != nil {
ret1 = result[1].(error)
diff --git a/internal/model/registry.go b/internal/model/registry.go
index 912733ff..13ef5b91 100644
--- a/internal/model/registry.go
+++ b/internal/model/registry.go
@@ -110,6 +110,10 @@ var Registry = map[string]ResourceMeta{
"v1/persistentvolumeclaims": {
Renderer: &render.PersistentVolumeClaim{},
},
+ "v1/events": {
+ DAO: &dao.Table{},
+ Renderer: &render.Event{},
+ },
// Apps...
"apps/v1/deployments": {
diff --git a/internal/model/table.go b/internal/model/table.go
index cd45b048..d5d7f9da 100644
--- a/internal/model/table.go
+++ b/internal/model/table.go
@@ -240,7 +240,7 @@ func (t *Table) reconcile(ctx context.Context) error {
var rows render.Rows
if len(oo) > 0 {
- if _, ok := meta.Renderer.(*render.Generic); ok {
+ if meta.Renderer.IsGeneric() {
table, ok := oo[0].(*metav1beta1.Table)
if !ok {
return fmt.Errorf("expecting a meta table but got %T", oo[0])
@@ -300,8 +300,13 @@ func hydrate(ns string, oo []runtime.Object, rr render.Rows, re Renderer) error
return nil
}
+type Generic interface {
+ SetTable(*metav1beta1.Table)
+ Render(interface{}, string, *render.Row) error
+}
+
func genericHydrate(ns string, table *metav1beta1.Table, rr render.Rows, re Renderer) error {
- gr, ok := re.(*render.Generic)
+ gr, ok := re.(Generic)
if !ok {
return fmt.Errorf("expecting generic renderer but got %T", re)
}
diff --git a/internal/model/types.go b/internal/model/types.go
index 7b04da10..489fedd6 100644
--- a/internal/model/types.go
+++ b/internal/model/types.go
@@ -84,6 +84,9 @@ type Component interface {
// Renderer represents a resource renderer.
type Renderer interface {
+ // IsGeneric identifies a generic handler.
+ IsGeneric() bool
+
// Render converts raw resources to tabular data.
Render(o interface{}, ns string, row *render.Row) error
diff --git a/internal/render/alias.go b/internal/render/alias.go
index 1bb6550b..2cb65816 100644
--- a/internal/render/alias.go
+++ b/internal/render/alias.go
@@ -10,11 +10,8 @@ import (
)
// Alias renders a aliases to screen.
-type Alias struct{}
-
-// ColorerFunc colors a resource row.
-func (Alias) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type Alias struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/base.go b/internal/render/base.go
new file mode 100644
index 00000000..3a668993
--- /dev/null
+++ b/internal/render/base.go
@@ -0,0 +1,26 @@
+package render
+
+// DecoratorFunc decorates a string.
+type DecoratorFunc func(string) string
+
+// AgeDecorator represents a timestamped as human column.
+var AgeDecorator = func(a string) string {
+ return toAgeHuman(a)
+}
+
+type Base struct{}
+
+// IsGeneric identifies a generic handler.
+func (Base) IsGeneric() bool {
+ return false
+}
+
+// ColorerFunc colors a resource row.
+func (Base) ColorerFunc() ColorerFunc {
+ return DefaultColorer
+}
+
+// Happy returns true if resource is happy, false otherwise.
+func (Base) Happy(_ string, _ Row) bool {
+ return true
+}
diff --git a/internal/render/benchmark.go b/internal/render/benchmark.go
index 710660ea..4f8d7600 100644
--- a/internal/render/benchmark.go
+++ b/internal/render/benchmark.go
@@ -24,7 +24,9 @@ var (
)
// Benchmark renders a benchmarks to screen.
-type Benchmark struct{}
+type Benchmark struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (b Benchmark) ColorerFunc() ColorerFunc {
diff --git a/internal/render/container.go b/internal/render/container.go
index 2031d62a..c59778e0 100644
--- a/internal/render/container.go
+++ b/internal/render/container.go
@@ -35,7 +35,9 @@ type ContainerWithMetrics interface {
}
// Container renders a K8s Container to screen.
-type Container struct{}
+type Container struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (c Container) ColorerFunc() ColorerFunc {
diff --git a/internal/render/context.go b/internal/render/context.go
index c6ecef22..1a7efd66 100644
--- a/internal/render/context.go
+++ b/internal/render/context.go
@@ -12,7 +12,9 @@ import (
)
// Context renders a K8s ConfigMap to screen.
-type Context struct{}
+type Context struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (Context) ColorerFunc() ColorerFunc {
diff --git a/internal/render/cr.go b/internal/render/cr.go
index edb7071f..10b59e2e 100644
--- a/internal/render/cr.go
+++ b/internal/render/cr.go
@@ -10,11 +10,8 @@ import (
)
// ClusterRole renders a K8s ClusterRole to screen.
-type ClusterRole struct{}
-
-// ColorerFunc colors a resource row.
-func (ClusterRole) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type ClusterRole struct {
+ Base
}
// Header returns a header rbw.
diff --git a/internal/render/crb.go b/internal/render/crb.go
index a5928cbd..97216aa9 100644
--- a/internal/render/crb.go
+++ b/internal/render/crb.go
@@ -10,11 +10,8 @@ import (
)
// ClusterRoleBinding renders a K8s ClusterRoleBinding to screen.
-type ClusterRoleBinding struct{}
-
-// ColorerFunc colors a resource row.
-func (ClusterRoleBinding) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type ClusterRoleBinding struct {
+ Base
}
// Header returns a header rbw.
diff --git a/internal/render/crd.go b/internal/render/crd.go
index dc9ee390..ec1b2c6e 100644
--- a/internal/render/crd.go
+++ b/internal/render/crd.go
@@ -11,11 +11,8 @@ import (
)
// CustomResourceDefinition renders a K8s CustomResourceDefinition to screen.
-type CustomResourceDefinition struct{}
-
-// ColorerFunc colors a resource row.
-func (CustomResourceDefinition) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type CustomResourceDefinition struct {
+ Base
}
// Header returns a header rbw.
diff --git a/internal/render/cronjob.go b/internal/render/cronjob.go
index bd9d254d..bbaad851 100644
--- a/internal/render/cronjob.go
+++ b/internal/render/cronjob.go
@@ -13,11 +13,8 @@ import (
)
// CronJob renders a K8s CronJob to screen.
-type CronJob struct{}
-
-// ColorerFunc colors a resource row.
-func (CronJob) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type CronJob struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/dir.go b/internal/render/dir.go
index d955fb98..b27afd6d 100644
--- a/internal/render/dir.go
+++ b/internal/render/dir.go
@@ -12,6 +12,11 @@ import (
// Dir renders a directory entry to screen.
type Dir struct{}
+// IsGeneric identifies a generic handler.
+func (Dir) IsGeneric() bool {
+ return false
+}
+
// ColorerFunc colors a resource row.
func (Dir) ColorerFunc() ColorerFunc {
return func(ns string, _ Header, re RowEvent) tcell.Color {
diff --git a/internal/render/dp.go b/internal/render/dp.go
index 6260def5..21fc6313 100644
--- a/internal/render/dp.go
+++ b/internal/render/dp.go
@@ -12,7 +12,9 @@ import (
)
// Deployment renders a K8s Deployment to screen.
-type Deployment struct{}
+type Deployment struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (d Deployment) ColorerFunc() ColorerFunc {
diff --git a/internal/render/ds.go b/internal/render/ds.go
index 747fac11..d981eb1f 100644
--- a/internal/render/ds.go
+++ b/internal/render/ds.go
@@ -12,11 +12,8 @@ import (
)
// DaemonSet renders a K8s DaemonSet to screen.
-type DaemonSet struct{}
-
-// ColorerFunc colors a resource row.
-func (d DaemonSet) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type DaemonSet struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/ep.go b/internal/render/ep.go
index 9ca7f756..b44aa311 100644
--- a/internal/render/ep.go
+++ b/internal/render/ep.go
@@ -12,11 +12,8 @@ import (
)
// Endpoints renders a K8s Endpoints to screen.
-type Endpoints struct{}
-
-// ColorerFunc colors a resource row.
-func (Endpoints) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type Endpoints struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/ev.go b/internal/render/ev.go
index 33bb8a75..6b27334f 100644
--- a/internal/render/ev.go
+++ b/internal/render/ev.go
@@ -1,35 +1,25 @@
package render
import (
+ "fmt"
"strings"
+ "github.com/derailed/k9s/internal/client"
"github.com/gdamore/tcell/v2"
+ metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
)
-// BOZO!!
-// import (
-// "errors"
-// "fmt"
-// "strconv"
-// "strings"
-// "time"
-
-// "github.com/derailed/k9s/internal/client"
-// "github.com/derailed/tview"
-// "github.com/gdamore/tcell/v2"
-// v1 "k8s.io/api/core/v1"
-// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-// "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-// "k8s.io/apimachinery/pkg/runtime"
-// "k8s.io/apimachinery/pkg/util/duration"
-// api "k8s.io/kubernetes/pkg/apis/core"
-// )
-
// Event renders a K8s Event to screen.
-type Event struct{}
+type Event struct {
+ Generic
+}
+
+func (*Event) IsGeneric() bool {
+ return true
+}
// ColorerFunc colors a resource row.
-func (e Event) ColorerFunc() ColorerFunc {
+func (e *Event) ColorerFunc() ColorerFunc {
return func(ns string, h Header, re RowEvent) tcell.Color {
if !Happy(ns, h, re.Row) {
return ErrColor
@@ -46,128 +36,63 @@ func (e Event) ColorerFunc() ColorerFunc {
}
}
-// // Header returns a header rbw.
-// func (Event) Header(ns string) Header {
-// return Header{
-// HeaderColumn{Name: "NAMESPACE"},
-// HeaderColumn{Name: "LAST SEEN"},
-// HeaderColumn{Name: "TYPE"},
-// HeaderColumn{Name: "REASON"},
-// HeaderColumn{Name: "OBJECT"},
-// HeaderColumn{Name: "SUBOBJECT"},
-// HeaderColumn{Name: "SOURCE"},
-// HeaderColumn{Name: "MESSAGE", Wide: true},
-// HeaderColumn{Name: "FIRST SEEN", Wide: true},
-// HeaderColumn{Name: "COUNT", Align: tview.AlignRight},
-// HeaderColumn{Name: "NAME"},
-// HeaderColumn{Name: "VALID", Wide: true},
-// }
-// }
+var ageCols = map[string]struct{}{
+ "FIRST SEEN": {},
+ "LAST SEEN": {},
+}
-// // Render renders a K8s resource to screen.
-// func (e Event) Render(o interface{}, ns string, r *Row) error {
-// raw, ok := o.(*unstructured.Unstructured)
-// if !ok {
-// return fmt.Errorf("Expected Event, but got %T", o)
-// }
-// var ev api.Event
-// err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &ev)
-// if err != nil {
-// return err
-// }
+var wideCols = map[string]struct{}{
+ "SUBOBJECT": {},
+ "SOURCE": {},
+ "FIRST SEEN": {},
+ "NAME": {},
+ "MESSAGE": {},
+}
-// firstTimestamp := translateTimestampSince(ev.FirstTimestamp)
-// if ev.FirstTimestamp.IsZero() {
-// firstTimestamp = translateMicroTimestampSince(ev.EventTime)
-// }
+func (e *Event) Header(ns string) Header {
+ if e.table == nil {
+ return Header{}
+ }
+ hh := make(Header, 0, len(e.table.ColumnDefinitions))
+ hh = append(hh, HeaderColumn{Name: "NAMESPACE"})
+ for _, h := range e.table.ColumnDefinitions {
+ header := HeaderColumn{Name: strings.ToUpper(h.Name)}
+ if _, ok := ageCols[header.Name]; ok {
+ header.Time = true
+ }
+ if _, ok := wideCols[header.Name]; ok {
+ header.Wide = true
+ }
+ hh = append(hh, header)
+ }
-// lastTimestamp := translateTimestampSince(ev.LastTimestamp)
-// if ev.LastTimestamp.IsZero() {
-// lastTimestamp = firstTimestamp
-// }
-// count := ev.Count
-// if ev.Series != nil {
-// lastTimestamp = translateMicroTimestampSince(ev.Series.LastObservedTime)
-// count = ev.Series.Count
-// } else if count == 0 {
-// // Singleton events don't have a count set in the new API.
-// count = 1
-// }
+ return hh
+}
-// var target string
-// if len(ev.InvolvedObject.Name) > 0 {
-// target = fmt.Sprintf("%s/%s", strings.ToLower(ev.InvolvedObject.Kind), ev.InvolvedObject.Name)
-// } else {
-// target = strings.ToLower(ev.InvolvedObject.Kind)
-// }
+// Render renders a K8s resource to screen.
+func (e *Event) Render(o interface{}, ns string, r *Row) error {
+ row, ok := o.(metav1beta1.TableRow)
+ if !ok {
+ return fmt.Errorf("expecting a TableRow but got %T", o)
+ }
+ nns, name, err := resourceNS(row.Object.Raw)
+ if err != nil {
+ return err
+ }
-// r.ID = client.MetaFQN(ev.ObjectMeta)
-// r.Fields = Fields{
-// ev.Namespace,
-// lastTimestamp,
-// ev.Type,
-// ev.Reason,
-// target,
-// ev.InvolvedObject.FieldPath,
-// fmtEventSource(ev.Source, ev.ReportingController, ev.ReportingInstance),
-// strings.TrimSpace(ev.Message),
-// firstTimestamp,
-// strconv.Itoa(int(count)),
-// ev.Name,
-// asStatus(e.diagnose(ev.Type)),
-// }
+ if !ok {
+ return fmt.Errorf("expecting row 0 to be a string but got %T", row.Cells[0])
+ }
+ r.ID = client.FQN(nns, name)
+ r.Fields = make(Fields, 0, len(e.Header(ns)))
+ r.Fields = append(r.Fields, nns)
+ for _, c := range row.Cells {
+ if c == nil {
+ r.Fields = append(r.Fields, Blank)
+ continue
+ }
+ r.Fields = append(r.Fields, fmt.Sprintf("%v", c))
+ }
-// return nil
-// }
-
-// func translateMicroTimestampSince(timestamp metav1.MicroTime) string {
-// if timestamp.IsZero() {
-// return ""
-// }
-
-// return duration.HumanDuration(time.Since(timestamp.Time))
-// }
-
-// func translateTimestampSince(timestamp metav1.Time) string {
-// if timestamp.IsZero() {
-// return ""
-// }
-
-// return duration.HumanDuration(time.Since(timestamp.Time))
-// }
-// func fmtEventSource(es api.EventSource, reportingController, reportingInstance string) string {
-// return fmtEventSourceComponentInstance(
-// firstNonEmpty(es.Component, reportingController),
-// firstNonEmpty(es.Host, reportingInstance),
-// )
-// }
-
-// func fmtEventSourceComponentInstance(component, instance string) string {
-// if len(instance) == 0 {
-// return component
-// }
-// return component + ", " + instance
-// }
-
-// func firstNonEmpty(ss ...string) string {
-// for _, s := range ss {
-// if len(s) > 0 {
-// return s
-// }
-// }
-// return ""
-// }
-
-// // Happy returns true if resource is happy, false otherwise.
-// func (Event) diagnose(kind string) error {
-// if kind != "Normal" {
-// return errors.New("failed event")
-// }
-// return nil
-// }
-
-// // Helpers...
-
-// func asRef(r v1.ObjectReference) string {
-// return strings.ToLower(r.Kind) + ":" + r.Name
-// }
+ return nil
+}
diff --git a/internal/render/generic.go b/internal/render/generic.go
index 3a67550a..321b0344 100644
--- a/internal/render/generic.go
+++ b/internal/render/generic.go
@@ -14,13 +14,13 @@ const ageTableCol = "Age"
// Generic renders a generic resource to screen.
type Generic struct {
+ Base
table *metav1beta1.Table
ageIndex int
}
-// Happy returns true if resource is happy, false otherwise.
-func (Generic) Happy(ns string, r Row) bool {
+func (*Generic) IsGeneric() bool {
return true
}
@@ -30,7 +30,7 @@ func (g *Generic) SetTable(t *metav1beta1.Table) {
}
// ColorerFunc colors a resource row.
-func (Generic) ColorerFunc() ColorerFunc {
+func (*Generic) ColorerFunc() ColorerFunc {
return DefaultColorer
}
diff --git a/internal/render/helm.go b/internal/render/helm.go
index 67402cf5..727031b9 100644
--- a/internal/render/helm.go
+++ b/internal/render/helm.go
@@ -15,6 +15,11 @@ import (
// Helm renders a helm chart to screen.
type Helm struct{}
+// IsGeneric identifies a generic handler.
+func (Helm) IsGeneric() bool {
+ return false
+}
+
// ColorerFunc colors a resource row.
func (Helm) ColorerFunc() ColorerFunc {
return func(ns string, h Header, re RowEvent) tcell.Color {
diff --git a/internal/render/hpa.go b/internal/render/hpa.go
index 3ff09480..89b35912 100644
--- a/internal/render/hpa.go
+++ b/internal/render/hpa.go
@@ -15,11 +15,8 @@ import (
)
// HorizontalPodAutoscaler renders a K8s HorizontalPodAutoscaler to screen.
-type HorizontalPodAutoscaler struct{}
-
-// ColorerFunc colors a resource row.
-func (HorizontalPodAutoscaler) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type HorizontalPodAutoscaler struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/job.go b/internal/render/job.go
index d3fb9da8..11888927 100644
--- a/internal/render/job.go
+++ b/internal/render/job.go
@@ -16,11 +16,8 @@ import (
)
// Job renders a K8s Job to screen.
-type Job struct{}
-
-// ColorerFunc colors a resource row.
-func (Job) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type Job struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/node.go b/internal/render/node.go
index 5723320f..b8b89803 100644
--- a/internal/render/node.go
+++ b/internal/render/node.go
@@ -22,11 +22,8 @@ const (
)
// Node renders a K8s Node to screen.
-type Node struct{}
-
-// ColorerFunc colors a resource row.
-func (n Node) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type Node struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/np.go b/internal/render/np.go
index b355574b..a2c7f2db 100644
--- a/internal/render/np.go
+++ b/internal/render/np.go
@@ -12,11 +12,8 @@ import (
)
// NetworkPolicy renders a K8s NetworkPolicy to screen.
-type NetworkPolicy struct{}
-
-// ColorerFunc colors a resource row.
-func (NetworkPolicy) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type NetworkPolicy struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/ns.go b/internal/render/ns.go
index 35c083b6..fe6ac2c9 100644
--- a/internal/render/ns.go
+++ b/internal/render/ns.go
@@ -13,7 +13,9 @@ import (
)
// Namespace renders a K8s Namespace to screen.
-type Namespace struct{}
+type Namespace struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (n Namespace) ColorerFunc() ColorerFunc {
diff --git a/internal/render/pdb.go b/internal/render/pdb.go
index 4f3c4d0a..143f1d1b 100644
--- a/internal/render/pdb.go
+++ b/internal/render/pdb.go
@@ -13,11 +13,8 @@ import (
)
// PodDisruptionBudget renders a K8s PodDisruptionBudget to screen.
-type PodDisruptionBudget struct{}
-
-// ColorerFunc colors a resource row.
-func (p PodDisruptionBudget) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type PodDisruptionBudget struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/pod.go b/internal/render/pod.go
index 5ac012d3..dfe23e7c 100644
--- a/internal/render/pod.go
+++ b/internal/render/pod.go
@@ -17,7 +17,9 @@ import (
)
// Pod renders a K8s Pod to screen.
-type Pod struct{}
+type Pod struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (p Pod) ColorerFunc() ColorerFunc {
diff --git a/internal/render/policy.go b/internal/render/policy.go
index 5936f207..d8132572 100644
--- a/internal/render/policy.go
+++ b/internal/render/policy.go
@@ -24,7 +24,9 @@ func rbacVerbHeader() Header {
}
// Policy renders a rbac policy to screen.
-type Policy struct{}
+type Policy struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (Policy) ColorerFunc() ColorerFunc {
diff --git a/internal/render/popeye.go b/internal/render/popeye.go
index de56a98a..fc304830 100644
--- a/internal/render/popeye.go
+++ b/internal/render/popeye.go
@@ -15,7 +15,9 @@ import (
)
// Popeye renders a sanitizer to screen.
-type Popeye struct{}
+type Popeye struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (Popeye) ColorerFunc() ColorerFunc {
diff --git a/internal/render/portforward.go b/internal/render/portforward.go
index 512927b2..2465d255 100644
--- a/internal/render/portforward.go
+++ b/internal/render/portforward.go
@@ -32,7 +32,9 @@ type Forwarder interface {
}
// PortForward renders a portforwards to screen.
-type PortForward struct{}
+type PortForward struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (PortForward) ColorerFunc() ColorerFunc {
diff --git a/internal/render/pv.go b/internal/render/pv.go
index a66012ea..57c8df04 100644
--- a/internal/render/pv.go
+++ b/internal/render/pv.go
@@ -13,7 +13,9 @@ import (
)
// PersistentVolume renders a K8s PersistentVolume to screen.
-type PersistentVolume struct{}
+type PersistentVolume struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (p PersistentVolume) ColorerFunc() ColorerFunc {
diff --git a/internal/render/pvc.go b/internal/render/pvc.go
index 4e26b373..57ded4e5 100644
--- a/internal/render/pvc.go
+++ b/internal/render/pvc.go
@@ -10,11 +10,8 @@ import (
)
// PersistentVolumeClaim renders a K8s PersistentVolumeClaim to screen.
-type PersistentVolumeClaim struct{}
-
-// ColorerFunc colors a resource row.
-func (p PersistentVolumeClaim) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type PersistentVolumeClaim struct {
+ Base
}
// Header returns a header rbw.
diff --git a/internal/render/rbac.go b/internal/render/rbac.go
index bd74785b..21bc79cd 100644
--- a/internal/render/rbac.go
+++ b/internal/render/rbac.go
@@ -30,7 +30,9 @@ var (
)
// Rbac renders a rbac to screen.
-type Rbac struct{}
+type Rbac struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (Rbac) ColorerFunc() ColorerFunc {
diff --git a/internal/render/reference.go b/internal/render/reference.go
index 82608bba..06d9c14d 100644
--- a/internal/render/reference.go
+++ b/internal/render/reference.go
@@ -10,7 +10,9 @@ import (
)
// Reference renders a reference to screen.
-type Reference struct{}
+type Reference struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (Reference) ColorerFunc() ColorerFunc {
diff --git a/internal/render/ro.go b/internal/render/ro.go
index a56a9c09..b5f4797b 100644
--- a/internal/render/ro.go
+++ b/internal/render/ro.go
@@ -10,11 +10,8 @@ import (
)
// Role renders a K8s Role to screen.
-type Role struct{}
-
-// ColorerFunc colors a resource row.
-func (Role) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type Role struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/rob.go b/internal/render/rob.go
index d7443e2f..f2521c27 100644
--- a/internal/render/rob.go
+++ b/internal/render/rob.go
@@ -11,11 +11,8 @@ import (
)
// RoleBinding renders a K8s RoleBinding to screen.
-type RoleBinding struct{}
-
-// ColorerFunc colors a resource row.
-func (RoleBinding) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type RoleBinding struct {
+ Base
}
// Header returns a header rbw.
diff --git a/internal/render/rs.go b/internal/render/rs.go
index d7d05bcb..32c76203 100644
--- a/internal/render/rs.go
+++ b/internal/render/rs.go
@@ -12,7 +12,9 @@ import (
)
// ReplicaSet renders a K8s ReplicaSet to screen.
-type ReplicaSet struct{}
+type ReplicaSet struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (r ReplicaSet) ColorerFunc() ColorerFunc {
diff --git a/internal/render/sa.go b/internal/render/sa.go
index 472b8feb..cfb31342 100644
--- a/internal/render/sa.go
+++ b/internal/render/sa.go
@@ -11,11 +11,8 @@ import (
)
// ServiceAccount renders a K8s ServiceAccount to screen.
-type ServiceAccount struct{}
-
-// ColorerFunc colors a resource row.
-func (ServiceAccount) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type ServiceAccount struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/sc.go b/internal/render/sc.go
index ba2165fd..05508865 100644
--- a/internal/render/sc.go
+++ b/internal/render/sc.go
@@ -10,11 +10,8 @@ import (
)
// StorageClass renders a K8s StorageClass to screen.
-type StorageClass struct{}
-
-// ColorerFunc colors a resource row.
-func (StorageClass) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type StorageClass struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/screen_dump.go b/internal/render/screen_dump.go
index e08fa0b4..6feaad3c 100644
--- a/internal/render/screen_dump.go
+++ b/internal/render/screen_dump.go
@@ -12,7 +12,9 @@ import (
)
// ScreenDump renders a screendumps to screen.
-type ScreenDump struct{}
+type ScreenDump struct {
+ Base
+}
// ColorerFunc colors a resource row.
func (ScreenDump) ColorerFunc() ColorerFunc {
@@ -21,14 +23,6 @@ func (ScreenDump) ColorerFunc() ColorerFunc {
}
}
-// DecoratorFunc decorates a string.
-type DecoratorFunc func(string) string
-
-// AgeDecorator represents a timestamped as human column.
-var AgeDecorator = func(a string) string {
- return toAgeHuman(a)
-}
-
// Header returns a header row.
func (ScreenDump) Header(ns string) Header {
return Header{
diff --git a/internal/render/sts.go b/internal/render/sts.go
index a01d57f8..46339f1d 100644
--- a/internal/render/sts.go
+++ b/internal/render/sts.go
@@ -11,11 +11,8 @@ import (
)
// StatefulSet renders a K8s StatefulSet to screen.
-type StatefulSet struct{}
-
-// ColorerFunc colors a resource row.
-func (s StatefulSet) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type StatefulSet struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/render/subject.go b/internal/render/subject.go
index c4fc619d..2d47e32e 100644
--- a/internal/render/subject.go
+++ b/internal/render/subject.go
@@ -9,11 +9,8 @@ import (
)
// Subject renders a rbac to screen.
-type Subject struct{}
-
-// Happy returns true if resource is happy, false otherwise.
-func (Subject) Happy(_ string, _ Row) bool {
- return true
+type Subject struct {
+ Base
}
// ColorerFunc colors a resource row.
diff --git a/internal/render/svc.go b/internal/render/svc.go
index 62d7b43b..c81f5aca 100644
--- a/internal/render/svc.go
+++ b/internal/render/svc.go
@@ -13,11 +13,8 @@ import (
)
// Service renders a K8s Service to screen.
-type Service struct{}
-
-// ColorerFunc colors a resource row.
-func (Service) ColorerFunc() ColorerFunc {
- return DefaultColorer
+type Service struct {
+ Base
}
// Header returns a header row.
diff --git a/internal/view/alias_test.go b/internal/view/alias_test.go
index 63b4ac20..bccf38af 100644
--- a/internal/view/alias_test.go
+++ b/internal/view/alias_test.go
@@ -88,8 +88,8 @@ func (k ks) CurrentNamespaceName() (string, error) {
return "test", nil
}
-func (k ks) ClusterNames() ([]string, error) {
- return []string{"test"}, nil
+func (k ks) ClusterNames() (map[string]struct{}, error) {
+ return map[string]struct{}{"test": {}}, nil
}
func (k ks) NamespaceNames(nn []v1.Namespace) []string {
diff --git a/internal/view/app.go b/internal/view/app.go
index a94fb262..50e0938c 100644
--- a/internal/view/app.go
+++ b/internal/view/app.go
@@ -276,10 +276,10 @@ func (a *App) Resume() {
go a.clusterUpdater(ctx)
if err := a.StylesWatcher(ctx, a); err != nil {
- log.Error().Err(err).Msgf("Styles watcher failed")
+ log.Warn().Err(err).Msgf("Styles watcher failed")
}
if err := a.CustomViewsWatcher(ctx, a); err != nil {
- log.Error().Err(err).Msgf("CustomView watcher failed")
+ log.Warn().Err(err).Msgf("CustomView watcher failed")
}
}
@@ -357,6 +357,10 @@ func (a *App) switchNS(ns string) error {
if ns == client.ClusterScope {
ns = client.AllNamespaces
}
+ if ns == a.Config.ActiveNamespace() {
+ return nil
+ }
+
ok, err := a.isValidNS(ns)
if err != nil {
return err
@@ -365,7 +369,10 @@ func (a *App) switchNS(ns string) error {
return fmt.Errorf("Invalid namespace %q", ns)
}
if err := a.Config.SetActiveNamespace(ns); err != nil {
- return fmt.Errorf("Unable to save active namespace in config")
+ return err
+ }
+ if err := a.Config.Save(); err != nil {
+ return err
}
return a.factory.SetActiveNS(ns)
diff --git a/internal/view/browser.go b/internal/view/browser.go
index 8abae968..003cc02c 100644
--- a/internal/view/browser.go
+++ b/internal/view/browser.go
@@ -134,9 +134,6 @@ func (b *Browser) Start() {
if err := b.app.switchNS(ns); err != nil {
log.Error().Err(err).Msgf("ns switch failed")
}
- if err := b.app.Config.Save(); err != nil {
- log.Error().Err(err).Msgf("Config Save")
- }
b.Stop()
b.GetModel().AddListener(b)
diff --git a/internal/view/event.go b/internal/view/event.go
index c39b3f5f..0b29524d 100644
--- a/internal/view/event.go
+++ b/internal/view/event.go
@@ -17,7 +17,8 @@ func NewEvent(gvr client.GVR) ResourceViewer {
e := Event{
ResourceViewer: NewBrowser(gvr),
}
- e.GetTable().SetColorerFn(render.Event{}.ColorerFunc())
+ var r *render.Event
+ e.GetTable().SetColorerFn(r.ColorerFunc())
e.AddBindKeysFn(e.bindKeys)
e.GetTable().SetSortCol("LAST SEEN", false)
@@ -25,9 +26,10 @@ func NewEvent(gvr client.GVR) ResourceViewer {
}
func (e *Event) bindKeys(aa ui.KeyActions) {
- aa.Delete(tcell.KeyCtrlD, ui.KeyE)
+ aa.Delete(tcell.KeyCtrlD, ui.KeyE, ui.KeyA)
aa.Add(ui.KeyActions{
ui.KeyShiftL: ui.NewKeyAction("Sort LastSeen", e.GetTable().SortColCmd("LAST SEEN", false), false),
+ ui.KeyShiftF: ui.NewKeyAction("Sort FirstSeen", e.GetTable().SortColCmd("FIRST SEEN", false), false),
ui.KeyShiftT: ui.NewKeyAction("Sort Type", e.GetTable().SortColCmd("TYPE", true), false),
ui.KeyShiftR: ui.NewKeyAction("Sort Reason", e.GetTable().SortColCmd("REASON", true), false),
ui.KeyShiftS: ui.NewKeyAction("Sort Source", e.GetTable().SortColCmd("SOURCE", true), false),
diff --git a/internal/view/table_int_test.go b/internal/view/table_int_test.go
index ea5bd61d..7fde100b 100644
--- a/internal/view/table_int_test.go
+++ b/internal/view/table_int_test.go
@@ -171,8 +171,8 @@ func (k ks) CurrentNamespaceName() (string, error) {
return "test", nil
}
-func (k ks) ClusterNames() ([]string, error) {
- return []string{"test"}, nil
+func (k ks) ClusterNames() (map[string]struct{}, error) {
+ return map[string]struct{}{"test": {}}, nil
}
func (k ks) NamespaceNames(nn []v1.Namespace) []string {
diff --git a/internal/xray/section.go b/internal/xray/section.go
index a4f23965..4cd377eb 100644
--- a/internal/xray/section.go
+++ b/internal/xray/section.go
@@ -10,7 +10,9 @@ import (
)
// Section represents an xray renderer.
-type Section struct{}
+type Section struct {
+ render.Base
+}
// Render renders an xray node.
func (s *Section) Render(ctx context.Context, ns string, o interface{}) error {