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() {
|
||||
const secFmt = "%-15s "
|
||||
const sectionFmt = "%-15s "
|
||||
|
||||
printLogo(printer.ColorCyan)
|
||||
printTuple(secFmt, "Configuration", config.K9sConfigFile)
|
||||
printTuple(secFmt, "Logs", config.K9sLogs)
|
||||
printTuple(sectionFmt, "Configuration", config.K9sConfigFile)
|
||||
printTuple(sectionFmt, "Logs", config.K9sLogs)
|
||||
printTuple(sectionFmt, "Screen Dumps", config.K9sDumpDir)
|
||||
}
|
||||
|
||||
func printLogo(color int) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ var (
|
|||
K9sConfigFile = filepath.Join(K9sHome, "config.yml")
|
||||
// K9sLogs represents K9s log.
|
||||
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 (
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
const (
|
||||
splashTime = 1
|
||||
devMode = "dev"
|
||||
clusterRefresh = time.Duration(15 * time.Second)
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
@ -154,6 +155,20 @@ func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags)
|
|||
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() {
|
||||
if a.stopCh != nil {
|
||||
close(a.stopCh)
|
||||
|
|
@ -229,6 +244,10 @@ func (a *appView) stylesUpdater(ctx context.Context) error {
|
|||
|
||||
// Run starts the application loop
|
||||
func (a *appView) Run() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go a.clusterUpdater(ctx)
|
||||
|
||||
// Only enable skin updater while in dev mode.
|
||||
if a.version == devMode && a.hasSkins {
|
||||
var ctx context.Context
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ type benchView struct {
|
|||
*tview.Pages
|
||||
|
||||
app *appView
|
||||
current igniter
|
||||
cancel context.CancelFunc
|
||||
selectedItem string
|
||||
selectedRow int
|
||||
|
|
@ -48,7 +47,6 @@ func newBenchView(_ string, app *appView, _ resource.List) resourceViewer {
|
|||
Pages: tview.NewPages(),
|
||||
actions: make(keyActions),
|
||||
app: app,
|
||||
current: app.content.GetPrimitive("main").(igniter),
|
||||
}
|
||||
|
||||
tv := newTableView(app, benchTitle)
|
||||
|
|
@ -119,7 +117,6 @@ func (v *benchView) selChanged(r, c int) {
|
|||
}
|
||||
v.selectedRow = r
|
||||
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 {
|
||||
|
|
@ -209,7 +206,6 @@ func (v *benchView) hydrate() resource.TableData {
|
|||
}
|
||||
|
||||
dir := filepath.Join(K9sBenchDir, v.app.config.K9s.CurrentCluster)
|
||||
log.Debug().Msgf("----> DIR %s", dir)
|
||||
ff, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Reading bench dir")
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ func forwardColorer(string, *resource.RowEvent) tcell.Color {
|
|||
return tcell.ColorSkyblue
|
||||
}
|
||||
|
||||
func dumpColorer(ns string, r *resource.RowEvent) tcell.Color {
|
||||
return tcell.ColorNavajoWhite
|
||||
}
|
||||
|
||||
func benchColorer(ns string, r *resource.RowEvent) tcell.Color {
|
||||
c := tcell.ColorPaleGreen
|
||||
|
||||
|
|
|
|||
|
|
@ -62,12 +62,6 @@ func (c *command) run(cmd string) bool {
|
|||
case cmd == "?", cmd == "help":
|
||||
c.app.inject(newHelpView(c.app))
|
||||
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":
|
||||
c.app.inject(newAliasView(c.app))
|
||||
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 {
|
||||
if clear {
|
||||
clearScreen()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ type forwardView struct {
|
|||
*tview.Pages
|
||||
|
||||
app *appView
|
||||
current igniter
|
||||
cancel context.CancelFunc
|
||||
bench *benchmark
|
||||
}
|
||||
|
|
@ -45,8 +44,6 @@ func newForwardView(ns string, app *appView, list resource.List) resourceViewer
|
|||
tv.colorerFn = forwardColorer
|
||||
tv.currentNS = ""
|
||||
v.AddPage("table", tv, true, true)
|
||||
|
||||
v.current = app.content.GetPrimitive("main").(igniter)
|
||||
v.registerActions()
|
||||
|
||||
return &v
|
||||
|
|
@ -248,7 +245,7 @@ func (v *forwardView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if tv.cmdBuff.isActive() {
|
||||
tv.cmdBuff.reset()
|
||||
} else {
|
||||
v.app.inject(v.current)
|
||||
v.app.inject(v.app.content.GetPrimitive("main").(igniter))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -362,7 +359,5 @@ func watchFS(ctx context.Context, app *appView, dir, file string, cb func()) err
|
|||
}
|
||||
|
||||
func benchConfig(cluster string) string {
|
||||
path := filepath.Join(config.K9sHome, config.K9sBench+"-"+cluster+".yml")
|
||||
log.Debug().Msgf("!!!!!!!!!!!!!! Loading bench config from: %s", path)
|
||||
return path
|
||||
return filepath.Join(config.K9sHome, config.K9sBench+"-"+cluster+".yml")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ func (v *logView) update() {
|
|||
// Actions...
|
||||
|
||||
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")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -125,7 +126,7 @@ func (v *logView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
now := time.Now().UnixNano()
|
||||
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
|
||||
file, err := os.OpenFile(path, mod, 0644)
|
||||
defer func() {
|
||||
|
|
|
|||
|
|
@ -303,6 +303,11 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
|||
api: "",
|
||||
viewFn: newBenchView,
|
||||
},
|
||||
"sd": {
|
||||
title: "ScreenDumps",
|
||||
api: "",
|
||||
viewFn: newDumpView,
|
||||
},
|
||||
}
|
||||
|
||||
rev, ok, err := c.SupportsRes("autoscaling", []string{"v1", "v2beta1", "v2beta2"})
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
noSelection = ""
|
||||
clusterRefresh = time.Duration(15 * time.Second)
|
||||
)
|
||||
const noSelection = ""
|
||||
|
||||
type updatable interface {
|
||||
restartUpdates()
|
||||
|
|
@ -124,20 +121,6 @@ func (v *resourceView) init(ctx context.Context, ns string) {
|
|||
}
|
||||
|
||||
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) {
|
||||
for {
|
||||
select {
|
||||
|
|
@ -164,7 +147,6 @@ func (v *resourceView) getTitle() string {
|
|||
func (v *resourceView) selChanged(r, c int) {
|
||||
v.selectedRow = r
|
||||
v.selectItem(r, c)
|
||||
v.getTV().cmdBuff.setActive(false)
|
||||
}
|
||||
|
||||
func (v *resourceView) getSelectedItem() string {
|
||||
|
|
|
|||
|
|
@ -89,9 +89,7 @@ func newTableView(app *appView, title string) *tableView {
|
|||
}
|
||||
|
||||
func (v *tableView) bindKeys() {
|
||||
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, false)
|
||||
v.actions[tcell.KeyCtrlS] = newKeyAction("Save", v.saveCmd, true)
|
||||
|
||||
v.actions[KeySlash] = newKeyAction("Filter Mode", v.activateCmd, false)
|
||||
v.actions[tcell.KeyEscape] = newKeyAction("Filter Reset", v.resetCmd, 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.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[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 (
|
||||
fullFmat = "%s-%s-%d.csv"
|
||||
noNSFmat = "%s-%d.csv"
|
||||
)
|
||||
|
||||
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")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -162,7 +159,7 @@ func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
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
|
||||
file, err := os.OpenFile(path, mod, 0644)
|
||||
defer func() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue