checkpoint

mine
derailed 2020-02-20 00:03:32 -07:00
parent 355f4ce396
commit 98b07e6961
19 changed files with 92 additions and 89 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ gen.sh
*.log
*~
faas
demos

View File

@ -210,9 +210,9 @@ func newStyle() Style {
func newCharts() Charts {
return Charts{
BgColor: "#111111",
DialBgColor: "#111111",
ChartBgColor: "#111111",
BgColor: "default",
DialBgColor: "default",
ChartBgColor: "default",
DefaultDialColors: Colors{Color("palegreen"), Color("orangered")},
DefaultChartColors: Colors{Color("palegreen"), Color("orangered")},
}

View File

@ -13,6 +13,8 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
const defaultRefreshRate = 5 * time.Second
// PulseListener represents a health model listener.
type PulseListener interface {
// PulseChanged notifies the model data changed.
@ -29,14 +31,14 @@ type Pulse struct {
inUpdate int32
listeners []PulseListener
refreshRate time.Duration
health *Health
health *PulseHealth
data health.Checks
}
func NewPulse(gvr string) *Pulse {
return &Pulse{
gvr: gvr,
refreshRate: 2 * time.Second,
refreshRate: defaultRefreshRate,
}
}
@ -48,7 +50,7 @@ func (p *Pulse) Watch(ctx context.Context) {
func (p *Pulse) updater(ctx context.Context) {
defer log.Debug().Msgf("Pulse canceled -- %q", p.gvr)
rate := initTreeRefreshRate
rate := initRefreshRate
for {
select {
case <-ctx.Done():
@ -88,7 +90,7 @@ func (p *Pulse) list(ctx context.Context) ([]runtime.Object, error) {
return nil, fmt.Errorf("expected Factory in context but got %T", ctx.Value(internal.KeyFactory))
}
if p.health == nil {
p.health = NewHealth(f)
p.health = NewPulseHealth(f)
}
ctx = context.WithValue(ctx, internal.KeyFields, "")
ctx = context.WithValue(ctx, internal.KeyWithMetrics, false)

View File

@ -14,19 +14,19 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
type Health struct {
type PulseHealth struct {
factory dao.Factory
}
func NewHealth(f dao.Factory) *Health {
return &Health{
func NewPulseHealth(f dao.Factory) *PulseHealth {
return &PulseHealth{
factory: f,
}
}
func (h *Health) List(ctx context.Context, ns string) ([]runtime.Object, error) {
func (h *PulseHealth) List(ctx context.Context, ns string) ([]runtime.Object, error) {
defer func(t time.Time) {
log.Debug().Msgf("HealthCheck %v", time.Since(t))
log.Debug().Msgf("PulseHealthCheck %v", time.Since(t))
}(time.Now())
gvrs := []string{
@ -60,7 +60,7 @@ func (h *Health) List(ctx context.Context, ns string) ([]runtime.Object, error)
return hh, nil
}
func (h *Health) checkMetrics() (health.Checks, error) {
func (h *PulseHealth) checkMetrics() (health.Checks, error) {
dial := client.DialMetrics(h.factory.Client())
nmx, err := dial.FetchNodesMetrics()
if err != nil {
@ -81,11 +81,7 @@ func (h *Health) checkMetrics() (health.Checks, error) {
return health.Checks{c1, c2}, nil
}
func (h *Health) check(ctx context.Context, ns, gvr string) (*health.Check, error) {
defer func(t time.Time) {
log.Debug().Msgf(" CHECK %s - %v", gvr, time.Since(t))
}(time.Now())
func (h *PulseHealth) check(ctx context.Context, ns, gvr string) (*health.Check, error) {
meta, ok := Registry[gvr]
if !ok {
return nil, fmt.Errorf("No meta for %q", gvr)

View File

@ -31,7 +31,7 @@ func NewComponent(id string) *Component {
return &Component{
Box: tview.NewBox(),
id: id,
noColor: tcell.ColorBlack,
noColor: tcell.ColorDefault,
seriesColors: []tcell.Color{tview.Styles.PrimaryTextColor, tview.Styles.FocusColor},
dimmed: tcell.StyleDefault.Background(tview.Styles.PrimitiveBackgroundColor).Foreground(tcell.ColorGray).Dim(true),
}

View File

@ -50,7 +50,7 @@ func TestDial(t *testing.T) {
// Helpers...
const hChar, vChar = '⠶', '⠿'
const hChar, vChar = '▤', '▥'
var numbers = []tchart.Matrix{
[][]rune{

View File

@ -56,6 +56,9 @@ func (g *Gauge) drawNum(sc tcell.Screen, ok bool, o image.Point, n int, dn delta
}
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)
@ -102,7 +105,7 @@ func (g *Gauge) Draw(sc tcell.Screen) {
if g.HasFocus() {
legend = "[:aqua]" + g.legend + "[::]"
}
tview.Print(sc, legend, rect.Min.X, rect.Max.Y-1, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
tview.Print(sc, legend, rect.Min.X, rect.Max.Y, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
}
}

View File

@ -41,7 +41,6 @@ type SparkLine struct {
*Component
data []Metric
lastWidth int
}
// NewSparkLine returns a new graph.
@ -75,24 +74,24 @@ func (s *SparkLine) Draw(screen tcell.Screen) {
}
rect := s.asRect()
s.lastWidth = rect.Dx()
s.cutSet(rect.Dx())
max := s.computeMax()
cX := rect.Min.X + 1
if len(s.data) < rect.Dx() {
cX = rect.Max.X - len(s.data)
cX, idx := rect.Min.X+1, 0
if len(s.data)*2 < rect.Dx() {
cX = rect.Max.X - len(s.data)*2
} else {
idx = len(s.data) - rect.Dx()/2
}
scale := float64(len(sparks)) * float64((rect.Dy() - pad)) / float64(max)
c1, c2 := s.colorForSeries()
for _, d := range s.data {
for _, d := range s.data[idx:] {
b := toBlocks(d, scale)
cY := rect.Max.Y - pad
cY = s.drawBlock(screen, cX, cY, b.oks, c1)
s.drawBlock(screen, cX, cY, b.oks, c1)
s.drawBlock(screen, cX, cY, b.errs, c2)
cX++
cX += 2
}
if rect.Dx() > 0 && rect.Dy() > 0 && s.legend != "" {
@ -100,11 +99,11 @@ func (s *SparkLine) Draw(screen tcell.Screen) {
if s.HasFocus() {
legend = "[:aqua:]" + s.legend + "[::]"
}
tview.Print(screen, legend, rect.Min.X, rect.Max.Y-1, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
tview.Print(screen, legend, rect.Min.X, rect.Max.Y, rect.Dx(), tview.AlignCenter, tcell.ColorWhite)
}
}
func (s *SparkLine) drawBlock(screen tcell.Screen, x, y int, b block, c tcell.Color) int {
func (s *SparkLine) drawBlock(screen tcell.Screen, x, y int, b block, c tcell.Color) {
style := tcell.StyleDefault.Foreground(c).Background(s.bgColor)
for i := 0; i < b.full; i++ {
@ -114,48 +113,46 @@ func (s *SparkLine) drawBlock(screen tcell.Screen, x, y int, b block, c tcell.Co
if b.partial != 0 {
screen.SetContent(x, y, b.partial, nil, style)
}
return y
}
func (s *SparkLine) cutSet(w int) {
if w <= 0 || len(s.data) == 0 {
func (s *SparkLine) cutSet(width int) {
if width <= 0 || len(s.data) == 0 {
return
}
if w < len(s.data) {
s.data = s.data[len(s.data)-w:]
if len(s.data) >= width*2 {
s.data = s.data[len(s.data)-width:]
}
}
func (s *SparkLine) computeMax() int {
var max int
for _, d := range s.data {
sum := d.Sum()
if sum > max {
max = sum
if max < d.OK {
max = d.OK
}
}
return max
}
func toBlocks(value Metric, scale float64) blocks {
if value.Sum() <= 0 {
func toBlocks(m Metric, scale float64) blocks {
if m.Sum() <= 0 {
return blocks{}
}
return blocks{oks: makeBlocks(m.OK, false, scale), errs: makeBlocks(m.Fault, true, scale)}
}
oks := int(math.Floor(float64(value.OK) * scale))
part, okB := oks%len(sparks), block{full: oks / len(sparks)}
func makeBlocks(v int, isErr bool, scale float64) block {
scaled := int(math.Round(float64(v) * scale))
part, b := scaled%len(sparks), block{full: scaled / len(sparks)}
// Err might get scaled way down if so nudge.
if v > 0 && isErr && scaled == 0 {
part = 1
}
if part > 0 {
okB.partial = sparks[part-1]
b.partial = sparks[part-1]
}
errs := int(math.Round(float64(value.Fault) * scale))
part, errB := errs%len(sparks), block{full: errs / len(sparks)}
if part > 0 {
errB.partial = sparks[part-1]
}
return blocks{oks: okB, errs: errB}
return b
}

View File

@ -31,7 +31,7 @@ func NewFlash(app *App) *Flash {
TextView: tview.NewTextView(),
}
f.SetTextColor(tcell.ColorAqua)
f.SetTextAlign(tview.AlignLeft)
f.SetTextAlign(tview.AlignCenter)
f.SetBorderPadding(0, 0, 1, 1)
f.app.Styles.AddListener(&f)

View File

@ -25,7 +25,7 @@ func NewLogo(styles *config.Styles) *Logo {
}
l.SetDirection(tview.FlexRow)
l.AddItem(l.logo, 0, 6, false)
l.AddItem(l.status, 0, 1, false)
l.AddItem(l.status, 1, 0, false)
l.refreshLogo(styles.Body().LogoColor)
l.SetBackgroundColor(styles.BgColor())
styles.AddListener(&l)

View File

@ -68,14 +68,14 @@ func hotKeyActions(r Runner, aa ui.KeyActions) {
}
aa[key] = ui.NewSharedKeyAction(
hk.Description,
gotoCmd(r, hk.Command),
gotoCmd(r, "", hk.Command),
false)
}
}
func gotoCmd(r Runner, cmd string) ui.ActionHandler {
func gotoCmd(r Runner, cmd, path string) ui.ActionHandler {
return func(evt *tcell.EventKey) *tcell.EventKey {
if err := r.App().gotoResource(cmd, true); err != nil {
if err := r.App().gotoResource(cmd, path, true); err != nil {
r.App().Flash().Err(err)
}
return nil

View File

@ -59,7 +59,7 @@ func (a *Alias) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
if r != 0 {
s := ui.TrimCell(a.GetTable().SelectTable, r, 1)
tokens := strings.Split(s, ",")
if err := a.App().gotoResource(tokens[0], true); err != nil {
if err := a.App().gotoResource(tokens[0], "", true); err != nil {
a.App().Flash().Err(err)
}
return nil

View File

@ -104,8 +104,8 @@ func (a *App) Init(version string, rate int) error {
main := tview.NewFlex().SetDirection(tview.FlexRow)
main.AddItem(a.statusIndicator(), 1, 1, false)
main.AddItem(a.Content, 0, 10, true)
main.AddItem(a.Crumbs(), 2, 1, false)
main.AddItem(flash, 2, 1, false)
main.AddItem(a.Crumbs(), 1, 1, false)
main.AddItem(flash, 1, 1, false)
a.Main.AddPage("main", main, true, false)
a.Main.AddPage("splash", ui.NewSplash(a.Styles, version), true, true)
@ -136,7 +136,7 @@ func (a *App) toggleHeader(flag bool) {
}
if a.showHeader {
flex.RemoveItemAtIndex(0)
flex.AddItemAtIndex(0, a.buildHeader(), 7, 1, false)
flex.AddItemAtIndex(0, a.buildHeader(), 8, 1, false)
} else {
flex.RemoveItemAtIndex(0)
flex.AddItemAtIndex(0, a.statusIndicator(), 1, 1, false)
@ -271,7 +271,7 @@ 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 {
if err := a.gotoResource("pods", "", true); loadPods && err != nil {
a.Flash().Err(err)
}
a.clusterModel.Reset(a.factory)
@ -382,7 +382,7 @@ func (a *App) toggleHeaderCmd(evt *tcell.EventKey) *tcell.EventKey {
func (a *App) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
if a.CmdBuff().IsActive() && !a.CmdBuff().Empty() {
if err := a.gotoResource(a.GetCmd(), true); err != nil {
if err := a.gotoResource(a.GetCmd(), "", true); err != nil {
log.Error().Err(err).Msgf("Goto resource for %q failed", a.GetCmd())
a.Flash().Err(err)
}
@ -431,8 +431,8 @@ func (a *App) viewResource(gvr, path string, clearStack bool) error {
return a.command.run(gvr, path, clearStack)
}
func (a *App) gotoResource(cmd string, clearStack bool) error {
return a.command.run(cmd, "", clearStack)
func (a *App) gotoResource(cmd, path string, clearStack bool) error {
return a.command.run(cmd, path, clearStack)
}
func (a *App) inject(c model.Component) error {

View File

@ -30,6 +30,7 @@ func NewClusterInfo(app *App) *ClusterInfo {
// Init initializes the view.
func (c *ClusterInfo) Init() {
c.SetBorderPadding(0, 0, 1, 0)
c.app.Styles.AddListener(c)
c.layout()
c.StylesChanged(c.app.Styles)

View File

@ -42,7 +42,7 @@ func (n *Namespace) bindKeys(aa ui.KeyActions) {
func (n *Namespace) switchNs(app *App, model ui.Tabular, gvr, path string) {
n.useNamespace(path)
if err := app.gotoResource("pods", true); err != nil {
if err := app.gotoResource("pods", "", true); err != nil {
app.Flash().Err(err)
}
}

View File

@ -27,13 +27,13 @@ func ShowPortForwards(v ResourceViewer, path string, ports []string, okFn PortFo
SetFieldTextColor(styles.K9s.Info.SectionColor.Color())
p1, p2, address := ports[0], extractPort(ports[0]), "localhost"
f.AddInputField("Container Port:", p1, 20, nil, func(p string) {
f.AddInputField("Container Port:", p1, 30, nil, func(p string) {
p1 = p
})
f.AddInputField("Local Port:", p2, 20, nil, func(p string) {
f.AddInputField("Local Port:", p2, 30, nil, func(p string) {
p2 = p
})
f.AddInputField("Address:", address, 20, nil, func(h string) {
f.AddInputField("Address:", address, 30, nil, func(h string) {
address = h
})
@ -48,22 +48,24 @@ func ShowPortForwards(v ResourceViewer, path string, ports []string, okFn PortFo
okFn(v, path, extractContainer(p1), tunnel)
})
f.AddButton("Cancel", func() {
DismissPortForwards(pages)
DismissPortForwards(v.App(), pages)
})
modal := tview.NewModalForm(fmt.Sprintf("<PortForward on %s>", path), f)
modal.SetText("Exposed Ports: " + strings.Join(ports, ","))
modal.SetDoneFunc(func(_ int, b string) {
DismissPortForwards(pages)
DismissPortForwards(v.App(), pages)
})
pages.AddPage(portForwardKey, modal, false, false)
pages.AddPage(portForwardKey, modal, false, true)
pages.ShowPage(portForwardKey)
v.App().SetFocus(pages.GetPrimitive(portForwardKey))
}
// DismissPortForwards dismiss the port forward dialog.
func DismissPortForwards(p *ui.Pages) {
func DismissPortForwards(app *App, p *ui.Pages) {
p.RemovePage(portForwardKey)
app.SetFocus(p.CurrentPage().Item)
}
// ----------------------------------------------------------------------------

View File

@ -76,7 +76,7 @@ func runForward(v ResourceViewer, pf watch.Forwarder, f *portforward.PortForward
v.App().QueueUpdateDraw(func() {
v.App().Flash().Infof("PortForward activated %s:%s", pf.Path(), pf.Ports()[0])
DismissPortForwards(v.App().Content.Pages)
DismissPortForwards(v.App(), v.App().Content.Pages)
})
pf.SetActive(true)
@ -124,7 +124,6 @@ func showFwdDialog(v ResourceViewer, path string, cb PortForwardFunc) error {
ports = append(ports, client.FQN(co, p.Name)+":"+strconv.Itoa(int(p.ContainerPort)))
}
}
if len(ports) == 0 {
return fmt.Errorf("no tcp ports found on %s", path)
}

View File

@ -141,15 +141,19 @@ func (p *Pulse) PulseChanged(c *health.Check) {
v.SetLegend(fmt.Sprintf(" %s - %dMi", strings.Title(gvr.R()), c.Tally(health.OK)))
default:
nn := v.GetSeriesColorNames()
v.SetLegend(fmt.Sprintf(" %s(%d:[%s::]%d:[%s::b]%d[-::])",
if c.Tally(health.OK) == 0 {
nn[0] = "gray"
}
if c.Tally(health.Toast) == 0 {
nn[1] = "gray"
}
v.SetLegend(fmt.Sprintf(" %s - [%s::]%d/[%s::b]%d[-::]",
strings.Title(gvr.R()),
c.Tally(health.Corpus),
nn[0],
c.Tally(health.OK),
nn[1],
c.Tally(health.Toast),
),
)
))
}
v.Add(tchart.Metric{OK: c.Tally(health.OK), Fault: c.Tally(health.Toast)})
}
@ -173,12 +177,10 @@ func (p *Pulse) bindKeys() {
}
func (p *Pulse) keyboard(evt *tcell.EventKey) *tcell.EventKey {
log.Debug().Msgf("Pulse GOT EVENT %#v", evt)
key := evt.Key()
if key == tcell.KeyRune {
key = tcell.Key(evt.Rune())
}
if a, ok := p.actions[key]; ok {
return a.Action(evt)
}
@ -275,7 +277,7 @@ func (p *Pulse) enterCmd(evt *tcell.EventKey) *tcell.EventKey {
}
log.Debug().Msgf("Selected %s", s.ID())
gvr := client.NewGVR(s.ID())
if err := p.App().gotoResource(gvr.R(), false); err != nil {
if err := p.App().gotoResource(gvr.R(), "", false); err != nil {
p.App().Flash().Err(err)
}

View File

@ -137,7 +137,7 @@ func extViewers(vv MetaViewers) {
func showCRD(app *App, _ ui.Tabular, _, path string) {
_, crdGVR := client.Namespaced(path)
tokens := strings.Split(crdGVR, ".")
if err := app.gotoResource(tokens[0], false); err != nil {
if err := app.gotoResource(tokens[0], "", false); err != nil {
app.Flash().Err(err)
}
}