ctop/containermap.go

174 lines
3.7 KiB
Go
Raw Normal View History

2016-12-30 22:17:46 +00:00
package main
import (
2017-01-12 19:24:12 +00:00
"sort"
2017-02-24 09:10:14 +00:00
"sync"
"time"
2017-01-12 19:24:12 +00:00
2017-02-07 03:33:09 +00:00
"github.com/bcicen/ctop/config"
2017-02-23 02:03:55 +00:00
"github.com/bcicen/ctop/metrics"
2016-12-30 22:17:46 +00:00
"github.com/fsouza/go-dockerclient"
)
2017-02-24 09:10:14 +00:00
var lock = sync.RWMutex{}
type ContainerMap struct {
client *docker.Client
containers Containers
collectors map[string]metrics.Collector
needsRefresh map[string]int // container IDs requiring refresh
}
2016-12-30 22:17:46 +00:00
func NewContainerMap() *ContainerMap {
// init docker client
client, err := docker.NewClient(config.GetVal("dockerHost"))
if err != nil {
panic(err)
}
cm := &ContainerMap{
client: client,
collectors: make(map[string]metrics.Collector),
needsRefresh: make(map[string]int),
2016-12-30 22:17:46 +00:00
}
cm.refreshAll()
2017-02-24 09:10:14 +00:00
go cm.UpdateLoop()
go cm.watchEvents()
return cm
2016-12-30 22:17:46 +00:00
}
// Docker events watcher
2017-02-24 09:10:14 +00:00
func (cm *ContainerMap) watchEvents() {
log.Info("docker event listener starting")
events := make(chan *docker.APIEvents)
cm.client.AddEventListener(events)
for e := range events {
2017-02-24 09:10:14 +00:00
if e.Type != "container" {
continue
}
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
case "destroy":
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
cm.DelByID(e.ID)
}
}
}
func (cm *ContainerMap) refresh(id string) {
insp := cm.inspect(id)
// remove container if no longer exists
if insp == nil {
cm.DelByID(id)
return
}
c, ok := cm.Get(id)
// append container struct for new containers
if !ok {
2017-02-24 09:10:14 +00:00
c = NewContainer(id, insp.Name)
lock.Lock()
cm.containers = append(cm.containers, c)
2017-02-24 09:10:14 +00:00
lock.Unlock()
// create collector
if _, ok := cm.collectors[id]; ok == false {
cm.collectors[id] = metrics.NewDocker(cm.client, id)
}
}
c.SetState(insp.State.Status)
2017-02-23 22:00:05 +00:00
// start collector if needed
if c.state == "running" && !cm.collectors[c.id].Running() {
cm.collectors[c.id].Start()
c.Read(cm.collectors[c.id].Stream())
}
2017-02-24 09:10:14 +00:00
// stop collector if needed
if c.state != "running" && cm.collectors[c.id].Running() {
cm.collectors[c.id].Stop()
}
}
func (cm *ContainerMap) inspect(id string) *docker.Container {
c, err := cm.client.InspectContainer(id)
if err != nil {
if _, ok := err.(*docker.NoSuchContainer); ok == false {
log.Errorf(err.Error())
}
}
return c
}
func (cm *ContainerMap) refreshAll() {
opts := docker.ListContainersOptions{All: true}
allContainers, err := cm.client.ListContainers(opts)
if err != nil {
panic(err)
}
for _, c := range allContainers {
cm.needsRefresh[c.ID] = 1
}
}
2017-02-24 09:10:14 +00:00
func (cm *ContainerMap) UpdateLoop() {
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(1 * time.Second)
}
}
}
2016-12-30 22:17:46 +00:00
// Return number of containers/rows
func (cm *ContainerMap) Len() uint {
return uint(len(cm.containers))
}
// Get a single container, by ID
2017-02-23 22:00:05 +00:00
func (cm *ContainerMap) Get(id string) (*Container, bool) {
for _, c := range cm.containers {
if c.id == id {
return c, true
}
}
return nil, false
2016-12-30 22:17:46 +00:00
}
// Remove containers by ID
func (cm *ContainerMap) DelByID(id string) {
for n, c := range cm.containers {
if c.id == id {
cm.Del(n)
return
}
}
}
2017-02-23 22:00:05 +00:00
// Remove one or more containers by index
func (cm *ContainerMap) Del(idx ...int) {
2017-02-24 09:10:14 +00:00
lock.Lock()
defer lock.Unlock()
2017-02-23 22:00:05 +00:00
for _, i := range idx {
cm.containers = append(cm.containers[:i], cm.containers[i+1:]...)
}
2017-02-24 09:10:14 +00:00
log.Infof("removed %d dead containers", len(idx))
}
2017-01-03 17:37:09 +00:00
// Return array of all containers, sorted by field
2016-12-30 22:17:46 +00:00
func (cm *ContainerMap) All() []*Container {
2017-02-23 22:00:05 +00:00
sort.Sort(cm.containers)
return cm.containers
2016-12-30 22:17:46 +00:00
}