bug fixes + maintenance items

mine
derailed 2019-07-11 23:47:03 -06:00
parent 1e179f8391
commit 970ab18637
25 changed files with 259 additions and 109 deletions

15
.dockerignore Normal file
View File

@ -0,0 +1,15 @@
/k8s
/change_logs
.github
.semaphore
.vscode
assets
/dist
/execs
/notes
/skins
README.md
LICENSE
cov.out
/k9s
.travis.yml

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
# Build...
FROM golang:1.12.6-alpine AS build
WORKDIR /k9s
COPY go.mod go.sum main.go Makefile ./
COPY internal internal
COPY cmd cmd
RUN apk --no-cache add make git gcc libc-dev curl && make build
# -----------------------------------------------------------------------------
# Build Image...
FROM alpine:3.10.0
COPY --from=build /k9s/execs/k9s /bin/k9s
ENTRYPOINT [ "/bin/k9s" ]

View File

@ -0,0 +1,31 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.7.12
## 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
Maintenance release. Just code clean up and bug fixes.
---
## Resolved Bugs/Features
+ [Issue #259](https://github.com/derailed/k9s/issues/259)
+ [Issue #258](https://github.com/derailed/k9s/issues/258)
+ [Issue #256](https://github.com/derailed/k9s/issues/256)
+ [Issue #255](https://github.com/derailed/k9s/issues/255)
+ [Issue #252](https://github.com/derailed/k9s/issues/252)
+ [Issue #250](https://github.com/derailed/k9s/issues/250)
+ [Issue #246](https://github.com/derailed/k9s/issues/246)
---
<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)

View File

@ -90,9 +90,13 @@ func loadConfiguration() *config.Config {
if err := k9sCfg.Load(config.K9sConfigFile); err != nil {
log.Warn().Msg("Unable to locate K9s config. Generating new configuration...")
}
k9sCfg.K9s.RefreshRate = refreshRate
if refreshRate != defaultRefreshRate {
k9sCfg.K9s.OverrideRefreshRate(refreshRate)
}
if err := k9sCfg.Refine(k8sFlags); err != nil {
log.Panic().Err(err).Msg("Refine Config")
log.Panic().Err(err).Msg("Unable to locate K8s cluster configuration")
}
k9sCfg.SetConnection(k8s.InitConnectionOrDie(k8sCfg, log.Logger))

2
go.mod
View File

@ -14,7 +14,7 @@ replace (
require (
github.com/Azure/go-autorest/autorest v0.1.0 // indirect
github.com/derailed/tview v0.1.11
github.com/derailed/tview v0.1.12
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect

5
go.sum
View File

@ -22,6 +22,7 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -35,10 +36,10 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/derailed/tview v0.1.10 h1:QWjK82ccTl3C7Tfyfmv765eRqEt/T3aXp40464cfnlw=
github.com/derailed/tview v0.1.10/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
github.com/derailed/tview v0.1.11 h1:aHe5bNiKC27qRLjjyu54Xoq6bRdtW3S0//r34rHzUbU=
github.com/derailed/tview v0.1.11/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
github.com/derailed/tview v0.1.12 h1:EVTzx+Mq3PJzbGnCDwUVu5fD479mbQU/0rabxTm7tHA=
github.com/derailed/tview v0.1.12/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=

View File

@ -1,6 +1,8 @@
package config
import "github.com/derailed/k9s/internal/k8s"
import (
"github.com/derailed/k9s/internal/k8s"
)
const (
defaultRefreshRate = 2
@ -10,13 +12,14 @@ const (
// K9s tracks K9s configuration options.
type K9s struct {
RefreshRate int `yaml:"refreshRate"`
LogBufferSize int `yaml:"logBufferSize"`
LogRequestSize int `yaml:"logRequestSize"`
CurrentContext string `yaml:"currentContext"`
CurrentCluster string `yaml:"currentCluster"`
Clusters map[string]*Cluster `yaml:"clusters,omitempty"`
Aliases map[string]string `yaml:"aliases,omitempty"`
RefreshRate int `yaml:"refreshRate"`
manualRefreshRate int
LogBufferSize int `yaml:"logBufferSize"`
LogRequestSize int `yaml:"logRequestSize"`
CurrentContext string `yaml:"currentContext"`
CurrentCluster string `yaml:"currentCluster"`
Clusters map[string]*Cluster `yaml:"clusters,omitempty"`
Aliases map[string]string `yaml:"aliases,omitempty"`
}
// NewK9s create a new K9s configuration.
@ -30,6 +33,21 @@ func NewK9s() *K9s {
}
}
// OverrideRefreshRate set the refresh rate manually.
func (k *K9s) OverrideRefreshRate(r int) {
k.manualRefreshRate = r
}
// GetRefreshRate returns the current refresh rate.
func (k *K9s) GetRefreshRate() int {
rate := k.RefreshRate
if k.manualRefreshRate != 0 {
rate = k.manualRefreshRate
}
return rate
}
// ActiveCluster returns the currently active cluster.
func (k *K9s) ActiveCluster() *Cluster {
if k.Clusters == nil {

View File

@ -189,6 +189,9 @@ func (b *Base) podLogs(ctx context.Context, c chan<- string, sel map[string]stri
return err
}
if len(pods) > 1 {
opts.MultiPods = true
}
pr := NewPod(b.Connection)
for _, p := range pods {
po := p.(*v1.Pod)

View File

@ -16,9 +16,11 @@ type (
LogOptions struct {
Fqn
Lines int64
Previous bool
Color color.Paint
Lines int64
Previous bool
Color color.Paint
SingleContainer bool
MultiPods bool
}
)
@ -50,10 +52,37 @@ func (o LogOptions) FixedSizeName() string {
return Truncate(strings.Join(s, "-"), 15) + "-" + tokens[len(tokens)-1]
}
// NormalizeName colorizes a pod name.
func (o LogOptions) NormalizeName() string {
if o.Color == 0 {
// // NormalizeName colorizes a pod name.
// func (o LogOptions) NormalizeName() string {
// if o.Color == 0 {
// return ""
// }
// return color.Colorize(o.Name+":"+o.Container+" ", o.Color)
// // return o.Name + ":" + o.Container + " "
// }
func colorize(c color.Paint, txt string) string {
if c == 0 {
return ""
}
return color.Colorize(o.Name+":"+o.Container+" ", o.Color)
return color.Colorize(txt, c)
}
// DecorateLog add a log header to display po/co information along with the log message.
func (o LogOptions) DecorateLog(msg string) string {
if msg == "" {
return msg
}
if o.MultiPods {
return colorize(o.Color, o.Name+":"+o.Container+" ") + msg
}
if !o.SingleContainer {
return colorize(o.Color, o.Container+" ") + msg
}
return msg
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"io"
"strconv"
"sync/atomic"
"time"
"github.com/derailed/k9s/internal/color"
@ -117,14 +118,6 @@ func (r *Pod) Containers(path string, includeInit bool) ([]string, error) {
return r.Resource.(k8s.Loggable).Containers(ns, po, includeInit)
}
func asColor(n string) color.Paint {
var sum int
for _, r := range n {
sum += int(r)
}
return color.Paint(30 + 1 + sum%6)
}
// PodLogs tail logs for all containers in a running Pod.
func (r *Pod) PodLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
i := ctx.Value(IKey("informer")).(*watch.Informer)
@ -135,6 +128,10 @@ func (r *Pod) PodLogs(ctx context.Context, c chan<- string, opts LogOptions) err
po := p.(*v1.Pod)
opts.Color = asColor(po.Name)
if len(po.Spec.InitContainers)+len(po.Spec.Containers) == 1 {
opts.SingleContainer = true
}
for _, co := range po.Spec.InitContainers {
opts.Container = co.Name
if err := r.Logs(ctx, c, opts); err != nil {
@ -175,35 +172,47 @@ func tailLogs(ctx context.Context, res k8s.Loggable, c chan<- string, opts LogOp
Previous: opts.Previous,
}
req := res.Logs(opts.Namespace, opts.Name, &o)
req.Context(ctx)
ctxt, cancelFunc := context.WithCancel(ctx)
req.Context(ctxt)
var blocked int32 = 1
go logsTimeout(cancelFunc, &blocked)
// This call will block if nothing is in the stream!!
stream, err := req.Stream()
atomic.StoreInt32(&blocked, 0)
if err != nil {
log.Error().Err(err).Msgf("Stream canceled `%s", opts.Path())
return err
log.Error().Err(err).Msgf("Log stream failed for `%s", opts.Path())
return fmt.Errorf("Unable to obtain log stream for %s", opts.Path())
}
// atomic.StoreInt32(&blocked, 0)
go readLogs(ctx, stream, c, opts)
return nil
}
func logsTimeout(cancel context.CancelFunc, blocked *int32) {
select {
case <-time.After(defaultTimeout):
if atomic.LoadInt32(blocked) == 1 {
log.Debug().Msg("Timed out reading the log stream")
cancel()
}
}
}
func readLogs(ctx context.Context, stream io.ReadCloser, c chan<- string, opts LogOptions) {
defer func() {
log.Debug().Msgf(">>> Closing stream `%s", opts.Path())
stream.Close()
}()
scanner, head := bufio.NewScanner(stream), opts.NormalizeName()
scanner := bufio.NewScanner(stream)
for scanner.Scan() {
select {
case <-ctx.Done():
return
case c <- head + scanner.Text():
default:
// Ensures we get back to scanning
c <- opts.DecorateLog(scanner.Text())
}
}
}
@ -464,3 +473,13 @@ func (r *Pod) loggableContainers(s v1.PodStatus) []string {
}
return rcos
}
// Helpers..
func asColor(n string) color.Paint {
var sum int
for _, r := range n {
sum += int(r)
}
return color.Paint(30 + 2 + sum%6)
}

View File

@ -1,7 +1,5 @@
package views
import "github.com/rs/zerolog/log"
const maxStackSize = 10
type cmdStack struct {
@ -24,10 +22,10 @@ func (s *cmdStack) pop() (string, bool) {
if s.empty() {
return "", false
}
log.Info().Msgf("Before Pop %v", s.stack)
top := s.stack[len(s.stack)-1]
s.stack = s.stack[:len(s.stack)-1]
log.Info().Msgf("After Pop %v", s.stack)
return top, true
}
@ -35,7 +33,6 @@ func (s *cmdStack) top() (string, bool) {
if s.empty() {
return "", false
}
log.Info().Msgf("Top %v -- %s", s.stack, s.stack[len(s.stack)-1])
return s.stack[len(s.stack)-1], true
}

View File

@ -70,7 +70,6 @@ func (c *command) isStdCmd(cmd string) bool {
}
func (c *command) isAliasCmd(cmd string) bool {
cmds := make(map[string]resCmd, 30)
resourceViews(c.app.conn(), cmds)
res, ok := cmds[cmd]

View File

@ -62,6 +62,12 @@ func newDetailsView(app *appView, backFn actionHandler) *detailsView {
app.Draw()
})
v.bindKeys()
return &v
}
func (v *detailsView) bindKeys() {
v.actions = keyActions{
tcell.KeyBackspace2: newKeyAction("Erase", v.eraseCmd, false),
tcell.KeyBackspace: newKeyAction("Erase", v.eraseCmd, false),
@ -70,8 +76,6 @@ func newDetailsView(app *appView, backFn actionHandler) *detailsView {
tcell.KeyTab: newKeyAction("Next Match", v.nextCmd, false),
tcell.KeyBacktab: newKeyAction("Previous Match", v.prevCmd, false),
}
return &v
}
func (v *detailsView) setCategory(n string) {

View File

@ -29,20 +29,29 @@ type (
)
func newHelpView(app *appView, current igniter) *helpView {
v := helpView{TextView: tview.NewTextView(), app: app, actions: make(keyActions)}
v := helpView{
TextView: tview.NewTextView(),
app: app,
actions: make(keyActions),
}
v.SetTextColor(tcell.ColorAqua)
v.SetBorder(true)
v.SetBorderPadding(0, 0, 1, 1)
v.SetDynamicColors(true)
v.SetInputCapture(v.keyboard)
v.current = current
v.actions[tcell.KeyEsc] = newKeyAction("Back", v.backCmd, true)
v.actions[tcell.KeyEnter] = newKeyAction("Back", v.backCmd, false)
v.bindKeys()
return &v
}
func (v *helpView) bindKeys() {
v.actions = keyActions{
tcell.KeyEsc: newKeyAction("Back", v.backCmd, true),
tcell.KeyEnter: newKeyAction("Back", v.backCmd, false),
}
}
func (v *helpView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
key := evt.Key()
if key == tcell.KeyRune {
@ -108,7 +117,7 @@ func (v *helpView) showGeneral() {
{"Ctrl-r", "Refresh"},
{"Shift-i", "Invert Sort"},
{"p", "Previous resource view"},
{"q", "Quit"},
{":q", "Quit"},
}
fmt.Fprintf(v, "🏠 [aqua::b]%s\n", "General")
for _, h := range general {

View File

@ -43,7 +43,7 @@ func TestNewHelpView(t *testing.T) {
v := newHelpView(a, nil)
v.init(nil, "")
const e = "🏠 General\n :<cmd> Command mode\n /<term> Filter mode\n esc Clear filter\n tab Next term match\n backtab Previous term match\n Ctrl-r Refresh\n Shift-i Invert Sort\n p Previous resource view\n q Quit\n\n🤖 View Navigation\n g Goto Top\n G Goto Bottom\n Ctrl-b Page Down\n Ctrl-f Page Up\n h Left\n l Right\n k Up\n j Down\n\n😱 Help\n ? Help\n Ctrl-a Aliases view\n"
const e = "🏠 General\n :<cmd> Command mode\n /<term> Filter mode\n esc Clear filter\n tab Next term match\n backtab Previous term match\n Ctrl-r Refresh\n Shift-i Invert Sort\n p Previous resource view\n :q Quit\n\n🤖 View Navigation\n g Goto Top\n G Goto Bottom\n Ctrl-b Page Down\n Ctrl-f Page Up\n h Left\n l Right\n k Up\n j Down\n\n😱 Help\n ? Help\n Ctrl-a Aliases view\n"
assert.Equal(t, e, v.GetText(true))
assert.Equal(t, "Help", v.getTitle())
}

View File

@ -66,7 +66,7 @@ func newLogView(_ string, app *appView, backFn actionHandler) *logView {
v.logs.SetWrap(true)
v.logs.SetMaxBuffer(app.config.K9s.LogBufferSize)
}
v.ansiWriter = tview.ANSIWriter(v.logs)
v.ansiWriter = tview.ANSIWriter(v.logs, app.styles.Views().Log.FgColor, app.styles.Views().Log.BgColor)
v.status = newStatusView(app.styles)
v.AddItem(v.status, 1, 1, false)
v.AddItem(v.logs, 0, 1, true)
@ -119,8 +119,8 @@ func (v *logView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
return evt
}
func (v *logView) logLine(line string) {
fmt.Fprintln(v.ansiWriter, tview.Escape(line))
func (v *logView) log(lines string) {
fmt.Fprintln(v.ansiWriter, tview.Escape(lines))
log.Debug().Msgf("LOG LINES %d", v.logs.GetLineCount())
}
@ -129,7 +129,7 @@ func (v *logView) flush(index int, buff []string) {
return
}
v.logLine(strings.Join(buff[:index], "\n"))
v.log(strings.Join(buff[:index], "\n"))
if atomic.LoadInt32(&v.autoScroll) == 1 {
v.app.QueueUpdateDraw(func() {
v.update()

View File

@ -14,13 +14,13 @@ import (
func TestAnsi(t *testing.T) {
buff := bytes.NewBufferString("")
w := tview.ANSIWriter(buff)
w := tview.ANSIWriter(buff, "white", "black")
fmt.Fprintf(w, "[YELLOW] ok")
assert.Equal(t, "[YELLOW] ok", buff.String())
v := tview.NewTextView()
v.SetDynamicColors(true)
aw := tview.ANSIWriter(v)
aw := tview.ANSIWriter(v, "white", "black")
s := "[2019-03-27T15:05:15,246][INFO ][o.e.c.r.a.AllocationService] [es-0] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.monitoring-es-6-2019.03.27][0]]"
fmt.Fprintf(aw, s)
assert.Equal(t, s+"\n", v.GetText(false))

View File

@ -86,13 +86,14 @@ func (v *logsView) load(container string, prevLogs bool) {
if err := v.doLoad(v.parent.getSelection(), container, prevLogs); err != nil {
v.app.flash().err(err)
l := v.CurrentPage().Item.(*logView)
l.logLine("😂 Doh! No logs are available at this time. Check again later on...")
l.log("😂 Doh! No logs are available at this time. Check again later on...")
return
}
v.app.SetFocus(v)
}
func (v *logsView) doLoad(path, co string, prevLogs bool) error {
log.Debug().Msgf("----Container %q", co)
v.stop()
l := v.CurrentPage().Item.(*logView)
@ -142,7 +143,6 @@ func updateLogs(c <-chan string, l *logView, buffSize int) {
l.flush(index, buff)
return
}
log.Debug().Msgf("Got line %s", line)
if index < buffSize {
buff[index] = line
index++

View File

@ -1,6 +1,7 @@
package views
import (
"context"
"path"
"github.com/derailed/k9s/internal/resource"
@ -22,6 +23,7 @@ type (
*pageView
currentNS string
title string
enterFn enterFn
extraActionsFn func(keyActions)
}
@ -35,23 +37,23 @@ func newPageView(app *appView) *pageView {
}
}
func newMasterDetail(title string, app *appView, ns string) *masterDetail {
func newMasterDetail(title, ns string, app *appView, backCmd actionHandler) *masterDetail {
v := masterDetail{
pageView: newPageView(app),
currentNS: ns,
title: title,
}
tv := newTableView(app, title)
tv := newTableView(v.app, v.title)
tv.SetSelectionChangedFunc(v.selChanged)
v.AddPage("master", tv, true, true)
details := newDetailsView(v.app, backCmd)
v.AddPage("details", details, true, false)
return &v
}
func (v *masterDetail) init(ns string, backCmd actionHandler) {
details := newDetailsView(v.app, backCmd)
v.AddPage("details", details, true, false)
func (v *masterDetail) init(ctx context.Context, ns string) {
if v.currentNS != resource.NotNamespaced {
v.currentNS = ns
}

View File

@ -2,7 +2,6 @@ package views
import (
"context"
"errors"
"fmt"
"github.com/derailed/k9s/internal/resource"
@ -150,15 +149,9 @@ func (v *podView) viewLogs(prev bool) bool {
if !v.rowSelected() {
return false
}
v.showLogs(v.selectedItem, "", v, prev)
status := trimCellRelative(v.masterPage(), v.selectedRow, 2)
if status == "Running" || status == "Completed" {
v.showLogs(v.selectedItem, "", v, prev)
return true
}
v.app.flash().err(errors.New("Selected pod is not running"))
return false
return true
}
func (v *podView) showLogs(path, co string, parent loggable, prev bool) {

View File

@ -56,7 +56,7 @@ func (v *policyView) init(c context.Context, ns string) {
select {
case <-ctx.Done():
return
case <-time.After(time.Duration(v.app.config.K9s.RefreshRate) * time.Second):
case <-time.After(time.Duration(v.app.config.K9s.GetRefreshRate()) * time.Second):
v.refresh()
v.app.Draw()
}

View File

@ -101,7 +101,7 @@ func (v *rbacView) init(c context.Context, ns string) {
select {
case <-ctx.Done():
return
case <-time.After(time.Duration(v.app.config.K9s.RefreshRate) * time.Second):
case <-time.After(time.Duration(v.app.config.K9s.GetRefreshRate()) * time.Second):
v.app.QueueUpdateDraw(func() {
v.refresh()
})

View File

@ -34,17 +34,20 @@ type (
func newResourceView(title string, app *appView, list resource.List) resourceViewer {
v := resourceView{
masterDetail: newMasterDetail(title, app, list.GetNamespace()),
list: list,
list: list,
}
v.masterPage().setFilterFn(v.filterResource)
v.masterDetail = newMasterDetail(title, list.GetNamespace(), app, v.backCmd)
return &v
}
// Init watches all running pods in given namespace
func (v *resourceView) init(ctx context.Context, ns string) {
v.masterDetail.init(ns, v.backCmd)
v.masterDetail.init(ctx, ns)
v.masterPage().setFilterFn(v.filterResource)
if v.colorerFn != nil {
v.masterPage().setColorer(v.colorerFn)
}
v.parentCtx = ctx
var vctx context.Context
@ -69,7 +72,6 @@ func (v *resourceView) init(ctx context.Context, ns string) {
func (v *resourceView) setColorerFn(f colorerFn) {
v.colorerFn = f
v.masterPage().setColorer(f)
}
func (v *resourceView) setDecorateFn(f decorateFn) {
@ -104,7 +106,7 @@ func (v *resourceView) update(ctx context.Context) {
case <-ctx.Done():
log.Debug().Msgf("%s updater canceled!", v.list.GetName())
return
case <-time.After(time.Duration(v.app.config.K9s.RefreshRate) * time.Second):
case <-time.After(time.Duration(v.app.config.K9s.GetRefreshRate()) * time.Second):
v.app.QueueUpdateDraw(func() {
v.refresh()
})
@ -113,14 +115,31 @@ func (v *resourceView) update(ctx context.Context) {
}(ctx)
}
// ----------------------------------------------------------------------------
// Actions...
func (v *resourceView) backCmd(*tcell.EventKey) *tcell.EventKey {
v.switchPage("master")
return nil
}
func (v *resourceView) switchPage(p string) {
log.Debug().Msgf("Switching page to %s", p)
if _, ok := v.CurrentPage().Item.(*tableView); ok {
v.stopUpdates()
}
v.SwitchToPage(p)
v.currentNS = v.list.GetNamespace()
if vu, ok := v.GetPrimitive(p).(hinter); ok {
v.app.setHints(vu.hints())
}
if _, ok := v.CurrentPage().Item.(*tableView); ok {
v.restartUpdates()
}
}
// ----------------------------------------------------------------------------
// Actions...
func (v *resourceView) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
// If in command mode run filter otherwise enter function.
if v.masterPage().filterCmd(evt) == nil || !v.rowSelected() {
@ -163,7 +182,11 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
func (v *resourceView) defaultEnter(ns, resource, selection string) {
func (v *resourceView) defaultEnter(ns, _, selection string) {
if !v.list.Access(resource.DescribeAccess) {
return
}
yaml, err := v.list.Resource().Describe(v.masterPage().baseTitle, selection)
if err != nil {
v.app.flash().errf("Describe command failed %s", err)
@ -285,23 +308,6 @@ func (v *resourceView) refresh() {
v.selectItem(v.selectedRow, 0)
}
func (v *resourceView) switchPage(p string) {
log.Debug().Msgf("Switching page to %s", p)
if _, ok := v.CurrentPage().Item.(*tableView); ok {
v.stopUpdates()
}
v.SwitchToPage(p)
v.currentNS = v.list.GetNamespace()
if vu, ok := v.GetPrimitive(p).(hinter); ok {
v.app.setHints(vu.hints())
}
if _, ok := v.CurrentPage().Item.(*tableView); ok {
v.restartUpdates()
}
}
func (v *resourceView) namespaceActions() {
if !v.list.Access(resource.NamespaceAccess) {
return

View File

@ -69,7 +69,7 @@ func (v *subjectView) init(c context.Context, _ string) {
case <-ctx.Done():
log.Debug().Msgf("Subject:%s Watch bailing out!", v.subjectKind)
return
case <-time.After(time.Duration(v.app.config.K9s.RefreshRate) * time.Second):
case <-time.After(time.Duration(v.app.config.K9s.GetRefreshRate()) * time.Second):
v.refresh()
v.app.Draw()
}

View File

@ -116,10 +116,15 @@ func (v *tableView) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
func (v *tableView) sortColCmd(col int) func(evt *tcell.EventKey) *tcell.EventKey {
return func(evt *tcell.EventKey) *tcell.EventKey {
if col == -1 {
v.sortCol.index, v.sortCol.asc = v.GetColumnCount()-1, true
} else {
v.sortCol.index, v.sortCol.asc = v.nameColIndex()+col, true
v.sortCol.asc = true
switch col {
case -2:
v.sortCol.index = 0
case -1:
v.sortCol.index = v.GetColumnCount() - 1
default:
v.sortCol.index = v.nameColIndex() + col
}
v.refresh()
@ -214,7 +219,7 @@ func (v *tableView) adjustSorter(data resource.TableData) {
func (v *tableView) doUpdate(data resource.TableData) {
v.currentNS = data.Namespace
if v.currentNS == resource.AllNamespaces && v.currentNS != "*" {
v.actions[KeyShiftP] = newKeyAction("Sort Namespace", v.sortColCmd(0), true)
v.actions[KeyShiftP] = newKeyAction("Sort Namespace", v.sortColCmd(-2), true)
} else {
delete(v.actions, KeyShiftP)
}