1
1
package main
2
2
3
3
import (
4
+ "encoding/json"
5
+ "flag"
6
+ "fmt"
4
7
"github.com/fsouza/go-dockerclient"
8
+ "io"
9
+ "net"
10
+ "net/http"
11
+ "net/http/httputil"
5
12
"os"
13
+ "os/signal"
6
14
"path/filepath"
15
+ "syscall"
7
16
"text/template"
8
17
)
9
18
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
+
10
27
func usage () {
11
- println ("Usage: docker-log template.file " )
28
+ println ("Usage: docker-gen [-watch] < template> [<dest>] " )
12
29
}
13
30
14
31
func generateFile (templatePath string , containers []docker.APIContainers ) {
@@ -17,12 +34,99 @@ func generateFile(templatePath string, containers []docker.APIContainers) {
17
34
panic (err )
18
35
}
19
36
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 )
21
122
}
22
123
23
124
func main () {
24
125
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 {
26
130
usage ()
27
131
os .Exit (1 )
28
132
}
@@ -34,12 +138,17 @@ func main() {
34
138
panic (err )
35
139
}
36
140
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
+ }
42
152
}
43
153
44
- generateFile (os .Args [1 ], containers )
45
154
}
0 commit comments