derailed 2020-02-05 21:10:27 -08:00
parent 7df87a80ab
commit f7badc4a2a
20 changed files with 236 additions and 116 deletions

View File

@ -0,0 +1,29 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.13.8
## 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 is as ever very much noticed and appreciated!
Also if you dig this tool, please 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)
---
### GH Sponsorships
WOOT!! Big Thank you to [Mark Baumann](https://github.com/mtreeman) for your contributions and support for K9s!
---
## Resolved Bugs/Features/PRs
* [Issue #523](https://github.com/derailed/k9s/issues/523)
* [Issue #522](https://github.com/derailed/k9s/issues/522)
* [Issue #521](https://github.com/derailed/k9s/issues/521)
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -3,6 +3,7 @@ package config
import (
"io/ioutil"
"path/filepath"
"sync"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
@ -20,16 +21,17 @@ type ShortNames map[string][]string
// Aliases represents a collection of aliases.
type Aliases struct {
Alias Alias `yaml:"alias"`
mx sync.RWMutex
}
// NewAliases return a new alias.
func NewAliases() Aliases {
return Aliases{
func NewAliases() *Aliases {
return &Aliases{
Alias: make(Alias, 50),
}
}
func (a Aliases) loadDefaults() {
func (a *Aliases) loadDefaults() {
const (
contexts = "contexts"
portFwds = "portforwards"
@ -39,6 +41,9 @@ func (a Aliases) loadDefaults() {
users = "users"
)
a.mx.Lock()
defer a.mx.Unlock()
a.Alias["dp"] = "apps/v1/deployments"
a.Alias["sec"] = "v1/secrets"
a.Alias["jo"] = "batch/v1/jobs"
@ -80,19 +85,52 @@ func (a Aliases) loadDefaults() {
}
// Load K9s aliases.
func (a Aliases) Load() error {
func (a *Aliases) Load() error {
a.loadDefaults()
return a.LoadAliases(K9sAlias)
}
// ShortNames return all shortnames.
func (a *Aliases) ShortNames() ShortNames {
a.mx.RLock()
defer a.mx.RUnlock()
m := make(ShortNames, len(a.Alias))
for alias, gvr := range a.Alias {
if _, ok := m[gvr]; ok {
m[gvr] = append(m[gvr], alias)
} else {
m[gvr] = []string{alias}
}
}
return m
}
// Clear remove all aliases.
func (a *Aliases) Clear() {
a.mx.Lock()
defer a.mx.Unlock()
for k := range a.Alias {
delete(a.Alias, k)
}
}
// Get retrieves an alias.
func (a Aliases) Get(k string) (string, bool) {
func (a *Aliases) Get(k string) (string, bool) {
a.mx.RLock()
defer a.mx.RUnlock()
v, ok := a.Alias[k]
return v, ok
}
// Define declares a new alias.
func (a Aliases) Define(gvr string, aliases ...string) {
func (a *Aliases) Define(gvr string, aliases ...string) {
a.mx.Lock()
defer a.mx.Unlock()
for _, alias := range aliases {
if _, ok := a.Alias[alias]; ok {
continue
@ -102,17 +140,20 @@ func (a Aliases) Define(gvr string, aliases ...string) {
}
// LoadAliases loads alias from a given file.
func (a Aliases) LoadAliases(path string) error {
func (a *Aliases) LoadAliases(path string) error {
f, err := ioutil.ReadFile(path)
if err != nil {
log.Warn().Err(err).Msgf("No custom aliases found")
return nil
}
var aa Aliases
var aa *Aliases
if err := yaml.Unmarshal(f, &aa); err != nil {
return err
}
a.mx.Lock()
defer a.mx.Unlock()
for k, v := range aa.Alias {
a.Alias[k] = v
}
@ -121,13 +162,13 @@ func (a Aliases) LoadAliases(path string) error {
}
// Save alias to disk.
func (a Aliases) Save() error {
func (a *Aliases) Save() error {
log.Debug().Msg("[Config] Saving Aliases...")
return a.SaveAliases(K9sAlias)
}
// SaveAliases saves aliases to a given file.
func (a Aliases) SaveAliases(path string) error {
func (a *Aliases) SaveAliases(path string) error {
EnsurePath(path, DefaultDirMod)
cfg, err := yaml.Marshal(a)
if err != nil {

View File

@ -3,6 +3,7 @@ package dao
import (
"context"
"errors"
"fmt"
"sort"
"strings"
@ -18,7 +19,7 @@ var _ Accessor = (*Alias)(nil)
// Alias tracks standard and custom command aliases.
type Alias struct {
NonResource
config.Aliases
*config.Aliases
}
// NewAlias returns a new set of aliases.
@ -29,13 +30,6 @@ func NewAlias(f Factory) *Alias {
return &a
}
// Clear remove all aliases.
func (a *Alias) Clear() {
for k := range a.Alias {
delete(a.Alias, k)
}
}
// Check verifies an alias is defined for this command.
func (a *Alias) Check(cmd string) bool {
_, ok := a.Aliases.Get(cmd)
@ -43,22 +37,12 @@ func (a *Alias) Check(cmd string) bool {
}
// List returns a collection of aliases.
// BOZO!! Already have aliases here. Refact!!
func (a *Alias) List(ctx context.Context, _ string) ([]runtime.Object, error) {
a, ok := ctx.Value(internal.KeyAliases).(*Alias)
aa, ok := ctx.Value(internal.KeyAliases).(*Alias)
if !ok {
return nil, errors.New("no aliases found in context")
return nil, fmt.Errorf("expecting *Alias but got %T", ctx.Value(internal.KeyAliases))
}
m := make(config.ShortNames, len(a.Alias))
for alias, gvr := range a.Alias {
if _, ok := m[gvr]; ok {
m[gvr] = append(m[gvr], alias)
} else {
m[gvr] = []string{alias}
}
}
m := aa.ShortNames()
oo := make([]runtime.Object, 0, len(m))
for gvr, aliases := range m {
sort.StringSlice(aliases).Sort()
@ -84,7 +68,7 @@ func (a *Alias) Get(_ context.Context, _ string) (runtime.Object, error) {
// Ensure makes sure alias are loaded.
func (a *Alias) Ensure() (config.Alias, error) {
if err := LoadResources(a.Factory); err != nil {
if err := MetaAccess.LoadResources(a.Factory); err != nil {
return config.Alias{}, err
}
return a.Alias, a.load()
@ -95,8 +79,8 @@ func (a *Alias) load() error {
return err
}
for _, gvr := range AllGVRs() {
meta, err := MetaFor(gvr)
for _, gvr := range MetaAccess.AllGVRs() {
meta, err := MetaAccess.MetaFor(gvr)
if err != nil {
return err
}

View File

@ -33,7 +33,7 @@ func TestAliasList(t *testing.T) {
func makeAliases() *dao.Alias {
return &dao.Alias{
Aliases: config.Aliases{
Aliases: &config.Aliases{
Alias: config.Alias{
"fred": "v1/fred",
"f": "v1/fred",

View File

@ -4,6 +4,7 @@ import (
"fmt"
"sort"
"strings"
"sync"
"github.com/derailed/k9s/internal/client"
"github.com/rs/zerolog/log"
@ -13,7 +14,19 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
var resMetas = ResourceMetas{}
// MetaAccess tracks resources metadata.
var MetaAccess = NewMeta()
// Meta represents available resource metas.
type Meta struct {
resMetas ResourceMetas
mx sync.RWMutex
}
// NewMeta returns a resource meta.
func NewMeta() *Meta {
return &Meta{resMetas: make(ResourceMetas)}
}
// AccessorFor returns a client accessor for a resource if registered.
// Otherwise it returns a generic accessor.
@ -47,14 +60,20 @@ func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) {
}
// RegisterMeta registers a new resource meta object.
func RegisterMeta(gvr string, res metav1.APIResource) {
resMetas[client.NewGVR(gvr)] = res
func (m *Meta) RegisterMeta(gvr string, res metav1.APIResource) {
m.mx.Lock()
defer m.mx.Unlock()
m.resMetas[client.NewGVR(gvr)] = res
}
// AllGVRs returns all cluster resources.
func AllGVRs() client.GVRs {
kk := make(client.GVRs, 0, len(resMetas))
for k := range resMetas {
func (m *Meta) AllGVRs() client.GVRs {
m.mx.RLock()
defer m.mx.RUnlock()
kk := make(client.GVRs, 0, len(m.resMetas))
for k := range m.resMetas {
kk = append(kk, k)
}
sort.Sort(kk)
@ -63,12 +82,15 @@ func AllGVRs() client.GVRs {
}
// MetaFor returns a resource metadata for a given gvr.
func MetaFor(gvr client.GVR) (metav1.APIResource, error) {
m, ok := resMetas[gvr]
func (m *Meta) MetaFor(gvr client.GVR) (metav1.APIResource, error) {
m.mx.RLock()
defer m.mx.RUnlock()
meta, ok := m.resMetas[gvr]
if !ok {
return metav1.APIResource{}, fmt.Errorf("no resource meta defined for %q", gvr)
}
return m, nil
return meta, nil
}
// IsK8sMeta checks for non resource meta.
@ -94,13 +116,16 @@ func IsK9sMeta(m metav1.APIResource) bool {
}
// LoadResources hydrates server preferred+CRDs resource metadata.
func LoadResources(f Factory) error {
resMetas = make(ResourceMetas, 100)
if err := loadPreferred(f, resMetas); err != nil {
func (m *Meta) LoadResources(f Factory) error {
m.mx.Lock()
defer m.mx.Unlock()
m.resMetas = make(ResourceMetas, 100)
if err := loadPreferred(f, m.resMetas); err != nil {
return err
}
loadNonResource(resMetas)
loadCRDs(f, resMetas)
loadNonResource(m.resMetas)
loadCRDs(f, m.resMetas)
return nil
}

View File

@ -15,6 +15,8 @@ import (
"github.com/sahilm/fuzzy"
)
const logMaxBufferSize = 50
// LogsListener represents a log model listener.
type LogsListener interface {
// LogChanged notifies the model changed.
@ -65,8 +67,10 @@ func (l *Log) Init(f dao.Factory) {
// Clear the logs.
func (l *Log) Clear() {
l.mx.Lock()
defer l.mx.Unlock()
l.lines, l.lastSent = []string{}, 0
{
l.lines, l.lastSent = []string{}, 0
}
l.mx.Unlock()
l.fireLogCleared()
}
@ -74,6 +78,7 @@ func (l *Log) Clear() {
func (l *Log) Start() {
if err := l.load(); err != nil {
log.Error().Err(err).Msgf("Tail logs failed!")
l.fireLogError(err)
}
}
@ -91,6 +96,7 @@ func (l *Log) Stop() {
func (l *Log) Set(lines []string) {
l.mx.Lock()
defer l.mx.Unlock()
l.lines = lines
l.fireLogChanged(lines)
}
@ -99,6 +105,7 @@ func (l *Log) Set(lines []string) {
func (l *Log) ClearFilter() {
l.mx.RLock()
defer l.mx.RUnlock()
l.filter = ""
l.fireLogChanged(l.lines)
}
@ -133,7 +140,7 @@ func (l *Log) load() error {
}
logger, ok := accessor.(dao.Loggable)
if !ok {
return fmt.Errorf("Resource %s is not tailable", l.gvr)
return fmt.Errorf("Resource %s is not Loggable", l.gvr)
}
if err := logger.TailLogs(ctx, c, l.logOptions); err != nil {
@ -152,6 +159,7 @@ func (l *Log) Append(line string) {
if line == "" {
return
}
l.mx.Lock()
defer l.mx.Unlock()
@ -196,6 +204,15 @@ func (l *Log) updateLogs(ctx context.Context, c <-chan string) {
return
}
l.Append(line)
var overflow bool
l.mx.RLock()
{
overflow = len(l.lines)-l.lastSent > logMaxBufferSize
}
l.mx.RUnlock()
if overflow {
l.Notify(true)
}
case <-time.After(200 * time.Millisecond):
l.Notify(true)
case <-ctx.Done():

View File

@ -105,7 +105,7 @@ func TestLogStartStop(t *testing.T) {
assert.Equal(t, 1, v.dataCalled)
assert.Equal(t, 1, v.clearCalled)
assert.Equal(t, 0, v.errCalled)
assert.Equal(t, 1, v.errCalled)
assert.Equal(t, 2, len(v.data))
}

View File

@ -1,6 +1,8 @@
package model
import (
"sync"
"github.com/rs/zerolog/log"
)
@ -40,6 +42,7 @@ type StackListener interface {
type Stack struct {
components []Component
listeners []StackListener
mx sync.RWMutex
}
// NewStack returns a new initialized stack.
@ -49,6 +52,9 @@ func NewStack() *Stack {
// Flatten returns a string representation of the component stack.
func (s *Stack) Flatten() []string {
s.mx.RLock()
defer s.mx.RUnlock()
ss := make([]string, len(s.components))
for i, c := range s.components {
ss[i] = c.Name()
@ -84,7 +90,12 @@ func (s *Stack) Push(c Component) {
if top := s.Top(); top != nil {
top.Stop()
}
s.components = append(s.components, c)
s.mx.Lock()
{
s.components = append(s.components, c)
}
s.mx.Unlock()
s.notify(StackPush, c)
}
@ -94,8 +105,13 @@ func (s *Stack) Pop() (Component, bool) {
return nil, false
}
c := s.components[s.size()]
s.components = s.components[:s.size()]
var c Component
s.mx.Lock()
{
c = s.components[s.size()]
s.components = s.components[:s.size()]
}
s.mx.Unlock()
s.notify(StackPop, c)
return c, true
@ -103,6 +119,9 @@ func (s *Stack) Pop() (Component, bool) {
// Peek returns stack state.
func (s *Stack) Peek() []Component {
s.mx.RLock()
defer s.mx.RUnlock()
return s.components
}
@ -115,6 +134,9 @@ func (s *Stack) Clear() {
// Empty returns true if the stack is empty.
func (s *Stack) Empty() bool {
s.mx.RLock()
defer s.mx.RUnlock()
return len(s.components) == 0
}

View File

@ -3,6 +3,7 @@ package model
import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"
@ -35,6 +36,7 @@ type Table struct {
inUpdate int32
refreshRate time.Duration
instance string
mx sync.RWMutex
}
// NewTable returns a new table model.
@ -170,7 +172,10 @@ func (t *Table) Empty() bool {
// Peek returns model data.
func (t *Table) Peek() render.TableData {
return *t.data
t.mx.RLock()
defer t.mx.RUnlock()
return t.data.Clone()
}
func (t *Table) updater(ctx context.Context) {
@ -200,7 +205,7 @@ func (t *Table) refresh(ctx context.Context) {
t.fireTableLoadFailed(err)
return
}
t.fireTableChanged(*t.data)
t.fireTableChanged()
}
func (t *Table) list(ctx context.Context, a dao.Accessor) ([]runtime.Object, error) {
@ -252,15 +257,16 @@ func (t *Table) reconcile(ctx context.Context) error {
}
}
t.data.Mutex.Lock()
defer t.data.Mutex.Unlock()
t.mx.Lock()
defer t.mx.Unlock()
// if labelSelector in place might as well clear the model data.
sel, ok := ctx.Value(internal.KeyLabels).(string)
if ok && sel != "" {
t.data.Clear()
}
t.data.Update(rows)
t.data.Namespace, t.data.Header = t.namespace, meta.Renderer.Header(t.namespace)
t.data.SetHeader(t.namespace, meta.Renderer.Header(t.namespace))
return nil
}
@ -292,7 +298,8 @@ func (t *Table) resourceMeta() ResourceMeta {
return meta
}
func (t *Table) fireTableChanged(data render.TableData) {
func (t *Table) fireTableChanged() {
data := t.Peek()
for _, l := range t.listeners {
l.TableDataChanged(data)
}

View File

@ -1,20 +1,15 @@
package render
import (
"sync"
)
// TableData tracks a K8s resource for tabular display.
type TableData struct {
Header HeaderRow
RowEvents RowEvents
Namespace string
Mutex *sync.RWMutex
}
// NewTableData returns a new table.
func NewTableData() *TableData {
return &TableData{Mutex: &sync.RWMutex{}}
return &TableData{}
}
// Clear clears out the entire table.
@ -31,13 +26,18 @@ func cloneTable(t TableData) TableData {
return t
}
// SetHeader sets table header.
func (t *TableData) SetHeader(ns string, h HeaderRow) {
t.Namespace, t.Header = ns, h
}
// Update computes row deltas and update the table data.
func (t *TableData) Update(rows Rows) {
empty := len(t.RowEvents) == 0
kk := make([]string, 0, len(rows))
kk := make(map[string]struct{}, len(rows))
var blankDelta DeltaRow
for _, row := range rows {
kk = append(kk, row.ID)
kk[row.ID] = struct{}{}
if empty {
t.RowEvents = append(t.RowEvents, NewRowEvent(EventAdd, row))
continue
@ -61,19 +61,11 @@ func (t *TableData) Update(rows Rows) {
}
}
// Delete delete items in cache that are no longer valid.
func (t *TableData) Delete(newKeys []string) {
// Delete removes items in cache that are no longer valid.
func (t *TableData) Delete(newKeys map[string]struct{}) {
var victims []string
for _, re := range t.RowEvents {
var found bool
for i, key := range newKeys {
if key == re.Row.ID {
found = true
newKeys = append(newKeys[:i], newKeys[i+1:]...)
break
}
}
if !found {
if _, ok := newKeys[re.Row.ID]; !ok {
victims = append(victims, re.Row.ID)
}
}
@ -88,12 +80,10 @@ func (t *TableData) Diff(table TableData) bool {
if t.Namespace != table.Namespace {
return true
}
if t.Header.Diff(table.Header) {
return true
}
if t.RowEvents.Diff(table.RowEvents) {
return true
}
return false
return t.RowEvents.Diff(table.RowEvents)
}

View File

@ -10,7 +10,7 @@ import (
func TestTableDataDelete(t *testing.T) {
uu := map[string]struct {
re render.RowEvents
kk []string
kk map[string]struct{}
e render.RowEvents
}{
"ordered": {
@ -19,7 +19,7 @@ func TestTableDataDelete(t *testing.T) {
{Row: render.Row{ID: "B", Fields: render.Fields{"0", "2", "3"}}},
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3"}}},
},
kk: []string{"A", "C"},
kk: map[string]struct{}{"A": struct{}{}, "C": struct{}{}},
e: render.RowEvents{
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "3"}}},
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3"}}},
@ -32,7 +32,7 @@ func TestTableDataDelete(t *testing.T) {
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3"}}},
{Row: render.Row{ID: "D", Fields: render.Fields{"10", "2", "3"}}},
},
kk: []string{"C", "A"},
kk: map[string]struct{}{"C": struct{}{}, "A": struct{}{}},
e: render.RowEvents{
{Row: render.Row{ID: "A", Fields: render.Fields{"1", "2", "3"}}},
{Row: render.Row{ID: "C", Fields: render.Fields{"10", "2", "3"}}},

View File

@ -163,9 +163,6 @@ func (t *Table) SetSortCol(index, count int, asc bool) {
// Update table content.
func (t *Table) Update(data render.TableData) {
data.Mutex.RLock()
defer data.Mutex.RUnlock()
if t.decorateFn != nil {
data = t.decorateFn(data)
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"sync"
"time"
"github.com/derailed/k9s/internal"
@ -40,6 +41,7 @@ type App struct {
cancelFn context.CancelFunc
conRetry int
clusterModel *model.ClusterInfo
mx sync.Mutex
}
// NewApp returns a K9s app instance.
@ -59,6 +61,8 @@ func NewApp(cfg *config.Config) *App {
// ConOK checks the connection is cool, returns false otherwise.
func (a *App) ConOK() bool {
a.mx.Lock()
defer a.mx.Unlock()
return a.conRetry == 0
}
@ -194,6 +198,9 @@ func (a *App) clusterUpdater(ctx context.Context) {
}
func (a *App) refreshCluster() {
a.mx.Lock()
defer a.mx.Unlock()
c := a.Content.Top()
if ok := a.Conn().CheckConnectivity(); ok {
if a.conRetry > 0 {

View File

@ -40,7 +40,7 @@ func NewBrowser(gvr client.GVR) ResourceViewer {
// Init watches all running pods in given namespace
func (b *Browser) Init(ctx context.Context) error {
var err error
b.meta, err = dao.MetaFor(b.gvr)
b.meta, err = dao.MetaAccess.MetaFor(b.gvr)
if err != nil {
return err
}
@ -55,6 +55,7 @@ func (b *Browser) Init(ctx context.Context) error {
return e
}
}
b.app.CmdBuff().Reset()
b.bindKeys()
if b.bindKeysFn != nil {

View File

@ -69,11 +69,6 @@ func (c *Container) selectedContainer() string {
}
func (c *Container) viewLogs(app *App, model ui.Tabular, gvr, path string) {
status := c.GetTable().GetSelectedCell(3)
if status != "Running" && status != "Completed" {
app.Flash().Err(errors.New("No logs available"))
return
}
c.ResourceViewer.(*LogsExtender).showLogs(c.GetTable().Path, false)
}

View File

@ -103,7 +103,9 @@ func (l *Log) LogCleared() {
// LogFailed notifies an error occurred.
func (l *Log) LogFailed(err error) {
l.app.Flash().Err(err)
l.app.QueueUpdateDraw(func() {
l.app.Flash().Err(err)
})
}
// LogChanged updates the logs.

View File

@ -11,7 +11,7 @@ import (
)
func init() {
dao.RegisterMeta("v1/pods", metav1.APIResource{
dao.MetaAccess.RegisterMeta("v1/pods", metav1.APIResource{
Name: "pods",
SingularName: "pod",
Namespaced: true,
@ -19,7 +19,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("v1/namespaces", metav1.APIResource{
dao.MetaAccess.RegisterMeta("v1/namespaces", metav1.APIResource{
Name: "namespaces",
SingularName: "namespace",
Namespaced: true,
@ -27,7 +27,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("v1/services", metav1.APIResource{
dao.MetaAccess.RegisterMeta("v1/services", metav1.APIResource{
Name: "services",
SingularName: "service",
Namespaced: true,
@ -35,7 +35,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("v1/secrets", metav1.APIResource{
dao.MetaAccess.RegisterMeta("v1/secrets", metav1.APIResource{
Name: "secrets",
SingularName: "secret",
Namespaced: true,
@ -44,7 +44,7 @@ func init() {
Categories: []string{"k9s"},
})
dao.RegisterMeta("aliases", metav1.APIResource{
dao.MetaAccess.RegisterMeta("aliases", metav1.APIResource{
Name: "aliases",
SingularName: "alias",
Namespaced: true,
@ -52,7 +52,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("containers", metav1.APIResource{
dao.MetaAccess.RegisterMeta("containers", metav1.APIResource{
Name: "containers",
SingularName: "container",
Namespaced: true,
@ -60,7 +60,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("contexts", metav1.APIResource{
dao.MetaAccess.RegisterMeta("contexts", metav1.APIResource{
Name: "contexts",
SingularName: "context",
Namespaced: true,
@ -68,7 +68,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("subjects", metav1.APIResource{
dao.MetaAccess.RegisterMeta("subjects", metav1.APIResource{
Name: "subjects",
SingularName: "subject",
Namespaced: true,
@ -76,7 +76,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("rbac", metav1.APIResource{
dao.MetaAccess.RegisterMeta("rbac", metav1.APIResource{
Name: "rbacs",
SingularName: "rbac",
Namespaced: true,
@ -84,7 +84,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("portforwards", metav1.APIResource{
dao.MetaAccess.RegisterMeta("portforwards", metav1.APIResource{
Name: "portforwards",
SingularName: "portforward",
Namespaced: true,
@ -93,7 +93,7 @@ func init() {
Categories: []string{"k9s"},
})
dao.RegisterMeta("screendumps", metav1.APIResource{
dao.MetaAccess.RegisterMeta("screendumps", metav1.APIResource{
Name: "screendumps",
SingularName: "screendump",
Namespaced: true,
@ -101,7 +101,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("apps/v1/statefulsets", metav1.APIResource{
dao.MetaAccess.RegisterMeta("apps/v1/statefulsets", metav1.APIResource{
Name: "statefulsets",
SingularName: "statefulset",
Namespaced: true,
@ -109,7 +109,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("apps/v1/daemonsets", metav1.APIResource{
dao.MetaAccess.RegisterMeta("apps/v1/daemonsets", metav1.APIResource{
Name: "daemonsets",
SingularName: "daemonset",
Namespaced: true,
@ -117,7 +117,7 @@ func init() {
Verbs: []string{"get", "list", "watch", "delete"},
Categories: []string{"k9s"},
})
dao.RegisterMeta("apps/v1/deployments", metav1.APIResource{
dao.MetaAccess.RegisterMeta("apps/v1/deployments", metav1.APIResource{
Name: "deployments",
SingularName: "deployment",
Namespaced: true,

View File

@ -56,7 +56,7 @@ func (x *Xray) Init(ctx context.Context) error {
x.SetKeyListenerFn(x.keyEntered)
var err error
x.meta, err = dao.MetaFor(x.gvr)
x.meta, err = dao.MetaAccess.MetaFor(x.gvr)
if err != nil {
return err
}
@ -138,7 +138,7 @@ func (x *Xray) refreshActions() {
}
var err error
x.meta, err = dao.MetaFor(client.NewGVR(ref.GVR))
x.meta, err = dao.MetaAccess.MetaFor(client.NewGVR(ref.GVR))
if err != nil {
log.Warn().Msgf("NO meta for %q -- %s", ref.GVR, err)
return
@ -288,7 +288,7 @@ func (x *Xray) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
defer x.Start()
{
gvr := client.NewGVR(ref.GVR)
meta, err := dao.MetaFor(gvr)
meta, err := dao.MetaAccess.MetaFor(gvr)
if err != nil {
log.Warn().Msgf("NO meta for %q -- %s", ref.GVR, err)
return nil

View File

@ -150,6 +150,9 @@ func (f *Factory) SetActiveNS(ns string) {
}
func (f *Factory) isClusterWide() bool {
f.mx.RLock()
defer f.mx.RUnlock()
_, ok := f.factories[client.AllNamespaces]
return ok
}

View File

@ -336,7 +336,7 @@ func dumpStdOut(n *TreeNode, level int) {
}
func category(gvr string) string {
meta, err := dao.MetaFor(client.NewGVR(gvr))
meta, err := dao.MetaAccess.MetaFor(client.NewGVR(gvr))
if err != nil {
return ""
}