add ContainerSource interface, fix secondary sort method

This commit is contained in:
Bradley Cicenas 2017-02-26 21:12:28 +00:00
parent 4709624b17
commit 05b50af87b
5 changed files with 43 additions and 28 deletions

View File

@ -36,7 +36,7 @@ func (c *Container) Expand() {
var curWidgets widgets.ContainerWidgets var curWidgets widgets.ContainerWidgets
curWidgets = c.widgets curWidgets = c.widgets
c.widgets = widgets.NewExpanded(c.ShortID(), c.ShortName()) c.widgets = widgets.NewExpanded(c.ShortID(), c.name)
c.widgets.Render(0, 0) c.widgets.Render(0, 0)
c.widgets = curWidgets c.widgets = curWidgets
} }

View File

@ -12,32 +12,37 @@ import (
var lock = sync.RWMutex{} var lock = sync.RWMutex{}
type ContainerMap struct { type ContainerSource interface {
All() []*Container
Get(string) (*Container, bool)
}
type DockerContainerSource struct {
client *docker.Client client *docker.Client
containers Containers containers Containers
collectors map[string]metrics.Collector collectors map[string]metrics.Collector
needsRefresh map[string]int // container IDs requiring refresh needsRefresh map[string]int // container IDs requiring refresh
} }
func NewContainerMap() *ContainerMap { func NewDockerContainerSource() *DockerContainerSource {
// init docker client // init docker client
client, err := docker.NewClient(config.GetVal("dockerHost")) client, err := docker.NewClient(config.GetVal("dockerHost"))
if err != nil { if err != nil {
panic(err) panic(err)
} }
cm := &ContainerMap{ cm := &DockerContainerSource{
client: client, client: client,
collectors: make(map[string]metrics.Collector), collectors: make(map[string]metrics.Collector),
needsRefresh: make(map[string]int), needsRefresh: make(map[string]int),
} }
cm.refreshAll() cm.refreshAll()
go cm.UpdateLoop() go cm.Loop()
go cm.watchEvents() go cm.watchEvents()
return cm return cm
} }
// Docker events watcher // Docker events watcher
func (cm *ContainerMap) watchEvents() { func (cm *DockerContainerSource) 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)
@ -52,16 +57,16 @@ func (cm *ContainerMap) watchEvents() {
cm.needsRefresh[e.ID] = 1 cm.needsRefresh[e.ID] = 1
case "destroy": case "destroy":
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID) log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
cm.DelByID(e.ID) cm.delByID(e.ID)
} }
} }
} }
func (cm *ContainerMap) refresh(id string) { func (cm *DockerContainerSource) refresh(id string) {
insp := cm.inspect(id) insp := cm.inspect(id)
// remove container if no longer exists // remove container if no longer exists
if insp == nil { if insp == nil {
cm.DelByID(id) cm.delByID(id)
return return
} }
@ -91,7 +96,7 @@ func (cm *ContainerMap) refresh(id string) {
} }
} }
func (cm *ContainerMap) inspect(id string) *docker.Container { func (cm *DockerContainerSource) inspect(id string) *docker.Container {
c, err := cm.client.InspectContainer(id) c, err := cm.client.InspectContainer(id)
if err != nil { if err != nil {
if _, ok := err.(*docker.NoSuchContainer); ok == false { if _, ok := err.(*docker.NoSuchContainer); ok == false {
@ -101,7 +106,8 @@ func (cm *ContainerMap) inspect(id string) *docker.Container {
return c return c
} }
func (cm *ContainerMap) refreshAll() { // Mark all container IDs for refresh
func (cm *DockerContainerSource) refreshAll() {
opts := docker.ListContainersOptions{All: true} opts := docker.ListContainersOptions{All: true}
allContainers, err := cm.client.ListContainers(opts) allContainers, err := cm.client.ListContainers(opts)
if err != nil { if err != nil {
@ -113,7 +119,7 @@ func (cm *ContainerMap) refreshAll() {
} }
} }
func (cm *ContainerMap) UpdateLoop() { func (cm *DockerContainerSource) Loop() {
for { for {
switch { switch {
case len(cm.needsRefresh) > 0: case len(cm.needsRefresh) > 0:
@ -126,13 +132,13 @@ func (cm *ContainerMap) UpdateLoop() {
delete(cm.needsRefresh, id) delete(cm.needsRefresh, id)
} }
default: default:
time.Sleep(1 * time.Second) time.Sleep(3 * time.Second)
} }
} }
} }
// Get a single container, by ID // Get a single container, by ID
func (cm *ContainerMap) Get(id string) (*Container, bool) { func (cm *DockerContainerSource) Get(id string) (*Container, bool) {
for _, c := range cm.containers { for _, c := range cm.containers {
if c.id == id { if c.id == id {
return c, true return c, true
@ -142,17 +148,17 @@ func (cm *ContainerMap) Get(id string) (*Container, bool) {
} }
// Remove containers by ID // Remove containers by ID
func (cm *ContainerMap) DelByID(id string) { func (cm *DockerContainerSource) delByID(id string) {
for n, c := range cm.containers { for n, c := range cm.containers {
if c.id == id { if c.id == id {
cm.Del(n) cm.del(n)
return return
} }
} }
} }
// Remove one or more containers by index // Remove one or more containers by index
func (cm *ContainerMap) Del(idx ...int) { func (cm *DockerContainerSource) del(idx ...int) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
for _, i := range idx { for _, i := range idx {
@ -162,7 +168,7 @@ func (cm *ContainerMap) Del(idx ...int) {
} }
// Return array of all containers, sorted by field // Return array of all containers, sorted by field
func (cm *ContainerMap) All() []*Container { func (cm *DockerContainerSource) All() []*Container {
sort.Sort(cm.containers) sort.Sort(cm.containers)
return cm.containers return cm.containers
} }

12
grid.go
View File

@ -16,16 +16,16 @@ func maxRows() int {
type Grid struct { type Grid struct {
cursorID string // id of currently selected container cursorID string // id of currently selected container
cmap *ContainerMap cSource ContainerSource
containers Containers // sorted slice of containers containers Containers // sorted slice of containers
header *widgets.CTopHeader header *widgets.CTopHeader
} }
func NewGrid() *Grid { func NewGrid() *Grid {
cmap := NewContainerMap() cs := NewDockerContainerSource()
g := &Grid{ g := &Grid{
cmap: cmap, cSource: cs,
containers: cmap.All(), containers: cs.All(),
header: widgets.NewCTopHeader(), header: widgets.NewCTopHeader(),
} }
return g return g
@ -125,7 +125,7 @@ func (g *Grid) ExpandView() {
ui.Clear() ui.Clear()
ui.DefaultEvtStream.ResetHandlers() ui.DefaultEvtStream.ResetHandlers()
defer ui.DefaultEvtStream.ResetHandlers() defer ui.DefaultEvtStream.ResetHandlers()
container, _ := g.cmap.Get(g.cursorID) container, _ := g.cSource.Get(g.cursorID)
container.Expand() container.Expand()
} }
@ -187,7 +187,7 @@ func Display(g *Grid) bool {
}) })
ui.Handle("/timer/1s", func(e ui.Event) { ui.Handle("/timer/1s", func(e ui.Event) {
g.containers = g.cmap.All() // refresh containers for current sort order g.containers = g.cSource.All() // refresh containers for current sort order
g.redrawRows() g.redrawRows()
}) })

16
sort.go
View File

@ -16,32 +16,40 @@ var Sorters = map[string]sortMethod{
"id": idSorter, "id": idSorter,
"name": nameSorter, "name": nameSorter,
"cpu": func(c1, c2 *Container) bool { "cpu": func(c1, c2 *Container) bool {
// Use secondary sort method if equal values
if c1.metrics.CPUUtil == c2.metrics.CPUUtil { if c1.metrics.CPUUtil == c2.metrics.CPUUtil {
return nameSorter(c1, c2) return nameSorter(c1, c2)
} }
return c1.metrics.CPUUtil < c2.metrics.CPUUtil return c1.metrics.CPUUtil > c2.metrics.CPUUtil
}, },
"mem": func(c1, c2 *Container) bool { "mem": func(c1, c2 *Container) bool {
// Use secondary sort method if equal values
if c1.metrics.MemUsage == c2.metrics.MemUsage { if c1.metrics.MemUsage == c2.metrics.MemUsage {
return nameSorter(c1, c2) return nameSorter(c1, c2)
} }
return c1.metrics.MemUsage < c2.metrics.MemUsage return c1.metrics.MemUsage > c2.metrics.MemUsage
}, },
"mem %": func(c1, c2 *Container) bool { "mem %": func(c1, c2 *Container) bool {
// Use secondary sort method if equal values
if c1.metrics.MemPercent == c2.metrics.MemPercent { if c1.metrics.MemPercent == c2.metrics.MemPercent {
return nameSorter(c1, c2) return nameSorter(c1, c2)
} }
return c1.metrics.MemPercent < c2.metrics.MemPercent return c1.metrics.MemPercent > c2.metrics.MemPercent
}, },
"net": func(c1, c2 *Container) bool { "net": func(c1, c2 *Container) bool {
sum1 := sumNet(c1) sum1 := sumNet(c1)
sum2 := sumNet(c2) sum2 := sumNet(c2)
// Use secondary sort method if equal values
if sum1 == sum2 { if sum1 == sum2 {
return nameSorter(c1, c2) return nameSorter(c1, c2)
} }
return sum1 < sum2 return sum1 > sum2
}, },
"state": func(c1, c2 *Container) bool { "state": func(c1, c2 *Container) bool {
// Use secondary sort method if equal values
if c1.state == c2.state {
return nameSorter(c1, c2)
}
if c1.state == "running" { if c1.state == "running" {
return true return true
} }

View File

@ -287,5 +287,6 @@ func slimGauge() *ui.Gauge {
g.PaddingBottom = 0 g.PaddingBottom = 0
g.BarColor = ui.ColorGreen g.BarColor = ui.ColorGreen
g.Label = "-" g.Label = "-"
g.Bg = ui.ColorBlack
return g return g
} }