refactor dockersource container refresh into channel

This commit is contained in:
Bradley Cicenas 2017-03-05 06:46:41 +00:00
parent 8fb5c5de59
commit 3172f141f9
9 changed files with 85 additions and 100 deletions

View File

@ -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)

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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

11
grid.go
View File

@ -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/<up>", func(ui.Event) {
@ -178,7 +177,9 @@ func Display(g *Grid) bool {
g.cursorDown()
})
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
ui.StopLoop()
//c := g.containers[g.cursorIdx()]
//c.Widgets.ToggleExpand()
g.redrawRows()
})
ui.Handle("/sys/kbd/a", func(ui.Event) {

View File

@ -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}`,
)

View File

@ -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:]...)
}