mirror of
https://github.com/bcicen/ctop.git
synced 2024-08-30 18:23:19 +00:00
further containermap refactoring
This commit is contained in:
parent
cb9f38adb3
commit
c3bb7c6b96
18
container.go
18
container.go
@ -20,7 +20,7 @@ func NewContainer(id, name string) *Container {
|
|||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
}
|
}
|
||||||
c.Collapse()
|
c.widgets = widgets.NewCompact(c.ShortID(), c.ShortName(), c.state)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,11 +33,12 @@ func (c *Container) ShortName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Expand() {
|
func (c *Container) Expand() {
|
||||||
c.widgets = widgets.NewExpanded(c.id, c.name)
|
var curWidgets widgets.ContainerWidgets
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) Collapse() {
|
curWidgets = c.widgets
|
||||||
c.widgets = widgets.NewCompact(c.ShortID(), c.ShortName())
|
c.widgets = widgets.NewExpanded(c.ShortID(), c.ShortName())
|
||||||
|
c.widgets.Render()
|
||||||
|
c.widgets = curWidgets
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) SetState(s string) {
|
func (c *Container) SetState(s string) {
|
||||||
@ -45,6 +46,12 @@ func (c *Container) SetState(s string) {
|
|||||||
c.widgets.SetStatus(s)
|
c.widgets.SetStatus(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set metrics to zero state, clear widget gauges
|
||||||
|
func (c *Container) reset() {
|
||||||
|
c.metrics = metrics.Metrics{}
|
||||||
|
c.widgets.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
// Read metric stream, updating widgets
|
// Read metric stream, updating widgets
|
||||||
func (c *Container) Read(stream chan metrics.Metrics) {
|
func (c *Container) Read(stream chan metrics.Metrics) {
|
||||||
go func() {
|
go func() {
|
||||||
@ -55,6 +62,7 @@ func (c *Container) Read(stream chan metrics.Metrics) {
|
|||||||
c.widgets.SetNet(metrics.NetRx, metrics.NetTx)
|
c.widgets.SetNet(metrics.NetRx, metrics.NetTx)
|
||||||
}
|
}
|
||||||
log.Infof("reader stopped for container: %s", c.id)
|
log.Infof("reader stopped for container: %s", c.id)
|
||||||
|
c.reset()
|
||||||
}()
|
}()
|
||||||
log.Infof("reader started for container: %s", c.id)
|
log.Infof("reader started for container: %s", c.id)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,16 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/config"
|
"github.com/bcicen/ctop/config"
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/metrics"
|
||||||
"github.com/fsouza/go-dockerclient"
|
"github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var lock = sync.RWMutex{}
|
||||||
|
|
||||||
type ContainerMap struct {
|
type ContainerMap struct {
|
||||||
client *docker.Client
|
client *docker.Client
|
||||||
containers Containers
|
containers Containers
|
||||||
@ -27,32 +31,29 @@ func NewContainerMap() *ContainerMap {
|
|||||||
needsRefresh: make(map[string]int),
|
needsRefresh: make(map[string]int),
|
||||||
}
|
}
|
||||||
cm.refreshAll()
|
cm.refreshAll()
|
||||||
go cm.watch()
|
go cm.UpdateLoop()
|
||||||
|
go cm.watchEvents()
|
||||||
return cm
|
return cm
|
||||||
}
|
}
|
||||||
|
|
||||||
// Docker events watcher
|
// Docker events watcher
|
||||||
func (cm *ContainerMap) watch() {
|
func (cm *ContainerMap) watchEvents() {
|
||||||
log.Info("docker event listener starting")
|
log.Info("docker event listener starting")
|
||||||
events := make(chan *docker.APIEvents)
|
events := make(chan *docker.APIEvents)
|
||||||
cm.client.AddEventListener(events)
|
cm.client.AddEventListener(events)
|
||||||
|
|
||||||
for e := range events {
|
for e := range events {
|
||||||
cm.handleEvent(e)
|
if e.Type != "container" {
|
||||||
}
|
continue
|
||||||
}
|
}
|
||||||
|
switch e.Action {
|
||||||
// Docker event handler
|
case "start", "die", "pause", "unpause":
|
||||||
func (cm *ContainerMap) handleEvent(e *docker.APIEvents) {
|
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
|
||||||
// only process container events
|
cm.needsRefresh[e.ID] = 1
|
||||||
if e.Type != "container" {
|
case "destroy":
|
||||||
return
|
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
|
||||||
}
|
cm.DelByID(e.ID)
|
||||||
switch e.Action {
|
}
|
||||||
case "start", "die", "pause", "unpause":
|
|
||||||
cm.needsRefresh[e.ID] = 1
|
|
||||||
case "destroy":
|
|
||||||
cm.DelByID(e.ID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,12 +68,10 @@ func (cm *ContainerMap) refresh(id string) {
|
|||||||
c, ok := cm.Get(id)
|
c, ok := cm.Get(id)
|
||||||
// append container struct for new containers
|
// append container struct for new containers
|
||||||
if !ok {
|
if !ok {
|
||||||
c = &Container{
|
c = NewContainer(id, insp.Name)
|
||||||
id: id,
|
lock.Lock()
|
||||||
name: insp.Name,
|
|
||||||
}
|
|
||||||
c.Collapse()
|
|
||||||
cm.containers = append(cm.containers, c)
|
cm.containers = append(cm.containers, c)
|
||||||
|
lock.Unlock()
|
||||||
// create collector
|
// create collector
|
||||||
if _, ok := cm.collectors[id]; ok == false {
|
if _, ok := cm.collectors[id]; ok == false {
|
||||||
cm.collectors[id] = metrics.NewDocker(cm.client, id)
|
cm.collectors[id] = metrics.NewDocker(cm.client, id)
|
||||||
@ -86,6 +85,10 @@ func (cm *ContainerMap) refresh(id string) {
|
|||||||
cm.collectors[c.id].Start()
|
cm.collectors[c.id].Start()
|
||||||
c.Read(cm.collectors[c.id].Stream())
|
c.Read(cm.collectors[c.id].Stream())
|
||||||
}
|
}
|
||||||
|
// stop collector if needed
|
||||||
|
if c.state != "running" && cm.collectors[c.id].Running() {
|
||||||
|
cm.collectors[c.id].Stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerMap) inspect(id string) *docker.Container {
|
func (cm *ContainerMap) inspect(id string) *docker.Container {
|
||||||
@ -108,29 +111,26 @@ func (cm *ContainerMap) refreshAll() {
|
|||||||
for _, c := range allContainers {
|
for _, c := range allContainers {
|
||||||
cm.needsRefresh[c.ID] = 1
|
cm.needsRefresh[c.ID] = 1
|
||||||
}
|
}
|
||||||
cm.Update()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerMap) Update() {
|
func (cm *ContainerMap) UpdateLoop() {
|
||||||
var ids []string
|
for {
|
||||||
for id, _ := range cm.needsRefresh {
|
switch {
|
||||||
cm.refresh(id)
|
case len(cm.needsRefresh) > 0:
|
||||||
ids = append(ids, id)
|
processed := []string{}
|
||||||
}
|
for id, _ := range cm.needsRefresh {
|
||||||
for _, id := range ids {
|
cm.refresh(id)
|
||||||
delete(cm.needsRefresh, id)
|
processed = append(processed, id)
|
||||||
|
}
|
||||||
|
for _, id := range processed {
|
||||||
|
delete(cm.needsRefresh, id)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill a container by ID
|
|
||||||
//func (cm *ContainerMap) Kill(id string, sig docker.Signal) error {
|
|
||||||
//opts := docker.KillContainerOptions{
|
|
||||||
//ID: id,
|
|
||||||
//Signal: sig,
|
|
||||||
//}
|
|
||||||
//return cm.client.KillContainer(opts)
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Return number of containers/rows
|
// Return number of containers/rows
|
||||||
func (cm *ContainerMap) Len() uint {
|
func (cm *ContainerMap) Len() uint {
|
||||||
return uint(len(cm.containers))
|
return uint(len(cm.containers))
|
||||||
@ -158,9 +158,12 @@ func (cm *ContainerMap) DelByID(id string) {
|
|||||||
|
|
||||||
// Remove one or more containers by index
|
// Remove one or more containers by index
|
||||||
func (cm *ContainerMap) Del(idx ...int) {
|
func (cm *ContainerMap) Del(idx ...int) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
for _, i := range idx {
|
for _, i := range idx {
|
||||||
cm.containers = append(cm.containers[:i], cm.containers[i+1:]...)
|
cm.containers = append(cm.containers[:i], cm.containers[i+1:]...)
|
||||||
}
|
}
|
||||||
|
log.Infof("removed %d dead containers", len(idx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return array of all containers, sorted by field
|
// Return array of all containers, sorted by field
|
||||||
|
30
grid.go
30
grid.go
@ -23,11 +23,6 @@ func NewGrid() *Grid {
|
|||||||
containers: cmap.All(),
|
containers: cmap.All(),
|
||||||
header: widgets.NewCTopHeader(),
|
header: widgets.NewCTopHeader(),
|
||||||
}
|
}
|
||||||
// set initial cursor position
|
|
||||||
if len(g.containers) > 0 {
|
|
||||||
g.cursorID = g.containers[0].id
|
|
||||||
g.containers[0].widgets.Highlight()
|
|
||||||
}
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +30,14 @@ func (g *Grid) calcMaxRows() {
|
|||||||
g.maxRows = ui.TermHeight() - widgets.CompactHeader.Height - ui.Body.Y
|
g.maxRows = ui.TermHeight() - widgets.CompactHeader.Height - ui.Body.Y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set an initial cursor position, if possible
|
||||||
|
func (g *Grid) cursorReset() {
|
||||||
|
if len(g.containers) > 0 {
|
||||||
|
g.cursorID = g.containers[0].id
|
||||||
|
g.containers[0].widgets.Highlight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return current cursor index
|
// Return current cursor index
|
||||||
func (g *Grid) cursorIdx() int {
|
func (g *Grid) cursorIdx() int {
|
||||||
for n, c := range g.containers {
|
for n, c := range g.containers {
|
||||||
@ -94,14 +97,24 @@ func (g *Grid) redrawRows() {
|
|||||||
} else {
|
} else {
|
||||||
ui.Body.Y = 0
|
ui.Body.Y = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Body.AddRows(widgets.CompactHeader)
|
ui.Body.AddRows(widgets.CompactHeader)
|
||||||
|
|
||||||
|
var cursorVisible bool
|
||||||
for n, c := range g.containers.Filter() {
|
for n, c := range g.containers.Filter() {
|
||||||
if n >= g.maxRows {
|
if n >= g.maxRows {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if c.id == g.cursorID {
|
||||||
|
cursorVisible = true
|
||||||
|
}
|
||||||
ui.Body.AddRows(c.widgets.Row())
|
ui.Body.AddRows(c.widgets.Row())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !cursorVisible {
|
||||||
|
g.cursorReset()
|
||||||
|
}
|
||||||
|
|
||||||
ui.Body.Align()
|
ui.Body.Align()
|
||||||
resizeIndicator()
|
resizeIndicator()
|
||||||
ui.Render(ui.Body)
|
ui.Render(ui.Body)
|
||||||
@ -137,8 +150,6 @@ func (g *Grid) ExpandView() {
|
|||||||
defer ui.DefaultEvtStream.ResetHandlers()
|
defer ui.DefaultEvtStream.ResetHandlers()
|
||||||
container, _ := g.cmap.Get(g.cursorID)
|
container, _ := g.cmap.Get(g.cursorID)
|
||||||
container.Expand()
|
container.Expand()
|
||||||
container.widgets.Render()
|
|
||||||
container.Collapse()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func logEvent(e ui.Event) {
|
func logEvent(e ui.Event) {
|
||||||
@ -153,7 +164,6 @@ func logEvent(e ui.Event) {
|
|||||||
func Display(g *Grid) bool {
|
func Display(g *Grid) bool {
|
||||||
var menu func()
|
var menu func()
|
||||||
var expand bool
|
var expand bool
|
||||||
var loopIter int
|
|
||||||
|
|
||||||
ui.DefaultEvtStream.Hook(logEvent)
|
ui.DefaultEvtStream.Hook(logEvent)
|
||||||
|
|
||||||
@ -199,10 +209,6 @@ func Display(g *Grid) bool {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
ui.Handle("/timer/1s", func(e ui.Event) {
|
||||||
loopIter++
|
|
||||||
if loopIter%5 == 0 {
|
|
||||||
g.cmap.Update()
|
|
||||||
}
|
|
||||||
g.containers = g.cmap.All() // refresh containers for current sort order
|
g.containers = g.cmap.All() // refresh containers for current sort order
|
||||||
g.redrawRows()
|
g.redrawRows()
|
||||||
})
|
})
|
||||||
|
@ -15,6 +15,7 @@ const (
|
|||||||
type ContainerWidgets interface {
|
type ContainerWidgets interface {
|
||||||
Row() *ui.Row
|
Row() *ui.Row
|
||||||
Render()
|
Render()
|
||||||
|
Reset()
|
||||||
Highlight()
|
Highlight()
|
||||||
UnHighlight()
|
UnHighlight()
|
||||||
SetStatus(string)
|
SetStatus(string)
|
||||||
@ -42,15 +43,22 @@ type Compact struct {
|
|||||||
row *ui.Row
|
row *ui.Row
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCompact(id string, name string) *Compact {
|
func NewCompact(id, name, status string) *Compact {
|
||||||
w := &Compact{
|
w := &Compact{
|
||||||
Status: slimPar(mark),
|
Status: slimPar(mark),
|
||||||
Cid: slimPar(id),
|
Cid: slimPar(id),
|
||||||
Net: slimPar("-"),
|
|
||||||
Name: slimPar(name),
|
Name: slimPar(name),
|
||||||
Cpu: slimGauge(),
|
|
||||||
Memory: slimGauge(),
|
|
||||||
}
|
}
|
||||||
|
w.Reset()
|
||||||
|
w.SetStatus(status)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set gauges, counters to default unread values
|
||||||
|
func (w *Compact) Reset() {
|
||||||
|
w.Net = slimPar("-")
|
||||||
|
w.Cpu = slimGauge()
|
||||||
|
w.Memory = slimGauge()
|
||||||
w.row = ui.NewRow(
|
w.row = ui.NewRow(
|
||||||
ui.NewCol(1, 0, w.Status),
|
ui.NewCol(1, 0, w.Status),
|
||||||
ui.NewCol(2, 0, w.Name),
|
ui.NewCol(2, 0, w.Name),
|
||||||
@ -59,7 +67,6 @@ func NewCompact(id string, name string) *Compact {
|
|||||||
ui.NewCol(2, 0, w.Memory),
|
ui.NewCol(2, 0, w.Memory),
|
||||||
ui.NewCol(2, 0, w.Net),
|
ui.NewCol(2, 0, w.Net),
|
||||||
)
|
)
|
||||||
return w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Compact) Render() {
|
func (w *Compact) Render() {
|
||||||
|
@ -33,6 +33,9 @@ func NewInfo(id, name string) *ui.Table {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Expanded) Reset() {
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Expanded) Render() {
|
func (w *Expanded) Render() {
|
||||||
ui.Render(w.Info, w.Cpu, w.Mem, w.Net)
|
ui.Render(w.Info, w.Cpu, w.Mem, w.Net)
|
||||||
ui.Handle("/timer/1s", func(ui.Event) {
|
ui.Handle("/timer/1s", func(ui.Event) {
|
||||||
|
Loading…
Reference in New Issue
Block a user