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 {
|
||||
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
2
go.mod
|
|
@ -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
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/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=
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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++
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue