parent
c9783fac61
commit
a631712c05
|
|
@ -28,25 +28,27 @@ type LogsListener interface {
|
||||||
|
|
||||||
// Log represents a resource logger.
|
// Log represents a resource logger.
|
||||||
type Log struct {
|
type Log struct {
|
||||||
factory dao.Factory
|
factory dao.Factory
|
||||||
lines dao.LogItems
|
lines dao.LogItems
|
||||||
listeners []LogsListener
|
listeners []LogsListener
|
||||||
gvr client.GVR
|
gvr client.GVR
|
||||||
logOptions dao.LogOptions
|
logOptions dao.LogOptions
|
||||||
cancelFn context.CancelFunc
|
cancelFn context.CancelFunc
|
||||||
mx sync.RWMutex
|
mx sync.RWMutex
|
||||||
filter string
|
filter string
|
||||||
lastSent int
|
lastSent int
|
||||||
flushTimeout time.Duration
|
flushTimeout time.Duration
|
||||||
|
originalContainer string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLog returns a new model.
|
// NewLog returns a new model.
|
||||||
func NewLog(gvr client.GVR, opts dao.LogOptions, flushTimeout time.Duration) *Log {
|
func NewLog(gvr client.GVR, opts dao.LogOptions, flushTimeout time.Duration) *Log {
|
||||||
return &Log{
|
return &Log{
|
||||||
gvr: gvr,
|
gvr: gvr,
|
||||||
logOptions: opts,
|
logOptions: opts,
|
||||||
lines: nil,
|
lines: nil,
|
||||||
flushTimeout: flushTimeout,
|
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) {
|
func (l *Log) updateLogs(ctx context.Context, c dao.LogChan) {
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Debug().Msgf("updateLogs view bailing out!")
|
log.Debug().Msgf("updateLogs view bailing out!")
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,16 @@ func TestLogTimedout(t *testing.T) {
|
||||||
assert.Equal(t, e, string(v.data[0]))
|
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...
|
// Helpers...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
l.SetBorder(true)
|
l.SetBorder(true)
|
||||||
l.SetDirection(tview.FlexRow)
|
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.AddItem(l.indicator, 1, 1, false)
|
||||||
l.indicator.Refresh()
|
l.indicator.Refresh()
|
||||||
|
|
||||||
|
|
@ -198,6 +198,11 @@ func (l *Log) bindKeys() {
|
||||||
tcell.KeyCtrlS: ui.NewKeyAction("Save", l.SaveCmd, true),
|
tcell.KeyCtrlS: ui.NewKeyAction("Save", l.SaveCmd, true),
|
||||||
ui.KeyC: ui.NewKeyAction("Copy", l.cpCmd, 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 {
|
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 {
|
func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if !l.logs.cmdBuff.IsActive() {
|
if !l.logs.cmdBuff.IsActive() {
|
||||||
return evt
|
return evt
|
||||||
|
|
@ -416,6 +430,10 @@ func (l *Log) goFullScreen() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Log) isContainerLogView() bool {
|
||||||
|
return l.model.GetContainer() != ""
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,36 +8,40 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
autoscroll = "Autoscroll"
|
autoscroll = "Autoscroll"
|
||||||
fullscreen = "FullScreen"
|
fullscreen = "FullScreen"
|
||||||
timestamp = "Timestamps"
|
timestamp = "Timestamps"
|
||||||
wrap = "Wrap"
|
wrap = "Wrap"
|
||||||
on = "On"
|
allContainers = "AllContainers"
|
||||||
off = "Off"
|
on = "On"
|
||||||
spacer = " "
|
off = "Off"
|
||||||
bold = "[::b]"
|
spacer = " "
|
||||||
|
bold = "[::b]"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogIndicator represents a log view indicator.
|
// LogIndicator represents a log view indicator.
|
||||||
type LogIndicator struct {
|
type LogIndicator struct {
|
||||||
*tview.TextView
|
*tview.TextView
|
||||||
|
|
||||||
styles *config.Styles
|
styles *config.Styles
|
||||||
scrollStatus int32
|
scrollStatus int32
|
||||||
fullScreen bool
|
fullScreen bool
|
||||||
textWrap bool
|
textWrap bool
|
||||||
showTime bool
|
showTime bool
|
||||||
|
allContainers bool
|
||||||
|
shouldDisplayAllContainers bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogIndicator returns a new indicator.
|
// 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{
|
l := LogIndicator{
|
||||||
styles: styles,
|
styles: styles,
|
||||||
TextView: tview.NewTextView(),
|
TextView: tview.NewTextView(),
|
||||||
scrollStatus: 1,
|
scrollStatus: 1,
|
||||||
fullScreen: cfg.K9s.Logger.FullScreenLogs,
|
fullScreen: cfg.K9s.Logger.FullScreenLogs,
|
||||||
textWrap: cfg.K9s.Logger.TextWrap,
|
textWrap: cfg.K9s.Logger.TextWrap,
|
||||||
showTime: cfg.K9s.Logger.ShowTime,
|
showTime: cfg.K9s.Logger.ShowTime,
|
||||||
|
shouldDisplayAllContainers: isContainerLogView,
|
||||||
}
|
}
|
||||||
l.StylesChanged(styles)
|
l.StylesChanged(styles)
|
||||||
styles.AddListener(&l)
|
styles.AddListener(&l)
|
||||||
|
|
@ -100,9 +104,18 @@ func (l *LogIndicator) ToggleAutoScroll() {
|
||||||
l.Refresh()
|
l.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToggleTextWrap toggles the wrap mode.
|
||||||
|
func (l *LogIndicator) ToggleAllContainers() {
|
||||||
|
l.allContainers = !l.allContainers
|
||||||
|
l.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh updates the view.
|
// Refresh updates the view.
|
||||||
func (l *LogIndicator) Refresh() {
|
func (l *LogIndicator) Refresh() {
|
||||||
l.Clear()
|
l.Clear()
|
||||||
|
if l.shouldDisplayAllContainers {
|
||||||
|
l.update(allContainers, l.allContainers, spacer)
|
||||||
|
}
|
||||||
l.update(autoscroll, l.AutoScroll(), spacer)
|
l.update(autoscroll, l.AutoScroll(), spacer)
|
||||||
l.update(fullscreen, l.fullScreen, spacer)
|
l.update(fullscreen, l.fullScreen, spacer)
|
||||||
l.update(timestamp, l.showTime, spacer)
|
l.update(timestamp, l.showTime, spacer)
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,19 @@ import (
|
||||||
|
|
||||||
func TestLogIndicatorRefresh(t *testing.T) {
|
func TestLogIndicatorRefresh(t *testing.T) {
|
||||||
defaults := config.NewStyles()
|
defaults := config.NewStyles()
|
||||||
v := view.NewLogIndicator(config.NewConfig(nil), defaults)
|
uu := map[string]struct {
|
||||||
v.Refresh()
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ func TestLogAutoScroll(t *testing.T) {
|
||||||
v.GetModel().Set(dao.LogItems{dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo")})
|
v.GetModel().Set(dao.LogItems{dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo")})
|
||||||
v.GetModel().Notify()
|
v.GetModel().Notify()
|
||||||
|
|
||||||
assert.Equal(t, 15, len(v.Hints()))
|
assert.Equal(t, 16, len(v.Hints()))
|
||||||
|
|
||||||
v.toggleAutoScrollCmd(nil)
|
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) {
|
func TestLogViewNav(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/k9s/internal/view"
|
"github.com/derailed/k9s/internal/view"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -73,6 +74,24 @@ func TestLogViewSave(t *testing.T) {
|
||||||
assert.Equal(t, len(c2), len(c1)+1)
|
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...
|
// Helpers...
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue