From 3172f141f90c21d084c6acc964752084fec6f702 Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Sun, 5 Mar 2017 06:46:41 +0000 Subject: [PATCH] refactor dockersource container refresh into channel --- container.go | 10 ++-- cwidgets/compact/gauge.go | 10 ++++ cwidgets/compact/grid.go | 5 +- cwidgets/compact/main.go | 18 +++++-- cwidgets/util.go | 12 ----- dockersource.go | 110 ++++++++++++++------------------------ grid.go | 11 ++-- logging/main.go | 2 +- mocksource.go | 7 +-- 9 files changed, 85 insertions(+), 100 deletions(-) diff --git a/container.go b/container.go index 6cbe526..16e4124 100644 --- a/container.go +++ b/container.go @@ -17,14 +17,13 @@ type Container struct { collector metrics.Collector } -func NewContainer(id, name string, collector metrics.Collector) *Container { +func NewContainer(id string, collector metrics.Collector) *Container { return &Container{ Metrics: metrics.NewMetrics(), Id: id, - Name: name, Meta: make(map[string]string), Updates: make(chan [2]string), - Widgets: compact.NewCompact(id, name), + Widgets: compact.NewCompact(id), collector: collector, } } @@ -41,6 +40,11 @@ func (c *Container) SetMeta(k, v string) { c.Updates <- [2]string{k, v} } +func (c *Container) SetName(n string) { + c.Name = n + c.Widgets.Name.Set(n) +} + func (c *Container) SetState(s string) { c.State = s c.Widgets.Status.Set(s) diff --git a/cwidgets/compact/gauge.go b/cwidgets/compact/gauge.go index 26b3c0a..42d1650 100644 --- a/cwidgets/compact/gauge.go +++ b/cwidgets/compact/gauge.go @@ -23,3 +23,13 @@ func (w *GaugeCol) Reset() { w.Label = "-" w.Percent = 0 } + +func colorScale(n int) ui.Attribute { + if n > 70 { + return ui.ColorRed + } + if n > 30 { + return ui.ColorYellow + } + return ui.ColorGreen +} diff --git a/cwidgets/compact/grid.go b/cwidgets/compact/grid.go index 9747028..ec03f77 100644 --- a/cwidgets/compact/grid.go +++ b/cwidgets/compact/grid.go @@ -24,8 +24,9 @@ func (cg *CompactGrid) Align() { // Update y recursively cg.header.SetY(cg.Y) y := cg.Y + 1 - for n, r := range cg.Rows { - r.SetY(y + n) + for _, r := range cg.Rows { + r.SetY(y) + y += r.Height } // Update width recursively cg.header.SetWidth(cg.Width) diff --git a/cwidgets/compact/main.go b/cwidgets/compact/main.go index 432e8ef..c45712a 100644 --- a/cwidgets/compact/main.go +++ b/cwidgets/compact/main.go @@ -28,10 +28,14 @@ type Compact struct { Height int } -func NewCompact(id, name string) *Compact { +func NewCompact(id string) *Compact { + // truncate container id + if len(id) > 12 { + id = id[:12] + } row := &Compact{ Status: NewStatus(), - Name: NewTextCol(name), + Name: NewTextCol("-"), Cid: NewTextCol(id), Cpu: NewGaugeCol(), Memory: NewGaugeCol(), @@ -41,6 +45,14 @@ func NewCompact(id, name string) *Compact { return row } +//func (row *Compact) ToggleExpand() { +//if row.Height == 1 { +//row.Height = 4 +//} else { +//row.Height = 1 +//} +//} + func (row *Compact) SetMetrics(m metrics.Metrics) { row.SetCPU(m.CPUUtil) row.SetNet(m.NetRx, m.NetTx) @@ -114,7 +126,7 @@ func (row *Compact) SetNet(rx int64, tx int64) { } func (row *Compact) SetCPU(val int) { - row.Cpu.BarColor = cwidgets.ColorScale(val) + row.Cpu.BarColor = colorScale(val) row.Cpu.Label = fmt.Sprintf("%s%%", strconv.Itoa(val)) if val < 5 { val = 5 diff --git a/cwidgets/util.go b/cwidgets/util.go index 3288d43..ba6c326 100644 --- a/cwidgets/util.go +++ b/cwidgets/util.go @@ -3,8 +3,6 @@ package cwidgets import ( "fmt" "strconv" - - ui "github.com/gizak/termui" ) const ( @@ -48,13 +46,3 @@ func getPrecision(f float64) int { } return 2 // default precision } - -func ColorScale(n int) ui.Attribute { - if n > 70 { - return ui.ColorRed - } - if n > 30 { - return ui.ColorYellow - } - return ui.ColorGreen -} diff --git a/dockersource.go b/dockersource.go index 31b82b8..84d5d99 100644 --- a/dockersource.go +++ b/dockersource.go @@ -3,16 +3,12 @@ package main import ( "sort" "strings" - "sync" - "time" "github.com/bcicen/ctop/config" "github.com/bcicen/ctop/metrics" "github.com/fsouza/go-dockerclient" ) -var lock = sync.RWMutex{} - type ContainerSource interface { All() []*Container Get(string) (*Container, bool) @@ -20,8 +16,8 @@ type ContainerSource interface { type DockerContainerSource struct { client *docker.Client - containers Containers - needsRefresh map[string]int // container IDs requiring refresh + containers map[string]*Container + needsRefresh chan string // container IDs requiring refresh } func NewDockerContainerSource() *DockerContainerSource { @@ -32,7 +28,8 @@ func NewDockerContainerSource() *DockerContainerSource { } cm := &DockerContainerSource{ client: client, - needsRefresh: make(map[string]int), + containers: make(map[string]*Container), + needsRefresh: make(chan string, 60), } cm.refreshAll() go cm.Loop() @@ -53,7 +50,7 @@ func (cm *DockerContainerSource) watchEvents() { 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 + cm.needsRefresh <- e.ID case "destroy": log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID) cm.delByID(e.ID) @@ -61,26 +58,14 @@ func (cm *DockerContainerSource) watchEvents() { } } -func (cm *DockerContainerSource) refresh(id string) { - insp := cm.inspect(id) +func (cm *DockerContainerSource) refresh(c *Container) { + insp := cm.inspect(c.Id) // remove container if no longer exists if insp == nil { - cm.delByID(id) + cm.delByID(c.Id) return } - - c, ok := cm.Get(id) - // append container struct for new containers - if !ok { - // create collector - collector := metrics.NewDocker(cm.client, id) - // create container - c = NewContainer(shortID(id), shortName(insp.Name), collector) - lock.Lock() - cm.containers = append(cm.containers, c) - lock.Unlock() - } - + c.SetName(shortName(insp.Name)) c.SetState(insp.State.Status) } @@ -102,68 +87,55 @@ func (cm *DockerContainerSource) refreshAll() { panic(err) } - for _, c := range allContainers { - cm.needsRefresh[c.ID] = 1 + for _, i := range allContainers { + c := cm.MustGet(i.ID) + c.SetName(shortName(i.Names[0])) + c.SetState(i.State) + cm.needsRefresh <- c.Id } } func (cm *DockerContainerSource) Loop() { - 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(3 * time.Second) - } + for id := range cm.needsRefresh { + c := cm.MustGet(id) + cm.refresh(c) } } +// Get a single container, creating one anew if not existing +func (cm *DockerContainerSource) MustGet(id string) *Container { + c, ok := cm.Get(id) + // append container struct for new containers + if !ok { + // create collector + collector := metrics.NewDocker(cm.client, id) + // create container + c = NewContainer(id, collector) + cm.containers[id] = c + } + return c +} + // Get a single container, by ID func (cm *DockerContainerSource) Get(id string) (*Container, bool) { - for _, c := range cm.containers { - if c.Id == id { - return c, true - } - } - return nil, false + c, ok := cm.containers[id] + return c, ok } // Remove containers by ID func (cm *DockerContainerSource) delByID(id string) { - for n, c := range cm.containers { - if c.Id == id { - cm.del(n) - return - } - } -} - -// Remove one or more containers by index -func (cm *DockerContainerSource) 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)) + delete(cm.containers, id) + log.Infof("removed dead container: %s", id) } // Return array of all containers, sorted by field func (cm *DockerContainerSource) All() []*Container { - sort.Sort(cm.containers) - return cm.containers -} - -// truncate container id -func shortID(id string) string { - return id[:12] + var containers Containers + for _, c := range cm.containers { + containers = append(containers, c) + } + sort.Sort(containers) + return containers } // use primary container name diff --git a/grid.go b/grid.go index caff5f8..dcfa4ed 100644 --- a/grid.go +++ b/grid.go @@ -23,11 +23,9 @@ type Grid struct { } func NewGrid() *Grid { - cs := NewDockerContainerSource() g := &Grid{ - cSource: cs, - containers: cs.All(), - header: widgets.NewCTopHeader(), + cSource: NewDockerContainerSource(), + header: widgets.NewCTopHeader(), } return g } @@ -169,6 +167,7 @@ func Display(g *Grid) bool { ui.DefaultEvtStream.Hook(logEvent) // initial draw + g.containers = g.cSource.All() g.redrawRows() ui.Handle("/sys/kbd/", func(ui.Event) { @@ -178,7 +177,9 @@ func Display(g *Grid) bool { g.cursorDown() }) ui.Handle("/sys/kbd/", func(ui.Event) { - ui.StopLoop() + //c := g.containers[g.cursorIdx()] + //c.Widgets.ToggleExpand() + g.redrawRows() }) ui.Handle("/sys/kbd/a", func(ui.Event) { diff --git a/logging/main.go b/logging/main.go index cea31b0..18161ed 100644 --- a/logging/main.go +++ b/logging/main.go @@ -13,7 +13,7 @@ const ( var ( Log *CTopLogger exited bool - level = logging.INFO + level = logging.DEBUG format = logging.MustStringFormatter( `%{color}%{time:15:04:05.000} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`, ) diff --git a/mocksource.go b/mocksource.go index ed22ca5..8ffcb0b 100644 --- a/mocksource.go +++ b/mocksource.go @@ -29,10 +29,9 @@ func (cs *MockContainerSource) Init() { for i := 0; i < total; i++ { collector := metrics.NewMock() - c := NewContainer(makeID(), makeName(), collector) - lock.Lock() + c := NewContainer(makeID(), collector) + c.SetName(makeName()) cs.containers = append(cs.containers, c) - lock.Unlock() c.SetState(makeState()) } @@ -74,8 +73,6 @@ func (cs *MockContainerSource) delByID(id string) { // Remove one or more containers by index func (cs *MockContainerSource) del(idx ...int) { - lock.Lock() - defer lock.Unlock() for _, i := range idx { cs.containers = append(cs.containers[:i], cs.containers[i+1:]...) }