bug fixes + maintenance items
parent
1e179f8391
commit
970ab18637
|
|
@ -0,0 +1,15 @@
|
||||||
|
/k8s
|
||||||
|
/change_logs
|
||||||
|
.github
|
||||||
|
.semaphore
|
||||||
|
.vscode
|
||||||
|
assets
|
||||||
|
/dist
|
||||||
|
/execs
|
||||||
|
/notes
|
||||||
|
/skins
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
cov.out
|
||||||
|
/k9s
|
||||||
|
.travis.yml
|
||||||
|
|
@ -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" ]
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -90,9 +90,13 @@ func loadConfiguration() *config.Config {
|
||||||
if err := k9sCfg.Load(config.K9sConfigFile); err != nil {
|
if err := k9sCfg.Load(config.K9sConfigFile); err != nil {
|
||||||
log.Warn().Msg("Unable to locate K9s config. Generating new configuration...")
|
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 {
|
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))
|
k9sCfg.SetConnection(k8s.InitConnectionOrDie(k8sCfg, log.Logger))
|
||||||
|
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -14,7 +14,7 @@ replace (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/go-autorest/autorest v0.1.0 // indirect
|
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/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
|
||||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
|
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
|
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
|
||||||
|
|
|
||||||
5
go.sum
5
go.sum
|
|
@ -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 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
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/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/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/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=
|
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:aHe5bNiKC27qRLjjyu54Xoq6bRdtW3S0//r34rHzUbU=
|
||||||
github.com/derailed/tview v0.1.11/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
|
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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
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=
|
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/derailed/k9s/internal/k8s"
|
import (
|
||||||
|
"github.com/derailed/k9s/internal/k8s"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultRefreshRate = 2
|
defaultRefreshRate = 2
|
||||||
|
|
@ -11,6 +13,7 @@ const (
|
||||||
// K9s tracks K9s configuration options.
|
// K9s tracks K9s configuration options.
|
||||||
type K9s struct {
|
type K9s struct {
|
||||||
RefreshRate int `yaml:"refreshRate"`
|
RefreshRate int `yaml:"refreshRate"`
|
||||||
|
manualRefreshRate int
|
||||||
LogBufferSize int `yaml:"logBufferSize"`
|
LogBufferSize int `yaml:"logBufferSize"`
|
||||||
LogRequestSize int `yaml:"logRequestSize"`
|
LogRequestSize int `yaml:"logRequestSize"`
|
||||||
CurrentContext string `yaml:"currentContext"`
|
CurrentContext string `yaml:"currentContext"`
|
||||||
|
|
@ -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.
|
// ActiveCluster returns the currently active cluster.
|
||||||
func (k *K9s) ActiveCluster() *Cluster {
|
func (k *K9s) ActiveCluster() *Cluster {
|
||||||
if k.Clusters == nil {
|
if k.Clusters == nil {
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,9 @@ func (b *Base) podLogs(ctx context.Context, c chan<- string, sel map[string]stri
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(pods) > 1 {
|
||||||
|
opts.MultiPods = true
|
||||||
|
}
|
||||||
pr := NewPod(b.Connection)
|
pr := NewPod(b.Connection)
|
||||||
for _, p := range pods {
|
for _, p := range pods {
|
||||||
po := p.(*v1.Pod)
|
po := p.(*v1.Pod)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ type (
|
||||||
Lines int64
|
Lines int64
|
||||||
Previous bool
|
Previous bool
|
||||||
Color color.Paint
|
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]
|
return Truncate(strings.Join(s, "-"), 15) + "-" + tokens[len(tokens)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NormalizeName colorizes a pod name.
|
// // NormalizeName colorizes a pod name.
|
||||||
func (o LogOptions) NormalizeName() string {
|
// func (o LogOptions) NormalizeName() string {
|
||||||
if o.Color == 0 {
|
// 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 ""
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/color"
|
"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)
|
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.
|
// PodLogs tail logs for all containers in a running Pod.
|
||||||
func (r *Pod) PodLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
func (r *Pod) PodLogs(ctx context.Context, c chan<- string, opts LogOptions) error {
|
||||||
i := ctx.Value(IKey("informer")).(*watch.Informer)
|
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)
|
po := p.(*v1.Pod)
|
||||||
opts.Color = asColor(po.Name)
|
opts.Color = asColor(po.Name)
|
||||||
|
if len(po.Spec.InitContainers)+len(po.Spec.Containers) == 1 {
|
||||||
|
opts.SingleContainer = true
|
||||||
|
}
|
||||||
|
|
||||||
for _, co := range po.Spec.InitContainers {
|
for _, co := range po.Spec.InitContainers {
|
||||||
opts.Container = co.Name
|
opts.Container = co.Name
|
||||||
if err := r.Logs(ctx, c, opts); err != nil {
|
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,
|
Previous: opts.Previous,
|
||||||
}
|
}
|
||||||
req := res.Logs(opts.Namespace, opts.Name, &o)
|
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!!
|
// This call will block if nothing is in the stream!!
|
||||||
stream, err := req.Stream()
|
stream, err := req.Stream()
|
||||||
|
atomic.StoreInt32(&blocked, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Stream canceled `%s", opts.Path())
|
log.Error().Err(err).Msgf("Log stream failed for `%s", opts.Path())
|
||||||
return err
|
return fmt.Errorf("Unable to obtain log stream for %s", opts.Path())
|
||||||
}
|
}
|
||||||
|
|
||||||
// atomic.StoreInt32(&blocked, 0)
|
|
||||||
go readLogs(ctx, stream, c, opts)
|
go readLogs(ctx, stream, c, opts)
|
||||||
|
|
||||||
return nil
|
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) {
|
func readLogs(ctx context.Context, stream io.ReadCloser, c chan<- string, opts LogOptions) {
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Debug().Msgf(">>> Closing stream `%s", opts.Path())
|
log.Debug().Msgf(">>> Closing stream `%s", opts.Path())
|
||||||
stream.Close()
|
stream.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
scanner, head := bufio.NewScanner(stream), opts.NormalizeName()
|
scanner := bufio.NewScanner(stream)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case c <- head + scanner.Text():
|
|
||||||
default:
|
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
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import "github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
const maxStackSize = 10
|
const maxStackSize = 10
|
||||||
|
|
||||||
type cmdStack struct {
|
type cmdStack struct {
|
||||||
|
|
@ -24,10 +22,10 @@ func (s *cmdStack) pop() (string, bool) {
|
||||||
if s.empty() {
|
if s.empty() {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
log.Info().Msgf("Before Pop %v", s.stack)
|
|
||||||
top := s.stack[len(s.stack)-1]
|
top := s.stack[len(s.stack)-1]
|
||||||
s.stack = 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
|
return top, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +33,6 @@ func (s *cmdStack) top() (string, bool) {
|
||||||
if s.empty() {
|
if s.empty() {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
log.Info().Msgf("Top %v -- %s", s.stack, s.stack[len(s.stack)-1])
|
|
||||||
|
|
||||||
return s.stack[len(s.stack)-1], true
|
return s.stack[len(s.stack)-1], true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ func (c *command) isStdCmd(cmd string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *command) isAliasCmd(cmd string) bool {
|
func (c *command) isAliasCmd(cmd string) bool {
|
||||||
|
|
||||||
cmds := make(map[string]resCmd, 30)
|
cmds := make(map[string]resCmd, 30)
|
||||||
resourceViews(c.app.conn(), cmds)
|
resourceViews(c.app.conn(), cmds)
|
||||||
res, ok := cmds[cmd]
|
res, ok := cmds[cmd]
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,12 @@ func newDetailsView(app *appView, backFn actionHandler) *detailsView {
|
||||||
app.Draw()
|
app.Draw()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
v.bindKeys()
|
||||||
|
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *detailsView) bindKeys() {
|
||||||
v.actions = keyActions{
|
v.actions = keyActions{
|
||||||
tcell.KeyBackspace2: newKeyAction("Erase", v.eraseCmd, false),
|
tcell.KeyBackspace2: newKeyAction("Erase", v.eraseCmd, false),
|
||||||
tcell.KeyBackspace: 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.KeyTab: newKeyAction("Next Match", v.nextCmd, false),
|
||||||
tcell.KeyBacktab: newKeyAction("Previous Match", v.prevCmd, false),
|
tcell.KeyBacktab: newKeyAction("Previous Match", v.prevCmd, false),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *detailsView) setCategory(n string) {
|
func (v *detailsView) setCategory(n string) {
|
||||||
|
|
|
||||||
|
|
@ -29,20 +29,29 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
func newHelpView(app *appView, current igniter) *helpView {
|
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.SetTextColor(tcell.ColorAqua)
|
||||||
v.SetBorder(true)
|
v.SetBorder(true)
|
||||||
v.SetBorderPadding(0, 0, 1, 1)
|
v.SetBorderPadding(0, 0, 1, 1)
|
||||||
v.SetDynamicColors(true)
|
v.SetDynamicColors(true)
|
||||||
v.SetInputCapture(v.keyboard)
|
v.SetInputCapture(v.keyboard)
|
||||||
v.current = current
|
v.current = current
|
||||||
|
v.bindKeys()
|
||||||
v.actions[tcell.KeyEsc] = newKeyAction("Back", v.backCmd, true)
|
|
||||||
v.actions[tcell.KeyEnter] = newKeyAction("Back", v.backCmd, false)
|
|
||||||
|
|
||||||
return &v
|
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 {
|
func (v *helpView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
key := evt.Key()
|
key := evt.Key()
|
||||||
if key == tcell.KeyRune {
|
if key == tcell.KeyRune {
|
||||||
|
|
@ -108,7 +117,7 @@ func (v *helpView) showGeneral() {
|
||||||
{"Ctrl-r", "Refresh"},
|
{"Ctrl-r", "Refresh"},
|
||||||
{"Shift-i", "Invert Sort"},
|
{"Shift-i", "Invert Sort"},
|
||||||
{"p", "Previous resource view"},
|
{"p", "Previous resource view"},
|
||||||
{"q", "Quit"},
|
{":q", "Quit"},
|
||||||
}
|
}
|
||||||
fmt.Fprintf(v, "🏠 [aqua::b]%s\n", "General")
|
fmt.Fprintf(v, "🏠 [aqua::b]%s\n", "General")
|
||||||
for _, h := range general {
|
for _, h := range general {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func TestNewHelpView(t *testing.T) {
|
||||||
v := newHelpView(a, nil)
|
v := newHelpView(a, nil)
|
||||||
v.init(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, e, v.GetText(true))
|
||||||
assert.Equal(t, "Help", v.getTitle())
|
assert.Equal(t, "Help", v.getTitle())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ func newLogView(_ string, app *appView, backFn actionHandler) *logView {
|
||||||
v.logs.SetWrap(true)
|
v.logs.SetWrap(true)
|
||||||
v.logs.SetMaxBuffer(app.config.K9s.LogBufferSize)
|
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.status = newStatusView(app.styles)
|
||||||
v.AddItem(v.status, 1, 1, false)
|
v.AddItem(v.status, 1, 1, false)
|
||||||
v.AddItem(v.logs, 0, 1, true)
|
v.AddItem(v.logs, 0, 1, true)
|
||||||
|
|
@ -119,8 +119,8 @@ func (v *logView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *logView) logLine(line string) {
|
func (v *logView) log(lines string) {
|
||||||
fmt.Fprintln(v.ansiWriter, tview.Escape(line))
|
fmt.Fprintln(v.ansiWriter, tview.Escape(lines))
|
||||||
log.Debug().Msgf("LOG LINES %d", v.logs.GetLineCount())
|
log.Debug().Msgf("LOG LINES %d", v.logs.GetLineCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@ func (v *logView) flush(index int, buff []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v.logLine(strings.Join(buff[:index], "\n"))
|
v.log(strings.Join(buff[:index], "\n"))
|
||||||
if atomic.LoadInt32(&v.autoScroll) == 1 {
|
if atomic.LoadInt32(&v.autoScroll) == 1 {
|
||||||
v.app.QueueUpdateDraw(func() {
|
v.app.QueueUpdateDraw(func() {
|
||||||
v.update()
|
v.update()
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ import (
|
||||||
|
|
||||||
func TestAnsi(t *testing.T) {
|
func TestAnsi(t *testing.T) {
|
||||||
buff := bytes.NewBufferString("")
|
buff := bytes.NewBufferString("")
|
||||||
w := tview.ANSIWriter(buff)
|
w := tview.ANSIWriter(buff, "white", "black")
|
||||||
fmt.Fprintf(w, "[YELLOW] ok")
|
fmt.Fprintf(w, "[YELLOW] ok")
|
||||||
assert.Equal(t, "[YELLOW] ok", buff.String())
|
assert.Equal(t, "[YELLOW] ok", buff.String())
|
||||||
|
|
||||||
v := tview.NewTextView()
|
v := tview.NewTextView()
|
||||||
v.SetDynamicColors(true)
|
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]]"
|
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)
|
fmt.Fprintf(aw, s)
|
||||||
assert.Equal(t, s+"\n", v.GetText(false))
|
assert.Equal(t, s+"\n", v.GetText(false))
|
||||||
|
|
|
||||||
|
|
@ -86,13 +86,14 @@ func (v *logsView) load(container string, prevLogs bool) {
|
||||||
if err := v.doLoad(v.parent.getSelection(), container, prevLogs); err != nil {
|
if err := v.doLoad(v.parent.getSelection(), container, prevLogs); err != nil {
|
||||||
v.app.flash().err(err)
|
v.app.flash().err(err)
|
||||||
l := v.CurrentPage().Item.(*logView)
|
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
|
return
|
||||||
}
|
}
|
||||||
v.app.SetFocus(v)
|
v.app.SetFocus(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *logsView) doLoad(path, co string, prevLogs bool) error {
|
func (v *logsView) doLoad(path, co string, prevLogs bool) error {
|
||||||
|
log.Debug().Msgf("----Container %q", co)
|
||||||
v.stop()
|
v.stop()
|
||||||
|
|
||||||
l := v.CurrentPage().Item.(*logView)
|
l := v.CurrentPage().Item.(*logView)
|
||||||
|
|
@ -142,7 +143,6 @@ func updateLogs(c <-chan string, l *logView, buffSize int) {
|
||||||
l.flush(index, buff)
|
l.flush(index, buff)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("Got line %s", line)
|
|
||||||
if index < buffSize {
|
if index < buffSize {
|
||||||
buff[index] = line
|
buff[index] = line
|
||||||
index++
|
index++
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
|
|
@ -22,6 +23,7 @@ type (
|
||||||
*pageView
|
*pageView
|
||||||
|
|
||||||
currentNS string
|
currentNS string
|
||||||
|
title string
|
||||||
enterFn enterFn
|
enterFn enterFn
|
||||||
extraActionsFn func(keyActions)
|
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{
|
v := masterDetail{
|
||||||
pageView: newPageView(app),
|
pageView: newPageView(app),
|
||||||
currentNS: ns,
|
currentNS: ns,
|
||||||
|
title: title,
|
||||||
}
|
}
|
||||||
|
tv := newTableView(v.app, v.title)
|
||||||
tv := newTableView(app, title)
|
|
||||||
tv.SetSelectionChangedFunc(v.selChanged)
|
tv.SetSelectionChangedFunc(v.selChanged)
|
||||||
v.AddPage("master", tv, true, true)
|
v.AddPage("master", tv, true, true)
|
||||||
|
|
||||||
|
details := newDetailsView(v.app, backCmd)
|
||||||
|
v.AddPage("details", details, true, false)
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *masterDetail) init(ns string, backCmd actionHandler) {
|
func (v *masterDetail) init(ctx context.Context, ns string) {
|
||||||
details := newDetailsView(v.app, backCmd)
|
|
||||||
v.AddPage("details", details, true, false)
|
|
||||||
|
|
||||||
if v.currentNS != resource.NotNamespaced {
|
if v.currentNS != resource.NotNamespaced {
|
||||||
v.currentNS = ns
|
v.currentNS = ns
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package views
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
|
|
@ -150,15 +149,9 @@ func (v *podView) viewLogs(prev bool) bool {
|
||||||
if !v.rowSelected() {
|
if !v.rowSelected() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
status := trimCellRelative(v.masterPage(), v.selectedRow, 2)
|
|
||||||
if status == "Running" || status == "Completed" {
|
|
||||||
v.showLogs(v.selectedItem, "", v, prev)
|
v.showLogs(v.selectedItem, "", v, prev)
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
v.app.flash().err(errors.New("Selected pod is not running"))
|
return true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *podView) showLogs(path, co string, parent loggable, prev bool) {
|
func (v *podView) showLogs(path, co string, parent loggable, prev bool) {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func (v *policyView) init(c context.Context, ns string) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
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.refresh()
|
||||||
v.app.Draw()
|
v.app.Draw()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ func (v *rbacView) init(c context.Context, ns string) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
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.app.QueueUpdateDraw(func() {
|
||||||
v.refresh()
|
v.refresh()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,20 @@ type (
|
||||||
|
|
||||||
func newResourceView(title string, app *appView, list resource.List) resourceViewer {
|
func newResourceView(title string, app *appView, list resource.List) resourceViewer {
|
||||||
v := resourceView{
|
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
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init watches all running pods in given namespace
|
// Init watches all running pods in given namespace
|
||||||
func (v *resourceView) init(ctx context.Context, ns string) {
|
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
|
v.parentCtx = ctx
|
||||||
var vctx context.Context
|
var vctx context.Context
|
||||||
|
|
@ -69,7 +72,6 @@ func (v *resourceView) init(ctx context.Context, ns string) {
|
||||||
|
|
||||||
func (v *resourceView) setColorerFn(f colorerFn) {
|
func (v *resourceView) setColorerFn(f colorerFn) {
|
||||||
v.colorerFn = f
|
v.colorerFn = f
|
||||||
v.masterPage().setColorer(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *resourceView) setDecorateFn(f decorateFn) {
|
func (v *resourceView) setDecorateFn(f decorateFn) {
|
||||||
|
|
@ -104,7 +106,7 @@ func (v *resourceView) update(ctx context.Context) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Debug().Msgf("%s updater canceled!", v.list.GetName())
|
log.Debug().Msgf("%s updater canceled!", v.list.GetName())
|
||||||
return
|
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.app.QueueUpdateDraw(func() {
|
||||||
v.refresh()
|
v.refresh()
|
||||||
})
|
})
|
||||||
|
|
@ -113,14 +115,31 @@ func (v *resourceView) update(ctx context.Context) {
|
||||||
}(ctx)
|
}(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Actions...
|
|
||||||
|
|
||||||
func (v *resourceView) backCmd(*tcell.EventKey) *tcell.EventKey {
|
func (v *resourceView) backCmd(*tcell.EventKey) *tcell.EventKey {
|
||||||
v.switchPage("master")
|
v.switchPage("master")
|
||||||
return nil
|
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 {
|
func (v *resourceView) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
// If in command mode run filter otherwise enter function.
|
// If in command mode run filter otherwise enter function.
|
||||||
if v.masterPage().filterCmd(evt) == nil || !v.rowSelected() {
|
if v.masterPage().filterCmd(evt) == nil || !v.rowSelected() {
|
||||||
|
|
@ -163,7 +182,11 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
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)
|
yaml, err := v.list.Resource().Describe(v.masterPage().baseTitle, selection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.app.flash().errf("Describe command failed %s", err)
|
v.app.flash().errf("Describe command failed %s", err)
|
||||||
|
|
@ -285,23 +308,6 @@ func (v *resourceView) refresh() {
|
||||||
v.selectItem(v.selectedRow, 0)
|
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() {
|
func (v *resourceView) namespaceActions() {
|
||||||
if !v.list.Access(resource.NamespaceAccess) {
|
if !v.list.Access(resource.NamespaceAccess) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ func (v *subjectView) init(c context.Context, _ string) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Debug().Msgf("Subject:%s Watch bailing out!", v.subjectKind)
|
log.Debug().Msgf("Subject:%s Watch bailing out!", v.subjectKind)
|
||||||
return
|
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.refresh()
|
||||||
v.app.Draw()
|
v.app.Draw()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
func (v *tableView) sortColCmd(col int) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return func(evt *tcell.EventKey) *tcell.EventKey {
|
return func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if col == -1 {
|
v.sortCol.asc = true
|
||||||
v.sortCol.index, v.sortCol.asc = v.GetColumnCount()-1, true
|
switch col {
|
||||||
} else {
|
case -2:
|
||||||
v.sortCol.index, v.sortCol.asc = v.nameColIndex()+col, true
|
v.sortCol.index = 0
|
||||||
|
case -1:
|
||||||
|
v.sortCol.index = v.GetColumnCount() - 1
|
||||||
|
default:
|
||||||
|
v.sortCol.index = v.nameColIndex() + col
|
||||||
|
|
||||||
}
|
}
|
||||||
v.refresh()
|
v.refresh()
|
||||||
|
|
||||||
|
|
@ -214,7 +219,7 @@ func (v *tableView) adjustSorter(data resource.TableData) {
|
||||||
func (v *tableView) doUpdate(data resource.TableData) {
|
func (v *tableView) doUpdate(data resource.TableData) {
|
||||||
v.currentNS = data.Namespace
|
v.currentNS = data.Namespace
|
||||||
if v.currentNS == resource.AllNamespaces && v.currentNS != "*" {
|
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 {
|
} else {
|
||||||
delete(v.actions, KeyShiftP)
|
delete(v.actions, KeyShiftP)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue