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