Commit 8f9ade33 authored by derailed's avatar derailed
Browse files

added popeye viewer

Showing with 284 additions and 64 deletions
+284 -64
......@@ -122,7 +122,7 @@ K9s uses aliases to navigate most K8s resources.
|-----------------------|----------------------------------------------------|----------------------------|
| `:`alias`<ENTER>` | View a Kubernetes resource | `:po<ENTER>` |
| `?` | Show keyboard shortcuts and help | |
| `A` | Show all available resource alias | select+`<ENTER>` to view |
| `Ctrl-a` | Show all available resource alias | select+`<ENTER>` to view |
| `/`filter`ENTER`> | Filter out a resource view given a filter | `/bumblebeetuna` |
| `<Esc>` | Bails out of command mode | |
| `d`,`v`, `e`, `l`,... | Key mapping to describe, view, edit, view logs,... | `d` (describes a resource) |
......
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.4.7
## 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.
Thank you so much for your support and awesome suggestions to make K9s better!!
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
---
## Change Logs
### Popeye Support
Managing and operating a cluster is the wild is hard and getting harder.
I've created [Popeye](https://github.com/derailed/popeye) to help with
cluster sanitation and best practices. Since K9s folks are so
awesome, you're getting a sneak peek! I figured why not integrate it with K9s?
No need to install yet another CLI right? Provided I did not mess this up too much,
you should now be able to use command mod `:popeye` to access Popeye sanitizer reports within
K9s and scan your clusters. You can read more about it [here](https://medium.com/@fernand.galiana/k8s-clusters-oh-biff-em-popeye-637e9312963)
and if you like so give it a clap or two ;)
NOTE: In a K9s environment, if you'd like to specify a spinach config file, you must set it in your $HOME/.k9s/spinach.yml.
NOTE: There is a bit more that need to be done on this integration to be complete. Please file an issue if something does not work as expected.
NOTE: Popeye will run it's own course and K9s is just a viewer for it, so if you'd like additional sanitation or find Popeye related issues, please tune to the corresponding repo!
Let us know if you dig it? And share your before/after clusters scores!
---
## Resolved Bugs
+ Great find! Thank you @swe-covis! Moved alias view to `Ctrl-A` [Issue #156](https://github.com/derailed/k9s/issues/156)
+ Added toggle autoscroll via `s` key [Issue #155](https://github.com/derailed/k9s/issues/155)
---
<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)
module github.com/derailed/k9s
replace github.com/derailed/popeye => /Users/fernand/go_wk/derailed/src/github.com/derailed/popeye
require (
contrib.go.opencensus.io/exporter/ocagent v0.4.3 // indirect
github.com/Azure/go-autorest v11.4.0+incompatible // indirect
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
contrib.go.opencensus.io/exporter/ocagent v0.4.11 // indirect
github.com/derailed/popeye v0.1.2
github.com/derailed/tview v0.1.4
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.13.1 // indirect
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/evanphx/json-patch v4.1.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/gdamore/tcell v1.1.1
github.com/go-openapi/spec v0.19.0 // indirect
github.com/gogo/protobuf v1.1.1 // indirect
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v1.1.5 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/json-iterator/go v1.1.6 // indirect
github.com/mattn/go-runewidth v0.0.4
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/onsi/gomega v1.4.3 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 // indirect
github.com/rs/zerolog v1.12.0
github.com/russross/blackfriday v2.0.0+incompatible // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.4.0 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/stretchr/testify v1.2.2
go.opencensus.io v0.19.0 // indirect
golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd // indirect
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.2.2
k8s.io/api v0.0.0-20190202010724-74b699b93c15
k8s.io/apiextensions-apiserver v0.0.0-20190322231200-1c09d17c1352 // indirect
k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467
k8s.io/apiserver v0.0.0-20190322225753-5eae03fa38e7 // indirect
k8s.io/cli-runtime v0.0.0-20190207094101-a32b78e5dd0a
k8s.io/client-go v10.0.0+incompatible
k8s.io/component-base v0.0.0-20190313120452-4727f38490bc // indirect
k8s.io/klog v0.1.0 // indirect
k8s.io/kube-openapi v0.0.0-20190215190454-ea82251f3668 // indirect
k8s.io/klog v0.2.0 // indirect
k8s.io/kubernetes v1.13.3
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f
k8s.io/utils v0.0.0-20190212002617-cdba02414f76 // indirect
sigs.k8s.io/structured-merge-diff v0.0.0-20190130003954-e5e029740eb8 // indirect
sigs.k8s.io/structured-merge-diff v0.0.0-20190404181321-646549c5a231 // indirect
sigs.k8s.io/yaml v1.1.0
)
This diff is collapsed.
......@@ -87,7 +87,7 @@ func NewApp(cfg *config.Config) *appView {
v.actions[tcell.KeyCtrlR] = newKeyAction("Redraw", v.redrawCmd, false)
v.actions[tcell.KeyCtrlC] = newKeyAction("Quit", v.quitCmd, false)
v.actions[KeyHelp] = newKeyAction("Help", v.helpCmd, false)
v.actions[KeyShiftA] = newKeyAction("Aliases", v.aliasCmd, true)
v.actions[tcell.KeyCtrlA] = newKeyAction("Aliases", v.aliasCmd, true)
v.actions[tcell.KeyEscape] = newKeyAction("Exit Cmd", v.deactivateCmd, false)
v.actions[tcell.KeyEnter] = newKeyAction("Goto", v.gotoCmd, false)
v.actions[tcell.KeyBackspace2] = newKeyAction("Erase", v.eraseCmd, false)
......
......@@ -62,6 +62,9 @@ func (c *command) run(cmd string) bool {
case cmd == "alias":
c.app.inject(newAliasView(c.app))
return true
case cmd == "popeye":
c.app.inject(newPopeyeView(c.app))
return true
case policyMatcher.MatchString(cmd):
tokens := policyMatcher.FindAllStringSubmatch(cmd, -1)
if len(tokens) == 1 && len(tokens[0]) == 3 {
......
......@@ -28,9 +28,11 @@ func newLogView(title string, parent loggable) *logView {
return &v
}
func (l *logView) logLine(line string) {
func (l *logView) logLine(line string, scroll bool) {
fmt.Fprintln(l.ansiWriter, tview.Escape(line))
l.ScrollToEnd()
if scroll {
l.ScrollToEnd()
}
}
func (l *logView) log(lines fmt.Stringer) {
......
......@@ -26,6 +26,7 @@ type logsView struct {
containers []string
actions keyActions
cancelFunc context.CancelFunc
autoScroll bool
}
func newLogsView(pview string, parent loggable) *logsView {
......@@ -33,15 +34,17 @@ func newLogsView(pview string, parent loggable) *logsView {
Pages: tview.NewPages(),
parent: parent,
parentView: pview,
autoScroll: true,
containers: []string{},
}
v.setActions(keyActions{
tcell.KeyEscape: {description: "Back", action: v.back, visible: true},
KeyC: {description: "Clear", action: v.clearLogs, visible: true},
KeyG: {description: "Top", action: v.top, visible: false},
KeyShiftG: {description: "Bottom", action: v.bottom, visible: false},
KeyF: {description: "Up", action: v.pageUp, visible: false},
KeyB: {description: "Down", action: v.pageDown, visible: false},
tcell.KeyEscape: {description: "Back", action: v.backCmd, visible: true},
KeyC: {description: "Clear", action: v.clearCmd, visible: true},
KeyS: {description: "Toggle AutoScroll", action: v.toggleScrollCmd, visible: true},
KeyG: {description: "Top", action: v.topCmd, visible: false},
KeyShiftG: {description: "Bottom", action: v.bottomCmd, visible: false},
KeyF: {description: "Up", action: v.pageUpCmd, visible: false},
KeyB: {description: "Down", action: v.pageDownCmd, visible: false},
})
v.SetInputCapture(v.keyboard)
......@@ -104,7 +107,7 @@ func (v *logsView) addContainer(n string) {
l := newLogView(n, v.parent)
{
l.SetInputCapture(v.keyboard)
l.backFn = v.back
l.backFn = v.backCmd
}
v.AddPage(n, l, true, false)
}
......@@ -134,7 +137,7 @@ func (v *logsView) load(i int) {
if err := v.doLoad(v.parent.getSelection(), v.containers[i]); err != nil {
v.parent.appView().flash(flashErr, err.Error())
l := v.CurrentPage().Item.(*logView)
l.logLine("😂 Doh! No logs are available at this time. Check again later on...")
l.logLine("😂 Doh! No logs are available at this time. Check again later on...", false)
return
}
v.parent.appView().SetFocus(v)
......@@ -152,10 +155,12 @@ func (v *logsView) doLoad(path, co string) error {
select {
case line, ok := <-c:
if !ok {
l.ScrollToEnd()
if v.autoScroll {
l.ScrollToEnd()
}
return
}
l.logLine(line)
l.logLine(line, v.autoScroll)
}
}
}()
......@@ -179,14 +184,25 @@ func (v *logsView) doLoad(path, co string) error {
// ----------------------------------------------------------------------------
// Actions...
func (v *logsView) back(evt *tcell.EventKey) *tcell.EventKey {
func (v *logsView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
v.autoScroll = !v.autoScroll
if v.autoScroll {
v.parent.appView().flash(flashInfo, "Autoscroll is on")
} else {
v.parent.appView().flash(flashInfo, "Autoscroll is off")
}
return nil
}
func (v *logsView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
v.stop()
v.parent.switchPage(v.parentView)
return nil
}
func (v *logsView) top(evt *tcell.EventKey) *tcell.EventKey {
func (v *logsView) topCmd(evt *tcell.EventKey) *tcell.EventKey {
if p := v.CurrentPage(); p != nil {
v.parent.appView().flash(flashInfo, "Top of logs...")
p.Item.(*logView).ScrollToBeginning()
......@@ -195,7 +211,7 @@ func (v *logsView) top(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
func (v *logsView) bottom(*tcell.EventKey) *tcell.EventKey {
func (v *logsView) bottomCmd(*tcell.EventKey) *tcell.EventKey {
if p := v.CurrentPage(); p != nil {
v.parent.appView().flash(flashInfo, "Bottom of logs...")
p.Item.(*logView).ScrollToEnd()
......@@ -204,7 +220,7 @@ func (v *logsView) bottom(*tcell.EventKey) *tcell.EventKey {
return nil
}
func (v *logsView) pageUp(*tcell.EventKey) *tcell.EventKey {
func (v *logsView) pageUpCmd(*tcell.EventKey) *tcell.EventKey {
if p := v.CurrentPage(); p != nil {
if p.Item.(*logView).PageUp() {
v.parent.appView().flash(flashInfo, "Reached Top ...")
......@@ -214,7 +230,7 @@ func (v *logsView) pageUp(*tcell.EventKey) *tcell.EventKey {
return nil
}
func (v *logsView) pageDown(*tcell.EventKey) *tcell.EventKey {
func (v *logsView) pageDownCmd(*tcell.EventKey) *tcell.EventKey {
if p := v.CurrentPage(); p != nil {
if p.Item.(*logView).PageDown() {
v.parent.appView().flash(flashInfo, "Reached Bottom ...")
......@@ -224,7 +240,7 @@ func (v *logsView) pageDown(*tcell.EventKey) *tcell.EventKey {
return nil
}
func (v *logsView) clearLogs(*tcell.EventKey) *tcell.EventKey {
func (v *logsView) clearCmd(*tcell.EventKey) *tcell.EventKey {
if p := v.CurrentPage(); p != nil {
v.parent.appView().flash(flashInfo, "Clearing logs...")
p.Item.(*logView).Clear()
......
package views
import (
"context"
"io"
"os"
"path/filepath"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/popeye/pkg"
cfg "github.com/derailed/popeye/pkg/config"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
type popeyeView struct {
*detailsView
current igniter
ansiWriter io.Writer
}
func newPopeyeView(app *appView) *popeyeView {
v := popeyeView{}
{
v.detailsView = newDetailsView(app, v.backCmd)
v.SetBorderPadding(0, 0, 1, 1)
v.current = app.content.GetPrimitive("main").(igniter)
v.SetDynamicColors(true)
v.SetWrap(true)
v.setTitle("Popeye")
v.ansiWriter = tview.ANSIWriter(v)
}
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
return &v
}
func (v *popeyeView) init(ctx context.Context, ns string) {
c := cfg.New()
spinach := filepath.Join(config.K9sHome, "spinach.yml")
if _, err := os.Stat(spinach); err == nil {
c.Spinach = spinach
}
if v.app.config.K9s.CurrentContext != "" {
v.app.flags.Context = &v.app.config.K9s.CurrentContext
}
if err := c.Init(v.app.flags); err != nil {
log.Error().Err(err).Msg("Unable to load spinach config")
}
p := pkg.NewPopeye(c, v.ansiWriter)
p.Sanitize()
}
func (v *popeyeView) getTitle() string {
return "Popeye"
}
func (v *popeyeView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
v.app.command.previousCmd()
v.app.inject(v.current)
return nil
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment