diff --git a/internal/model/stack.go b/internal/model/stack.go index fc925135..daa81338 100644 --- a/internal/model/stack.go +++ b/internal/model/stack.go @@ -116,6 +116,18 @@ func (s *Stack) Pop() (Component, bool) { return c, true } +// Peek returns stack state. +func (s *Stack) Peek() []Component { + return s.components +} + +// Reset clear out the stack. +func (s *Stack) Reset() { + for range s.components { + s.Pop() + } +} + // Empty returns true if the stack is empty. func (s *Stack) Empty() bool { return len(s.components) == 0 diff --git a/internal/resource/container.go b/internal/resource/container.go index fbd0b850..831e3258 100644 --- a/internal/resource/container.go +++ b/internal/resource/container.go @@ -28,7 +28,7 @@ type ( func NewContainerList(c Connection, pod *v1.Pod) List { return NewList( "", - "co", + "coontainers", NewContainer(c, pod), 0, ) diff --git a/internal/resource/list.go b/internal/resource/list.go index bf115a77..00140246 100644 --- a/internal/resource/list.go +++ b/internal/resource/list.go @@ -289,8 +289,7 @@ func (l *list) Reconcile(informer *wa.Informer, path *string) error { ns = *path } - items, err := l.load(informer, ns) - if err == nil { + if items, err := l.load(informer, ns); err == nil { l.update(items) return nil } @@ -299,11 +298,11 @@ func (l *list) Reconcile(informer *wa.Informer, path *string) error { LabelSelector: l.labelSelector, FieldSelector: l.fieldSelector, } - - if items, err = l.resource.List(l.namespace, opts); err != nil { + if items, err := l.resource.List(l.namespace, opts); err == nil { + l.update(items) + } else { return err } - l.update(items) return nil } diff --git a/internal/resource/no.go b/internal/resource/no.go index e5828ad7..fdf4a122 100644 --- a/internal/resource/no.go +++ b/internal/resource/no.go @@ -28,7 +28,7 @@ type Node struct { func NewNodeList(c Connection, _ string) List { return NewList( NotNamespaced, - "no", + "nodes", NewNode(c), ViewAccess|DescribeAccess, ) diff --git a/internal/ui/app.go b/internal/ui/app.go index 258b48d0..2ef2d435 100644 --- a/internal/ui/app.go +++ b/internal/ui/app.go @@ -27,7 +27,6 @@ func NewApp() *App { Main: NewPages(), cmdBuff: NewCmdBuff(':', CommandBuff), } - a.RefreshStyles() a.views = map[string]tview.Primitive{ diff --git a/internal/ui/menu.go b/internal/ui/menu.go index 632066f0..4d82d156 100644 --- a/internal/ui/menu.go +++ b/internal/ui/menu.go @@ -41,8 +41,12 @@ func (v *Menu) StackPushed(c model.Component) { v.HydrateMenu(c.Hints()) } -func (v *Menu) StackPopped(o, n model.Component) { - v.HydrateMenu(n.Hints()) +func (v *Menu) StackPopped(o, top model.Component) { + if top != nil { + v.HydrateMenu(top.Hints()) + } else { + v.Clear() + } } func (v *Menu) StackTop(t model.Component) { diff --git a/internal/ui/pages.go b/internal/ui/pages.go index 4252dcb0..c7461121 100644 --- a/internal/ui/pages.go +++ b/internal/ui/pages.go @@ -1,6 +1,8 @@ package ui import ( + "fmt" + "github.com/derailed/k9s/internal/model" "github.com/derailed/tview" "github.com/rs/zerolog/log" @@ -21,40 +23,39 @@ func NewPages() *Pages { return &p } -// Get fetch a page given its name. -func (p *Pages) get(n string) model.Component { - if comp, ok := p.GetPrimitive(n).(model.Component); ok { - return comp +func (p *Pages) Show(c model.Component) { + p.SwitchToPage(componentID(c)) +} + +func (p *Pages) Current() model.Component { + c := p.CurrentPage() + if c == nil { + return nil } - return nil + return c.Item.(model.Component) } // AddAndShow adds a new page and bring it to front. func (p *Pages) addAndShow(c model.Component) { p.add(c) - p.Show(c.Name()) + p.Show(c) } // Add adds a new page. func (p *Pages) add(c model.Component) { - p.AddPage(c.Name(), c, true, true) + p.AddPage(componentID(c), c, true, true) } // Delete removes a page. func (p *Pages) delete(c model.Component) { - p.RemovePage(c.Name()) -} - -// Show brings a named page forward. -func (p *Pages) Show(n string) { - p.SwitchToPage(n) + p.RemovePage(componentID(c)) } func (p *Pages) DumpPages() { log.Debug().Msgf("Dumping Pages %p", p) - for i, n := range p.Stack.Flatten() { - log.Debug().Msgf("%d -- %s -- %#v", i, n, p.GetPrimitive(n)) + for i, c := range p.Stack.Peek() { + log.Debug().Msgf("%d -- %s -- %#v", i, componentID(c), p.GetPrimitive(componentID(c))) } } @@ -72,5 +73,11 @@ func (p *Pages) StackTop(top model.Component) { if top == nil { return } - p.Show(top.Name()) + p.Show(top) +} + +// Helpers... + +func componentID(c model.Component) string { + return fmt.Sprintf("%s-%p", c.Name(), c) } diff --git a/internal/ui/table.go b/internal/ui/table.go index f71f8a15..77c27430 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -60,7 +60,6 @@ func NewTable(title string) *Table { } func (t *Table) Init(ctx context.Context) { - log.Debug().Msgf("UI Table INIT %q", t.baseTitle) t.styles = ctx.Value(KeyStyles).(*config.Styles) t.SetFixed(1, 0) diff --git a/internal/view/app.go b/internal/view/app.go index 28a55679..0b744f99 100644 --- a/internal/view/app.go +++ b/internal/view/app.go @@ -83,6 +83,9 @@ func (a *App) ActiveView() model.Component { } func (a *App) PrevCmd(evt *tcell.EventKey) *tcell.EventKey { + log.Debug().Msgf("------ CONTENT PREVIOUS") + a.Content.DumpStack() + a.Content.DumpPages() if !a.Content.IsLast() { a.Content.Pop() } @@ -227,9 +230,9 @@ func (a *App) switchNS(ns string) bool { ns = resource.AllNamespaces } if ns == a.Config.ActiveNamespace() { - log.Debug().Msgf("Namespace did not change %s", ns) return true } + if err := a.Config.SetActiveNamespace(ns); err != nil { log.Error().Err(err).Msg("Config Set NS failed!") } @@ -248,6 +251,11 @@ func (a *App) switchCtx(ctx string, load bool) error { if err != nil { log.Info().Err(err).Msg("No namespace specified using all namespaces") } + if a.stopCh != nil { + close(a.stopCh) + a.stopCh = nil + } + a.informer = nil a.startInformer(ns) a.Config.Reset() if err := a.Config.Save(); err != nil { @@ -262,6 +270,12 @@ func (a *App) switchCtx(ctx string, load bool) error { } func (a *App) startInformer(ns string) bool { + // if informer watches all ns - don't start a new informer then. + if a.informer != nil && a.informer.Namespace == resource.AllNamespaces { + log.Debug().Msgf(">>>> Informer is already watching all namespaces. No restart needed ;)") + return true + } + if a.stopCh != nil { close(a.stopCh) a.stopCh = nil @@ -381,6 +395,7 @@ func (a *App) toggleHeaderCmd(evt *tcell.EventKey) *tcell.EventKey { func (a *App) gotoCmd(evt *tcell.EventKey) *tcell.EventKey { if a.CmdBuff().IsActive() && !a.CmdBuff().Empty() { + a.Content.Stack.Reset() a.gotoResource(a.GetCmd(), true) a.ResetCmd() return nil diff --git a/internal/view/bench.go b/internal/view/bench.go index 224956d9..2903826e 100644 --- a/internal/view/bench.go +++ b/internal/view/bench.go @@ -100,7 +100,6 @@ func (b *Bench) refresh() { func (b *Bench) keyBindings() { aa := ui.KeyActions{ - tcell.KeyEsc: ui.NewKeyAction("Back", b.app.PrevCmd, false), tcell.KeyEnter: ui.NewKeyAction("Enter", b.enterCmd, false), tcell.KeyCtrlD: ui.NewKeyAction("Delete", b.deleteCmd, false), } diff --git a/internal/view/context.go b/internal/view/context.go index 8a9e6013..606f1b48 100644 --- a/internal/view/context.go +++ b/internal/view/context.go @@ -1,6 +1,7 @@ package view import ( + "context" "strings" "github.com/derailed/k9s/internal/resource" @@ -14,14 +15,17 @@ type Context struct { // NewContext return a new context viewer. func NewContext(title, gvr string, list resource.List) ResourceViewer { - c := Context{ + return &Context{ Resource: NewResource(title, gvr, list), } +} + +func (c *Context) Init(ctx context.Context) { c.extraActionsFn = c.extraActions c.enterFn = c.useCtx - c.masterPage().SetSelectedFn(c.cleanser) + c.Resource.Init(ctx) - return &c + c.masterPage().SetSelectedFn(c.cleanser) } func (c *Context) extraActions(aa ui.KeyActions) { @@ -57,9 +61,7 @@ func (c *Context) useContext(name string) error { return err } c.refresh() - if tv, ok := c.GetPrimitive("ctx").(*Table); ok { - tv.Select(1, 0) - } + c.masterPage().Select(1, 0) return nil } diff --git a/internal/view/context_int_test.go b/internal/view/context_int_test.go new file mode 100644 index 00000000..ae7160e9 --- /dev/null +++ b/internal/view/context_int_test.go @@ -0,0 +1,24 @@ +package view + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCleaner(t *testing.T) { + uu := map[string]struct { + s, e string + }{ + "normal": {"fred", "fred"}, + "default": {"fred*", "fred"}, + "delta": {"fred(𝜟)", "fred"}, + } + + v := Context{} + for k, u := range uu { + t.Run(k, func(t *testing.T) { + assert.Equal(t, u.e, v.cleanser(u.s)) + }) + } +} diff --git a/internal/view/context_test.go b/internal/view/context_test.go index 234dd83c..063664a6 100644 --- a/internal/view/context_test.go +++ b/internal/view/context_test.go @@ -1,34 +1,17 @@ package view_test -// import ( -// "testing" +import ( + "testing" -// "github.com/derailed/k9s/internal/config" -// "github.com/derailed/k9s/internal/resource" -// "github.com/derailed/k9s/internal/view" -// "github.com/stretchr/testify/assert" -// ) + "github.com/derailed/k9s/internal/resource" + "github.com/derailed/k9s/internal/view" + "github.com/stretchr/testify/assert" +) -// func TestContext(t *testing.T) { -// l := resource.NewContextList(nil, "fred") -// v := view.NewContext("blee", "", NewApp(config.NewConfig(ks{})), l).(*contextView) +func TestContext(t *testing.T) { + ctx := view.NewContext("ctx", "", resource.NewContextList(nil, "fred")) + ctx.Init(makeCtx()) -// assert.Equal(t, 10, len(v.Hints())) -// } - -// func TestCleaner(t *testing.T) { -// uu := map[string]struct { -// s, e string -// }{ -// "normal": {"fred", "fred"}, -// "default": {"fred*", "fred"}, -// "delta": {"fred(𝜟)", "fred"}, -// } - -// v := contextView{} -// for k, u := range uu { -// t.Run(k, func(t *testing.T) { -// assert.Equal(t, u.e, v.cleanser(u.s)) -// }) -// } -// } + assert.Equal(t, "ctx", ctx.Name()) + assert.Equal(t, 13, len(ctx.Hints())) +} diff --git a/internal/view/master_detail.go b/internal/view/master_detail.go index 88fd89a5..b52160df 100644 --- a/internal/view/master_detail.go +++ b/internal/view/master_detail.go @@ -7,7 +7,6 @@ import ( "github.com/derailed/k9s/internal/resource" "github.com/derailed/k9s/internal/ui" "github.com/gdamore/tcell" - "github.com/rs/zerolog/log" ) // MasterDetail presents a master-detail viewer. @@ -16,6 +15,7 @@ type MasterDetail struct { enterFn enterFn extraActionsFn func(ui.KeyActions) + master *Table details *Details currentNS string title string @@ -32,7 +32,6 @@ func NewMasterDetail(title, ns string) *MasterDetail { // Init initializes the viewer. func (m *MasterDetail) Init(ctx context.Context) { - log.Debug().Msgf("\t>>>MasterDetail init %q", m.title) app := ctx.Value(ui.KeyApp).(*App) if m.currentNS != resource.NotNamespaced { m.currentNS = app.Config.ActiveNamespace() @@ -40,15 +39,14 @@ func (m *MasterDetail) Init(ctx context.Context) { m.PageStack.Init(ctx) m.AddListener(app.Menu()) - t := NewTable(m.title) - m.Push(t) + m.master = NewTable(m.title) + m.Push(m.master) m.details = NewDetails(m.app, func(evt *tcell.EventKey) *tcell.EventKey { m.Pop() return nil }) m.details.Init(ctx) - log.Debug().Msgf("\t<<<>>>> Starting Informer in namespace %q", ns) diff --git a/internal/watch/no.go b/internal/watch/no.go index 8c0bb153..005b1db4 100644 --- a/internal/watch/no.go +++ b/internal/watch/no.go @@ -11,7 +11,7 @@ import ( const ( // NodeIndex marker for stored nodes. - NodeIndex string = "no" + NodeIndex string = "nodes" nodeCols = 12 ) diff --git a/internal/watch/pod.go b/internal/watch/pod.go index 8d6911d7..c7647184 100644 --- a/internal/watch/pod.go +++ b/internal/watch/pod.go @@ -13,7 +13,7 @@ import ( const ( // PodIndex marker for stored pods. - PodIndex string = "po" + PodIndex string = "pods" podCols = 11 )