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
|
collector metrics.Collector
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainer(id, name string, collector metrics.Collector) *Container {
|
func NewContainer(id string, collector metrics.Collector) *Container {
|
||||||
return &Container{
|
return &Container{
|
||||||
Metrics: metrics.NewMetrics(),
|
Metrics: metrics.NewMetrics(),
|
||||||
Id: id,
|
Id: id,
|
||||||
Name: name,
|
|
||||||
Meta: make(map[string]string),
|
Meta: make(map[string]string),
|
||||||
Updates: make(chan [2]string),
|
Updates: make(chan [2]string),
|
||||||
Widgets: compact.NewCompact(id, name),
|
Widgets: compact.NewCompact(id),
|
||||||
collector: collector,
|
collector: collector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,6 +40,11 @@ func (c *Container) SetMeta(k, v string) {
|
|||||||
c.Updates <- [2]string{k, v}
|
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) {
|
func (c *Container) SetState(s string) {
|
||||||
c.State = s
|
c.State = s
|
||||||
c.Widgets.Status.Set(s)
|
c.Widgets.Status.Set(s)
|
||||||
|
@ -23,3 +23,13 @@ func (w *GaugeCol) Reset() {
|
|||||||
w.Label = "-"
|
w.Label = "-"
|
||||||
w.Percent = 0
|
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
|
// Update y recursively
|
||||||
cg.header.SetY(cg.Y)
|
cg.header.SetY(cg.Y)
|
||||||
y := cg.Y + 1
|
y := cg.Y + 1
|
||||||
for n, r := range cg.Rows {
|
for _, r := range cg.Rows {
|
||||||
r.SetY(y + n)
|
r.SetY(y)
|
||||||
|
y += r.Height
|
||||||
}
|
}
|
||||||
// Update width recursively
|
// Update width recursively
|
||||||
cg.header.SetWidth(cg.Width)
|
cg.header.SetWidth(cg.Width)
|
||||||
|
@ -28,10 +28,14 @@ type Compact struct {
|
|||||||
Height int
|
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{
|
row := &Compact{
|
||||||
Status: NewStatus(),
|
Status: NewStatus(),
|
||||||
Name: NewTextCol(name),
|
Name: NewTextCol("-"),
|
||||||
Cid: NewTextCol(id),
|
Cid: NewTextCol(id),
|
||||||
Cpu: NewGaugeCol(),
|
Cpu: NewGaugeCol(),
|
||||||
Memory: NewGaugeCol(),
|
Memory: NewGaugeCol(),
|
||||||
@ -41,6 +45,14 @@ func NewCompact(id, name string) *Compact {
|
|||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//func (row *Compact) ToggleExpand() {
|
||||||
|
//if row.Height == 1 {
|
||||||
|
//row.Height = 4
|
||||||
|
//} else {
|
||||||
|
//row.Height = 1
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
func (row *Compact) SetMetrics(m metrics.Metrics) {
|
func (row *Compact) SetMetrics(m metrics.Metrics) {
|
||||||
row.SetCPU(m.CPUUtil)
|
row.SetCPU(m.CPUUtil)
|
||||||
row.SetNet(m.NetRx, m.NetTx)
|
row.SetNet(m.NetRx, m.NetTx)
|
||||||
@ -114,7 +126,7 @@ func (row *Compact) SetNet(rx int64, tx int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (row *Compact) SetCPU(val int) {
|
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))
|
row.Cpu.Label = fmt.Sprintf("%s%%", strconv.Itoa(val))
|
||||||
if val < 5 {
|
if val < 5 {
|
||||||
val = 5
|
val = 5
|
||||||
|
@ -3,8 +3,6 @@ package cwidgets
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -48,13 +46,3 @@ func getPrecision(f float64) int {
|
|||||||
}
|
}
|
||||||
return 2 // default precision
|
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
|
|
||||||
}
|
|
||||||
|
110
dockersource.go
110
dockersource.go
@ -3,16 +3,12 @@ package main
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bcicen/ctop/config"
|
"github.com/bcicen/ctop/config"
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/metrics"
|
||||||
"github.com/fsouza/go-dockerclient"
|
"github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
var lock = sync.RWMutex{}
|
|
||||||
|
|
||||||
type ContainerSource interface {
|
type ContainerSource interface {
|
||||||
All() []*Container
|
All() []*Container
|
||||||
Get(string) (*Container, bool)
|
Get(string) (*Container, bool)
|
||||||
@ -20,8 +16,8 @@ type ContainerSource interface {
|
|||||||
|
|
||||||
type DockerContainerSource struct {
|
type DockerContainerSource struct {
|
||||||
client *docker.Client
|
client *docker.Client
|
||||||
containers Containers
|
containers map[string]*Container
|
||||||
needsRefresh map[string]int // container IDs requiring refresh
|
needsRefresh chan string // container IDs requiring refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDockerContainerSource() *DockerContainerSource {
|
func NewDockerContainerSource() *DockerContainerSource {
|
||||||
@ -32,7 +28,8 @@ func NewDockerContainerSource() *DockerContainerSource {
|
|||||||
}
|
}
|
||||||
cm := &DockerContainerSource{
|
cm := &DockerContainerSource{
|
||||||
client: client,
|
client: client,
|
||||||
needsRefresh: make(map[string]int),
|
containers: make(map[string]*Container),
|
||||||
|
needsRefresh: make(chan string, 60),
|
||||||
}
|
}
|
||||||
cm.refreshAll()
|
cm.refreshAll()
|
||||||
go cm.Loop()
|
go cm.Loop()
|
||||||
@ -53,7 +50,7 @@ func (cm *DockerContainerSource) watchEvents() {
|
|||||||
switch e.Action {
|
switch e.Action {
|
||||||
case "start", "die", "pause", "unpause":
|
case "start", "die", "pause", "unpause":
|
||||||
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.needsRefresh[e.ID] = 1
|
cm.needsRefresh <- e.ID
|
||||||
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)
|
||||||
@ -61,26 +58,14 @@ func (cm *DockerContainerSource) watchEvents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *DockerContainerSource) refresh(id string) {
|
func (cm *DockerContainerSource) refresh(c *Container) {
|
||||||
insp := cm.inspect(id)
|
insp := cm.inspect(c.Id)
|
||||||
// remove container if no longer exists
|
// remove container if no longer exists
|
||||||
if insp == nil {
|
if insp == nil {
|
||||||
cm.delByID(id)
|
cm.delByID(c.Id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.SetName(shortName(insp.Name))
|
||||||
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.SetState(insp.State.Status)
|
c.SetState(insp.State.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,68 +87,55 @@ func (cm *DockerContainerSource) refreshAll() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range allContainers {
|
for _, i := range allContainers {
|
||||||
cm.needsRefresh[c.ID] = 1
|
c := cm.MustGet(i.ID)
|
||||||
|
c.SetName(shortName(i.Names[0]))
|
||||||
|
c.SetState(i.State)
|
||||||
|
cm.needsRefresh <- c.Id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *DockerContainerSource) Loop() {
|
func (cm *DockerContainerSource) Loop() {
|
||||||
for {
|
for id := range cm.needsRefresh {
|
||||||
switch {
|
c := cm.MustGet(id)
|
||||||
case len(cm.needsRefresh) > 0:
|
cm.refresh(c)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Get a single container, by ID
|
||||||
func (cm *DockerContainerSource) Get(id string) (*Container, bool) {
|
func (cm *DockerContainerSource) Get(id string) (*Container, bool) {
|
||||||
for _, c := range cm.containers {
|
c, ok := cm.containers[id]
|
||||||
if c.Id == id {
|
return c, ok
|
||||||
return c, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove containers by ID
|
// Remove containers by ID
|
||||||
func (cm *DockerContainerSource) delByID(id string) {
|
func (cm *DockerContainerSource) delByID(id string) {
|
||||||
for n, c := range cm.containers {
|
delete(cm.containers, id)
|
||||||
if c.Id == id {
|
log.Infof("removed dead container: %s", 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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return array of all containers, sorted by field
|
// Return array of all containers, sorted by field
|
||||||
func (cm *DockerContainerSource) All() []*Container {
|
func (cm *DockerContainerSource) All() []*Container {
|
||||||
sort.Sort(cm.containers)
|
var containers Containers
|
||||||
return cm.containers
|
for _, c := range cm.containers {
|
||||||
}
|
containers = append(containers, c)
|
||||||
|
}
|
||||||
// truncate container id
|
sort.Sort(containers)
|
||||||
func shortID(id string) string {
|
return containers
|
||||||
return id[:12]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use primary container name
|
// use primary container name
|
||||||
|
11
grid.go
11
grid.go
@ -23,11 +23,9 @@ type Grid struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewGrid() *Grid {
|
func NewGrid() *Grid {
|
||||||
cs := NewDockerContainerSource()
|
|
||||||
g := &Grid{
|
g := &Grid{
|
||||||
cSource: cs,
|
cSource: NewDockerContainerSource(),
|
||||||
containers: cs.All(),
|
header: widgets.NewCTopHeader(),
|
||||||
header: widgets.NewCTopHeader(),
|
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@ -169,6 +167,7 @@ func Display(g *Grid) bool {
|
|||||||
ui.DefaultEvtStream.Hook(logEvent)
|
ui.DefaultEvtStream.Hook(logEvent)
|
||||||
|
|
||||||
// initial draw
|
// initial draw
|
||||||
|
g.containers = g.cSource.All()
|
||||||
g.redrawRows()
|
g.redrawRows()
|
||||||
|
|
||||||
ui.Handle("/sys/kbd/<up>", func(ui.Event) {
|
ui.Handle("/sys/kbd/<up>", func(ui.Event) {
|
||||||
@ -178,7 +177,9 @@ func Display(g *Grid) bool {
|
|||||||
g.cursorDown()
|
g.cursorDown()
|
||||||
})
|
})
|
||||||
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
|
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) {
|
ui.Handle("/sys/kbd/a", func(ui.Event) {
|
||||||
|
@ -13,7 +13,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
Log *CTopLogger
|
Log *CTopLogger
|
||||||
exited bool
|
exited bool
|
||||||
level = logging.INFO
|
level = logging.DEBUG
|
||||||
format = logging.MustStringFormatter(
|
format = logging.MustStringFormatter(
|
||||||
`%{color}%{time:15:04:05.000} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
|
`%{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++ {
|
for i := 0; i < total; i++ {
|
||||||
collector := metrics.NewMock()
|
collector := metrics.NewMock()
|
||||||
c := NewContainer(makeID(), makeName(), collector)
|
c := NewContainer(makeID(), collector)
|
||||||
lock.Lock()
|
c.SetName(makeName())
|
||||||
cs.containers = append(cs.containers, c)
|
cs.containers = append(cs.containers, c)
|
||||||
lock.Unlock()
|
|
||||||
|
|
||||||
c.SetState(makeState())
|
c.SetState(makeState())
|
||||||
}
|
}
|
||||||
@ -74,8 +73,6 @@ func (cs *MockContainerSource) delByID(id string) {
|
|||||||
|
|
||||||
// Remove one or more containers by index
|
// Remove one or more containers by index
|
||||||
func (cs *MockContainerSource) del(idx ...int) {
|
func (cs *MockContainerSource) del(idx ...int) {
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
for _, i := range idx {
|
for _, i := range idx {
|
||||||
cs.containers = append(cs.containers[:i], cs.containers[i+1:]...)
|
cs.containers = append(cs.containers[:i], cs.containers[i+1:]...)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user