github.com/jogo/docker@v1.7.0-rc1/integration-cli/docker_cli_events_test.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "os/exec" 7 "regexp" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/go-check/check" 14 ) 15 16 func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) { 17 image := "busybox" 18 19 // Start stopwatch, generate an event 20 time.Sleep(time.Second) // so that we don't grab events from previous test occured in the same second 21 start := daemonTime(c) 22 time.Sleep(time.Second) // remote API precision is only a second, wait a while before creating an event 23 dockerCmd(c, "tag", image, "timestamptest:1") 24 dockerCmd(c, "rmi", "timestamptest:1") 25 time.Sleep(time.Second) // so that until > since 26 end := daemonTime(c) 27 28 // List of available time formats to --since 29 unixTs := func(t time.Time) string { return fmt.Sprintf("%v", t.Unix()) } 30 rfc3339 := func(t time.Time) string { return t.Format(time.RFC3339) } 31 32 // --since=$start must contain only the 'untag' event 33 for _, f := range []func(time.Time) string{unixTs, rfc3339} { 34 since, until := f(start), f(end) 35 cmd := exec.Command(dockerBinary, "events", "--since="+since, "--until="+until) 36 out, _, err := runCommandWithOutput(cmd) 37 if err != nil { 38 c.Fatalf("docker events cmd failed: %v\nout=%s", err, out) 39 } 40 events := strings.Split(strings.TrimSpace(out), "\n") 41 if len(events) != 2 { 42 c.Fatalf("unexpected events, was expecting only 2 events tag/untag (since=%s, until=%s) out=%s", since, until, out) 43 } 44 if !strings.Contains(out, "untag") { 45 c.Fatalf("expected 'untag' event not found (since=%s, until=%s) out=%s", since, until, out) 46 } 47 } 48 49 } 50 51 func (s *DockerSuite) TestEventsUntag(c *check.C) { 52 image := "busybox" 53 dockerCmd(c, "tag", image, "utest:tag1") 54 dockerCmd(c, "tag", image, "utest:tag2") 55 dockerCmd(c, "rmi", "utest:tag1") 56 dockerCmd(c, "rmi", "utest:tag2") 57 eventsCmd := exec.Command(dockerBinary, "events", "--since=1") 58 out, exitCode, _, err := runCommandWithOutputForDuration(eventsCmd, time.Duration(time.Millisecond*200)) 59 if exitCode != 0 || err != nil { 60 c.Fatalf("Failed to get events - exit code %d: %s", exitCode, err) 61 } 62 events := strings.Split(out, "\n") 63 nEvents := len(events) 64 // The last element after the split above will be an empty string, so we 65 // get the two elements before the last, which are the untags we're 66 // looking for. 67 for _, v := range events[nEvents-3 : nEvents-1] { 68 if !strings.Contains(v, "untag") { 69 c.Fatalf("event should be untag, not %#v", v) 70 } 71 } 72 } 73 74 func (s *DockerSuite) TestEventsContainerFailStartDie(c *check.C) { 75 76 out, _ := dockerCmd(c, "images", "-q") 77 image := strings.Split(out, "\n")[0] 78 eventsCmd := exec.Command(dockerBinary, "run", "--name", "testeventdie", image, "blerg") 79 _, _, err := runCommandWithOutput(eventsCmd) 80 if err == nil { 81 c.Fatalf("Container run with command blerg should have failed, but it did not") 82 } 83 84 eventsCmd = exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 85 out, _, _ = runCommandWithOutput(eventsCmd) 86 events := strings.Split(out, "\n") 87 if len(events) <= 1 { 88 c.Fatalf("Missing expected event") 89 } 90 91 startEvent := strings.Fields(events[len(events)-3]) 92 dieEvent := strings.Fields(events[len(events)-2]) 93 94 if startEvent[len(startEvent)-1] != "start" { 95 c.Fatalf("event should be start, not %#v", startEvent) 96 } 97 if dieEvent[len(dieEvent)-1] != "die" { 98 c.Fatalf("event should be die, not %#v", dieEvent) 99 } 100 101 } 102 103 func (s *DockerSuite) TestEventsLimit(c *check.C) { 104 105 var waitGroup sync.WaitGroup 106 errChan := make(chan error, 17) 107 108 args := []string{"run", "--rm", "busybox", "true"} 109 for i := 0; i < 17; i++ { 110 waitGroup.Add(1) 111 go func() { 112 defer waitGroup.Done() 113 errChan <- exec.Command(dockerBinary, args...).Run() 114 }() 115 } 116 117 waitGroup.Wait() 118 close(errChan) 119 120 for err := range errChan { 121 if err != nil { 122 c.Fatalf("%q failed with error: %v", strings.Join(args, " "), err) 123 } 124 } 125 126 eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 127 out, _, _ := runCommandWithOutput(eventsCmd) 128 events := strings.Split(out, "\n") 129 nEvents := len(events) - 1 130 if nEvents != 64 { 131 c.Fatalf("events should be limited to 64, but received %d", nEvents) 132 } 133 } 134 135 func (s *DockerSuite) TestEventsContainerEvents(c *check.C) { 136 dockerCmd(c, "run", "--rm", "busybox", "true") 137 eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 138 out, exitCode, err := runCommandWithOutput(eventsCmd) 139 if exitCode != 0 || err != nil { 140 c.Fatalf("Failed to get events with exit code %d: %s", exitCode, err) 141 } 142 events := strings.Split(out, "\n") 143 events = events[:len(events)-1] 144 if len(events) < 4 { 145 c.Fatalf("Missing expected event") 146 } 147 createEvent := strings.Fields(events[len(events)-4]) 148 startEvent := strings.Fields(events[len(events)-3]) 149 dieEvent := strings.Fields(events[len(events)-2]) 150 destroyEvent := strings.Fields(events[len(events)-1]) 151 if createEvent[len(createEvent)-1] != "create" { 152 c.Fatalf("event should be create, not %#v", createEvent) 153 } 154 if startEvent[len(startEvent)-1] != "start" { 155 c.Fatalf("event should be start, not %#v", startEvent) 156 } 157 if dieEvent[len(dieEvent)-1] != "die" { 158 c.Fatalf("event should be die, not %#v", dieEvent) 159 } 160 if destroyEvent[len(destroyEvent)-1] != "destroy" { 161 c.Fatalf("event should be destroy, not %#v", destroyEvent) 162 } 163 164 } 165 166 func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) { 167 dockerCmd(c, "run", "--rm", "busybox", "true") 168 timeBeginning := time.Unix(0, 0).Format(time.RFC3339Nano) 169 timeBeginning = strings.Replace(timeBeginning, "Z", ".000000000Z", -1) 170 eventsCmd := exec.Command(dockerBinary, "events", fmt.Sprintf("--since='%s'", timeBeginning), 171 fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 172 out, exitCode, err := runCommandWithOutput(eventsCmd) 173 if exitCode != 0 || err != nil { 174 c.Fatalf("Failed to get events with exit code %d: %s", exitCode, err) 175 } 176 events := strings.Split(out, "\n") 177 events = events[:len(events)-1] 178 if len(events) < 4 { 179 c.Fatalf("Missing expected event") 180 } 181 createEvent := strings.Fields(events[len(events)-4]) 182 startEvent := strings.Fields(events[len(events)-3]) 183 dieEvent := strings.Fields(events[len(events)-2]) 184 destroyEvent := strings.Fields(events[len(events)-1]) 185 if createEvent[len(createEvent)-1] != "create" { 186 c.Fatalf("event should be create, not %#v", createEvent) 187 } 188 if startEvent[len(startEvent)-1] != "start" { 189 c.Fatalf("event should be start, not %#v", startEvent) 190 } 191 if dieEvent[len(dieEvent)-1] != "die" { 192 c.Fatalf("event should be die, not %#v", dieEvent) 193 } 194 if destroyEvent[len(destroyEvent)-1] != "destroy" { 195 c.Fatalf("event should be destroy, not %#v", destroyEvent) 196 } 197 198 } 199 200 func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) { 201 name := "testimageevents" 202 _, err := buildImage(name, 203 `FROM scratch 204 MAINTAINER "docker"`, 205 true) 206 if err != nil { 207 c.Fatal(err) 208 } 209 if err := deleteImages(name); err != nil { 210 c.Fatal(err) 211 } 212 eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 213 out, exitCode, err := runCommandWithOutput(eventsCmd) 214 if exitCode != 0 || err != nil { 215 c.Fatalf("Failed to get events with exit code %d: %s", exitCode, err) 216 } 217 events := strings.Split(out, "\n") 218 219 events = events[:len(events)-1] 220 if len(events) < 2 { 221 c.Fatalf("Missing expected event") 222 } 223 untagEvent := strings.Fields(events[len(events)-2]) 224 deleteEvent := strings.Fields(events[len(events)-1]) 225 if untagEvent[len(untagEvent)-1] != "untag" { 226 c.Fatalf("untag should be untag, not %#v", untagEvent) 227 } 228 if deleteEvent[len(deleteEvent)-1] != "delete" { 229 c.Fatalf("delete should be delete, not %#v", deleteEvent) 230 } 231 } 232 233 func (s *DockerSuite) TestEventsImageTag(c *check.C) { 234 time.Sleep(time.Second * 2) // because API has seconds granularity 235 since := daemonTime(c).Unix() 236 image := "testimageevents:tag" 237 dockerCmd(c, "tag", "busybox", image) 238 239 eventsCmd := exec.Command(dockerBinary, "events", 240 fmt.Sprintf("--since=%d", since), 241 fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 242 out, _, err := runCommandWithOutput(eventsCmd) 243 c.Assert(err, check.IsNil) 244 245 events := strings.Split(strings.TrimSpace(out), "\n") 246 if len(events) != 1 { 247 c.Fatalf("was expecting 1 event. out=%s", out) 248 } 249 event := strings.TrimSpace(events[0]) 250 expectedStr := image + ": tag" 251 252 if !strings.HasSuffix(event, expectedStr) { 253 c.Fatalf("wrong event format. expected='%s' got=%s", expectedStr, event) 254 } 255 256 } 257 258 func (s *DockerSuite) TestEventsImagePull(c *check.C) { 259 since := daemonTime(c).Unix() 260 testRequires(c, Network) 261 262 pullCmd := exec.Command(dockerBinary, "pull", "hello-world") 263 if out, _, err := runCommandWithOutput(pullCmd); err != nil { 264 c.Fatalf("pulling the hello-world image from has failed: %s, %v", out, err) 265 } 266 267 eventsCmd := exec.Command(dockerBinary, "events", 268 fmt.Sprintf("--since=%d", since), 269 fmt.Sprintf("--until=%d", daemonTime(c).Unix())) 270 out, _, _ := runCommandWithOutput(eventsCmd) 271 272 events := strings.Split(strings.TrimSpace(out), "\n") 273 event := strings.TrimSpace(events[len(events)-1]) 274 275 if !strings.HasSuffix(event, "hello-world:latest: pull") { 276 c.Fatalf("Missing pull event - got:%q", event) 277 } 278 279 } 280 281 func (s *DockerSuite) TestEventsImageImport(c *check.C) { 282 since := daemonTime(c).Unix() 283 284 id := make(chan string) 285 eventImport := make(chan struct{}) 286 eventsCmd := exec.Command(dockerBinary, "events", "--since", strconv.FormatInt(since, 10)) 287 stdout, err := eventsCmd.StdoutPipe() 288 if err != nil { 289 c.Fatal(err) 290 } 291 if err := eventsCmd.Start(); err != nil { 292 c.Fatal(err) 293 } 294 defer eventsCmd.Process.Kill() 295 296 go func() { 297 containerID := <-id 298 299 matchImport := regexp.MustCompile(containerID + `: import$`) 300 scanner := bufio.NewScanner(stdout) 301 for scanner.Scan() { 302 if matchImport.MatchString(scanner.Text()) { 303 close(eventImport) 304 } 305 } 306 }() 307 308 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true") 309 out, _, err := runCommandWithOutput(runCmd) 310 if err != nil { 311 c.Fatal("failed to create a container", out, err) 312 } 313 cleanedContainerID := strings.TrimSpace(out) 314 315 out, _, err = runCommandPipelineWithOutput( 316 exec.Command(dockerBinary, "export", cleanedContainerID), 317 exec.Command(dockerBinary, "import", "-"), 318 ) 319 if err != nil { 320 c.Errorf("import failed with errors: %v, output: %q", err, out) 321 } 322 newContainerID := strings.TrimSpace(out) 323 id <- newContainerID 324 325 select { 326 case <-time.After(5 * time.Second): 327 c.Fatal("failed to observe image import in timely fashion") 328 case <-eventImport: 329 // ignore, done 330 } 331 } 332 333 func (s *DockerSuite) TestEventsFilters(c *check.C) { 334 parseEvents := func(out, match string) { 335 events := strings.Split(out, "\n") 336 events = events[:len(events)-1] 337 for _, event := range events { 338 eventFields := strings.Fields(event) 339 eventName := eventFields[len(eventFields)-1] 340 if ok, err := regexp.MatchString(match, eventName); err != nil || !ok { 341 c.Fatalf("event should match %s, got %#v, err: %v", match, eventFields, err) 342 } 343 } 344 } 345 346 since := daemonTime(c).Unix() 347 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--rm", "busybox", "true")) 348 if err != nil { 349 c.Fatal(out, err) 350 } 351 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "--rm", "busybox", "true")) 352 if err != nil { 353 c.Fatal(out, err) 354 } 355 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=die")) 356 if err != nil { 357 c.Fatalf("Failed to get events: %s", err) 358 } 359 parseEvents(out, "die") 360 361 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", "event=die", "--filter", "event=start")) 362 if err != nil { 363 c.Fatalf("Failed to get events: %s", err) 364 } 365 parseEvents(out, "((die)|(start))") 366 367 // make sure we at least got 2 start events 368 count := strings.Count(out, "start") 369 if count < 2 { 370 c.Fatalf("should have had 2 start events but had %d, out: %s", count, out) 371 } 372 373 } 374 375 func (s *DockerSuite) TestEventsFilterImageName(c *check.C) { 376 since := daemonTime(c).Unix() 377 378 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "container_1", "-d", "busybox:latest", "true")) 379 if err != nil { 380 c.Fatal(out, err) 381 } 382 container1 := strings.TrimSpace(out) 383 384 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "container_2", "-d", "busybox", "true")) 385 if err != nil { 386 c.Fatal(out, err) 387 } 388 container2 := strings.TrimSpace(out) 389 390 name := "busybox" 391 eventsCmd := exec.Command(dockerBinary, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "--filter", fmt.Sprintf("image=%s", name)) 392 out, _, err = runCommandWithOutput(eventsCmd) 393 if err != nil { 394 c.Fatalf("Failed to get events, error: %s(%s)", err, out) 395 } 396 events := strings.Split(out, "\n") 397 events = events[:len(events)-1] 398 if len(events) == 0 { 399 c.Fatalf("Expected events but found none for the image busybox:latest") 400 } 401 count1 := 0 402 count2 := 0 403 404 for _, e := range events { 405 if strings.Contains(e, container1) { 406 count1++ 407 } else if strings.Contains(e, container2) { 408 count2++ 409 } 410 } 411 if count1 == 0 || count2 == 0 { 412 c.Fatalf("Expected events from each container but got %d from %s and %d from %s", count1, container1, count2, container2) 413 } 414 415 } 416 417 func (s *DockerSuite) TestEventsFilterContainer(c *check.C) { 418 since := fmt.Sprintf("%d", daemonTime(c).Unix()) 419 nameID := make(map[string]string) 420 421 for _, name := range []string{"container_1", "container_2"} { 422 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", name, "busybox", "true")) 423 if err != nil { 424 c.Fatalf("Error: %v, Output: %s", err, out) 425 } 426 id, err := inspectField(name, "Id") 427 if err != nil { 428 c.Fatal(err) 429 } 430 nameID[name] = id 431 } 432 433 until := fmt.Sprintf("%d", daemonTime(c).Unix()) 434 435 checkEvents := func(id string, events []string) error { 436 if len(events) != 3 { // create, start, die 437 return fmt.Errorf("expected 3 events, got %v", events) 438 } 439 for _, event := range events { 440 e := strings.Fields(event) 441 if len(e) < 3 { 442 return fmt.Errorf("got malformed event: %s", event) 443 } 444 445 // Check the id 446 parsedID := strings.TrimSuffix(e[1], ":") 447 if parsedID != id { 448 return fmt.Errorf("expected event for container id %s: %s - parsed container id: %s", id, event, parsedID) 449 } 450 } 451 return nil 452 } 453 454 for name, ID := range nameID { 455 // filter by names 456 eventsCmd := exec.Command(dockerBinary, "events", "--since", since, "--until", until, "--filter", "container="+name) 457 out, _, err := runCommandWithOutput(eventsCmd) 458 if err != nil { 459 c.Fatal(err) 460 } 461 462 events := strings.Split(strings.TrimSuffix(out, "\n"), "\n") 463 if err := checkEvents(ID, events); err != nil { 464 c.Fatal(err) 465 } 466 467 // filter by ID's 468 eventsCmd = exec.Command(dockerBinary, "events", "--since", since, "--until", until, "--filter", "container="+ID) 469 out, _, err = runCommandWithOutput(eventsCmd) 470 if err != nil { 471 c.Fatal(err) 472 } 473 474 events = strings.Split(strings.TrimSuffix(out, "\n"), "\n") 475 if err := checkEvents(ID, events); err != nil { 476 c.Fatal(err) 477 } 478 } 479 480 } 481 482 func (s *DockerSuite) TestEventsStreaming(c *check.C) { 483 start := daemonTime(c).Unix() 484 485 id := make(chan string) 486 eventCreate := make(chan struct{}) 487 eventStart := make(chan struct{}) 488 eventDie := make(chan struct{}) 489 eventDestroy := make(chan struct{}) 490 491 eventsCmd := exec.Command(dockerBinary, "events", "--since", strconv.FormatInt(start, 10)) 492 stdout, err := eventsCmd.StdoutPipe() 493 if err != nil { 494 c.Fatal(err) 495 } 496 if err := eventsCmd.Start(); err != nil { 497 c.Fatalf("failed to start 'docker events': %s", err) 498 } 499 defer eventsCmd.Process.Kill() 500 501 go func() { 502 containerID := <-id 503 504 matchCreate := regexp.MustCompile(containerID + `: \(from busybox:latest\) create$`) 505 matchStart := regexp.MustCompile(containerID + `: \(from busybox:latest\) start$`) 506 matchDie := regexp.MustCompile(containerID + `: \(from busybox:latest\) die$`) 507 matchDestroy := regexp.MustCompile(containerID + `: \(from busybox:latest\) destroy$`) 508 509 scanner := bufio.NewScanner(stdout) 510 for scanner.Scan() { 511 switch { 512 case matchCreate.MatchString(scanner.Text()): 513 close(eventCreate) 514 case matchStart.MatchString(scanner.Text()): 515 close(eventStart) 516 case matchDie.MatchString(scanner.Text()): 517 close(eventDie) 518 case matchDestroy.MatchString(scanner.Text()): 519 close(eventDestroy) 520 } 521 } 522 }() 523 524 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox:latest", "true") 525 out, _, err := runCommandWithOutput(runCmd) 526 if err != nil { 527 c.Fatal(out, err) 528 } 529 cleanedContainerID := strings.TrimSpace(out) 530 id <- cleanedContainerID 531 532 select { 533 case <-time.After(5 * time.Second): 534 c.Fatal("failed to observe container create in timely fashion") 535 case <-eventCreate: 536 // ignore, done 537 } 538 539 select { 540 case <-time.After(5 * time.Second): 541 c.Fatal("failed to observe container start in timely fashion") 542 case <-eventStart: 543 // ignore, done 544 } 545 546 select { 547 case <-time.After(5 * time.Second): 548 c.Fatal("failed to observe container die in timely fashion") 549 case <-eventDie: 550 // ignore, done 551 } 552 553 rmCmd := exec.Command(dockerBinary, "rm", cleanedContainerID) 554 out, _, err = runCommandWithOutput(rmCmd) 555 if err != nil { 556 c.Fatal(out, err) 557 } 558 559 select { 560 case <-time.After(5 * time.Second): 561 c.Fatal("failed to observe container destroy in timely fashion") 562 case <-eventDestroy: 563 // ignore, done 564 } 565 }