mirror of
https://github.com/bcicen/ctop.git
synced 2024-08-30 18:23:19 +00:00
refactor dockersource container refresh into channel
This commit is contained in:
parent
8fb5c5de59
commit
3172f141f9
10
container.go
10
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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
108
dockersource.go
108
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
|
||||
|
9
grid.go
9
grid.go
@ -23,10 +23,8 @@ type Grid struct {
|
||||
}
|
||||
|
||||
func NewGrid() *Grid {
|
||||
cs := NewDockerContainerSource()
|
||||
g := &Grid{
|
||||
cSource: cs,
|
||||
containers: cs.All(),
|
||||
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) {
|
||||
|
@ -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}`,
|
||||
)
|
||||
|
@ -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:]...)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user