1// Copyright 2014 go-dockerclient authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package docker 6 7import ( 8 "bufio" 9 "crypto/tls" 10 "crypto/x509" 11 "io/ioutil" 12 "net/http" 13 "net/http/httptest" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/google/go-cmp/cmp" 19) 20 21func TestEventListeners(t *testing.T) { 22 t.Parallel() 23 testEventListeners("TestEventListeners", t, httptest.NewServer, NewClient) 24} 25 26func TestTLSEventListeners(t *testing.T) { 27 t.Parallel() 28 testEventListeners("TestTLSEventListeners", t, func(handler http.Handler) *httptest.Server { 29 server := httptest.NewUnstartedServer(handler) 30 31 cert, err := tls.LoadX509KeyPair("testing/data/server.pem", "testing/data/serverkey.pem") 32 if err != nil { 33 t.Fatalf("Error loading server key pair: %s", err) 34 } 35 36 caCert, err := ioutil.ReadFile("testing/data/ca.pem") 37 if err != nil { 38 t.Fatalf("Error loading ca certificate: %s", err) 39 } 40 caPool := x509.NewCertPool() 41 if !caPool.AppendCertsFromPEM(caCert) { 42 t.Fatalf("Could not add ca certificate") 43 } 44 45 server.TLS = &tls.Config{ 46 Certificates: []tls.Certificate{cert}, 47 RootCAs: caPool, 48 } 49 server.StartTLS() 50 return server 51 }, func(url string) (*Client, error) { 52 return NewTLSClient(url, "testing/data/cert.pem", "testing/data/key.pem", "testing/data/ca.pem") 53 }) 54} 55 56func testEventListeners(testName string, t *testing.T, buildServer func(http.Handler) *httptest.Server, buildClient func(string) (*Client, error)) { 57 response := `{"action":"pull","type":"image","actor":{"id":"busybox:latest","attributes":{}},"time":1442421700,"timeNano":1442421700598988358} 58{"action":"create","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716853979870} 59{"action":"attach","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716894759198} 60{"action":"start","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716983607193} 61{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} 62{"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} 63{"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} 64{"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} 65{"Action":"create","Actor":{"Attributes":{"HAProxyMode":"http","HealthCheck":"HttpGet","HealthCheckArgs":"http://127.0.0.1:39051/status/check","ServicePort_8080":"17801","image":"datanerd.us/siteeng/sample-app-go:latest","name":"sample-app-client-go-69818c1223ddb5"},"ID":"a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16"},"Type":"container","from":"datanerd.us/siteeng/sample-app-go:latest","id":"a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16","status":"create","time":1459133932,"timeNano":1459133932961735842}` 66 67 server := buildServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 rsc := bufio.NewScanner(strings.NewReader(response)) 69 for rsc.Scan() { 70 w.Write(rsc.Bytes()) 71 w.(http.Flusher).Flush() 72 time.Sleep(10 * time.Millisecond) 73 } 74 })) 75 defer server.Close() 76 77 wantedEvents := []APIEvents{ 78 { 79 Action: "pull", 80 Type: "image", 81 Actor: APIActor{ 82 ID: "busybox:latest", 83 Attributes: map[string]string{}, 84 }, 85 86 Status: "pull", 87 ID: "busybox:latest", 88 89 Time: 1442421700, 90 TimeNano: 1442421700598988358, 91 }, 92 { 93 Action: "create", 94 Type: "container", 95 Actor: APIActor{ 96 ID: "5745704abe9caa5", 97 Attributes: map[string]string{ 98 "image": "busybox", 99 }, 100 }, 101 102 Status: "create", 103 ID: "5745704abe9caa5", 104 From: "busybox", 105 106 Time: 1442421716, 107 TimeNano: 1442421716853979870, 108 }, 109 { 110 Action: "attach", 111 Type: "container", 112 Actor: APIActor{ 113 ID: "5745704abe9caa5", 114 Attributes: map[string]string{ 115 "image": "busybox", 116 }, 117 }, 118 119 Status: "attach", 120 ID: "5745704abe9caa5", 121 From: "busybox", 122 123 Time: 1442421716, 124 TimeNano: 1442421716894759198, 125 }, 126 { 127 Action: "start", 128 Type: "container", 129 Actor: APIActor{ 130 ID: "5745704abe9caa5", 131 Attributes: map[string]string{ 132 "image": "busybox", 133 }, 134 }, 135 136 Status: "start", 137 ID: "5745704abe9caa5", 138 From: "busybox", 139 140 Time: 1442421716, 141 TimeNano: 1442421716983607193, 142 }, 143 144 { 145 Action: "create", 146 Type: "container", 147 Actor: APIActor{ 148 ID: "dfdf82bd3881", 149 Attributes: map[string]string{ 150 "image": "base:latest", 151 }, 152 }, 153 154 Status: "create", 155 ID: "dfdf82bd3881", 156 From: "base:latest", 157 158 Time: 1374067924, 159 }, 160 { 161 Action: "start", 162 Type: "container", 163 Actor: APIActor{ 164 ID: "dfdf82bd3881", 165 Attributes: map[string]string{ 166 "image": "base:latest", 167 }, 168 }, 169 170 Status: "start", 171 ID: "dfdf82bd3881", 172 From: "base:latest", 173 174 Time: 1374067924, 175 }, 176 { 177 Action: "stop", 178 Type: "container", 179 Actor: APIActor{ 180 ID: "dfdf82bd3881", 181 Attributes: map[string]string{ 182 "image": "base:latest", 183 }, 184 }, 185 186 Status: "stop", 187 ID: "dfdf82bd3881", 188 From: "base:latest", 189 190 Time: 1374067966, 191 }, 192 { 193 Action: "destroy", 194 Type: "container", 195 Actor: APIActor{ 196 ID: "dfdf82bd3881", 197 Attributes: map[string]string{ 198 "image": "base:latest", 199 }, 200 }, 201 202 Status: "destroy", 203 ID: "dfdf82bd3881", 204 From: "base:latest", 205 206 Time: 1374067970, 207 }, 208 { 209 Action: "create", 210 Type: "container", 211 Status: "create", 212 From: "datanerd.us/siteeng/sample-app-go:latest", 213 ID: "a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16", 214 Time: 1459133932, 215 TimeNano: 1459133932961735842, 216 Actor: APIActor{ 217 ID: "a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16", 218 Attributes: map[string]string{ 219 "HAProxyMode": "http", 220 "HealthCheck": "HttpGet", 221 "HealthCheckArgs": "http://127.0.0.1:39051/status/check", 222 "ServicePort_8080": "17801", 223 "image": "datanerd.us/siteeng/sample-app-go:latest", 224 "name": "sample-app-client-go-69818c1223ddb5", 225 }, 226 }, 227 }, 228 } 229 230 client, err := buildClient(server.URL) 231 if err != nil { 232 t.Errorf("Failed to create client: %s", err) 233 } 234 client.SkipServerVersionCheck = true 235 236 listener := make(chan *APIEvents, len(wantedEvents)+1) 237 defer func() { 238 if err = client.RemoveEventListener(listener); err != nil { 239 t.Error(err) 240 } 241 }() 242 243 err = client.AddEventListener(listener) 244 if err != nil { 245 t.Errorf("Failed to add event listener: %s", err) 246 } 247 248 timeout := time.After(5 * time.Second) 249 events := make([]APIEvents, 0, len(wantedEvents)) 250 251loop: 252 for i := range wantedEvents { 253 select { 254 case msg, ok := <-listener: 255 if !ok { 256 break loop 257 } 258 events = append(events, *msg) 259 case <-timeout: 260 t.Fatalf("%s: timed out waiting on events after %d events", testName, i) 261 } 262 } 263 cmpr := cmp.Comparer(func(e1, e2 APIEvents) bool { 264 return e1.Action == e2.Action && e1.Actor.ID == e2.Actor.ID 265 }) 266 if dff := cmp.Diff(events, wantedEvents, cmpr); dff != "" { 267 t.Errorf("wrong events:\n%s", dff) 268 } 269} 270 271func TestEventListenerReAdding(t *testing.T) { 272 t.Parallel() 273 endChan := make(chan bool) 274 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 275 <-endChan 276 })) 277 278 client, err := NewClient(server.URL) 279 if err != nil { 280 t.Errorf("Failed to create client: %s", err) 281 } 282 283 listener := make(chan *APIEvents, 10) 284 if err := client.AddEventListener(listener); err != nil { 285 t.Errorf("Failed to add event listener: %s", err) 286 } 287 288 // Make sure eventHijack() is started with the current eventMonitoringState. 289 time.Sleep(10 * time.Millisecond) 290 291 if err := client.RemoveEventListener(listener); err != nil { 292 t.Errorf("Failed to remove event listener: %s", err) 293 } 294 295 if err := client.AddEventListener(listener); err != nil { 296 t.Errorf("Failed to add event listener: %s", err) 297 } 298 299 endChan <- true 300 301 // Give the goroutine of the first eventHijack() time to handle the EOF. 302 time.Sleep(10 * time.Millisecond) 303} 304