maintenance release. Fix #920 and perhaps improved #663

mine
derailed 2020-11-02 23:35:08 -07:00
parent 0fc7ea3183
commit 2d2c6b06b6
16 changed files with 134 additions and 92 deletions

View File

@ -3,7 +3,7 @@ PACKAGE := github.com/derailed/$(NAME)
GIT := $(shell git rev-parse --short HEAD) GIT := $(shell git rev-parse --short HEAD)
SOURCE_DATE_EPOCH ?= $(shell date +%s) SOURCE_DATE_EPOCH ?= $(shell date +%s)
DATE := $(shell date -u -d @${SOURCE_DATE_EPOCH} +%FT%T%Z) DATE := $(shell date -u -d @${SOURCE_DATE_EPOCH} +%FT%T%Z)
VERSION ?= v0.23.3 VERSION ?= v0.23.4
IMG_NAME := derailed/k9s IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION} IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -0,0 +1,28 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.23.4
## 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 [sponsorhip 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)
---
## Maintenance Release!
---
## Resolved Issues/Features
* [Issue #920](https://github.com/derailed/k9s/issues/920) Timestamp stopped working
* [Issue #663](https://github.com/derailed/k9s/issues/663) Perf issues in v0.23.X - Better??
## Resolved PRs
---
<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

@ -53,7 +53,7 @@ func init() {
if err := flags.Set("stderrthreshold", "fatal"); err != nil { if err := flags.Set("stderrthreshold", "fatal"); err != nil {
panic(err) panic(err)
} }
if err := flags.Set("v", "0"); err != nil { if err := flags.Set("v", "-1"); err != nil {
panic(err) panic(err)
} }
if err := flags.Set("log_file", config.K9sLogs); err != nil { if err := flags.Set("log_file", config.K9sLogs); err != nil {

View File

@ -47,7 +47,6 @@ func (n *Namespace) Validate(c client.Connection, ks KubeSettings) {
// SetActive set the active namespace. // SetActive set the active namespace.
func (n *Namespace) SetActive(ns string, ks KubeSettings) error { func (n *Namespace) SetActive(ns string, ks KubeSettings) error {
log.Debug().Msgf("Setting active ns %q", ns)
n.Active = ns n.Active = ns
if ns != "" { if ns != "" {
n.addFavNS(ns) n.addFavNS(ns)

View File

@ -101,23 +101,28 @@ func (f *FishBuff) SetSuggestionFn(fn SuggestionFunc) {
} }
// Notify publish suggestions to all listeners. // Notify publish suggestions to all listeners.
func (f *FishBuff) Notify() { func (f *FishBuff) Notify(delete bool) {
if f.suggestionFn == nil { if f.suggestionFn == nil {
return return
} }
ss := f.suggestionFn(string(f.buff))
if len(ss) == 1 && !delete {
f.SetText(string(string(f.buff) + ss[0]))
return
}
f.fireSuggestionChanged(f.suggestionFn(string(f.buff))) f.fireSuggestionChanged(f.suggestionFn(string(f.buff)))
} }
// Add adds a new charater to the buffer. // Add adds a new character to the buffer.
func (f *FishBuff) Add(r rune) { func (f *FishBuff) Add(r rune) {
f.CmdBuff.Add(r) f.CmdBuff.Add(r)
f.Notify() f.Notify(false)
} }
// Delete removes the last character from the buffer. // Delete removes the last character from the buffer.
func (f *FishBuff) Delete() { func (f *FishBuff) Delete() {
f.CmdBuff.Delete() f.CmdBuff.Delete()
f.Notify() f.Notify(true)
} }
func (f *FishBuff) fireSuggestionChanged(ss []string) { func (f *FishBuff) fireSuggestionChanged(ss []string) {
@ -127,9 +132,10 @@ func (f *FishBuff) fireSuggestionChanged(ss []string) {
return return
} }
text, sug := f.GetText(), ss[f.suggestionIndex]
for _, l := range f.listeners { for _, l := range f.listeners {
if listener, ok := l.(SuggestionListener); ok { if listener, ok := l.(SuggestionListener); ok {
listener.SuggestionChanged(f.GetText(), ss[f.suggestionIndex]) listener.SuggestionChanged(text, sug)
} }
} }
} }

View File

@ -8,20 +8,37 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestFishExact(t *testing.T) {
m := mockSuggestionListener{}
f := model.NewFishBuff(' ', model.FilterBuffer)
f.AddListener(&m)
f.SetSuggestionFn(func(text string) sort.StringSlice {
return sort.StringSlice{"lee"}
})
f.Add('b')
f.SetActive(true)
assert.True(t, m.active)
assert.Equal(t, 1, m.buff)
assert.Equal(t, 0, m.sugg)
assert.Equal(t, "blee", m.text)
}
func TestFishAdd(t *testing.T) { func TestFishAdd(t *testing.T) {
m := mockSuggestionListener{} m := mockSuggestionListener{}
f := model.NewFishBuff(' ', model.FilterBuffer) f := model.NewFishBuff(' ', model.FilterBuffer)
f.AddListener(&m) f.AddListener(&m)
f.SetSuggestionFn(func(text string) sort.StringSlice { f.SetSuggestionFn(func(text string) sort.StringSlice {
return sort.StringSlice{"blee", "duh"} return sort.StringSlice{"blee", "brew"}
}) })
f.Add('a') f.Add('b')
f.SetActive(true) f.SetActive(true)
assert.True(t, m.active)
assert.Equal(t, 1, m.buff) assert.Equal(t, 1, m.buff)
assert.Equal(t, 1, m.sugg) assert.Equal(t, 1, m.sugg)
assert.True(t, m.active)
assert.Equal(t, "blee", m.suggestion) assert.Equal(t, "blee", m.suggestion)
c, ok := f.CurrentSuggestion() c, ok := f.CurrentSuggestion()
@ -30,7 +47,7 @@ func TestFishAdd(t *testing.T) {
c, ok = f.NextSuggestion() c, ok = f.NextSuggestion()
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, "duh", c) assert.Equal(t, "brew", c)
c, ok = f.PrevSuggestion() c, ok = f.PrevSuggestion()
assert.True(t, ok) assert.True(t, ok)
@ -70,9 +87,9 @@ func TestFishDelete(t *testing.T) {
// Helpers... // Helpers...
type mockSuggestionListener struct { type mockSuggestionListener struct {
buff, sugg int buff, sugg int
suggestion string suggestion, text string
active bool active bool
} }
func (m *mockSuggestionListener) BufferChanged(s string) { func (m *mockSuggestionListener) BufferChanged(s string) {
@ -80,6 +97,7 @@ func (m *mockSuggestionListener) BufferChanged(s string) {
} }
func (m *mockSuggestionListener) BufferCompleted(s string) { func (m *mockSuggestionListener) BufferCompleted(s string) {
m.text = s
} }
func (m *mockSuggestionListener) BufferActive(state bool, kind model.BufferKind) { func (m *mockSuggestionListener) BufferActive(state bool, kind model.BufferKind) {

View File

@ -50,14 +50,6 @@ func NewLog(gvr client.GVR, opts dao.LogOptions, flushTimeout time.Duration) *Lo
} }
} }
// LogOptions returns the current log options.
func (l *Log) LogOptions() dao.LogOptions {
l.mx.RLock()
defer l.mx.RUnlock()
return l.logOptions
}
// SinceSeconds returns since seconds option. // SinceSeconds returns since seconds option.
func (l *Log) SinceSeconds() int64 { func (l *Log) SinceSeconds() int64 {
l.mx.RLock() l.mx.RLock()
@ -66,14 +58,15 @@ func (l *Log) SinceSeconds() int64 {
return l.logOptions.SinceSeconds return l.logOptions.SinceSeconds
} }
// SetLogOptions updates logger options. // ToggleShowTimestamp toggles to logs timestamps.
func (l *Log) SetLogOptions(opts dao.LogOptions) { func (l *Log) ToggleShowTimestamp(b bool) {
l.mx.Lock() l.logOptions.ShowTimestamp = b
{ l.Refresh()
l.logOptions = opts }
}
l.mx.Unlock()
// SetSinceSeconds sets the logs retrieval time.
func (l *Log) SetSinceSeconds(i int64) {
l.logOptions.SinceSeconds = i
l.Restart() l.Restart()
} }

View File

@ -154,15 +154,14 @@ func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (resources,
if rList.Cpu() != nil { if rList.Cpu() != nil {
p[requestCPU] = percentMc(c.rCPU(), rList.Cpu()) p[requestCPU] = percentMc(c.rCPU(), rList.Cpu())
} }
if lList.Cpu() != nil {
p[limitCPU] = percentMc(c.rCPU(), lList.Cpu())
}
if rList.Memory() != nil { if rList.Memory() != nil {
p[requestMEM] = percentMi(c.rMEM(), rList.Memory()) p[requestMEM] = percentMi(c.rMEM(), rList.Memory())
} }
if lList.Memory() != nil {
if lList.Cpu() != nil { p[limitMEM] = percentMi(c.rMEM(), lList.Memory())
p[limitCPU] = percentMc(c.lCPU(), lList.Cpu())
}
if rList.Memory() != nil {
p[limitMEM] = percentMi(c.lMEM(), lList.Memory())
} }
return c, p, r return c, p, r

View File

@ -40,9 +40,9 @@ func TestContainer(t *testing.T) {
"20:20", "20:20",
"100:100", "100:100",
"50", "50",
"0", "50",
"20",
"20", "20",
"0",
"", "",
"container is not ready", "container is not ready",
}, },

View File

@ -1,7 +1,6 @@
package render package render
import ( import (
"fmt"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
@ -120,7 +119,7 @@ func join(a []string, sep string) string {
return a[0] return a[0]
} }
var b []string b := make([]string, 0, len(a))
for _, s := range a { for _, s := range a {
if s != "" { if s != "" {
b = append(b, s) b = append(b, s)
@ -258,28 +257,26 @@ func mapToIfc(m interface{}) (s string) {
return return
} }
func toMcPerc(v1, v2 *resource.Quantity) string { func toMcPerc(v1, v2 int64) string {
m := v1.MilliValue() return toMc(v1) + " (" + strconv.Itoa(client.ToPercentage(v1, v2)) + "%)"
return fmt.Sprintf("%s (%d%%)", toMc(m), client.ToPercentage(m, v2.MilliValue()))
} }
func toMiPerc(v1, v2 *resource.Quantity) string { func toMiPerc(v1, v2 int64) string {
m := v1.Value() return toMi(v1) + " (" + strconv.Itoa(client.ToPercentage(v1, v2)) + "%)"
return fmt.Sprintf("%s (%d%%)", toMi(m), client.ToPercentage(m, v2.Value()))
} }
func toMc(v int64) string { func toMc(v int64) string {
if v == 0 { if v == 0 {
return ZeroValue return ZeroValue
} }
return AsThousands(v) return strconv.Itoa(int(v))
} }
func toMi(v int64) string { func toMi(v int64) string {
if v == 0 { if v == 0 {
return ZeroValue return ZeroValue
} }
return AsThousands(client.ToMB(v)) return strconv.Itoa(int(client.ToMB(v)))
} }
func boolPtrToStr(b *bool) string { func boolPtrToStr(b *bool) string {

View File

@ -368,7 +368,7 @@ func TestToMc(t *testing.T) {
}{ }{
{0, "0"}, {0, "0"},
{2, "2"}, {2, "2"},
{1_000, "1,000"}, {1_000, "1000"},
} }
for _, u := range uu { for _, u := range uu {
@ -383,7 +383,7 @@ func TestToMi(t *testing.T) {
}{ }{
{0, "0"}, {0, "0"},
{2 * client.MegaByte, "2"}, {2 * client.MegaByte, "2"},
{1_000 * client.MegaByte, "1,000"}, {1_000 * client.MegaByte, "1000"},
} }
for _, u := range uu { for _, u := range uu {

View File

@ -41,10 +41,10 @@ func (Node) Header(_ string) Header {
HeaderColumn{Name: "INTERNAL-IP", Wide: true}, HeaderColumn{Name: "INTERNAL-IP", Wide: true},
HeaderColumn{Name: "EXTERNAL-IP", Wide: true}, HeaderColumn{Name: "EXTERNAL-IP", Wide: true},
HeaderColumn{Name: "PODS", Align: tview.AlignRight}, HeaderColumn{Name: "PODS", Align: tview.AlignRight},
HeaderColumn{Name: "%CPU", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "%MEM", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "CPU", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "CPU", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "MEM", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "MEM", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "%CPU", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "%MEM", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "CPU/R", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "CPU/R", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "CPU/L", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "CPU/L", Align: tview.AlignRight, MX: true},
HeaderColumn{Name: "MEM/R", Align: tview.AlignRight, MX: true}, HeaderColumn{Name: "MEM/R", Align: tview.AlignRight, MX: true},
@ -63,7 +63,6 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
if !ok { if !ok {
return fmt.Errorf("Expected *NodeAndMetrics, but got %T", o) return fmt.Errorf("Expected *NodeAndMetrics, but got %T", o)
} }
meta, ok := oo.Raw.Object["metadata"].(map[string]interface{}) meta, ok := oo.Raw.Object["metadata"].(map[string]interface{})
if !ok { if !ok {
return fmt.Errorf("Unable to extract meta") return fmt.Errorf("Unable to extract meta")
@ -96,9 +95,8 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
tlm.Add(*lList.Memory()) tlm.Add(*lList.Memory())
} }
} }
res := newResources(newResourceList(trc, trm), newResourceList(tlc, tlm))
statuses := make(sort.StringSlice, 10) statuses := make(sort.StringSlice, 10)
status(no.Status, no.Spec.Unschedulable, statuses) status(no.Status.Conditions, no.Spec.Unschedulable, statuses)
sort.Sort(statuses) sort.Sort(statuses)
roles := make(sort.StringSlice, 10) roles := make(sort.StringSlice, 10)
nodeRoles(&no, roles) nodeRoles(&no, roles)
@ -114,16 +112,16 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
iIP, iIP,
eIP, eIP,
strconv.Itoa(len(oo.Pods)), strconv.Itoa(len(oo.Pods)),
toMc(c.cpu),
toMi(c.mem),
strconv.Itoa(p.rCPU()), strconv.Itoa(p.rCPU()),
strconv.Itoa(p.rMEM()), strconv.Itoa(p.rMEM()),
toMc(c.rCPU().MilliValue()), toMcPerc(trc.MilliValue(), a.cpu),
toMi(c.rMEM().Value()), toMcPerc(tlc.MilliValue(), a.cpu),
toMcPerc(res.rCPU(), a.rCPU()), toMiPerc(trm.Value(), a.mem),
toMcPerc(res.lCPU(), a.rCPU()), toMiPerc(tlm.Value(), a.mem),
toMiPerc(res.rMEM(), a.rMEM()), toMc(a.cpu),
toMiPerc(res.lMEM(), a.rMEM()), toMi(a.mem),
toMc(a.rCPU().MilliValue()),
toMi(a.rMEM().Value()),
mapToStr(no.Labels), mapToStr(no.Labels),
asStatus(n.diagnose(statuses)), asStatus(n.diagnose(statuses)),
toAge(no.ObjectMeta.CreationTimestamp), toAge(no.ObjectMeta.CreationTimestamp),
@ -177,19 +175,24 @@ func (n *NodeWithMetrics) DeepCopyObject() runtime.Object {
return n return n
} }
func gatherNodeMX(no *v1.Node, mx *mv1beta1.NodeMetrics) (resources, percentages, resources) { type metric struct {
c, p, a := newResources(nil, nil), newPercentages(), newResources(no.Status.Allocatable, nil) cpu, mem int64
}
func gatherNodeMX(no *v1.Node, mx *mv1beta1.NodeMetrics) (metric, percentages, metric) {
c := metric{cpu: 0, mem: 0}
p := newPercentages()
a := metric{
cpu: no.Status.Allocatable.Cpu().MilliValue(),
mem: no.Status.Allocatable.Memory().Value(),
}
if mx == nil { if mx == nil {
return c, p, a return c, p, a
} }
c[requestCPU], c[requestMEM] = mx.Usage.Cpu(), mx.Usage.Memory() c.cpu, c.mem = mx.Usage.Cpu().MilliValue(), mx.Usage.Memory().Value()
if a.rCPU() != nil { p[requestCPU] = client.ToPercentage(c.cpu, a.cpu)
p[requestCPU] = percentMc(c.rCPU(), a.rCPU()) p[requestMEM] = client.ToPercentage(c.mem, a.mem)
}
if a.rMEM() != nil {
p[requestMEM] = percentMi(c.rMEM(), a.rMEM())
}
return c, p, a return c, p, a
} }
@ -230,11 +233,11 @@ func getIPs(addrs []v1.NodeAddress) (iIP, eIP string) {
return return
} }
func status(status v1.NodeStatus, exempt bool, res []string) { func status(conds []v1.NodeCondition, exempt bool, res []string) {
var index int var index int
conditions := make(map[v1.NodeConditionType]*v1.NodeCondition) conditions := make(map[v1.NodeConditionType]*v1.NodeCondition, len(conds))
for n := range status.Conditions { for n := range conds {
cond := status.Conditions[n] cond := conds[n]
conditions[cond.Type] = &cond conditions[cond.Type] = &cond
} }

View File

@ -12,7 +12,7 @@ import (
func TestNodeRender(t *testing.T) { func TestNodeRender(t *testing.T) {
pom := render.NodeWithMetrics{ pom := render.NodeWithMetrics{
Raw: load(t, "no"), Raw: load(t, "no"),
MX: makeNodeMX("n1", "10m", "10Mi"), MX: makeNodeMX("n1", "10m", "20Mi"),
} }
var no render.Node var no render.Node
@ -21,7 +21,7 @@ func TestNodeRender(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "minikube", r.ID) assert.Equal(t, "minikube", r.ID)
e := render.Fields{"minikube", "Ready", "master", "v1.15.2", "4.15.0", "192.168.64.107", "<none>", "0", "0", "0", "10", "10", "0 (0%)", "0 (0%)", "0 (0%)", "0 (0%)", "4,000", "7,874"} e := render.Fields{"minikube", "Ready", "master", "v1.15.2", "4.15.0", "192.168.64.107", "<none>", "0", "10", "20", "0", "0", "0 (0%)", "0 (0%)", "0 (0%)", "0 (0%)", "4000", "7874"}
assert.Equal(t, e, r.Fields[:18]) assert.Equal(t, e, r.Fields[:18])
} }

View File

@ -218,13 +218,12 @@ func (*Pod) gatherPodMX(pod *v1.Pod, mx *mv1beta1.PodMetrics) (resources, percen
if rList.Cpu() != nil { if rList.Cpu() != nil {
p[requestCPU] = percentMc(c.rCPU(), rList.Cpu()) p[requestCPU] = percentMc(c.rCPU(), rList.Cpu())
} }
if rList.Memory() != nil {
p[requestMEM] = percentMi(c.rMEM(), rList.Memory())
}
if lList.Cpu() != nil { if lList.Cpu() != nil {
p[limitCPU] = percentMc(c.rCPU(), lList.Cpu()) p[limitCPU] = percentMc(c.rCPU(), lList.Cpu())
} }
if rList.Memory() != nil {
p[requestMEM] = percentMi(c.rMEM(), rList.Memory())
}
if lList.Memory() != nil { if lList.Memory() != nil {
p[limitMEM] = percentMi(c.rMEM(), lList.Memory()) p[limitMEM] = percentMi(c.rMEM(), lList.Memory())
} }

View File

@ -46,7 +46,7 @@ type PromptModel interface {
ClearText(fire bool) ClearText(fire bool)
// Notify notifies all listener of current suggestions. // Notify notifies all listener of current suggestions.
Notify() Notify(bool)
// AddListener registers a command listener. // AddListener registers a command listener.
AddListener(model.BuffWatcher) AddListener(model.BuffWatcher)
@ -178,7 +178,7 @@ func (p *Prompt) InCmdMode() bool {
func (p *Prompt) activate() { func (p *Prompt) activate() {
p.SetCursorIndex(len(p.model.GetText())) p.SetCursorIndex(len(p.model.GetText()))
p.write(p.model.GetText(), "") p.write(p.model.GetText(), "")
p.model.Notify() p.model.Notify(false)
} }
func (p *Prompt) update(s string) { func (p *Prompt) update(s string) {

View File

@ -98,6 +98,8 @@ func (l *Log) Init(ctx context.Context) (err error) {
l.model.AddListener(l) l.model.AddListener(l)
l.updateTitle() l.updateTitle()
l.model.ToggleShowTimestamp(l.app.Config.K9s.Logger.ShowTime)
return nil return nil
} }
@ -130,12 +132,12 @@ func (l *Log) LogChanged(lines [][]byte) {
// BufferCompleted indicates input was accepted. // BufferCompleted indicates input was accepted.
func (l *Log) BufferCompleted(s string) { func (l *Log) BufferCompleted(s string) {
l.model.Filter(l.logs.cmdBuff.GetText()) l.model.Filter(s)
l.updateTitle() l.updateTitle()
} }
// BufferChanged indicates the buffer was changed. // BufferChanged indicates the buffer was changed.
func (l *Log) BufferChanged(s string) {} func (l *Log) BufferChanged(string) {}
// BufferActive indicates the buff activity changed. // BufferActive indicates the buff activity changed.
func (l *Log) BufferActive(state bool, k model.BufferKind) { func (l *Log) BufferActive(state bool, k model.BufferKind) {
@ -284,9 +286,7 @@ func (l *Log) Flush(lines [][]byte) {
func (l *Log) sinceCmd(a int) func(evt *tcell.EventKey) *tcell.EventKey { func (l *Log) sinceCmd(a int) func(evt *tcell.EventKey) *tcell.EventKey {
return func(evt *tcell.EventKey) *tcell.EventKey { return func(evt *tcell.EventKey) *tcell.EventKey {
opts := l.model.LogOptions() l.model.SetSinceSeconds(int64(a))
opts.SinceSeconds = int64(a)
l.model.SetLogOptions(opts)
l.updateTitle() l.updateTitle()
return nil return nil
} }
@ -305,7 +305,7 @@ func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
} }
// SaveCmd dumps the logs to file. // SaveCmd dumps the logs to file.
func (l *Log) SaveCmd(evt *tcell.EventKey) *tcell.EventKey { func (l *Log) SaveCmd(*tcell.EventKey) *tcell.EventKey {
if path, err := saveData(l.app.Config.K9s.CurrentCluster, l.model.GetPath(), l.logs.GetText(true)); err != nil { if path, err := saveData(l.app.Config.K9s.CurrentCluster, l.model.GetPath(), l.logs.GetText(true)); err != nil {
l.app.Flash().Err(err) l.app.Flash().Err(err)
} else { } else {
@ -314,7 +314,7 @@ func (l *Log) SaveCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil return nil
} }
func (l *Log) cpCmd(evt *tcell.EventKey) *tcell.EventKey { func (l *Log) cpCmd(*tcell.EventKey) *tcell.EventKey {
l.app.Flash().Info("Content copied to clipboard...") l.app.Flash().Info("Content copied to clipboard...")
if err := clipboard.WriteAll(l.logs.GetText(true)); err != nil { if err := clipboard.WriteAll(l.logs.GetText(true)); err != nil {
l.app.Flash().Err(err) l.app.Flash().Err(err)
@ -378,7 +378,7 @@ func (l *Log) toggleTimestampCmd(evt *tcell.EventKey) *tcell.EventKey {
} }
l.indicator.ToggleTimestamp() l.indicator.ToggleTimestamp()
l.model.Refresh() l.model.ToggleShowTimestamp(l.indicator.showTime)
return nil return nil
} }