Skip to content

Commit 7a6cb6f

Browse files
committed
Watch docker container events
Regenerate templates when containers are started and stop.
1 parent f468c8e commit 7a6cb6f

File tree

1 file changed

+118
-9
lines changed

1 file changed

+118
-9
lines changed

docker-gen.go

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
package main
22

33
import (
4+
"encoding/json"
5+
"flag"
6+
"fmt"
47
"github.com/fsouza/go-dockerclient"
8+
"io"
9+
"net"
10+
"net/http"
11+
"net/http/httputil"
512
"os"
13+
"os/signal"
614
"path/filepath"
15+
"syscall"
716
"text/template"
817
)
918

19+
var watch bool
20+
21+
type Event struct {
22+
ContainerId string `json:"id"`
23+
Status string `json:"status"`
24+
Image string `json:"from"`
25+
}
26+
1027
func usage() {
11-
println("Usage: docker-log template.file")
28+
println("Usage: docker-gen [-watch] <template> [<dest>]")
1229
}
1330

1431
func generateFile(templatePath string, containers []docker.APIContainers) {
@@ -17,12 +34,99 @@ func generateFile(templatePath string, containers []docker.APIContainers) {
1734
panic(err)
1835
}
1936

20-
err = tmpl.ExecuteTemplate(os.Stdout, filepath.Base(templatePath), containers)
37+
dest := os.Stdout
38+
if flag.NArg() == 2 {
39+
dest, err = os.Create(flag.Arg(1))
40+
if err != nil {
41+
fmt.Println("unable to create dest file %s: %s", flag.Arg(1), err)
42+
os.Exit(1)
43+
}
44+
}
45+
46+
err = tmpl.ExecuteTemplate(dest, filepath.Base(templatePath), containers)
47+
}
48+
49+
func newConn() (*httputil.ClientConn, error) {
50+
conn, err := net.Dial("unix", "/var/run/docker.sock")
51+
if err != nil {
52+
return nil, err
53+
}
54+
return httputil.NewClientConn(conn, nil), nil
55+
}
56+
57+
func getEvents() chan *Event {
58+
eventChan := make(chan *Event, 100)
59+
go func() {
60+
defer close(eventChan)
61+
62+
c, err := newConn()
63+
if err != nil {
64+
fmt.Printf("cannot connect to docker: %s", err)
65+
return
66+
}
67+
defer c.Close()
68+
69+
req, err := http.NewRequest("GET", "/events", nil)
70+
if err != nil {
71+
fmt.Printf("bad request for events: %s", err)
72+
return
73+
}
74+
75+
resp, err := c.Do(req)
76+
if err != nil {
77+
fmt.Printf("cannot connect to events endpoint: %s", err)
78+
return
79+
}
80+
defer resp.Body.Close()
81+
82+
// handle signals to stop the socket
83+
sigChan := make(chan os.Signal, 1)
84+
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
85+
go func() {
86+
for sig := range sigChan {
87+
fmt.Printf("received signal '%v', exiting\n", sig)
88+
89+
c.Close()
90+
close(eventChan)
91+
os.Exit(0)
92+
}
93+
}()
94+
95+
dec := json.NewDecoder(resp.Body)
96+
for {
97+
var event *Event
98+
if err := dec.Decode(&event); err != nil {
99+
if err == io.EOF {
100+
break
101+
}
102+
fmt.Printf("cannot decode json: %s", err)
103+
continue
104+
}
105+
eventChan <- event
106+
}
107+
fmt.Printf("closing event channel")
108+
}()
109+
return eventChan
110+
}
111+
112+
func generateFromContainers(client *docker.Client) {
113+
containers, err := client.ListContainers(docker.ListContainersOptions{
114+
All: false,
115+
})
116+
if err != nil {
117+
fmt.Printf("error listing containers: %s", err)
118+
return
119+
}
120+
121+
generateFile(flag.Arg(0), containers)
21122
}
22123

23124
func main() {
24125

25-
if len(os.Args) != 2 {
126+
flag.BoolVar(&watch, "watch", false, "watch for container changes")
127+
flag.Parse()
128+
129+
if flag.NArg() < 1 {
26130
usage()
27131
os.Exit(1)
28132
}
@@ -34,12 +138,17 @@ func main() {
34138
panic(err)
35139
}
36140

37-
containers, err := client.ListContainers(docker.ListContainersOptions{
38-
All: false,
39-
})
40-
if err != nil {
41-
panic(err)
141+
generateFromContainers(client)
142+
if !watch {
143+
return
144+
}
145+
146+
eventChan := getEvents()
147+
for {
148+
event := <-eventChan
149+
if event.Status == "start" || event.Status == "stop" {
150+
generateFromContainers(client)
151+
}
42152
}
43153

44-
generateFile(os.Args[1], containers)
45154
}

0 commit comments

Comments
 (0)