feat(log): Add all containers toggle (#1141)

Closes #1138
mine
Raul Cabello Martin 2021-06-04 16:25:48 +02:00 committed by GitHub
parent c9783fac61
commit a631712c05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 123 additions and 40 deletions

View File

@ -28,25 +28,27 @@ type LogsListener interface {
// Log represents a resource logger.
type Log struct {
factory dao.Factory
lines dao.LogItems
listeners []LogsListener
gvr client.GVR
logOptions dao.LogOptions
cancelFn context.CancelFunc
mx sync.RWMutex
filter string
lastSent int
flushTimeout time.Duration
factory dao.Factory
lines dao.LogItems
listeners []LogsListener
gvr client.GVR
logOptions dao.LogOptions
cancelFn context.CancelFunc
mx sync.RWMutex
filter string
lastSent int
flushTimeout time.Duration
originalContainer string
}
// NewLog returns a new model.
func NewLog(gvr client.GVR, opts dao.LogOptions, flushTimeout time.Duration) *Log {
return &Log{
gvr: gvr,
logOptions: opts,
lines: nil,
flushTimeout: flushTimeout,
gvr: gvr,
logOptions: opts,
lines: nil,
flushTimeout: flushTimeout,
originalContainer: opts.Container,
}
}
@ -250,6 +252,16 @@ func (l *Log) Notify() {
}
}
// ToggleShowTimestamp toggles to show all containers logs.
func (l *Log) ToggleAllContainers() {
if l.logOptions.Container != "" {
l.logOptions.Container = ""
} else {
l.logOptions.Container = l.originalContainer
}
l.Restart()
}
func (l *Log) updateLogs(ctx context.Context, c dao.LogChan) {
defer func() {
log.Debug().Msgf("updateLogs view bailing out!")

View File

@ -205,6 +205,16 @@ func TestLogTimedout(t *testing.T) {
assert.Equal(t, e, string(v.data[0]))
}
func TestToggleAllContainers(t *testing.T) {
m := model.NewLog(client.NewGVR(""), makeLogOpts(1), 10*time.Millisecond)
m.Init(makeFactory())
assert.Equal(t, m.GetContainer(), "blee")
m.ToggleAllContainers()
assert.Equal(t, m.GetContainer(), "")
m.ToggleAllContainers()
assert.Equal(t, m.GetContainer(), "blee")
}
// ----------------------------------------------------------------------------
// Helpers...

View File

@ -68,7 +68,7 @@ func (l *Log) Init(ctx context.Context) (err error) {
l.SetBorder(true)
l.SetDirection(tview.FlexRow)
l.indicator = NewLogIndicator(l.app.Config, l.app.Styles)
l.indicator = NewLogIndicator(l.app.Config, l.app.Styles, l.isContainerLogView())
l.AddItem(l.indicator, 1, 1, false)
l.indicator.Refresh()
@ -198,6 +198,11 @@ func (l *Log) bindKeys() {
tcell.KeyCtrlS: ui.NewKeyAction("Save", l.SaveCmd, true),
ui.KeyC: ui.NewKeyAction("Copy", l.cpCmd, true),
})
if l.isContainerLogView() {
l.logs.Actions().Set(ui.KeyActions{
ui.KeyA: ui.NewKeyAction("Toggle AllContainers", l.showAllContainers, true),
})
}
}
func (l *Log) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
@ -287,6 +292,15 @@ func (l *Log) sinceCmd(a int) func(evt *tcell.EventKey) *tcell.EventKey {
}
}
func (l *Log) showAllContainers(evt *tcell.EventKey) *tcell.EventKey {
if l.app.InCmdMode() {
return evt
}
l.indicator.ToggleAllContainers()
l.model.ToggleAllContainers()
return nil
}
func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.logs.cmdBuff.IsActive() {
return evt
@ -416,6 +430,10 @@ func (l *Log) goFullScreen() {
}
}
func (l *Log) isContainerLogView() bool {
return l.model.GetContainer() != ""
}
// ----------------------------------------------------------------------------
// Helpers...

View File

@ -8,36 +8,40 @@ import (
)
const (
autoscroll = "Autoscroll"
fullscreen = "FullScreen"
timestamp = "Timestamps"
wrap = "Wrap"
on = "On"
off = "Off"
spacer = " "
bold = "[::b]"
autoscroll = "Autoscroll"
fullscreen = "FullScreen"
timestamp = "Timestamps"
wrap = "Wrap"
allContainers = "AllContainers"
on = "On"
off = "Off"
spacer = " "
bold = "[::b]"
)
// LogIndicator represents a log view indicator.
type LogIndicator struct {
*tview.TextView
styles *config.Styles
scrollStatus int32
fullScreen bool
textWrap bool
showTime bool
styles *config.Styles
scrollStatus int32
fullScreen bool
textWrap bool
showTime bool
allContainers bool
shouldDisplayAllContainers bool
}
// NewLogIndicator returns a new indicator.
func NewLogIndicator(cfg *config.Config, styles *config.Styles) *LogIndicator {
func NewLogIndicator(cfg *config.Config, styles *config.Styles, isContainerLogView bool) *LogIndicator {
l := LogIndicator{
styles: styles,
TextView: tview.NewTextView(),
scrollStatus: 1,
fullScreen: cfg.K9s.Logger.FullScreenLogs,
textWrap: cfg.K9s.Logger.TextWrap,
showTime: cfg.K9s.Logger.ShowTime,
styles: styles,
TextView: tview.NewTextView(),
scrollStatus: 1,
fullScreen: cfg.K9s.Logger.FullScreenLogs,
textWrap: cfg.K9s.Logger.TextWrap,
showTime: cfg.K9s.Logger.ShowTime,
shouldDisplayAllContainers: isContainerLogView,
}
l.StylesChanged(styles)
styles.AddListener(&l)
@ -100,9 +104,18 @@ func (l *LogIndicator) ToggleAutoScroll() {
l.Refresh()
}
// ToggleTextWrap toggles the wrap mode.
func (l *LogIndicator) ToggleAllContainers() {
l.allContainers = !l.allContainers
l.Refresh()
}
// Refresh updates the view.
func (l *LogIndicator) Refresh() {
l.Clear()
if l.shouldDisplayAllContainers {
l.update(allContainers, l.allContainers, spacer)
}
l.update(autoscroll, l.AutoScroll(), spacer)
l.update(fullscreen, l.fullScreen, spacer)
l.update(timestamp, l.showTime, spacer)

View File

@ -10,8 +10,19 @@ import (
func TestLogIndicatorRefresh(t *testing.T) {
defaults := config.NewStyles()
v := view.NewLogIndicator(config.NewConfig(nil), defaults)
v.Refresh()
uu := map[string]struct {
li *view.LogIndicator
e string
}{
"all containers": {view.NewLogIndicator(config.NewConfig(nil), defaults, true), "[::b]AllContainers:Off [::b]Autoscroll:On [::b]FullScreen:Off [::b]Timestamps:Off [::b]Wrap:Off\n"},
"no all containers": {view.NewLogIndicator(config.NewConfig(nil), defaults, false), "[::b]Autoscroll:On [::b]FullScreen:Off [::b]Timestamps:Off [::b]Wrap:Off\n"},
}
assert.Equal(t, "[::b]Autoscroll:On [::b]FullScreen:Off [::b]Timestamps:Off [::b]Wrap:Off\n", v.GetText(false))
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.li.Refresh()
assert.Equal(t, u.li.GetText(false), u.e)
})
}
}

View File

@ -16,10 +16,10 @@ func TestLogAutoScroll(t *testing.T) {
v.GetModel().Set(dao.LogItems{dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo")})
v.GetModel().Notify()
assert.Equal(t, 15, len(v.Hints()))
assert.Equal(t, 16, len(v.Hints()))
v.toggleAutoScrollCmd(nil)
assert.Equal(t, "Autoscroll:Off FullScreen:Off Timestamps:Off Wrap:Off", v.Indicator().GetText(true))
assert.Equal(t, "AllContainers:Off Autoscroll:Off FullScreen:Off Timestamps:Off Wrap:Off", v.Indicator().GetText(true))
}
func TestLogViewNav(t *testing.T) {

View File

@ -10,6 +10,7 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/view"
"github.com/derailed/tview"
"github.com/stretchr/testify/assert"
@ -73,6 +74,24 @@ func TestLogViewSave(t *testing.T) {
assert.Equal(t, len(c2), len(c1)+1)
}
func TestAllContainerKeyBinding(t *testing.T) {
uu := map[string]struct {
l *view.Log
e bool
}{
"all containers": {view.NewLog(client.NewGVR("v1/pods"), "", "container", false), true},
"no all containers": {view.NewLog(client.NewGVR("v1/pods"), "", "", false), false},
}
for k := range uu {
u := uu[k]
t.Run(k, func(t *testing.T) {
u.l.Init(makeContext())
_, got := u.l.Logs().Actions()[ui.KeyA]
assert.Equal(t, u.e, got)
})
}
}
// ----------------------------------------------------------------------------
// Helpers...