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{}
|
|
|
|
|
2017-02-23 02:01:56 +00:00
|
|
|
type ContainerMap struct {
|
2017-02-24 01:18:59 +00:00
|
|
|
client *docker.Client
|
|
|
|
containers Containers
|
|
|
|
collectors map[string]metrics.Collector
|
|
|
|
needsRefresh map[string]int // container IDs requiring refresh
|
2017-02-23 02:01:56 +00:00
|
|
|
}
|
|
|
|
|
2016-12-30 22:17:46 +00:00
|
|
|
func NewContainerMap() *ContainerMap {
|
2017-01-01 22:42:13 +00:00
|
|
|
// init docker client
|
2017-02-16 04:06:05 +00:00
|
|
|
client, err := docker.NewClient(config.GetVal("dockerHost"))
|
2017-01-01 22:42:13 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
cm := &ContainerMap{
|
2017-02-24 01:18:59 +00:00
|
|
|
client: client,
|
|
|
|
collectors: make(map[string]metrics.Collector),
|
|
|
|
needsRefresh: make(map[string]int),
|
2016-12-30 22:17:46 +00:00
|
|
|
}
|
2017-02-24 01:18:59 +00:00
|
|
|
cm.refreshAll()
|
2017-02-24 09:10:14 +00:00
|
|
|
go cm.UpdateLoop()
|
|
|
|
go cm.watchEvents()
|
2017-01-01 22:42:13 +00:00
|
|
|
return cm
|
2016-12-30 22:17:46 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 01:18:59 +00:00
|
|
|
// Docker events watcher
|
2017-02-24 09:10:14 +00:00
|
|
|
func (cm *ContainerMap) watchEvents() {
|
2017-02-24 01:18:59 +00:00
|
|
|
log.Info("docker event listener starting")
|
|
|
|
events := make(chan *docker.APIEvents)
|
|
|
|
cm.client.AddEventListener(events)
|
2017-01-26 00:53:03 +00:00
|
|
|
|
2017-02-24 01:18:59 +00:00
|
|
|
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)
|
|
|
|
}
|
2017-02-24 01:18:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-23 02:01:56 +00:00
|
|
|
|
2017-02-24 01:18:59 +00:00
|
|
|
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()
|
2017-02-24 01:18:59 +00:00
|
|
|
cm.containers = append(cm.containers, c)
|
2017-02-24 09:10:14 +00:00
|
|
|
lock.Unlock()
|
2017-02-24 01:18:59 +00:00
|
|
|
// create collector
|
2017-02-23 02:24:26 +00:00
|
|
|
if _, ok := cm.collectors[id]; ok == false {
|
|
|
|
cm.collectors[id] = metrics.NewDocker(cm.client, id)
|
|
|
|
}
|
2017-01-01 22:42:13 +00:00
|
|
|
}
|
2017-01-26 00:53:03 +00:00
|
|
|
|
2017-02-24 01:18:59 +00:00
|
|
|
c.SetState(insp.State.Status)
|
2017-02-23 22:00:05 +00:00
|
|
|
|
2017-02-24 01:18:59 +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()
|
|
|
|
}
|
2017-02-24 01:18:59 +00:00
|
|
|
}
|
2017-02-23 02:24:26 +00:00
|
|
|
|
2017-02-24 01:18:59 +00:00
|
|
|
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())
|
2017-02-23 02:01:56 +00:00
|
|
|
}
|
2017-01-26 00:53:03 +00:00
|
|
|
}
|
2017-02-24 01:18:59 +00:00
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cm *ContainerMap) refreshAll() {
|
|
|
|
opts := docker.ListContainersOptions{All: true}
|
|
|
|
allContainers, err := cm.client.ListContainers(opts)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2017-01-26 00:53:03 +00:00
|
|
|
|
2017-02-24 01:18:59 +00:00
|
|
|
for _, c := range allContainers {
|
|
|
|
cm.needsRefresh[c.ID] = 1
|
|
|
|
}
|
2017-01-01 22:42:13 +00:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
2017-01-09 15:02:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-30 22:17:46 +00:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2017-02-24 01:18:59 +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-01-26 00:53:03 +00:00
|
|
|
}
|
2017-02-24 09:10:14 +00:00
|
|
|
log.Infof("removed %d dead containers", len(idx))
|
2017-01-26 00:53:03 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|