mirror of
https://github.com/bcicen/ctop.git
synced 2024-08-30 18:23:19 +00:00
146 lines
3.4 KiB
Go
146 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/bcicen/ctop/config"
|
|
"github.com/bcicen/ctop/metrics"
|
|
"github.com/fsouza/go-dockerclient"
|
|
)
|
|
|
|
type ContainerSource interface {
|
|
All() Containers
|
|
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),
|
|
}
|
|
cm.refreshAll()
|
|
go cm.Loop()
|
|
go cm.watchEvents()
|
|
return cm
|
|
}
|
|
|
|
// 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 {
|
|
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
|
|
case "destroy":
|
|
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
|
|
cm.delByID(e.ID)
|
|
}
|
|
}
|
|
}
|
|
|
|
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.SetMeta("name", shortName(insp.Name))
|
|
c.SetMeta("image", insp.Config.Image)
|
|
c.SetMeta("created", insp.Created.Format("Mon Jan 2 15:04:05 2006"))
|
|
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.SetMeta("name", 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
|
|
}
|
|
|
|
// Get a single container, by ID
|
|
func (cm *DockerContainerSource) Get(id string) (*Container, bool) {
|
|
c, ok := cm.containers[id]
|
|
return c, ok
|
|
}
|
|
|
|
// Remove containers by ID
|
|
func (cm *DockerContainerSource) delByID(id string) {
|
|
delete(cm.containers, id)
|
|
log.Infof("removed dead container: %s", id)
|
|
}
|
|
|
|
// Return array of all containers, sorted by field
|
|
func (cm *DockerContainerSource) All() (containers Containers) {
|
|
for _, c := range cm.containers {
|
|
containers = append(containers, c)
|
|
}
|
|
sort.Sort(containers)
|
|
return containers
|
|
}
|
|
|
|
// use primary container name
|
|
func shortName(name string) string {
|
|
return strings.Replace(name, "/", "", 1)
|
|
}
|