ctop/dockersource.go

145 lines
3.3 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-03-03 07:57:26 +00:00
"strings"
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"
)
type ContainerSource interface {
All() []*Container
Get(string) (*Container, bool)
}
type DockerContainerSource struct {
client *docker.Client
containers map[string]*Container
needsRefresh chan string // container IDs requiring refresh
}
func NewDockerContainerSource() *DockerContainerSource {
// init docker client
client, err := docker.NewClient(config.GetVal("dockerHost"))
if err != nil {
panic(err)
}
cm := &DockerContainerSource{
client: client,
containers: make(map[string]*Container),
needsRefresh: make(chan string, 60),
2016-12-30 22:17:46 +00:00
}
cm.refreshAll()
go cm.Loop()
2017-02-24 09:10:14 +00:00
go cm.watchEvents()
return cm
2016-12-30 22:17:46 +00:00
}
// Docker events watcher
func (cm *DockerContainerSource) 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
2017-02-24 09:10:14 +00:00
case "destroy":
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
cm.delByID(e.ID)
2017-02-24 09:10:14 +00:00
}
}
}
func (cm *DockerContainerSource) refresh(c *Container) {
insp := cm.inspect(c.Id)
// remove container if no longer exists
if insp == nil {
cm.delByID(c.Id)
return
}
c.SetName(shortName(insp.Name))
c.SetState(insp.State.Status)
}
func (cm *DockerContainerSource) 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
}
// Mark all container IDs for refresh
func (cm *DockerContainerSource) refreshAll() {
opts := docker.ListContainersOptions{All: true}
allContainers, err := cm.client.ListContainers(opts)
if err != nil {
panic(err)
}
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 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
}
2016-12-30 22:17:46 +00:00
// Get a single container, by ID
func (cm *DockerContainerSource) Get(id string) (*Container, bool) {
c, ok := cm.containers[id]
return c, ok
2016-12-30 22:17:46 +00:00
}
// Remove containers by ID
func (cm *DockerContainerSource) delByID(id string) {
delete(cm.containers, id)
log.Infof("removed dead container: %s", id)
}
2017-01-03 17:37:09 +00:00
// Return array of all containers, sorted by field
func (cm *DockerContainerSource) All() []*Container {
var containers Containers
for _, c := range cm.containers {
containers = append(containers, c)
}
sort.Sort(containers)
return containers
2017-03-03 07:57:26 +00:00
}
// use primary container name
func shortName(name string) string {
return strings.Replace(name, "/", "", 1)
}