add screen dump view + fix filtering
parent
dafc4376c8
commit
0163f9b5de
|
|
@ -0,0 +1,24 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.7.1
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Thank you to all that contributed with flushing out issues with 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 always very much appreciated!
|
||||||
|
|
||||||
|
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Change Logs
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Bugs/Features
|
||||||
|
|
||||||
|
+ [Issue #200](https://github.com/derailed/k9s/issues/200)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
@ -21,11 +21,12 @@ func infoCmd() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func printInfo() {
|
func printInfo() {
|
||||||
const secFmt = "%-15s "
|
const sectionFmt = "%-15s "
|
||||||
|
|
||||||
printLogo(printer.ColorCyan)
|
printLogo(printer.ColorCyan)
|
||||||
printTuple(secFmt, "Configuration", config.K9sConfigFile)
|
printTuple(sectionFmt, "Configuration", config.K9sConfigFile)
|
||||||
printTuple(secFmt, "Logs", config.K9sLogs)
|
printTuple(sectionFmt, "Logs", config.K9sLogs)
|
||||||
|
printTuple(sectionFmt, "Screen Dumps", config.K9sDumpDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printLogo(color int) {
|
func printLogo(color int) {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ var (
|
||||||
K9sConfigFile = filepath.Join(K9sHome, "config.yml")
|
K9sConfigFile = filepath.Join(K9sHome, "config.yml")
|
||||||
// K9sLogs represents K9s log.
|
// K9sLogs represents K9s log.
|
||||||
K9sLogs = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-%s.log", MustK9sUser()))
|
K9sLogs = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-%s.log", MustK9sUser()))
|
||||||
|
// K9sDumpDir represents a directory where K9s screen dumps will be persisted.
|
||||||
|
K9sDumpDir = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-screens-%s", MustK9sUser()))
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
const (
|
const (
|
||||||
splashTime = 1
|
splashTime = 1
|
||||||
devMode = "dev"
|
devMode = "dev"
|
||||||
|
clusterRefresh = time.Duration(15 * time.Second)
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
@ -154,6 +155,20 @@ func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags)
|
||||||
a.SetRoot(a.pages, true)
|
a.SetRoot(a.pages, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *appView) clusterUpdater(ctx context.Context) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Debug().Msg("Cluster updater canceled!")
|
||||||
|
return
|
||||||
|
case <-time.After(clusterRefresh):
|
||||||
|
a.QueueUpdateDraw(func() {
|
||||||
|
a.clusterInfoView.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *appView) startInformer() {
|
func (a *appView) startInformer() {
|
||||||
if a.stopCh != nil {
|
if a.stopCh != nil {
|
||||||
close(a.stopCh)
|
close(a.stopCh)
|
||||||
|
|
@ -229,6 +244,10 @@ func (a *appView) stylesUpdater(ctx context.Context) error {
|
||||||
|
|
||||||
// Run starts the application loop
|
// Run starts the application loop
|
||||||
func (a *appView) Run() {
|
func (a *appView) Run() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
go a.clusterUpdater(ctx)
|
||||||
|
|
||||||
// Only enable skin updater while in dev mode.
|
// Only enable skin updater while in dev mode.
|
||||||
if a.version == devMode && a.hasSkins {
|
if a.version == devMode && a.hasSkins {
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ type benchView struct {
|
||||||
*tview.Pages
|
*tview.Pages
|
||||||
|
|
||||||
app *appView
|
app *appView
|
||||||
current igniter
|
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
selectedItem string
|
selectedItem string
|
||||||
selectedRow int
|
selectedRow int
|
||||||
|
|
@ -48,7 +47,6 @@ func newBenchView(_ string, app *appView, _ resource.List) resourceViewer {
|
||||||
Pages: tview.NewPages(),
|
Pages: tview.NewPages(),
|
||||||
actions: make(keyActions),
|
actions: make(keyActions),
|
||||||
app: app,
|
app: app,
|
||||||
current: app.content.GetPrimitive("main").(igniter),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tv := newTableView(app, benchTitle)
|
tv := newTableView(app, benchTitle)
|
||||||
|
|
@ -119,7 +117,6 @@ func (v *benchView) selChanged(r, c int) {
|
||||||
}
|
}
|
||||||
v.selectedRow = r
|
v.selectedRow = r
|
||||||
v.selectedItem = strings.TrimSpace(tv.GetCell(r, 7).Text)
|
v.selectedItem = strings.TrimSpace(tv.GetCell(r, 7).Text)
|
||||||
v.getTV().cmdBuff.setActive(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *benchView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *benchView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -209,7 +206,6 @@ func (v *benchView) hydrate() resource.TableData {
|
||||||
}
|
}
|
||||||
|
|
||||||
dir := filepath.Join(K9sBenchDir, v.app.config.K9s.CurrentCluster)
|
dir := filepath.Join(K9sBenchDir, v.app.config.K9s.CurrentCluster)
|
||||||
log.Debug().Msgf("----> DIR %s", dir)
|
|
||||||
ff, err := ioutil.ReadDir(dir)
|
ff, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Reading bench dir")
|
log.Error().Err(err).Msg("Reading bench dir")
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ func forwardColorer(string, *resource.RowEvent) tcell.Color {
|
||||||
return tcell.ColorSkyblue
|
return tcell.ColorSkyblue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dumpColorer(ns string, r *resource.RowEvent) tcell.Color {
|
||||||
|
return tcell.ColorNavajoWhite
|
||||||
|
}
|
||||||
|
|
||||||
func benchColorer(ns string, r *resource.RowEvent) tcell.Color {
|
func benchColorer(ns string, r *resource.RowEvent) tcell.Color {
|
||||||
c := tcell.ColorPaleGreen
|
c := tcell.ColorPaleGreen
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,12 +62,6 @@ func (c *command) run(cmd string) bool {
|
||||||
case cmd == "?", cmd == "help":
|
case cmd == "?", cmd == "help":
|
||||||
c.app.inject(newHelpView(c.app))
|
c.app.inject(newHelpView(c.app))
|
||||||
return true
|
return true
|
||||||
case cmd == "pf":
|
|
||||||
c.app.inject(newForwardView("", c.app, nil))
|
|
||||||
return true
|
|
||||||
case cmd == "be":
|
|
||||||
c.app.inject(newBenchView("", c.app, nil))
|
|
||||||
return true
|
|
||||||
case cmd == "alias":
|
case cmd == "alias":
|
||||||
c.app.inject(newAliasView(c.app))
|
c.app.inject(newAliasView(c.app))
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,246 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
"github.com/derailed/k9s/internal/resource"
|
||||||
|
"github.com/derailed/tview"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dumpTitle = "Screen Dumps"
|
||||||
|
dumpTitleFmt = " [mediumvioletred::b]%s([fuchsia::b]%d[fuchsia::-])[mediumvioletred::-] "
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dumpHeader = resource.Row{"NAME", "AGE"}
|
||||||
|
)
|
||||||
|
|
||||||
|
type dumpView struct {
|
||||||
|
*tview.Pages
|
||||||
|
|
||||||
|
app *appView
|
||||||
|
cancel context.CancelFunc
|
||||||
|
selectedItem string
|
||||||
|
selectedRow int
|
||||||
|
actions keyActions
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDumpView(_ string, app *appView, _ resource.List) resourceViewer {
|
||||||
|
v := dumpView{
|
||||||
|
Pages: tview.NewPages(),
|
||||||
|
actions: make(keyActions),
|
||||||
|
app: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
tv := newTableView(app, dumpTitle)
|
||||||
|
{
|
||||||
|
tv.SetSelectionChangedFunc(v.selChanged)
|
||||||
|
tv.SetBorderFocusColor(tcell.ColorSteelBlue)
|
||||||
|
tv.SetSelectedStyle(tcell.ColorWhite, tcell.ColorRoyalBlue, tcell.AttrNone)
|
||||||
|
tv.colorerFn = dumpColorer
|
||||||
|
tv.currentNS = ""
|
||||||
|
}
|
||||||
|
v.AddPage("table", tv, true, true)
|
||||||
|
|
||||||
|
details := newDetailsView(app, v.backCmd)
|
||||||
|
v.AddPage("details", details, true, false)
|
||||||
|
v.registerActions()
|
||||||
|
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) setEnterFn(enterFn) {}
|
||||||
|
func (v *dumpView) setColorerFn(colorerFn) {}
|
||||||
|
func (v *dumpView) setDecorateFn(decorateFn) {}
|
||||||
|
func (v *dumpView) setExtraActionsFn(actionsFn) {}
|
||||||
|
|
||||||
|
// Init the view.
|
||||||
|
func (v *dumpView) init(ctx context.Context, _ string) {
|
||||||
|
if err := v.watchDumpDir(ctx); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Dumpdir watch failed!")
|
||||||
|
v.app.flash().errf("Unable to watch dumpmarks directory %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tv := v.getTV()
|
||||||
|
v.refresh()
|
||||||
|
tv.sortCol.index, tv.sortCol.asc = tv.nameColIndex()+1, true
|
||||||
|
tv.refresh()
|
||||||
|
tv.Select(1, 0)
|
||||||
|
v.app.SetFocus(tv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) refresh() {
|
||||||
|
tv := v.getTV()
|
||||||
|
tv.update(v.hydrate())
|
||||||
|
tv.resetTitle()
|
||||||
|
v.selChanged(v.selectedRow, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) registerActions() {
|
||||||
|
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
|
||||||
|
v.actions[tcell.KeyEnter] = newKeyAction("Enter", v.enterCmd, true)
|
||||||
|
v.actions[tcell.KeyCtrlD] = newKeyAction("Delete", v.deleteCmd, true)
|
||||||
|
|
||||||
|
vu := v.getTV()
|
||||||
|
vu.setActions(v.actions)
|
||||||
|
v.app.setHints(vu.hints())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) getTitle() string {
|
||||||
|
return dumpTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) selChanged(r, c int) {
|
||||||
|
log.Debug().Msgf("Selection changed %d:%c", r, c)
|
||||||
|
tv := v.getTV()
|
||||||
|
if r == 0 || tv.GetCell(r, 0) == nil {
|
||||||
|
v.selectedItem = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.selectedRow = r
|
||||||
|
v.selectedItem = strings.TrimSpace(tv.GetCell(r, 0).Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
return func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
tv := v.getTV()
|
||||||
|
tv.sortCol.index, tv.sortCol.asc = tv.nameColIndex()+col, asc
|
||||||
|
tv.refresh()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if v.getTV().cmdBuff.isActive() {
|
||||||
|
return v.getTV().filterCmd(evt)
|
||||||
|
}
|
||||||
|
sel := v.selectedItem
|
||||||
|
if sel == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster)
|
||||||
|
if !run(true, v.app, filepath.Join(dir, sel)) {
|
||||||
|
log.Error().Msg("Failed to launch editor")
|
||||||
|
v.app.flash().err(errors.New("Failed to launch editor"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
sel := v.selectedItem
|
||||||
|
if sel == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster)
|
||||||
|
showModal(v.Pages, fmt.Sprintf("Deleting `%s are you sure?", sel), "table", func() {
|
||||||
|
if err := os.Remove(filepath.Join(dir, sel)); err != nil {
|
||||||
|
v.app.flash().errf("Unable to delete file %s", err)
|
||||||
|
log.Error().Err(err).Msg("Delete failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.refresh()
|
||||||
|
v.app.flash().infof("ScreenDump file %s deleted!", sel)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if v.cancel != nil {
|
||||||
|
v.cancel()
|
||||||
|
}
|
||||||
|
v.SwitchToPage("table")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) hints() hints {
|
||||||
|
return v.CurrentPage().Item.(hinter).hints()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) hydrate() resource.TableData {
|
||||||
|
data := resource.TableData{
|
||||||
|
Header: dumpHeader,
|
||||||
|
Rows: make(resource.RowEvents, 10),
|
||||||
|
Namespace: resource.NotNamespaced,
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster)
|
||||||
|
ff, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Reading dump dir")
|
||||||
|
v.app.flash().errf("Unable to read dump directory %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range ff {
|
||||||
|
fields := resource.Row{f.Name(), time.Since(f.ModTime()).String()}
|
||||||
|
data.Rows[f.Name()] = &resource.RowEvent{
|
||||||
|
Action: resource.New,
|
||||||
|
Fields: fields,
|
||||||
|
Deltas: fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) resetTitle() {
|
||||||
|
v.SetTitle(fmt.Sprintf(dumpTitleFmt, dumpTitle, v.getTV().GetRowCount()-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) watchDumpDir(ctx context.Context) error {
|
||||||
|
w, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case evt := <-w.Events:
|
||||||
|
log.Debug().Msgf("Dump event %#v", evt)
|
||||||
|
v.app.QueueUpdateDraw(func() {
|
||||||
|
v.refresh()
|
||||||
|
})
|
||||||
|
case err := <-w.Errors:
|
||||||
|
log.Info().Err(err).Msg("Dir Watcher failed")
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Debug().Msg("!!!! FS WATCHER DONE!!")
|
||||||
|
w.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return w.Add(filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) getTV() *tableView {
|
||||||
|
if vu, ok := v.GetPrimitive("table").(*tableView); ok {
|
||||||
|
return vu
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *dumpView) getDetails() *detailsView {
|
||||||
|
if vu, ok := v.GetPrimitive("details").(*detailsView); ok {
|
||||||
|
return vu
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,21 @@ func runK(clear bool, app *appView, args ...string) bool {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func run(clear bool, app *appView, args ...string) bool {
|
||||||
|
bin, err := exec.LookPath(os.Getenv("EDITOR"))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msgf("Unable to find editor command in path %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.Suspend(func() {
|
||||||
|
if err := execute(clear, bin, args...); err != nil {
|
||||||
|
log.Error().Msgf("Command exited: %T %v %v", err, err, args)
|
||||||
|
app.flash().errf("Command exited: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func execute(clear bool, bin string, args ...string) error {
|
func execute(clear bool, bin string, args ...string) error {
|
||||||
if clear {
|
if clear {
|
||||||
clearScreen()
|
clearScreen()
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ type forwardView struct {
|
||||||
*tview.Pages
|
*tview.Pages
|
||||||
|
|
||||||
app *appView
|
app *appView
|
||||||
current igniter
|
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
bench *benchmark
|
bench *benchmark
|
||||||
}
|
}
|
||||||
|
|
@ -45,8 +44,6 @@ func newForwardView(ns string, app *appView, list resource.List) resourceViewer
|
||||||
tv.colorerFn = forwardColorer
|
tv.colorerFn = forwardColorer
|
||||||
tv.currentNS = ""
|
tv.currentNS = ""
|
||||||
v.AddPage("table", tv, true, true)
|
v.AddPage("table", tv, true, true)
|
||||||
|
|
||||||
v.current = app.content.GetPrimitive("main").(igniter)
|
|
||||||
v.registerActions()
|
v.registerActions()
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
|
|
@ -248,7 +245,7 @@ func (v *forwardView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if tv.cmdBuff.isActive() {
|
if tv.cmdBuff.isActive() {
|
||||||
tv.cmdBuff.reset()
|
tv.cmdBuff.reset()
|
||||||
} else {
|
} else {
|
||||||
v.app.inject(v.current)
|
v.app.inject(v.app.content.GetPrimitive("main").(igniter))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -362,7 +359,5 @@ func watchFS(ctx context.Context, app *appView, dir, file string, cb func()) err
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchConfig(cluster string) string {
|
func benchConfig(cluster string) string {
|
||||||
path := filepath.Join(config.K9sHome, config.K9sBench+"-"+cluster+".yml")
|
return filepath.Join(config.K9sHome, config.K9sBench+"-"+cluster+".yml")
|
||||||
log.Debug().Msgf("!!!!!!!!!!!!!! Loading bench config from: %s", path)
|
|
||||||
return path
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@ func (v *logView) update() {
|
||||||
// Actions...
|
// Actions...
|
||||||
|
|
||||||
func (v *logView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *logView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if err := os.MkdirAll(K9sDump, 0744); err != nil {
|
dir := filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster)
|
||||||
|
if err := os.MkdirAll(dir, 0744); err != nil {
|
||||||
log.Error().Err(err).Msgf("Mkdir K9s dump")
|
log.Error().Err(err).Msgf("Mkdir K9s dump")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +126,7 @@ func (v *logView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
now := time.Now().UnixNano()
|
now := time.Now().UnixNano()
|
||||||
fName := fmt.Sprintf("%s-%d.log", strings.Replace(v.path, "/", "-", -1), now)
|
fName := fmt.Sprintf("%s-%d.log", strings.Replace(v.path, "/", "-", -1), now)
|
||||||
|
|
||||||
path := filepath.Join(K9sDump, fName)
|
path := filepath.Join(dir, fName)
|
||||||
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||||
file, err := os.OpenFile(path, mod, 0644)
|
file, err := os.OpenFile(path, mod, 0644)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,11 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
||||||
api: "",
|
api: "",
|
||||||
viewFn: newBenchView,
|
viewFn: newBenchView,
|
||||||
},
|
},
|
||||||
|
"sd": {
|
||||||
|
title: "ScreenDumps",
|
||||||
|
api: "",
|
||||||
|
viewFn: newDumpView,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
rev, ok, err := c.SupportsRes("autoscaling", []string{"v1", "v2beta1", "v2beta2"})
|
rev, ok, err := c.SupportsRes("autoscaling", []string{"v1", "v2beta1", "v2beta2"})
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,7 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const noSelection = ""
|
||||||
noSelection = ""
|
|
||||||
clusterRefresh = time.Duration(15 * time.Second)
|
|
||||||
)
|
|
||||||
|
|
||||||
type updatable interface {
|
type updatable interface {
|
||||||
restartUpdates()
|
restartUpdates()
|
||||||
|
|
@ -124,20 +121,6 @@ func (v *resourceView) init(ctx context.Context, ns string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *resourceView) update(ctx context.Context) {
|
func (v *resourceView) update(ctx context.Context) {
|
||||||
go func(ctx context.Context) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
log.Debug().Msgf("%s cluster updater canceled!", v.list.GetName())
|
|
||||||
return
|
|
||||||
case <-time.After(clusterRefresh):
|
|
||||||
v.app.QueueUpdateDraw(func() {
|
|
||||||
v.app.clusterInfoView.refresh()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(ctx)
|
|
||||||
|
|
||||||
go func(ctx context.Context) {
|
go func(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -164,7 +147,6 @@ func (v *resourceView) getTitle() string {
|
||||||
func (v *resourceView) selChanged(r, c int) {
|
func (v *resourceView) selChanged(r, c int) {
|
||||||
v.selectedRow = r
|
v.selectedRow = r
|
||||||
v.selectItem(r, c)
|
v.selectItem(r, c)
|
||||||
v.getTV().cmdBuff.setActive(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *resourceView) getSelectedItem() string {
|
func (v *resourceView) getSelectedItem() string {
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,7 @@ func newTableView(app *appView, title string) *tableView {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *tableView) bindKeys() {
|
func (v *tableView) bindKeys() {
|
||||||
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, false)
|
|
||||||
v.actions[tcell.KeyCtrlS] = newKeyAction("Save", v.saveCmd, true)
|
v.actions[tcell.KeyCtrlS] = newKeyAction("Save", v.saveCmd, true)
|
||||||
|
|
||||||
v.actions[KeySlash] = newKeyAction("Filter Mode", v.activateCmd, false)
|
v.actions[KeySlash] = newKeyAction("Filter Mode", v.activateCmd, false)
|
||||||
v.actions[tcell.KeyEscape] = newKeyAction("Filter Reset", v.resetCmd, false)
|
v.actions[tcell.KeyEscape] = newKeyAction("Filter Reset", v.resetCmd, false)
|
||||||
v.actions[tcell.KeyEnter] = newKeyAction("Filter", v.filterCmd, false)
|
v.actions[tcell.KeyEnter] = newKeyAction("Filter", v.filterCmd, false)
|
||||||
|
|
@ -100,6 +98,7 @@ func (v *tableView) bindKeys() {
|
||||||
v.actions[tcell.KeyBackspace] = newKeyAction("Erase", v.eraseCmd, false)
|
v.actions[tcell.KeyBackspace] = newKeyAction("Erase", v.eraseCmd, false)
|
||||||
v.actions[tcell.KeyDelete] = newKeyAction("Erase", v.eraseCmd, false)
|
v.actions[tcell.KeyDelete] = newKeyAction("Erase", v.eraseCmd, false)
|
||||||
|
|
||||||
|
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, false)
|
||||||
v.actions[KeyShiftN] = newKeyAction("Sort Name", v.sortColCmd(0), true)
|
v.actions[KeyShiftN] = newKeyAction("Sort Name", v.sortColCmd(0), true)
|
||||||
v.actions[KeyShiftA] = newKeyAction("Sort Age", v.sortColCmd(-1), true)
|
v.actions[KeyShiftA] = newKeyAction("Sort Age", v.sortColCmd(-1), true)
|
||||||
}
|
}
|
||||||
|
|
@ -139,16 +138,14 @@ func (v *tableView) setSelection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// K9sDump represents a directory where K9s artifacts will be persisted.
|
|
||||||
var K9sDump = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-screens-%s", config.MustK9sUser()))
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
fullFmat = "%s-%s-%d.csv"
|
fullFmat = "%s-%s-%d.csv"
|
||||||
noNSFmat = "%s-%d.csv"
|
noNSFmat = "%s-%d.csv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if err := os.MkdirAll(K9sDump, 0744); err != nil {
|
dir := filepath.Join(config.K9sDumpDir, v.app.config.K9s.CurrentCluster)
|
||||||
|
if err := os.MkdirAll(dir, 0744); err != nil {
|
||||||
log.Error().Err(err).Msgf("Mkdir K9s dump")
|
log.Error().Err(err).Msgf("Mkdir K9s dump")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +159,7 @@ func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
fName = fmt.Sprintf(noNSFmat, v.baseTitle, now)
|
fName = fmt.Sprintf(noNSFmat, v.baseTitle, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(K9sDump, fName)
|
path := filepath.Join(dir, fName)
|
||||||
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||||
file, err := os.OpenFile(path, mod, 0644)
|
file, err := os.OpenFile(path, mod, 0644)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue