reworked pulses view
parent
6bc84c2304
commit
3212a751ea
|
|
@ -79,7 +79,7 @@ linters-settings:
|
|||
# exclude: /path/to/file.txt
|
||||
|
||||
funlen:
|
||||
lines: 60
|
||||
lines: 65
|
||||
statements: 40
|
||||
|
||||
govet:
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ func infoCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
func printInfo() {
|
||||
const sectionFmt = "%-15s "
|
||||
const fmat = "%-25s %s\n"
|
||||
|
||||
printLogo(color.Cyan)
|
||||
printTuple(sectionFmt, "Configuration", config.K9sConfigFile, color.Cyan)
|
||||
printTuple(sectionFmt, "Logs", config.K9sLogs, color.Cyan)
|
||||
printTuple(sectionFmt, "Screen Dumps", config.K9sDumpDir, color.Cyan)
|
||||
printTuple(fmat, "Configuration", config.K9sConfigFile, color.Cyan)
|
||||
printTuple(fmat, "Logs", config.K9sLogs, color.Cyan)
|
||||
printTuple(fmat, "Screen Dumps", config.K9sDumpDir, color.Cyan)
|
||||
}
|
||||
|
||||
func printLogo(c color.Paint) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func versionCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
func printVersion(short bool) {
|
||||
const secFmt = "%-10s "
|
||||
const fmat = "%-20s %s\n"
|
||||
var outputColor color.Paint
|
||||
|
||||
if short {
|
||||
|
|
@ -34,15 +34,15 @@ func printVersion(short bool) {
|
|||
outputColor = color.Cyan
|
||||
printLogo(outputColor)
|
||||
}
|
||||
printTuple(secFmt, "Version", version, outputColor)
|
||||
printTuple(secFmt, "Commit", commit, outputColor)
|
||||
printTuple(secFmt, "Date", date, outputColor)
|
||||
printTuple(fmat, "Version", version, outputColor)
|
||||
printTuple(fmat, "Commit", commit, outputColor)
|
||||
printTuple(fmat, "Date", date, outputColor)
|
||||
}
|
||||
|
||||
func printTuple(format, section, value string, outputColor color.Paint) {
|
||||
func printTuple(fmat, section, value string, outputColor color.Paint) {
|
||||
if outputColor != -1 {
|
||||
section = color.Colorize(fmt.Sprintf(section+":"), outputColor)
|
||||
value = color.Colorize(value, color.White)
|
||||
fmt.Printf(fmat, color.Colorize(section+":", outputColor), color.Colorize(value, color.White))
|
||||
return
|
||||
}
|
||||
fmt.Println(fmt.Sprintf(format, section), value)
|
||||
fmt.Printf(fmat, section, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ const (
|
|||
type Component struct {
|
||||
*tview.Box
|
||||
|
||||
bgColor, noColor tcell.Color
|
||||
seriesColors []tcell.Color
|
||||
dimmed tcell.Style
|
||||
id, legend string
|
||||
blur func(tcell.Key)
|
||||
mx sync.RWMutex
|
||||
bgColor, noColor tcell.Color
|
||||
focusFgColor, focusBgColor string
|
||||
seriesColors []tcell.Color
|
||||
dimmed tcell.Style
|
||||
id, legend string
|
||||
blur func(tcell.Key)
|
||||
mx sync.RWMutex
|
||||
}
|
||||
|
||||
// NewComponent returns a new component.
|
||||
|
|
@ -36,6 +37,11 @@ func NewComponent(id string) *Component {
|
|||
}
|
||||
}
|
||||
|
||||
// SetFocusColorNames sets the focus color names.
|
||||
func (c *Component) SetFocusColorNames(fg, bg string) {
|
||||
c.focusFgColor, c.focusBgColor = fg, bg
|
||||
}
|
||||
|
||||
// SetBackgroundColor sets the graph bg color.
|
||||
func (c *Component) SetBackgroundColor(color tcell.Color) {
|
||||
c.Box.SetBackgroundColor(color)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,28 @@ package tchart
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
||||
var dots = []rune{' ', '⠂', '▤', '▥'}
|
||||
|
||||
const (
|
||||
b = ' '
|
||||
h = tview.BoxDrawingsHeavyHorizontal
|
||||
v = tview.BoxDrawingsHeavyVertical
|
||||
tl = tview.BoxDrawingsHeavyDownAndRight
|
||||
tr = tview.BoxDrawingsHeavyDownAndLeft
|
||||
bl = tview.BoxDrawingsHeavyUpAndRight
|
||||
br = tview.BoxDrawingsHeavyUpAndLeft
|
||||
teeL = tview.BoxDrawingsHeavyVerticalAndLeft
|
||||
teeR = tview.BoxDrawingsHeavyVerticalAndRight
|
||||
lh = '\u2578'
|
||||
rh = '\u257a'
|
||||
hv = '\u2579'
|
||||
lv = '\u257b'
|
||||
)
|
||||
|
||||
// Segment represents a dial segment.
|
||||
type Segment []int
|
||||
|
||||
|
|
@ -33,6 +51,9 @@ func NewDotMatrix(row, col int) DotMatrix {
|
|||
|
||||
// Print prints the matrix.
|
||||
func (d DotMatrix) Print(n int) Matrix {
|
||||
if d.row == d.col {
|
||||
return To3x3Char(n)
|
||||
}
|
||||
m := make(Matrix, d.row)
|
||||
segs := asSegments(n)
|
||||
for row := 0; row < d.row; row++ {
|
||||
|
|
@ -71,9 +92,9 @@ func asSegments(n int) Segment {
|
|||
}
|
||||
}
|
||||
|
||||
// CharFor return a char based on row/col.
|
||||
// CharFor returns a char based on row/col.
|
||||
func (s Segment) CharFor(row, col int) rune {
|
||||
c := ' '
|
||||
c := dots[0]
|
||||
segs := ToSegments(row, col)
|
||||
if segs == nil {
|
||||
return c
|
||||
|
|
@ -110,3 +131,69 @@ var segs = map[int][][]int{
|
|||
func ToSegments(row, col int) []int {
|
||||
return segs[row][col]
|
||||
}
|
||||
|
||||
// To3x3Char returns 3x3 number matrix
|
||||
func To3x3Char(numb int) Matrix {
|
||||
switch numb {
|
||||
case 1:
|
||||
return Matrix{
|
||||
[]rune{b, lv, b},
|
||||
[]rune{b, v, b},
|
||||
[]rune{b, hv, b},
|
||||
}
|
||||
case 2:
|
||||
return Matrix{
|
||||
[]rune{rh, h, tr},
|
||||
[]rune{tl, h, br},
|
||||
[]rune{bl, h, lh},
|
||||
}
|
||||
case 3:
|
||||
return Matrix{
|
||||
[]rune{h, h, tr},
|
||||
[]rune{rh, h, teeL},
|
||||
[]rune{h, h, br},
|
||||
}
|
||||
case 4:
|
||||
return Matrix{
|
||||
[]rune{lv, b, lv},
|
||||
[]rune{bl, h, teeL},
|
||||
[]rune{b, b, hv},
|
||||
}
|
||||
case 5:
|
||||
return Matrix{
|
||||
[]rune{tl, h, lh},
|
||||
[]rune{bl, h, tr},
|
||||
[]rune{rh, h, br},
|
||||
}
|
||||
case 6:
|
||||
return Matrix{
|
||||
[]rune{tl, h, lh},
|
||||
[]rune{teeR, h, tr},
|
||||
[]rune{bl, h, br},
|
||||
}
|
||||
case 7:
|
||||
return Matrix{
|
||||
[]rune{h, h, tr},
|
||||
[]rune{b, b, v},
|
||||
[]rune{b, b, hv},
|
||||
}
|
||||
case 8:
|
||||
return Matrix{
|
||||
[]rune{tl, h, tr},
|
||||
[]rune{teeR, h, teeL},
|
||||
[]rune{bl, h, br},
|
||||
}
|
||||
case 9:
|
||||
return Matrix{
|
||||
[]rune{tl, h, tr},
|
||||
[]rune{bl, h, teeL},
|
||||
[]rune{rh, h, br},
|
||||
}
|
||||
default:
|
||||
return Matrix{
|
||||
[]rune{tl, h, tr},
|
||||
[]rune{v, b, v},
|
||||
[]rune{bl, h, br},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package tchart_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
|
|
@ -48,6 +49,17 @@ func TestDial5x3(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDial3x3(t *testing.T) {
|
||||
d := tchart.NewDotMatrix(3, 3)
|
||||
for n := 0; n <= 2; n++ {
|
||||
i := n
|
||||
t.Run(strconv.Itoa(n), func(t *testing.T) {
|
||||
fmt.Println(tchart.To3x3Char(i))
|
||||
assert.Equal(t, tchart.To3x3Char(i), d.Print(i))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers...
|
||||
|
||||
const hChar, vChar = '▤', '▥'
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type Gauge struct {
|
|||
*Component
|
||||
|
||||
data Metric
|
||||
resolution int
|
||||
deltaOk, deltaFault delta
|
||||
}
|
||||
|
||||
|
|
@ -38,6 +39,10 @@ func NewGauge(id string) *Gauge {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Gauge) SetResolution(n int) {
|
||||
g.resolution = n
|
||||
}
|
||||
|
||||
// IsDial returns true if chart is a dial
|
||||
func (g *Gauge) IsDial() bool {
|
||||
return true
|
||||
|
|
@ -60,32 +65,60 @@ func (g *Gauge) Draw(sc tcell.Screen) {
|
|||
defer g.mx.RUnlock()
|
||||
|
||||
rect := g.asRect()
|
||||
mid := image.Point{X: rect.Min.X + rect.Dx()/2 - 1, Y: rect.Min.Y + rect.Dy()/2 - 2}
|
||||
|
||||
mid := image.Point{X: rect.Min.X + rect.Dx()/2, Y: rect.Min.Y + rect.Dy()/2 - 1}
|
||||
style := tcell.StyleDefault.Background(g.bgColor)
|
||||
style = style.Foreground(tcell.ColorYellow)
|
||||
sc.SetContent(mid.X+1, mid.Y+2, '⠔', nil, style)
|
||||
sc.SetContent(mid.X, mid.Y, '⠔', nil, style)
|
||||
|
||||
max := g.data.MaxDigits()
|
||||
if max < g.resolution {
|
||||
max = g.resolution
|
||||
}
|
||||
var (
|
||||
max = g.data.MaxDigits()
|
||||
fmat = "%" + fmt.Sprintf(gaugeFmt, max)
|
||||
o = image.Point{X: mid.X - 3, Y: mid.Y}
|
||||
o = image.Point{X: mid.X, Y: mid.Y - 1}
|
||||
)
|
||||
|
||||
s1C, s2C := g.colorForSeries()
|
||||
d1, d2 := fmt.Sprintf(fmat, g.data.OK), fmt.Sprintf(fmat, g.data.Fault)
|
||||
o.X -= (len(d1) - 1) * 5
|
||||
o.X -= len(d1) * 3
|
||||
g.drawNum(sc, true, o, g.data.OK, g.deltaOk, d1, style.Foreground(s1C).Dim(false))
|
||||
|
||||
o.X = mid.X + 3
|
||||
o.X = mid.X + 1
|
||||
g.drawNum(sc, false, o, g.data.Fault, g.deltaFault, d2, style.Foreground(s2C).Dim(false))
|
||||
|
||||
if rect.Dx() > 0 && rect.Dy() > 0 && g.legend != "" {
|
||||
legend := g.legend
|
||||
if g.HasFocus() {
|
||||
legend = "[:aqua]" + g.legend + "[::]"
|
||||
legend = fmt.Sprintf("[%s:%s:]", g.focusFgColor, g.focusBgColor) + g.legend + "[::]"
|
||||
}
|
||||
tview.Print(sc, legend, rect.Min.X, rect.Max.Y, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
|
||||
tview.Print(sc, legend, rect.Min.X, o.Y+3, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Gauge) drawNum(sc tcell.Screen, ok bool, o image.Point, n int, dn delta, ns string, style tcell.Style) {
|
||||
c1, _ := g.colorForSeries()
|
||||
if ok {
|
||||
style = style.Foreground(c1)
|
||||
printDelta(sc, dn, o, style)
|
||||
}
|
||||
|
||||
dm, significant := NewDotMatrix(3, 3), n == 0
|
||||
if n == 0 {
|
||||
style = g.dimmed
|
||||
}
|
||||
for i := 0; i < len(ns); i++ {
|
||||
if ns[i] == '0' && !significant {
|
||||
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, g.dimmed)
|
||||
} else {
|
||||
significant = true
|
||||
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, style)
|
||||
}
|
||||
o.X += 3
|
||||
}
|
||||
if !ok {
|
||||
o.X++
|
||||
printDelta(sc, dn, o, style)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,33 +135,6 @@ func (g *Gauge) drawDial(sc tcell.Screen, m Matrix, o image.Point, style tcell.S
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Gauge) drawNum(sc tcell.Screen, ok bool, o image.Point, n int, dn delta, ns string, style tcell.Style) {
|
||||
c1, _ := g.colorForSeries()
|
||||
if ok {
|
||||
o.X -= 1
|
||||
style = style.Foreground(c1)
|
||||
printDelta(sc, dn, o, style)
|
||||
o.X += 1
|
||||
}
|
||||
|
||||
dm, sig := NewDotMatrix(5, 3), n == 0
|
||||
if n == 0 {
|
||||
style = g.dimmed
|
||||
}
|
||||
for i := 0; i < len(ns); i++ {
|
||||
if ns[i] == '0' && !sig {
|
||||
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, g.dimmed)
|
||||
} else {
|
||||
sig = true
|
||||
g.drawDial(sc, dm.Print(int(ns[i]-48)), o, style)
|
||||
}
|
||||
o.X += 5
|
||||
}
|
||||
if !ok {
|
||||
printDelta(sc, dn, o, style)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
|
|
@ -152,8 +158,8 @@ func printDelta(sc tcell.Screen, d delta, o image.Point, s tcell.Style) {
|
|||
s = s.Dim(false)
|
||||
switch d {
|
||||
case DeltaLess:
|
||||
sc.SetContent(o.X-1, o.Y+2, '↓', nil, s)
|
||||
sc.SetContent(o.X-1, o.Y+1, '↓', nil, s)
|
||||
case DeltaMore:
|
||||
sc.SetContent(o.X-1, o.Y+2, '↑', nil, s)
|
||||
sc.SetContent(o.X-1, o.Y+1, '↑', nil, s)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,8 +274,7 @@ func (a *App) switchNS(ns string) bool {
|
|||
}
|
||||
|
||||
func (a *App) switchCtx(name string, loadPods bool) error {
|
||||
log.Debug().Msgf("Switching Context %q", name)
|
||||
|
||||
log.Debug().Msgf("--> Switching Context %q--%q", name, a.Config.ActiveView())
|
||||
a.Halt()
|
||||
defer a.Resume()
|
||||
{
|
||||
|
|
@ -294,7 +293,11 @@ func (a *App) switchCtx(name string, loadPods bool) error {
|
|||
}
|
||||
a.Flash().Infof("Switching context to %s", name)
|
||||
a.ReloadStyles(name)
|
||||
if err := a.gotoResource("pods", "", true); loadPods && err != nil {
|
||||
v := a.Config.ActiveView()
|
||||
if v == "" {
|
||||
v = "pod"
|
||||
}
|
||||
if err := a.gotoResource(v, ns, true); loadPods && err != nil {
|
||||
a.Flash().Err(err)
|
||||
}
|
||||
a.clusterModel.Reset(a.factory)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func useContext(app *App, name string) error {
|
|||
log.Error().Err(err).Msgf("Context switch failed")
|
||||
return err
|
||||
}
|
||||
if err := app.switchCtx(name, false); err != nil {
|
||||
if err := app.switchCtx(name, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ type Grapheable interface {
|
|||
// GetSeriesColorNames returns the series color names.
|
||||
GetSeriesColorNames() []string
|
||||
|
||||
// SetFocusColorNames sets the focus color names.
|
||||
SetFocusColorNames(fg, bg string)
|
||||
|
||||
// SetBackgroundColor sets chart bg color.
|
||||
SetBackgroundColor(tcell.Color)
|
||||
|
||||
|
|
@ -80,19 +83,19 @@ func (p *Pulse) Init(ctx context.Context) error {
|
|||
}
|
||||
|
||||
p.charts = []Grapheable{
|
||||
p.makeGA(image.Point{X: 0, Y: 0}, image.Point{X: 3, Y: 2}, "apps/v1/deployments"),
|
||||
p.makeGA(image.Point{X: 0, Y: 2}, image.Point{X: 3, Y: 2}, "apps/v1/replicasets"),
|
||||
p.makeGA(image.Point{X: 0, Y: 4}, image.Point{X: 3, Y: 2}, "apps/v1/statefulsets"),
|
||||
p.makeGA(image.Point{X: 0, Y: 6}, image.Point{X: 3, Y: 2}, "apps/v1/daemonsets"),
|
||||
p.makeSP(true, image.Point{X: 3, Y: 0}, image.Point{X: 3, Y: 4}, "v1/pods"),
|
||||
p.makeSP(true, image.Point{X: 3, Y: 4}, image.Point{X: 3, Y: 4}, "v1/events"),
|
||||
p.makeSP(true, image.Point{X: 6, Y: 0}, image.Point{X: 3, Y: 4}, "batch/v1/jobs"),
|
||||
p.makeSP(true, image.Point{X: 6, Y: 4}, image.Point{X: 3, Y: 4}, "v1/persistentvolumes"),
|
||||
p.makeGA(image.Point{X: 0, Y: 0}, image.Point{X: 2, Y: 2}, "apps/v1/deployments"),
|
||||
p.makeGA(image.Point{X: 0, Y: 2}, image.Point{X: 2, Y: 2}, "apps/v1/replicasets"),
|
||||
p.makeGA(image.Point{X: 0, Y: 4}, image.Point{X: 2, Y: 2}, "apps/v1/statefulsets"),
|
||||
p.makeGA(image.Point{X: 0, Y: 6}, image.Point{X: 2, Y: 2}, "apps/v1/daemonsets"),
|
||||
p.makeSP(true, image.Point{X: 2, Y: 0}, image.Point{X: 3, Y: 2}, "v1/pods"),
|
||||
p.makeSP(true, image.Point{X: 2, Y: 2}, image.Point{X: 3, Y: 2}, "v1/events"),
|
||||
p.makeSP(true, image.Point{X: 2, Y: 4}, image.Point{X: 3, Y: 2}, "batch/v1/jobs"),
|
||||
p.makeSP(true, image.Point{X: 2, Y: 6}, image.Point{X: 3, Y: 2}, "v1/persistentvolumes"),
|
||||
}
|
||||
if p.app.Conn().HasMetrics() {
|
||||
p.charts = append(p.charts,
|
||||
p.makeSP(false, image.Point{X: 9, Y: 0}, image.Point{X: 2, Y: 4}, "cpu"),
|
||||
p.makeSP(false, image.Point{X: 9, Y: 4}, image.Point{X: 2, Y: 4}, "mem"),
|
||||
p.makeSP(false, image.Point{X: 5, Y: 0}, image.Point{X: 2, Y: 4}, "cpu"),
|
||||
p.makeSP(false, image.Point{X: 5, Y: 4}, image.Point{X: 2, Y: 4}, "mem"),
|
||||
)
|
||||
}
|
||||
p.bindKeys()
|
||||
|
|
@ -108,6 +111,7 @@ func (p *Pulse) Init(ctx context.Context) error {
|
|||
func (p *Pulse) StylesChanged(s *config.Styles) {
|
||||
p.SetBackgroundColor(s.Charts().BgColor.Color())
|
||||
for _, c := range p.charts {
|
||||
c.SetFocusColorNames(s.Table().BgColor.String(), s.Table().CursorColor.String())
|
||||
if c.IsDial() {
|
||||
c.SetBackgroundColor(s.Charts().DialBgColor.Color())
|
||||
c.SetSeriesColors(s.Charts().DefaultDialColors.Colors()...)
|
||||
|
|
@ -318,8 +322,9 @@ func (p *Pulse) makeSP(multi bool, loc image.Point, span image.Point, gvr string
|
|||
|
||||
func (p *Pulse) makeGA(loc image.Point, span image.Point, gvr string) *tchart.Gauge {
|
||||
g := tchart.NewGauge(gvr)
|
||||
// g.SetResolution(3)
|
||||
g.SetBackgroundColor(p.app.Styles.Charts().BgColor.Color())
|
||||
g.SetBorderPadding(0, 1, 0, 1)
|
||||
// g.SetBorderPadding(0, 1, 0, 1)
|
||||
if cc, ok := p.app.Styles.Charts().ResourceColors[gvr]; ok {
|
||||
g.SetSeriesColors(cc.Colors()...)
|
||||
} else {
|
||||
|
|
@ -327,7 +332,7 @@ func (p *Pulse) makeGA(loc image.Point, span image.Point, gvr string) *tchart.Ga
|
|||
}
|
||||
g.SetLegend(fmt.Sprintf(" %s ", strings.Title(client.NewGVR(gvr).R())))
|
||||
g.SetInputCapture(p.keyboard)
|
||||
p.AddItem(g, loc.X, loc.Y, span.X, span.Y, span.X, span.Y, true)
|
||||
p.AddItem(g, loc.X, loc.Y, span.X, span.Y, 0, 0, true)
|
||||
|
||||
return g
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue