forked from bcicen/ctop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdockersource.go
146 lines (129 loc) · 3.38 KB
/
dockersource.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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)
containers.Filter()
return containers
}
// use primary container name
func shortName(name string) string {
return strings.Replace(name, "/", "", 1)
}