From c3bb7c6b964a3f82899dd9ea89625aa74ba7246f Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Fri, 24 Feb 2017 09:10:14 +0000 Subject: [PATCH] further containermap refactoring --- container.go | 18 +++++++--- containermap.go | 83 +++++++++++++++++++++++---------------------- grid.go | 30 +++++++++------- widgets/compact.go | 17 +++++++--- widgets/expanded.go | 3 ++ 5 files changed, 89 insertions(+), 62 deletions(-) diff --git a/container.go b/container.go index a6cf7e9..f5fd2ae 100644 --- a/container.go +++ b/container.go @@ -20,7 +20,7 @@ func NewContainer(id, name string) *Container { id: id, name: name, } - c.Collapse() + c.widgets = widgets.NewCompact(c.ShortID(), c.ShortName(), c.state) return c } @@ -33,11 +33,12 @@ func (c *Container) ShortName() string { } func (c *Container) Expand() { - c.widgets = widgets.NewExpanded(c.id, c.name) -} + var curWidgets widgets.ContainerWidgets -func (c *Container) Collapse() { - c.widgets = widgets.NewCompact(c.ShortID(), c.ShortName()) + curWidgets = c.widgets + c.widgets = widgets.NewExpanded(c.ShortID(), c.ShortName()) + c.widgets.Render() + c.widgets = curWidgets } func (c *Container) SetState(s string) { @@ -45,6 +46,12 @@ func (c *Container) SetState(s string) { 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 func (c *Container) Read(stream chan metrics.Metrics) { go func() { @@ -55,6 +62,7 @@ func (c *Container) Read(stream chan metrics.Metrics) { c.widgets.SetNet(metrics.NetRx, metrics.NetTx) } log.Infof("reader stopped for container: %s", c.id) + c.reset() }() log.Infof("reader started for container: %s", c.id) } diff --git a/containermap.go b/containermap.go index 20920de..d19f45e 100644 --- a/containermap.go +++ b/containermap.go @@ -2,12 +2,16 @@ package main import ( "sort" + "sync" + "time" "github.com/bcicen/ctop/config" "github.com/bcicen/ctop/metrics" "github.com/fsouza/go-dockerclient" ) +var lock = sync.RWMutex{} + type ContainerMap struct { client *docker.Client containers Containers @@ -27,32 +31,29 @@ func NewContainerMap() *ContainerMap { needsRefresh: make(map[string]int), } cm.refreshAll() - go cm.watch() + go cm.UpdateLoop() + go cm.watchEvents() return cm } // Docker events watcher -func (cm *ContainerMap) watch() { +func (cm *ContainerMap) watchEvents() { log.Info("docker event listener starting") events := make(chan *docker.APIEvents) cm.client.AddEventListener(events) for e := range events { - cm.handleEvent(e) - } -} - -// Docker event handler -func (cm *ContainerMap) handleEvent(e *docker.APIEvents) { - // only process container events - if e.Type != "container" { - return - } - switch e.Action { - case "start", "die", "pause", "unpause": - cm.needsRefresh[e.ID] = 1 - case "destroy": - cm.DelByID(e.ID) + if e.Type != "container" { + continue + } + switch e.Action { + case "start", "die", "pause", "unpause": + log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID) + cm.needsRefresh[e.ID] = 1 + case "destroy": + log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID) + cm.DelByID(e.ID) + } } } @@ -67,12 +68,10 @@ func (cm *ContainerMap) refresh(id string) { c, ok := cm.Get(id) // append container struct for new containers if !ok { - c = &Container{ - id: id, - name: insp.Name, - } - c.Collapse() + c = NewContainer(id, insp.Name) + lock.Lock() cm.containers = append(cm.containers, c) + lock.Unlock() // create collector if _, ok := cm.collectors[id]; ok == false { cm.collectors[id] = metrics.NewDocker(cm.client, id) @@ -86,6 +85,10 @@ func (cm *ContainerMap) refresh(id string) { cm.collectors[c.id].Start() 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 { @@ -108,29 +111,26 @@ func (cm *ContainerMap) refreshAll() { for _, c := range allContainers { cm.needsRefresh[c.ID] = 1 } - cm.Update() } -func (cm *ContainerMap) Update() { - var ids []string - for id, _ := range cm.needsRefresh { - cm.refresh(id) - ids = append(ids, id) - } - for _, id := range ids { - delete(cm.needsRefresh, id) +func (cm *ContainerMap) UpdateLoop() { + for { + switch { + case len(cm.needsRefresh) > 0: + processed := []string{} + for id, _ := range cm.needsRefresh { + cm.refresh(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 func (cm *ContainerMap) Len() uint { return uint(len(cm.containers)) @@ -158,9 +158,12 @@ func (cm *ContainerMap) DelByID(id string) { // Remove one or more containers by index func (cm *ContainerMap) Del(idx ...int) { + lock.Lock() + defer lock.Unlock() for _, i := range idx { 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 diff --git a/grid.go b/grid.go index c203c85..e912a98 100644 --- a/grid.go +++ b/grid.go @@ -23,11 +23,6 @@ func NewGrid() *Grid { containers: cmap.All(), 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 } @@ -35,6 +30,14 @@ func (g *Grid) calcMaxRows() { 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 func (g *Grid) cursorIdx() int { for n, c := range g.containers { @@ -94,14 +97,24 @@ func (g *Grid) redrawRows() { } else { ui.Body.Y = 0 } + ui.Body.AddRows(widgets.CompactHeader) + + var cursorVisible bool for n, c := range g.containers.Filter() { if n >= g.maxRows { break } + if c.id == g.cursorID { + cursorVisible = true + } ui.Body.AddRows(c.widgets.Row()) } + if !cursorVisible { + g.cursorReset() + } + ui.Body.Align() resizeIndicator() ui.Render(ui.Body) @@ -137,8 +150,6 @@ func (g *Grid) ExpandView() { defer ui.DefaultEvtStream.ResetHandlers() container, _ := g.cmap.Get(g.cursorID) container.Expand() - container.widgets.Render() - container.Collapse() } func logEvent(e ui.Event) { @@ -153,7 +164,6 @@ func logEvent(e ui.Event) { func Display(g *Grid) bool { var menu func() var expand bool - var loopIter int ui.DefaultEvtStream.Hook(logEvent) @@ -199,10 +209,6 @@ func Display(g *Grid) bool { }) 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.redrawRows() }) diff --git a/widgets/compact.go b/widgets/compact.go index 9070f3c..e2ff9a6 100644 --- a/widgets/compact.go +++ b/widgets/compact.go @@ -15,6 +15,7 @@ const ( type ContainerWidgets interface { Row() *ui.Row Render() + Reset() Highlight() UnHighlight() SetStatus(string) @@ -42,15 +43,22 @@ type Compact struct { row *ui.Row } -func NewCompact(id string, name string) *Compact { +func NewCompact(id, name, status string) *Compact { w := &Compact{ Status: slimPar(mark), Cid: slimPar(id), - Net: slimPar("-"), 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( ui.NewCol(1, 0, w.Status), 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.Net), ) - return w } func (w *Compact) Render() { diff --git a/widgets/expanded.go b/widgets/expanded.go index d066bd0..5584589 100644 --- a/widgets/expanded.go +++ b/widgets/expanded.go @@ -33,6 +33,9 @@ func NewInfo(id, name string) *ui.Table { return p } +func (w *Expanded) Reset() { +} + func (w *Expanded) Render() { ui.Render(w.Info, w.Cpu, w.Mem, w.Net) ui.Handle("/timer/1s", func(ui.Event) {