github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/integration-cli/docker_cli_events_unix_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "bufio" 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "strings" 13 "testing" 14 "time" 15 "unicode" 16 17 "github.com/creack/pty" 18 "github.com/docker/docker/integration-cli/cli/build" 19 "golang.org/x/sys/unix" 20 "gotest.tools/assert" 21 ) 22 23 // #5979 24 func (s *DockerSuite) TestEventsRedirectStdout(c *testing.T) { 25 since := daemonUnixTime(c) 26 dockerCmd(c, "run", "busybox", "true") 27 28 file, err := ioutil.TempFile("", "") 29 assert.NilError(c, err, "could not create temp file") 30 defer os.Remove(file.Name()) 31 32 command := fmt.Sprintf("%s events --since=%s --until=%s > %s", dockerBinary, since, daemonUnixTime(c), file.Name()) 33 _, tty, err := pty.Open() 34 assert.NilError(c, err, "Could not open pty") 35 cmd := exec.Command("sh", "-c", command) 36 cmd.Stdin = tty 37 cmd.Stdout = tty 38 cmd.Stderr = tty 39 assert.NilError(c, cmd.Run(), "run err for command %q", command) 40 41 scanner := bufio.NewScanner(file) 42 for scanner.Scan() { 43 for _, ch := range scanner.Text() { 44 assert.Check(c, unicode.IsControl(ch) == false, "found control character %v", []byte(string(ch))) 45 } 46 } 47 assert.NilError(c, scanner.Err(), "Scan err for command %q", command) 48 } 49 50 func (s *DockerSuite) TestEventsOOMDisableFalse(c *testing.T) { 51 testRequires(c, DaemonIsLinux, oomControl, memoryLimitSupport, swapMemorySupport, NotPpc64le) 52 53 errChan := make(chan error) 54 go func() { 55 defer close(errChan) 56 out, exitCode, _ := dockerCmdWithError("run", "--name", "oomFalse", "-m", "10MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done") 57 if expected := 137; exitCode != expected { 58 errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) 59 } 60 }() 61 select { 62 case err := <-errChan: 63 assert.NilError(c, err) 64 case <-time.After(30 * time.Second): 65 c.Fatal("Timeout waiting for container to die on OOM") 66 } 67 68 out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=oomFalse", "--until", daemonUnixTime(c)) 69 events := strings.Split(strings.TrimSuffix(out, "\n"), "\n") 70 nEvents := len(events) 71 72 assert.Assert(c, nEvents >= 5) 73 assert.Equal(c, parseEventAction(c, events[nEvents-5]), "create") 74 assert.Equal(c, parseEventAction(c, events[nEvents-4]), "attach") 75 assert.Equal(c, parseEventAction(c, events[nEvents-3]), "start") 76 assert.Equal(c, parseEventAction(c, events[nEvents-2]), "oom") 77 assert.Equal(c, parseEventAction(c, events[nEvents-1]), "die") 78 } 79 80 func (s *DockerSuite) TestEventsOOMDisableTrue(c *testing.T) { 81 testRequires(c, DaemonIsLinux, oomControl, memoryLimitSupport, NotArm, swapMemorySupport, NotPpc64le) 82 83 errChan := make(chan error) 84 observer, err := newEventObserver(c) 85 assert.NilError(c, err) 86 err = observer.Start() 87 assert.NilError(c, err) 88 defer observer.Stop() 89 90 go func() { 91 defer close(errChan) 92 out, exitCode, _ := dockerCmdWithError("run", "--oom-kill-disable=true", "--name", "oomTrue", "-m", "10MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done") 93 if expected := 137; exitCode != expected { 94 errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) 95 } 96 }() 97 98 assert.NilError(c, waitRun("oomTrue")) 99 defer dockerCmdWithResult("kill", "oomTrue") 100 containerID := inspectField(c, "oomTrue", "Id") 101 102 testActions := map[string]chan bool{ 103 "oom": make(chan bool), 104 } 105 106 matcher := matchEventLine(containerID, "container", testActions) 107 processor := processEventMatch(testActions) 108 go observer.Match(matcher, processor) 109 110 select { 111 case <-time.After(20 * time.Second): 112 observer.CheckEventError(c, containerID, "oom", matcher) 113 case <-testActions["oom"]: 114 // ignore, done 115 case errRun := <-errChan: 116 if errRun != nil { 117 c.Fatalf("%v", errRun) 118 } else { 119 c.Fatalf("container should be still running but it's not") 120 } 121 } 122 123 status := inspectField(c, "oomTrue", "State.Status") 124 assert.Equal(c, strings.TrimSpace(status), "running", "container should be still running") 125 } 126 127 // #18453 128 func (s *DockerSuite) TestEventsContainerFilterByName(c *testing.T) { 129 testRequires(c, DaemonIsLinux) 130 cOut, _ := dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top") 131 c1 := strings.TrimSpace(cOut) 132 waitRun("foo") 133 cOut, _ = dockerCmd(c, "run", "--name=bar", "-d", "busybox", "top") 134 c2 := strings.TrimSpace(cOut) 135 waitRun("bar") 136 out, _ := dockerCmd(c, "events", "-f", "container=foo", "--since=0", "--until", daemonUnixTime(c)) 137 assert.Assert(c, strings.Contains(out, c1), out) 138 assert.Assert(c, !strings.Contains(out, c2), out) 139 } 140 141 // #18453 142 func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *testing.T) { 143 testRequires(c, DaemonIsLinux) 144 buf := &bytes.Buffer{} 145 cmd := exec.Command(dockerBinary, "events", "-f", "container=foo", "--since=0") 146 cmd.Stdout = buf 147 assert.NilError(c, cmd.Start()) 148 defer cmd.Wait() 149 defer cmd.Process.Kill() 150 151 // Sleep for a second to make sure we are testing the case where events are listened before container starts. 152 time.Sleep(time.Second) 153 id, _ := dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top") 154 cID := strings.TrimSpace(id) 155 for i := 0; ; i++ { 156 out := buf.String() 157 if strings.Contains(out, cID) { 158 break 159 } 160 if i > 30 { 161 c.Fatalf("Missing event of container (foo, %v), got %q", cID, out) 162 } 163 time.Sleep(500 * time.Millisecond) 164 } 165 } 166 167 func (s *DockerSuite) TestVolumeEvents(c *testing.T) { 168 testRequires(c, DaemonIsLinux) 169 170 since := daemonUnixTime(c) 171 172 // Observe create/mount volume actions 173 dockerCmd(c, "volume", "create", "test-event-volume-local") 174 dockerCmd(c, "run", "--name", "test-volume-container", "--volume", "test-event-volume-local:/foo", "-d", "busybox", "true") 175 waitRun("test-volume-container") 176 177 // Observe unmount/destroy volume actions 178 dockerCmd(c, "rm", "-f", "test-volume-container") 179 dockerCmd(c, "volume", "rm", "test-event-volume-local") 180 181 until := daemonUnixTime(c) 182 out, _ := dockerCmd(c, "events", "--since", since, "--until", until) 183 events := strings.Split(strings.TrimSpace(out), "\n") 184 assert.Assert(c, len(events) > 4) 185 186 volumeEvents := eventActionsByIDAndType(c, events, "test-event-volume-local", "volume") 187 assert.Equal(c, len(volumeEvents), 5) 188 assert.Equal(c, volumeEvents[0], "create") 189 assert.Equal(c, volumeEvents[1], "create") 190 assert.Equal(c, volumeEvents[2], "mount") 191 assert.Equal(c, volumeEvents[3], "unmount") 192 assert.Equal(c, volumeEvents[4], "destroy") 193 } 194 195 func (s *DockerSuite) TestNetworkEvents(c *testing.T) { 196 testRequires(c, DaemonIsLinux) 197 198 since := daemonUnixTime(c) 199 200 // Observe create/connect network actions 201 dockerCmd(c, "network", "create", "test-event-network-local") 202 dockerCmd(c, "run", "--name", "test-network-container", "--net", "test-event-network-local", "-d", "busybox", "true") 203 waitRun("test-network-container") 204 205 // Observe disconnect/destroy network actions 206 dockerCmd(c, "rm", "-f", "test-network-container") 207 dockerCmd(c, "network", "rm", "test-event-network-local") 208 209 until := daemonUnixTime(c) 210 out, _ := dockerCmd(c, "events", "--since", since, "--until", until) 211 events := strings.Split(strings.TrimSpace(out), "\n") 212 assert.Assert(c, len(events) > 4) 213 214 netEvents := eventActionsByIDAndType(c, events, "test-event-network-local", "network") 215 assert.Equal(c, len(netEvents), 4) 216 assert.Equal(c, netEvents[0], "create") 217 assert.Equal(c, netEvents[1], "connect") 218 assert.Equal(c, netEvents[2], "disconnect") 219 assert.Equal(c, netEvents[3], "destroy") 220 } 221 222 func (s *DockerSuite) TestEventsContainerWithMultiNetwork(c *testing.T) { 223 testRequires(c, DaemonIsLinux) 224 225 // Observe create/connect network actions 226 dockerCmd(c, "network", "create", "test-event-network-local-1") 227 dockerCmd(c, "network", "create", "test-event-network-local-2") 228 dockerCmd(c, "run", "--name", "test-network-container", "--net", "test-event-network-local-1", "-td", "busybox", "sh") 229 waitRun("test-network-container") 230 dockerCmd(c, "network", "connect", "test-event-network-local-2", "test-network-container") 231 232 since := daemonUnixTime(c) 233 234 dockerCmd(c, "stop", "-t", "1", "test-network-container") 235 236 until := daemonUnixTime(c) 237 out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "-f", "type=network") 238 netEvents := strings.Split(strings.TrimSpace(out), "\n") 239 240 // received two network disconnect events 241 assert.Equal(c, len(netEvents), 2) 242 assert.Assert(c, strings.Contains(netEvents[0], "disconnect")) 243 assert.Assert(c, strings.Contains(netEvents[1], "disconnect")) 244 245 //both networks appeared in the network event output 246 assert.Assert(c, strings.Contains(out, "test-event-network-local-1")) 247 assert.Assert(c, strings.Contains(out, "test-event-network-local-2")) 248 } 249 250 func (s *DockerSuite) TestEventsStreaming(c *testing.T) { 251 testRequires(c, DaemonIsLinux) 252 253 observer, err := newEventObserver(c) 254 assert.NilError(c, err) 255 err = observer.Start() 256 assert.NilError(c, err) 257 defer observer.Stop() 258 259 out, _ := dockerCmd(c, "run", "-d", "busybox:latest", "true") 260 containerID := strings.TrimSpace(out) 261 262 testActions := map[string]chan bool{ 263 "create": make(chan bool, 1), 264 "start": make(chan bool, 1), 265 "die": make(chan bool, 1), 266 "destroy": make(chan bool, 1), 267 } 268 269 matcher := matchEventLine(containerID, "container", testActions) 270 processor := processEventMatch(testActions) 271 go observer.Match(matcher, processor) 272 273 select { 274 case <-time.After(5 * time.Second): 275 observer.CheckEventError(c, containerID, "create", matcher) 276 case <-testActions["create"]: 277 // ignore, done 278 } 279 280 select { 281 case <-time.After(5 * time.Second): 282 observer.CheckEventError(c, containerID, "start", matcher) 283 case <-testActions["start"]: 284 // ignore, done 285 } 286 287 select { 288 case <-time.After(5 * time.Second): 289 observer.CheckEventError(c, containerID, "die", matcher) 290 case <-testActions["die"]: 291 // ignore, done 292 } 293 294 dockerCmd(c, "rm", containerID) 295 296 select { 297 case <-time.After(5 * time.Second): 298 observer.CheckEventError(c, containerID, "destroy", matcher) 299 case <-testActions["destroy"]: 300 // ignore, done 301 } 302 } 303 304 func (s *DockerSuite) TestEventsImageUntagDelete(c *testing.T) { 305 testRequires(c, DaemonIsLinux) 306 307 observer, err := newEventObserver(c) 308 assert.NilError(c, err) 309 err = observer.Start() 310 assert.NilError(c, err) 311 defer observer.Stop() 312 313 name := "testimageevents" 314 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM scratch 315 MAINTAINER "docker"`)) 316 imageID := getIDByName(c, name) 317 assert.NilError(c, deleteImages(name)) 318 319 testActions := map[string]chan bool{ 320 "untag": make(chan bool, 1), 321 "delete": make(chan bool, 1), 322 } 323 324 matcher := matchEventLine(imageID, "image", testActions) 325 processor := processEventMatch(testActions) 326 go observer.Match(matcher, processor) 327 328 select { 329 case <-time.After(10 * time.Second): 330 observer.CheckEventError(c, imageID, "untag", matcher) 331 case <-testActions["untag"]: 332 // ignore, done 333 } 334 335 select { 336 case <-time.After(10 * time.Second): 337 observer.CheckEventError(c, imageID, "delete", matcher) 338 case <-testActions["delete"]: 339 // ignore, done 340 } 341 } 342 343 func (s *DockerSuite) TestEventsFilterVolumeAndNetworkType(c *testing.T) { 344 testRequires(c, DaemonIsLinux) 345 346 since := daemonUnixTime(c) 347 348 dockerCmd(c, "network", "create", "test-event-network-type") 349 dockerCmd(c, "volume", "create", "test-event-volume-type") 350 351 out, _ := dockerCmd(c, "events", "--filter", "type=volume", "--filter", "type=network", "--since", since, "--until", daemonUnixTime(c)) 352 events := strings.Split(strings.TrimSpace(out), "\n") 353 assert.Assert(c, len(events) >= 2, out) 354 355 networkActions := eventActionsByIDAndType(c, events, "test-event-network-type", "network") 356 volumeActions := eventActionsByIDAndType(c, events, "test-event-volume-type", "volume") 357 358 assert.Equal(c, volumeActions[0], "create") 359 assert.Equal(c, networkActions[0], "create") 360 } 361 362 func (s *DockerSuite) TestEventsFilterVolumeID(c *testing.T) { 363 testRequires(c, DaemonIsLinux) 364 365 since := daemonUnixTime(c) 366 367 dockerCmd(c, "volume", "create", "test-event-volume-id") 368 out, _ := dockerCmd(c, "events", "--filter", "volume=test-event-volume-id", "--since", since, "--until", daemonUnixTime(c)) 369 events := strings.Split(strings.TrimSpace(out), "\n") 370 assert.Equal(c, len(events), 1) 371 372 assert.Equal(c, len(events), 1) 373 assert.Assert(c, strings.Contains(events[0], "test-event-volume-id")) 374 assert.Assert(c, strings.Contains(events[0], "driver=local")) 375 } 376 377 func (s *DockerSuite) TestEventsFilterNetworkID(c *testing.T) { 378 testRequires(c, DaemonIsLinux) 379 380 since := daemonUnixTime(c) 381 382 dockerCmd(c, "network", "create", "test-event-network-local") 383 out, _ := dockerCmd(c, "events", "--filter", "network=test-event-network-local", "--since", since, "--until", daemonUnixTime(c)) 384 events := strings.Split(strings.TrimSpace(out), "\n") 385 assert.Equal(c, len(events), 1) 386 assert.Assert(c, strings.Contains(events[0], "test-event-network-local")) 387 assert.Assert(c, strings.Contains(events[0], "type=bridge")) 388 } 389 390 func (s *DockerDaemonSuite) TestDaemonEvents(c *testing.T) { 391 392 // daemon config file 393 configFilePath := "test.json" 394 configFile, err := os.Create(configFilePath) 395 assert.NilError(c, err) 396 defer os.Remove(configFilePath) 397 398 daemonConfig := `{"labels":["foo=bar"]}` 399 fmt.Fprintf(configFile, "%s", daemonConfig) 400 configFile.Close() 401 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 402 403 // Get daemon ID 404 out, err := s.d.Cmd("info") 405 assert.NilError(c, err) 406 daemonID := "" 407 daemonName := "" 408 for _, line := range strings.Split(out, "\n") { 409 if strings.HasPrefix(line, "ID: ") { 410 daemonID = strings.TrimPrefix(line, "ID: ") 411 } else if strings.HasPrefix(line, "Name: ") { 412 daemonName = strings.TrimPrefix(line, "Name: ") 413 } 414 } 415 assert.Assert(c, daemonID != "") 416 417 configFile, err = os.Create(configFilePath) 418 assert.NilError(c, err) 419 daemonConfig = `{"max-concurrent-downloads":1,"labels":["bar=foo"], "shutdown-timeout": 10}` 420 fmt.Fprintf(configFile, "%s", daemonConfig) 421 configFile.Close() 422 423 assert.NilError(c, s.d.Signal(unix.SIGHUP)) 424 425 time.Sleep(3 * time.Second) 426 427 out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c)) 428 assert.NilError(c, err) 429 430 // only check for values known (daemon ID/name) or explicitly set above, 431 // otherwise just check for names being present. 432 expectedSubstrings := []string{ 433 " daemon reload " + daemonID + " ", 434 "(allow-nondistributable-artifacts=[", 435 " cluster-advertise=, ", 436 " cluster-store=, ", 437 " cluster-store-opts=", 438 " debug=true, ", 439 " default-ipc-mode=", 440 " default-runtime=", 441 " default-shm-size=", 442 " insecure-registries=[", 443 " labels=[\"bar=foo\"], ", 444 " live-restore=", 445 " max-concurrent-downloads=1, ", 446 " max-concurrent-uploads=5, ", 447 " name=" + daemonName, 448 " registry-mirrors=[", 449 " runtimes=", 450 " shutdown-timeout=10)", 451 } 452 453 for _, s := range expectedSubstrings { 454 assert.Check(c, strings.Contains(out, s)) 455 } 456 } 457 458 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *testing.T) { 459 460 // daemon config file 461 configFilePath := "test.json" 462 configFile, err := os.Create(configFilePath) 463 assert.NilError(c, err) 464 defer os.Remove(configFilePath) 465 466 daemonConfig := `{"labels":["foo=bar"]}` 467 fmt.Fprintf(configFile, "%s", daemonConfig) 468 configFile.Close() 469 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 470 471 // Get daemon ID 472 out, err := s.d.Cmd("info") 473 assert.NilError(c, err) 474 daemonID := "" 475 daemonName := "" 476 for _, line := range strings.Split(out, "\n") { 477 if strings.HasPrefix(line, "ID: ") { 478 daemonID = strings.TrimPrefix(line, "ID: ") 479 } else if strings.HasPrefix(line, "Name: ") { 480 daemonName = strings.TrimPrefix(line, "Name: ") 481 } 482 } 483 assert.Assert(c, daemonID != "") 484 assert.NilError(c, s.d.Signal(unix.SIGHUP)) 485 486 time.Sleep(3 * time.Second) 487 488 out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonID)) 489 assert.NilError(c, err) 490 assert.Assert(c, strings.Contains(out, fmt.Sprintf("daemon reload %s", daemonID))) 491 492 out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonName)) 493 assert.NilError(c, err) 494 assert.Assert(c, strings.Contains(out, fmt.Sprintf("daemon reload %s", daemonID))) 495 496 out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "daemon=foo") 497 assert.NilError(c, err) 498 assert.Assert(c, !strings.Contains(out, fmt.Sprintf("daemon reload %s", daemonID))) 499 500 out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=daemon") 501 assert.NilError(c, err) 502 assert.Assert(c, strings.Contains(out, fmt.Sprintf("daemon reload %s", daemonID))) 503 504 out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=container") 505 assert.NilError(c, err) 506 assert.Assert(c, !strings.Contains(out, fmt.Sprintf("daemon reload %s", daemonID))) 507 }