github.com/moby/docker@v26.1.3+incompatible/integration-cli/docker_cli_daemon_test.go (about) 1 //go:build linux 2 3 package main 4 5 import ( 6 "bufio" 7 "bytes" 8 "context" 9 "crypto/tls" 10 "crypto/x509" 11 "encoding/json" 12 "fmt" 13 "io" 14 "net" 15 "os" 16 "os/exec" 17 "path" 18 "path/filepath" 19 "regexp" 20 "strconv" 21 "strings" 22 "sync" 23 "testing" 24 "time" 25 26 "github.com/cloudflare/cfssl/helpers" 27 "github.com/creack/pty" 28 "github.com/docker/docker/api/types" 29 "github.com/docker/docker/integration-cli/checker" 30 "github.com/docker/docker/integration-cli/cli" 31 "github.com/docker/docker/integration-cli/cli/build" 32 "github.com/docker/docker/integration-cli/daemon" 33 "github.com/docker/docker/libnetwork/iptables" 34 "github.com/docker/docker/opts" 35 "github.com/docker/docker/testutil" 36 testdaemon "github.com/docker/docker/testutil/daemon" 37 "github.com/moby/sys/mount" 38 "golang.org/x/sys/unix" 39 "gotest.tools/v3/assert" 40 is "gotest.tools/v3/assert/cmp" 41 "gotest.tools/v3/icmd" 42 "gotest.tools/v3/poll" 43 "gotest.tools/v3/skip" 44 ) 45 46 const containerdSocket = "/var/run/docker/containerd/containerd.sock" 47 48 // TestLegacyDaemonCommand test starting docker daemon using "deprecated" docker daemon 49 // command. Remove this test when we remove this. 50 func (s *DockerDaemonSuite) TestLegacyDaemonCommand(c *testing.T) { 51 cmd := exec.Command(dockerBinary, "daemon", "--storage-driver=vfs", "--debug") 52 err := cmd.Start() 53 go cmd.Wait() 54 assert.NilError(c, err, "could not start daemon using 'docker daemon'") 55 assert.NilError(c, cmd.Process.Kill()) 56 } 57 58 func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *testing.T) { 59 s.d.StartWithBusybox(testutil.GetContext(c), c) 60 61 cli.Docker( 62 cli.Args("run", "-d", "--name", "top1", "-p", "1234:80", "--restart", "always", "busybox:latest", "top"), 63 cli.Daemon(s.d), 64 ).Assert(c, icmd.Success) 65 66 cli.Docker( 67 cli.Args("run", "-d", "--name", "top2", "-p", "80", "busybox:latest", "top"), 68 cli.Daemon(s.d), 69 ).Assert(c, icmd.Success) 70 71 testRun := func(m map[string]bool, prefix string) { 72 var format string 73 for cont, shouldRun := range m { 74 out := cli.Docker(cli.Args("ps"), cli.Daemon(s.d)).Assert(c, icmd.Success).Combined() 75 if shouldRun { 76 format = "%scontainer %q is not running" 77 } else { 78 format = "%scontainer %q is running" 79 } 80 if shouldRun != strings.Contains(out, cont) { 81 c.Fatalf(format, prefix, cont) 82 } 83 } 84 } 85 86 testRun(map[string]bool{"top1": true, "top2": true}, "") 87 88 s.d.Restart(c) 89 testRun(map[string]bool{"top1": true, "top2": false}, "After daemon restart: ") 90 } 91 92 func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *testing.T) { 93 s.d.StartWithBusybox(testutil.GetContext(c), c) 94 95 if out, err := s.d.Cmd("run", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil { 96 c.Fatal(err, out) 97 } 98 99 s.d.Restart(c) 100 101 if out, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil { 102 c.Fatal(err, out) 103 } 104 105 if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil { 106 c.Fatal(err, out) 107 } 108 109 out, err := s.d.Cmd("inspect", "-f", `{{range .Mounts}}{{.Destination}}{{"\n"}}{{end}}`, "volrestarttest1") 110 assert.Check(c, err) 111 assert.Check(c, is.Contains(strings.Split(out, "\n"), "/foo")) 112 } 113 114 // #11008 115 func (s *DockerDaemonSuite) TestDaemonRestartUnlessStopped(c *testing.T) { 116 s.d.StartWithBusybox(testutil.GetContext(c), c) 117 118 out, err := s.d.Cmd("run", "-d", "--name", "top1", "--restart", "always", "busybox:latest", "top") 119 assert.NilError(c, err, "run top1: %v", out) 120 121 out, err = s.d.Cmd("run", "-d", "--name", "top2", "--restart", "unless-stopped", "busybox:latest", "top") 122 assert.NilError(c, err, "run top2: %v", out) 123 124 out, err = s.d.Cmd("run", "-d", "--name", "exit", "--restart", "unless-stopped", "busybox:latest", "false") 125 assert.NilError(c, err, "run exit: %v", out) 126 127 testRun := func(m map[string]bool, prefix string) { 128 var format string 129 for name, shouldRun := range m { 130 out, err := s.d.Cmd("ps") 131 assert.Assert(c, err == nil, "run ps: %v", out) 132 if shouldRun { 133 format = "%scontainer %q is not running" 134 } else { 135 format = "%scontainer %q is running" 136 } 137 assert.Equal(c, strings.Contains(out, name), shouldRun, fmt.Sprintf(format, prefix, name)) 138 } 139 } 140 141 // both running 142 testRun(map[string]bool{"top1": true, "top2": true, "exit": true}, "") 143 144 out, err = s.d.Cmd("stop", "exit") 145 assert.NilError(c, err, out) 146 147 out, err = s.d.Cmd("stop", "top1") 148 assert.NilError(c, err, out) 149 150 out, err = s.d.Cmd("stop", "top2") 151 assert.NilError(c, err, out) 152 153 // both stopped 154 testRun(map[string]bool{"top1": false, "top2": false, "exit": false}, "") 155 156 s.d.Restart(c) 157 158 // restart=always running 159 testRun(map[string]bool{"top1": true, "top2": false, "exit": false}, "After daemon restart: ") 160 161 out, err = s.d.Cmd("start", "top2") 162 assert.NilError(c, err, "start top2: %v", out) 163 164 out, err = s.d.Cmd("start", "exit") 165 assert.NilError(c, err, "start exit: %v", out) 166 167 s.d.Restart(c) 168 169 // both running 170 testRun(map[string]bool{"top1": true, "top2": true, "exit": true}, "After second daemon restart: ") 171 } 172 173 func (s *DockerDaemonSuite) TestDaemonRestartOnFailure(c *testing.T) { 174 s.d.StartWithBusybox(testutil.GetContext(c), c) 175 176 out, err := s.d.Cmd("run", "-d", "--name", "test1", "--restart", "on-failure:3", "busybox:latest", "false") 177 assert.NilError(c, err, "run top1: %v", out) 178 179 // wait test1 to stop 180 hostArgs := []string{"--host", s.d.Sock()} 181 err = daemon.WaitInspectWithArgs(dockerBinary, "test1", "{{.State.Running}} {{.State.Restarting}}", "false false", 10*time.Second, hostArgs...) 182 assert.NilError(c, err, "test1 should exit but not") 183 184 // record last start time 185 out, err = s.d.Cmd("inspect", "-f={{.State.StartedAt}}", "test1") 186 assert.NilError(c, err, "out: %v", out) 187 lastStartTime := out 188 189 s.d.Restart(c) 190 191 // test1 shouldn't restart at all 192 err = daemon.WaitInspectWithArgs(dockerBinary, "test1", "{{.State.Running}} {{.State.Restarting}}", "false false", 0, hostArgs...) 193 assert.NilError(c, err, "test1 should exit but not") 194 195 // make sure test1 isn't restarted when daemon restart 196 // if "StartAt" time updates, means test1 was once restarted. 197 out, err = s.d.Cmd("inspect", "-f={{.State.StartedAt}}", "test1") 198 assert.NilError(c, err, "out: %v", out) 199 assert.Equal(c, out, lastStartTime, "test1 shouldn't start after daemon restarts") 200 } 201 202 func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *testing.T) { 203 s.d.Start(c, "--iptables=false") 204 } 205 206 // Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and 207 // no longer has an IP associated, we should gracefully handle that case and associate 208 // an IP with it rather than fail daemon start 209 func (s *DockerDaemonSuite) TestDaemonStartBridgeWithoutIPAssociation(c *testing.T) { 210 // rather than depending on brctl commands to verify docker0 is created and up 211 // let's start the daemon and stop it, and then make a modification to run the 212 // actual test 213 s.d.Start(c) 214 s.d.Stop(c) 215 216 // now we will remove the ip from docker0 and then try starting the daemon 217 icmd.RunCommand("ip", "addr", "flush", "dev", "docker0").Assert(c, icmd.Success) 218 219 if err := s.d.StartWithError(); err != nil { 220 warning := "**WARNING: Docker bridge network in bad state--delete docker0 bridge interface to fix" 221 c.Fatalf("Could not start daemon when docker0 has no IP address: %v\n%s", err, warning) 222 } 223 } 224 225 func (s *DockerDaemonSuite) TestDaemonIptablesClean(c *testing.T) { 226 s.d.StartWithBusybox(testutil.GetContext(c), c) 227 228 if out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil { 229 c.Fatalf("Could not run top: %s, %v", out, err) 230 } 231 232 ipTablesSearchString := "tcp dpt:80" 233 234 // get output from iptables with container running 235 verifyIPTablesContains(c, ipTablesSearchString) 236 237 s.d.Stop(c) 238 239 // get output from iptables after restart 240 verifyIPTablesDoesNotContains(c, ipTablesSearchString) 241 } 242 243 func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *testing.T) { 244 s.d.StartWithBusybox(testutil.GetContext(c), c) 245 246 if out, err := s.d.Cmd("run", "-d", "--name", "top", "--restart=always", "-p", "80", "busybox:latest", "top"); err != nil { 247 c.Fatalf("Could not run top: %s, %v", out, err) 248 } 249 250 // get output from iptables with container running 251 ipTablesSearchString := "tcp dpt:80" 252 verifyIPTablesContains(c, ipTablesSearchString) 253 254 s.d.Restart(c) 255 256 // make sure the container is not running 257 runningOut, err := s.d.Cmd("inspect", "--format={{.State.Running}}", "top") 258 if err != nil { 259 c.Fatalf("Could not inspect on container: %s, %v", runningOut, err) 260 } 261 if strings.TrimSpace(runningOut) != "true" { 262 c.Fatalf("Container should have been restarted after daemon restart. Status running should have been true but was: %q", strings.TrimSpace(runningOut)) 263 } 264 265 // get output from iptables after restart 266 verifyIPTablesContains(c, ipTablesSearchString) 267 } 268 269 func verifyIPTablesContains(c *testing.T, ipTablesSearchString string) { 270 result := icmd.RunCommand("iptables", "-nvL") 271 result.Assert(c, icmd.Success) 272 if !strings.Contains(result.Combined(), ipTablesSearchString) { 273 c.Fatalf("iptables output should have contained %q, but was %q", ipTablesSearchString, result.Combined()) 274 } 275 } 276 277 func verifyIPTablesDoesNotContains(c *testing.T, ipTablesSearchString string) { 278 result := icmd.RunCommand("iptables", "-nvL") 279 result.Assert(c, icmd.Success) 280 if strings.Contains(result.Combined(), ipTablesSearchString) { 281 c.Fatalf("iptables output should not have contained %q, but was %q", ipTablesSearchString, result.Combined()) 282 } 283 } 284 285 // TestDaemonIPv6Enabled checks that when the daemon is started with --ipv6=true that the docker0 bridge 286 // has the fe80::1 address and that a container is assigned a link-local address 287 func (s *DockerDaemonSuite) TestDaemonIPv6Enabled(c *testing.T) { 288 testRequires(c, IPv6) 289 290 setupV6(c) 291 defer teardownV6(c) 292 293 s.d.StartWithBusybox(testutil.GetContext(c), c, "--ipv6") 294 295 iface, err := net.InterfaceByName("docker0") 296 if err != nil { 297 c.Fatalf("Error getting docker0 interface: %v", err) 298 } 299 300 addrs, err := iface.Addrs() 301 if err != nil { 302 c.Fatalf("Error getting addresses for docker0 interface: %v", err) 303 } 304 305 var found bool 306 expected := "fe80::1/64" 307 308 for i := range addrs { 309 if addrs[i].String() == expected { 310 found = true 311 break 312 } 313 } 314 315 if !found { 316 c.Fatalf("Bridge does not have an IPv6 Address") 317 } 318 319 if out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "busybox:latest"); err != nil { 320 c.Fatalf("Could not run container: %s, %v", out, err) 321 } 322 323 out, err := s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.LinkLocalIPv6Address}}'", "ipv6test") 324 if err != nil { 325 c.Fatalf("Error inspecting container: %s, %v", out, err) 326 } 327 out = strings.Trim(out, " \r\n'") 328 329 if ip := net.ParseIP(out); ip == nil { 330 c.Fatalf("Container should have a link-local IPv6 address") 331 } 332 333 out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test") 334 if err != nil { 335 c.Fatalf("Error inspecting container: %s, %v", out, err) 336 } 337 out = strings.Trim(out, " \r\n'") 338 339 if ip := net.ParseIP(out); ip != nil { 340 c.Fatalf("Container should not have a global IPv6 address: %v", out) 341 } 342 } 343 344 // TestDaemonIPv6FixedCIDR checks that when the daemon is started with --ipv6=true and a fixed CIDR 345 // that running containers are given a link-local and global IPv6 address 346 func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *testing.T) { 347 // IPv6 setup is messing with local bridge address. 348 testRequires(c, testEnv.IsLocalDaemon) 349 // Delete the docker0 bridge if its left around from previous daemon. It has to be recreated with 350 // ipv6 enabled 351 deleteInterface(c, "docker0") 352 353 s.d.StartWithBusybox(testutil.GetContext(c), c, "--ipv6", "--fixed-cidr-v6=2001:db8:2::/64", "--default-gateway-v6=2001:db8:2::100") 354 355 out, err := s.d.Cmd("run", "-d", "--name=ipv6test", "busybox:latest", "top") 356 assert.NilError(c, err, "Could not run container: %s, %v", out, err) 357 358 out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test") 359 assert.NilError(c, err, out) 360 out = strings.Trim(out, " \r\n'") 361 362 ip := net.ParseIP(out) 363 assert.Assert(c, ip != nil, "Container should have a global IPv6 address") 364 365 out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.IPv6Gateway}}", "ipv6test") 366 assert.NilError(c, err, out) 367 368 assert.Equal(c, strings.Trim(out, " \r\n'"), "2001:db8:2::100", "Container should have a global IPv6 gateway") 369 } 370 371 // TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR 372 // the running containers are given an IPv6 address derived from the MAC address and the ipv6 fixed CIDR 373 func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDRAndMac(c *testing.T) { 374 // IPv6 setup is messing with local bridge address. 375 testRequires(c, testEnv.IsLocalDaemon) 376 // Delete the docker0 bridge if its left around from previous daemon. It has to be recreated with 377 // ipv6 enabled 378 deleteInterface(c, "docker0") 379 380 s.d.StartWithBusybox(testutil.GetContext(c), c, "--ipv6", "--fixed-cidr-v6=2001:db8:1::/64") 381 382 out, err := s.d.Cmd("run", "-d", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox", "top") 383 assert.NilError(c, err, out) 384 385 out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test") 386 assert.NilError(c, err, out) 387 assert.Equal(c, strings.Trim(out, " \r\n'"), "2001:db8:1::aabb:ccdd:eeff") 388 } 389 390 // TestDaemonIPv6HostMode checks that when the running a container with 391 // network=host the host ipv6 addresses are not removed 392 func (s *DockerDaemonSuite) TestDaemonIPv6HostMode(c *testing.T) { 393 testRequires(c, testEnv.IsLocalDaemon) 394 deleteInterface(c, "docker0") 395 396 s.d.StartWithBusybox(testutil.GetContext(c), c, "--ipv6", "--fixed-cidr-v6=2001:db8:2::/64") 397 out, err := s.d.Cmd("run", "-d", "--name=hostcnt", "--network=host", "busybox:latest", "top") 398 assert.NilError(c, err, "Could not run container: %s, %v", out, err) 399 400 out, err = s.d.Cmd("exec", "hostcnt", "ip", "-6", "addr", "show", "docker0") 401 assert.NilError(c, err, out) 402 assert.Assert(c, strings.Contains(strings.Trim(out, " \r\n'"), "2001:db8:2::1")) 403 } 404 405 func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *testing.T) { 406 assert.Assert(c, s.d.StartWithError("--log-level=bogus") != nil, "Daemon shouldn't start with wrong log level") 407 } 408 409 func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *testing.T) { 410 s.d.Start(c, "--log-level=debug") 411 content, err := s.d.ReadLogFile() 412 assert.NilError(c, err) 413 if !strings.Contains(string(content), `level=debug`) { 414 c.Fatalf(`Missing level="debug" in log file:\n%s`, string(content)) 415 } 416 } 417 418 func (s *DockerDaemonSuite) TestDaemonLogLevelFatal(c *testing.T) { 419 // we creating new daemons to create new logFile 420 s.d.Start(c, "--log-level=fatal") 421 content, err := s.d.ReadLogFile() 422 assert.NilError(c, err) 423 if strings.Contains(string(content), `level=debug`) { 424 c.Fatalf(`Should not have level="debug" in log file:\n%s`, string(content)) 425 } 426 } 427 428 func (s *DockerDaemonSuite) TestDaemonFlagD(c *testing.T) { 429 s.d.Start(c, "-D") 430 content, err := s.d.ReadLogFile() 431 assert.NilError(c, err) 432 if !strings.Contains(string(content), `level=debug`) { 433 c.Fatalf(`Should have level="debug" in log file using -D:\n%s`, string(content)) 434 } 435 } 436 437 func (s *DockerDaemonSuite) TestDaemonFlagDebug(c *testing.T) { 438 s.d.Start(c, "--debug") 439 content, err := s.d.ReadLogFile() 440 assert.NilError(c, err) 441 if !strings.Contains(string(content), `level=debug`) { 442 c.Fatalf(`Should have level="debug" in log file using --debug:\n%s`, string(content)) 443 } 444 } 445 446 func (s *DockerDaemonSuite) TestDaemonFlagDebugLogLevelFatal(c *testing.T) { 447 s.d.Start(c, "--debug", "--log-level=fatal") 448 content, err := s.d.ReadLogFile() 449 assert.NilError(c, err) 450 if !strings.Contains(string(content), `level=debug`) { 451 c.Fatalf(`Should have level="debug" in log file when using both --debug and --log-level=fatal:\n%s`, string(content)) 452 } 453 } 454 455 func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *testing.T) { 456 type listener struct { 457 daemon string 458 client string 459 port string 460 } 461 listeningPorts := []listener{ 462 {"0.0.0.0", "0.0.0.0", "5678"}, 463 {"127.0.0.1", "127.0.0.1", "1234"}, 464 {"localhost", "127.0.0.1", "1235"}, 465 } 466 467 cmdArgs := make([]string, 0, len(listeningPorts)*2) 468 for _, l := range listeningPorts { 469 cmdArgs = append(cmdArgs, "--tls=false", "--host", "tcp://"+net.JoinHostPort(l.daemon, l.port)) 470 } 471 472 s.d.StartWithBusybox(testutil.GetContext(c), c, cmdArgs...) 473 474 for _, l := range listeningPorts { 475 output, err := s.d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", l.client, l.port), "busybox", "true") 476 if err == nil { 477 c.Fatalf("Container should not start, expected port already allocated error: %q", output) 478 } else if !strings.Contains(output, "port is already allocated") { 479 c.Fatalf("Expected port is already allocated error: %q", output) 480 } 481 } 482 } 483 484 // GH#11320 - verify that the daemon exits on failure properly 485 // Note that this explicitly tests the conflict of {-b,--bridge} and {--bip} options as the means 486 // to get a daemon init failure; no other tests for -b/--bip conflict are therefore required 487 func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *testing.T) { 488 // attempt to start daemon with incorrect flags (we know -b and --bip conflict) 489 if err := s.d.StartWithError("--bridge", "nosuchbridge", "--bip", "1.1.1.1"); err != nil { 490 // verify we got the right error 491 if !strings.Contains(err.Error(), "daemon exited") { 492 c.Fatalf("Expected daemon not to start, got %v", err) 493 } 494 // look in the log and make sure we got the message that daemon is shutting down 495 icmd.RunCommand("grep", "failed to start daemon", s.d.LogFileName()).Assert(c, icmd.Success) 496 } else { 497 // if we didn't get an error and the daemon is running, this is a failure 498 c.Fatal("Conflicting options should cause the daemon to error out with a failure") 499 } 500 } 501 502 func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *testing.T) { 503 d := s.d 504 err := d.StartWithError("--bridge", "nosuchbridge") 505 assert.ErrorContains(c, err, "", `--bridge option with an invalid bridge should cause the daemon to fail`) 506 defer d.Restart(c) 507 508 // make sure the default docker0 bridge doesn't interfere with the test, 509 // which may happen if it was created with the same IP range. 510 deleteInterface(c, "docker0") 511 512 bridgeName := "ext-bridge1" 513 bridgeIP := "192.169.1.1/24" 514 _, bridgeIPNet, _ := net.ParseCIDR(bridgeIP) 515 516 createInterface(c, "bridge", bridgeName, bridgeIP) 517 defer deleteInterface(c, bridgeName) 518 519 d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName) 520 521 ipTablesSearchString := bridgeIPNet.String() 522 icmd.RunCommand("iptables", "-t", "nat", "-nvL").Assert(c, icmd.Expected{ 523 Out: ipTablesSearchString, 524 }) 525 526 out, err := d.Cmd("run", "-d", "--name", "ExtContainer", "busybox", "top") 527 assert.NilError(c, err, out) 528 529 containerIP := d.FindContainerIP(c, "ExtContainer") 530 ip := net.ParseIP(containerIP) 531 assert.Assert(c, bridgeIPNet.Contains(ip), "Container IP-Address must be in the same subnet range : %s", containerIP) 532 } 533 534 func (s *DockerDaemonSuite) TestDaemonBridgeNone(c *testing.T) { 535 // start with bridge none 536 d := s.d 537 d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", "none") 538 defer d.Restart(c) 539 540 // verify docker0 iface is not there 541 icmd.RunCommand("ifconfig", "docker0").Assert(c, icmd.Expected{ 542 ExitCode: 1, 543 Error: "exit status 1", 544 Err: "Device not found", 545 }) 546 547 // verify default "bridge" network is not there 548 out, err := d.Cmd("network", "inspect", "bridge") 549 assert.ErrorContains(c, err, "", `"bridge" network should not be present if daemon started with --bridge=none`) 550 assert.Assert(c, strings.Contains(out, "No such network")) 551 } 552 553 func createInterface(c *testing.T, ifType string, ifName string, ipNet string) { 554 icmd.RunCommand("ip", "link", "add", "name", ifName, "type", ifType).Assert(c, icmd.Success) 555 icmd.RunCommand("ifconfig", ifName, ipNet, "up").Assert(c, icmd.Success) 556 } 557 558 func deleteInterface(c *testing.T, ifName string) { 559 icmd.RunCommand("ip", "link", "delete", ifName).Assert(c, icmd.Success) 560 icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(c, icmd.Success) 561 icmd.RunCommand("iptables", "--flush").Assert(c, icmd.Success) 562 } 563 564 func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *testing.T) { 565 // TestDaemonBridgeIP Steps 566 // 1. Delete the existing docker0 Bridge 567 // 2. Set --bip daemon configuration and start the new Docker Daemon 568 // 3. Check if the bip config has taken effect using ifconfig and iptables commands 569 // 4. Launch a Container and make sure the IP-Address is in the expected subnet 570 // 5. Delete the docker0 Bridge 571 // 6. Restart the Docker Daemon (via deferred action) 572 // This Restart takes care of bringing docker0 interface back to auto-assigned IP 573 574 defaultNetworkBridge := "docker0" 575 deleteInterface(c, defaultNetworkBridge) 576 577 d := s.d 578 579 bridgeIP := "192.169.1.1/24" 580 ip, bridgeIPNet, _ := net.ParseCIDR(bridgeIP) 581 582 d.StartWithBusybox(testutil.GetContext(c), c, "--bip", bridgeIP) 583 defer d.Restart(c) 584 585 ifconfigSearchString := ip.String() 586 icmd.RunCommand("ifconfig", defaultNetworkBridge).Assert(c, icmd.Expected{ 587 Out: ifconfigSearchString, 588 }) 589 590 ipTablesSearchString := bridgeIPNet.String() 591 icmd.RunCommand("iptables", "-t", "nat", "-nvL").Assert(c, icmd.Expected{ 592 Out: ipTablesSearchString, 593 }) 594 595 out, err := d.Cmd("run", "-d", "--name", "test", "busybox", "top") 596 assert.NilError(c, err, out) 597 598 containerIP := d.FindContainerIP(c, "test") 599 ip = net.ParseIP(containerIP) 600 assert.Equal(c, bridgeIPNet.Contains(ip), true, fmt.Sprintf("Container IP-Address must be in the same subnet range : %s", containerIP)) 601 deleteInterface(c, defaultNetworkBridge) 602 } 603 604 func (s *DockerDaemonSuite) TestDaemonRestartWithBridgeIPChange(c *testing.T) { 605 s.d.Start(c) 606 defer s.d.Restart(c) 607 s.d.Stop(c) 608 609 // now we will change the docker0's IP and then try starting the daemon 610 bridgeIP := "192.169.100.1/24" 611 _, bridgeIPNet, _ := net.ParseCIDR(bridgeIP) 612 613 icmd.RunCommand("ifconfig", "docker0", bridgeIP).Assert(c, icmd.Success) 614 615 s.d.Start(c, "--bip", bridgeIP) 616 617 // check if the iptables contains new bridgeIP MASQUERADE rule 618 ipTablesSearchString := bridgeIPNet.String() 619 icmd.RunCommand("iptables", "-t", "nat", "-nvL").Assert(c, icmd.Expected{ 620 Out: ipTablesSearchString, 621 }) 622 } 623 624 func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *testing.T) { 625 d := s.d 626 627 // make sure the default docker0 bridge doesn't interfere with the test, 628 // which may happen if it was created with the same IP range. 629 deleteInterface(c, "docker0") 630 631 bridgeName := "ext-bridge2" 632 bridgeIP := "192.169.1.1/24" 633 634 createInterface(c, "bridge", bridgeName, bridgeIP) 635 defer deleteInterface(c, bridgeName) 636 637 args := []string{"--bridge", bridgeName, "--fixed-cidr", "192.169.1.0/30"} 638 d.StartWithBusybox(testutil.GetContext(c), c, args...) 639 defer d.Restart(c) 640 641 for i := 0; i < 4; i++ { 642 cName := "Container" + strconv.Itoa(i) 643 out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top") 644 if err != nil { 645 assert.Assert(c, strings.Contains(out, "no available IPv4 addresses"), "Could not run a Container : %s %s", err.Error(), out) 646 } 647 } 648 } 649 650 func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *testing.T) { 651 d := s.d 652 653 // make sure the default docker0 bridge doesn't interfere with the test, 654 // which may happen if it was created with the same IP range. 655 deleteInterface(c, "docker0") 656 657 bridgeName := "ext-bridge3" 658 bridgeIP := "10.2.2.1/16" 659 660 createInterface(c, "bridge", bridgeName, bridgeIP) 661 defer deleteInterface(c, bridgeName) 662 663 d.StartWithBusybox(testutil.GetContext(c), c, "--bip", bridgeIP, "--fixed-cidr", "10.2.2.0/24") 664 defer s.d.Restart(c) 665 666 out, err := d.Cmd("run", "-d", "--name", "bb", "busybox", "top") 667 assert.NilError(c, err, out) 668 defer d.Cmd("stop", "bb") 669 670 out, err = d.Cmd("exec", "bb", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'") 671 assert.NilError(c, err) 672 assert.Equal(c, out, "10.2.2.0\n") 673 674 out, err = d.Cmd("run", "--rm", "busybox", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'") 675 assert.NilError(c, err, out) 676 assert.Equal(c, out, "10.2.2.2\n") 677 } 678 679 func (s *DockerDaemonSuite) TestDaemonBridgeFixedCIDREqualBridgeNetwork(c *testing.T) { 680 d := s.d 681 682 // make sure the default docker0 bridge doesn't interfere with the test, 683 // which may happen if it was created with the same IP range. 684 deleteInterface(c, "docker0") 685 686 bridgeName := "ext-bridge4" 687 bridgeIP := "172.27.42.1/16" 688 689 createInterface(c, "bridge", bridgeName, bridgeIP) 690 defer deleteInterface(c, bridgeName) 691 692 d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName, "--fixed-cidr", bridgeIP) 693 defer s.d.Restart(c) 694 695 out, err := d.Cmd("run", "-d", "busybox", "top") 696 assert.NilError(c, err, out) 697 cid1 := strings.TrimSpace(out) 698 defer d.Cmd("stop", cid1) 699 } 700 701 func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Implicit(c *testing.T) { 702 defaultNetworkBridge := "docker0" 703 deleteInterface(c, defaultNetworkBridge) 704 705 d := s.d 706 707 bridgeIP := "192.169.1.1" 708 bridgeIPNet := fmt.Sprintf("%s/24", bridgeIP) 709 710 d.StartWithBusybox(testutil.GetContext(c), c, "--bip", bridgeIPNet) 711 defer d.Restart(c) 712 713 expectedMessage := fmt.Sprintf("default via %s dev", bridgeIP) 714 out, err := d.Cmd("run", "busybox", "ip", "-4", "route", "list", "0/0") 715 assert.NilError(c, err, out) 716 assert.Equal(c, strings.Contains(out, expectedMessage), true, fmt.Sprintf("Implicit default gateway should be bridge IP %s, but default route was '%s'", bridgeIP, strings.TrimSpace(out))) 717 deleteInterface(c, defaultNetworkBridge) 718 } 719 720 func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Explicit(c *testing.T) { 721 defaultNetworkBridge := "docker0" 722 deleteInterface(c, defaultNetworkBridge) 723 724 d := s.d 725 726 bridgeIP := "192.169.1.1" 727 bridgeIPNet := fmt.Sprintf("%s/24", bridgeIP) 728 gatewayIP := "192.169.1.254" 729 730 d.StartWithBusybox(testutil.GetContext(c), c, "--bip", bridgeIPNet, "--default-gateway", gatewayIP) 731 defer d.Restart(c) 732 733 expectedMessage := fmt.Sprintf("default via %s dev", gatewayIP) 734 out, err := d.Cmd("run", "busybox", "ip", "-4", "route", "list", "0/0") 735 assert.NilError(c, err, out) 736 assert.Equal(c, strings.Contains(out, expectedMessage), true, fmt.Sprintf("Explicit default gateway should be %s, but default route was '%s'", gatewayIP, strings.TrimSpace(out))) 737 deleteInterface(c, defaultNetworkBridge) 738 } 739 740 func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4ExplicitOutsideContainerSubnet(c *testing.T) { 741 defaultNetworkBridge := "docker0" 742 deleteInterface(c, defaultNetworkBridge) 743 744 // Program a custom default gateway outside of the container subnet, daemon should accept it and start 745 s.d.StartWithBusybox(testutil.GetContext(c), c, "--bip", "172.16.0.10/16", "--fixed-cidr", "172.16.1.0/24", "--default-gateway", "172.16.0.254") 746 747 deleteInterface(c, defaultNetworkBridge) 748 s.d.Restart(c) 749 } 750 751 func (s *DockerDaemonSuite) TestDaemonIP(c *testing.T) { 752 d := s.d 753 754 // make sure the default docker0 bridge doesn't interfere with the test, 755 // which may happen if it was created with the same IP range. 756 deleteInterface(c, "docker0") 757 758 ipStr := "192.170.1.1/24" 759 ip, _, _ := net.ParseCIDR(ipStr) 760 args := []string{"--ip", ip.String()} 761 d.StartWithBusybox(testutil.GetContext(c), c, args...) 762 defer d.Restart(c) 763 764 out, err := d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") 765 assert.Assert(c, err != nil, "Running a container must fail with an invalid --ip option") 766 assert.Equal(c, strings.Contains(out, "Error starting userland proxy"), true) 767 768 ifName := "dummy" 769 createInterface(c, "dummy", ifName, ipStr) 770 defer deleteInterface(c, ifName) 771 772 _, err = d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") 773 assert.NilError(c, err, out) 774 775 result := icmd.RunCommand("iptables", "-t", "nat", "-nvL") 776 result.Assert(c, icmd.Success) 777 regex := fmt.Sprintf("DNAT.*%s.*dpt:8000", ip.String()) 778 matched, _ := regexp.MatchString(regex, result.Combined()) 779 assert.Equal(c, matched, true, fmt.Sprintf("iptables output should have contained %q, but was %q", regex, result.Combined())) 780 } 781 782 func (s *DockerDaemonSuite) TestDaemonICCPing(c *testing.T) { 783 testRequires(c, bridgeNfIptables) 784 d := s.d 785 786 // make sure the default docker0 bridge doesn't interfere with the test, 787 // which may happen if it was created with the same IP range. 788 deleteInterface(c, "docker0") 789 790 const bridgeName = "ext-bridge5" 791 const bridgeIP = "192.169.1.1/24" 792 793 createInterface(c, "bridge", bridgeName, bridgeIP) 794 defer deleteInterface(c, bridgeName) 795 796 d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName, "--icc=false") 797 defer d.Restart(c) 798 799 result := icmd.RunCommand("sh", "-c", "iptables -vL FORWARD | grep DROP") 800 result.Assert(c, icmd.Success) 801 802 // strip whitespace and newlines to verify we only found a single DROP 803 out := strings.TrimSpace(result.Stdout()) 804 assert.Assert(c, is.Equal(strings.Count(out, "\n"), 0), "only expected a single DROP rules") 805 806 // Column headers are stripped because of grep-ing, but should be: 807 // 808 // pkts bytes target prot opt in out source destination 809 // 0 0 DROP all -- ext-bridge5 ext-bridge5 anywhere anywhere 810 // 811 //nolint:dupword 812 cols := strings.Fields(out) 813 814 expected := []string{"0", "0", "DROP", "all", "--", bridgeName, bridgeName, "anywhere", "anywhere"} 815 assert.DeepEqual(c, cols, expected) 816 817 // Pinging another container must fail with --icc=false 818 pingContainers(c, d, true) 819 820 const cidr = "192.171.1.1/24" 821 ip, _, _ := net.ParseCIDR(cidr) 822 const ifName = "icc-dummy" 823 824 createInterface(c, "dummy", ifName, cidr) 825 defer deleteInterface(c, ifName) 826 827 // But, Pinging external or a Host interface must succeed 828 pingCmd := fmt.Sprintf("ping -c 1 %s -W 1", ip.String()) 829 runArgs := []string{"run", "--rm", "busybox", "sh", "-c", pingCmd} 830 out, err := d.Cmd(runArgs...) 831 assert.NilError(c, err, out) 832 } 833 834 func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *testing.T) { 835 d := s.d 836 837 // make sure the default docker0 bridge doesn't interfere with the test, 838 // which may happen if it was created with the same IP range. 839 deleteInterface(c, "docker0") 840 841 const bridgeName = "ext-bridge6" 842 const bridgeIP = "192.169.1.1/24" 843 844 createInterface(c, "bridge", bridgeName, bridgeIP) 845 defer deleteInterface(c, bridgeName) 846 847 d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName, "--icc=false") 848 defer d.Restart(c) 849 850 result := icmd.RunCommand("sh", "-c", "iptables -vL FORWARD | grep DROP") 851 result.Assert(c, icmd.Success) 852 853 // strip whitespace and newlines to verify we only found a single DROP 854 out := strings.TrimSpace(result.Stdout()) 855 assert.Assert(c, is.Equal(strings.Count(out, "\n"), 0), "only expected a single DROP rules") 856 857 // Column headers are stripped because of grep-ing, but should be: 858 // 859 // pkts bytes target prot opt in out source destination 860 // 0 0 DROP all -- ext-bridge6 ext-bridge6 anywhere anywhere 861 // 862 //nolint:dupword 863 cols := strings.Fields(out) 864 865 expected := []string{"0", "0", "DROP", "all", "--", bridgeName, bridgeName, "anywhere", "anywhere"} 866 assert.DeepEqual(c, cols, expected) 867 868 out, err := d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567") 869 assert.NilError(c, err, out) 870 871 out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567") 872 assert.NilError(c, err, out) 873 } 874 875 func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *testing.T) { 876 // make sure the default docker0 bridge doesn't interfere with the test, 877 // which may happen if it was created with the same IP range. 878 deleteInterface(c, "docker0") 879 880 bridgeName := "ext-bridge7" 881 bridgeIP := "192.169.1.1/24" 882 883 createInterface(c, "bridge", bridgeName, bridgeIP) 884 defer deleteInterface(c, bridgeName) 885 886 s.d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName, "--icc=false") 887 defer s.d.Restart(c) 888 889 out, err := s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top") 890 assert.NilError(c, err, out) 891 out, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top") 892 assert.NilError(c, err, out) 893 894 childIP := s.d.FindContainerIP(c, "child") 895 parentIP := s.d.FindContainerIP(c, "parent") 896 897 sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"} 898 destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"} 899 iptable := iptables.GetIptable(iptables.IPv4) 900 if !iptable.Exists("filter", "DOCKER", sourceRule...) || !iptable.Exists("filter", "DOCKER", destinationRule...) { 901 c.Fatal("Iptables rules not found") 902 } 903 904 s.d.Cmd("rm", "--link", "parent/http") 905 if iptable.Exists("filter", "DOCKER", sourceRule...) || iptable.Exists("filter", "DOCKER", destinationRule...) { 906 c.Fatal("Iptables rules should be removed when unlink") 907 } 908 909 s.d.Cmd("kill", "child") 910 s.d.Cmd("kill", "parent") 911 } 912 913 func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *testing.T) { 914 s.d.StartWithBusybox(testutil.GetContext(c), c, "--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024") 915 916 out, err := s.d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -u)") 917 if err != nil { 918 c.Fatal(err, out) 919 } 920 921 outArr := strings.Split(out, "\n") 922 if len(outArr) < 2 { 923 c.Fatalf("got unexpected output: %s", out) 924 } 925 nofile := strings.TrimSpace(outArr[0]) 926 nproc := strings.TrimSpace(outArr[1]) 927 928 if nofile != "42" { 929 c.Fatalf("expected `ulimit -n` to be `42`, got: %s", nofile) 930 } 931 if nproc != "2048" { 932 c.Fatalf("expected `ulimit -u` to be 2048, got: %s", nproc) 933 } 934 935 // Now restart daemon with a new default 936 s.d.Restart(c, "--default-ulimit", "nofile=43") 937 938 out, err = s.d.Cmd("start", "-a", "test") 939 if err != nil { 940 c.Fatal(err, out) 941 } 942 943 outArr = strings.Split(out, "\n") 944 if len(outArr) < 2 { 945 c.Fatalf("got unexpected output: %s", out) 946 } 947 nofile = strings.TrimSpace(outArr[0]) 948 nproc = strings.TrimSpace(outArr[1]) 949 950 if nofile != "43" { 951 c.Fatalf("expected `ulimit -n` to be `43`, got: %s", nofile) 952 } 953 if nproc != "2048" { 954 c.Fatalf("expected `ulimit -u` to be 2048, got: %s", nproc) 955 } 956 } 957 958 // #11315 959 func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *testing.T) { 960 s.d.StartWithBusybox(testutil.GetContext(c), c) 961 962 if out, err := s.d.Cmd("run", "--name=test", "busybox"); err != nil { 963 c.Fatal(err, out) 964 } 965 966 if out, err := s.d.Cmd("rename", "test", "test2"); err != nil { 967 c.Fatal(err, out) 968 } 969 970 s.d.Restart(c) 971 972 if out, err := s.d.Cmd("start", "test2"); err != nil { 973 c.Fatal(err, out) 974 } 975 } 976 977 func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *testing.T) { 978 s.d.StartWithBusybox(testutil.GetContext(c), c) 979 980 out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline") 981 assert.NilError(c, err, out) 982 id, err := s.d.GetIDByName("test") 983 assert.NilError(c, err) 984 985 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 986 987 if _, err := os.Stat(logPath); err != nil { 988 c.Fatal(err) 989 } 990 f, err := os.Open(logPath) 991 if err != nil { 992 c.Fatal(err) 993 } 994 defer f.Close() 995 996 var res struct { 997 Log string `json:"log"` 998 Stream string `json:"stream"` 999 Time time.Time `json:"time"` 1000 } 1001 if err := json.NewDecoder(f).Decode(&res); err != nil { 1002 c.Fatal(err) 1003 } 1004 if res.Log != "testline\n" { 1005 c.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n") 1006 } 1007 if res.Stream != "stdout" { 1008 c.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout") 1009 } 1010 if !time.Now().After(res.Time) { 1011 c.Fatalf("Log time %v in future", res.Time) 1012 } 1013 } 1014 1015 func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *testing.T) { 1016 s.d.StartWithBusybox(testutil.GetContext(c), c) 1017 1018 out, err := s.d.Cmd("run", "--name=test", "--log-driver=none", "busybox", "echo", "testline") 1019 if err != nil { 1020 c.Fatal(out, err) 1021 } 1022 id, err := s.d.GetIDByName("test") 1023 assert.NilError(c, err) 1024 1025 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 1026 1027 if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) { 1028 c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err) 1029 } 1030 } 1031 1032 func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *testing.T) { 1033 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=none") 1034 1035 out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline") 1036 if err != nil { 1037 c.Fatal(out, err) 1038 } 1039 id, err := s.d.GetIDByName("test") 1040 assert.NilError(c, err) 1041 1042 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 1043 1044 if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) { 1045 c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err) 1046 } 1047 } 1048 1049 func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *testing.T) { 1050 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=none") 1051 1052 out, err := s.d.Cmd("run", "--name=test", "--log-driver=json-file", "busybox", "echo", "testline") 1053 if err != nil { 1054 c.Fatal(out, err) 1055 } 1056 id, err := s.d.GetIDByName("test") 1057 assert.NilError(c, err) 1058 1059 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 1060 1061 if _, err := os.Stat(logPath); err != nil { 1062 c.Fatal(err) 1063 } 1064 f, err := os.Open(logPath) 1065 if err != nil { 1066 c.Fatal(err) 1067 } 1068 defer f.Close() 1069 1070 var res struct { 1071 Log string `json:"log"` 1072 Stream string `json:"stream"` 1073 Time time.Time `json:"time"` 1074 } 1075 if err := json.NewDecoder(f).Decode(&res); err != nil { 1076 c.Fatal(err) 1077 } 1078 if res.Log != "testline\n" { 1079 c.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n") 1080 } 1081 if res.Stream != "stdout" { 1082 c.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout") 1083 } 1084 if !time.Now().After(res.Time) { 1085 c.Fatalf("Log time %v in future", res.Time) 1086 } 1087 } 1088 1089 func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *testing.T) { 1090 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=none") 1091 1092 out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline") 1093 assert.NilError(c, err, out) 1094 1095 out, err = s.d.Cmd("logs", "test") 1096 assert.Assert(c, err != nil, "Logs should fail with 'none' driver") 1097 expected := `configured logging driver does not support reading` 1098 assert.Assert(c, strings.Contains(out, expected)) 1099 } 1100 1101 func (s *DockerDaemonSuite) TestDaemonLoggingDriverShouldBeIgnoredForBuild(c *testing.T) { 1102 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=splunk") 1103 1104 result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d), 1105 build.WithDockerfile(` 1106 FROM busybox 1107 RUN echo foo`), 1108 build.WithoutCache, 1109 ) 1110 comment := fmt.Sprintf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error) 1111 assert.Assert(c, result.Error == nil, comment) 1112 assert.Equal(c, result.ExitCode, 0, comment) 1113 assert.Assert(c, strings.Contains(result.Combined(), "foo"), comment) 1114 } 1115 1116 func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *testing.T) { 1117 dir, err := os.MkdirTemp("", "socket-cleanup-test") 1118 if err != nil { 1119 c.Fatal(err) 1120 } 1121 defer os.RemoveAll(dir) 1122 1123 sockPath := filepath.Join(dir, "docker.sock") 1124 s.d.Start(c, "--host", "unix://"+sockPath) 1125 1126 if _, err := os.Stat(sockPath); err != nil { 1127 c.Fatal("socket does not exist") 1128 } 1129 1130 s.d.Stop(c) 1131 1132 if _, err := os.Stat(sockPath); err == nil || !os.IsNotExist(err) { 1133 c.Fatal("unix socket is not cleaned up") 1134 } 1135 } 1136 1137 func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *testing.T) { 1138 s.d.StartWithBusybox(testutil.GetContext(c), c) 1139 1140 out, err := s.d.Cmd("run", "-id", "busybox", "/bin/cat") 1141 if err != nil { 1142 c.Fatalf("Could not run /bin/cat: err=%v\n%s", err, out) 1143 } 1144 containerID := strings.TrimSpace(out) 1145 1146 if out, err := s.d.Cmd("kill", containerID); err != nil { 1147 c.Fatalf("Could not kill %s: err=%v\n%s", containerID, err, out) 1148 } 1149 1150 s.d.Restart(c) 1151 1152 errchan := make(chan error, 1) 1153 go func() { 1154 if out, err := s.d.Cmd("wait", containerID); err != nil { 1155 errchan <- fmt.Errorf("%v:\n%s", err, out) 1156 } 1157 close(errchan) 1158 }() 1159 1160 select { 1161 case <-time.After(5 * time.Second): 1162 c.Fatal("Waiting on a stopped (killed) container timed out") 1163 case err := <-errchan: 1164 if err != nil { 1165 c.Fatal(err) 1166 } 1167 } 1168 } 1169 1170 // TestHTTPSInfo connects via two-way authenticated HTTPS to the info endpoint 1171 func (s *DockerDaemonSuite) TestHTTPSInfo(c *testing.T) { 1172 const ( 1173 testDaemonHTTPSAddr = "tcp://localhost:4271" 1174 ) 1175 1176 s.d.Start(c, 1177 "--tlsverify", 1178 "--tlscacert", "fixtures/https/ca.pem", 1179 "--tlscert", "fixtures/https/server-cert.pem", 1180 "--tlskey", "fixtures/https/server-key.pem", 1181 "-H", testDaemonHTTPSAddr) 1182 1183 args := []string{ 1184 "--host", testDaemonHTTPSAddr, 1185 "--tlsverify", 1186 "--tlscacert", "fixtures/https/ca.pem", 1187 "--tlscert", "fixtures/https/client-cert.pem", 1188 "--tlskey", "fixtures/https/client-key.pem", 1189 "info", 1190 } 1191 out, err := s.d.Cmd(args...) 1192 if err != nil { 1193 c.Fatalf("Error Occurred: %s and output: %s", err, out) 1194 } 1195 } 1196 1197 // TestHTTPSRun connects via two-way authenticated HTTPS to the create, attach, start, and wait endpoints. 1198 // https://github.com/docker/docker/issues/19280 1199 func (s *DockerDaemonSuite) TestHTTPSRun(c *testing.T) { 1200 const ( 1201 testDaemonHTTPSAddr = "tcp://localhost:4271" 1202 ) 1203 1204 s.d.StartWithBusybox(testutil.GetContext(c), c, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", 1205 "--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHTTPSAddr) 1206 1207 args := []string{ 1208 "--host", testDaemonHTTPSAddr, 1209 "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", 1210 "--tlscert", "fixtures/https/client-cert.pem", 1211 "--tlskey", "fixtures/https/client-key.pem", 1212 "run", "busybox", "echo", "TLS response", 1213 } 1214 out, err := s.d.Cmd(args...) 1215 if err != nil { 1216 c.Fatalf("Error Occurred: %s and output: %s", err, out) 1217 } 1218 1219 if !strings.Contains(out, "TLS response") { 1220 c.Fatalf("expected output to include `TLS response`, got %v", out) 1221 } 1222 } 1223 1224 // TestTLSVerify verifies that --tlsverify=false turns on tls 1225 func (s *DockerDaemonSuite) TestTLSVerify(c *testing.T) { 1226 out, err := exec.Command(dockerdBinary, "--tlsverify=false").CombinedOutput() 1227 if err == nil || !strings.Contains(string(out), "could not load X509 key pair") { 1228 c.Fatalf("Daemon should not have started due to missing certs: %v\n%s", err, string(out)) 1229 } 1230 } 1231 1232 // TestHTTPSInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint 1233 // by using a rogue client certificate and checks that it fails with the expected error. 1234 func (s *DockerDaemonSuite) TestHTTPSInfoRogueCert(c *testing.T) { 1235 const ( 1236 errBadCertificate = "bad certificate" 1237 testDaemonHTTPSAddr = "tcp://localhost:4271" 1238 ) 1239 1240 s.d.Start(c, 1241 "--tlsverify", 1242 "--tlscacert", "fixtures/https/ca.pem", 1243 "--tlscert", "fixtures/https/server-cert.pem", 1244 "--tlskey", "fixtures/https/server-key.pem", 1245 "-H", testDaemonHTTPSAddr) 1246 1247 args := []string{ 1248 "--host", testDaemonHTTPSAddr, 1249 "--tlsverify", 1250 "--tlscacert", "fixtures/https/ca.pem", 1251 "--tlscert", "fixtures/https/client-rogue-cert.pem", 1252 "--tlskey", "fixtures/https/client-rogue-key.pem", 1253 "info", 1254 } 1255 out, err := s.d.Cmd(args...) 1256 if err == nil || !strings.Contains(out, errBadCertificate) { 1257 c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out) 1258 } 1259 } 1260 1261 // TestHTTPSInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint 1262 // which provides a rogue server certificate and checks that it fails with the expected error 1263 func (s *DockerDaemonSuite) TestHTTPSInfoRogueServerCert(c *testing.T) { 1264 const ( 1265 errCaUnknown = "x509: certificate signed by unknown authority" 1266 testDaemonRogueHTTPSAddr = "tcp://localhost:4272" 1267 ) 1268 s.d.Start(c, 1269 "--tlsverify", 1270 "--tlscacert", "fixtures/https/ca.pem", 1271 "--tlscert", "fixtures/https/server-rogue-cert.pem", 1272 "--tlskey", "fixtures/https/server-rogue-key.pem", 1273 "-H", testDaemonRogueHTTPSAddr) 1274 1275 args := []string{ 1276 "--host", testDaemonRogueHTTPSAddr, 1277 "--tlsverify", 1278 "--tlscacert", "fixtures/https/ca.pem", 1279 "--tlscert", "fixtures/https/client-rogue-cert.pem", 1280 "--tlskey", "fixtures/https/client-rogue-key.pem", 1281 "info", 1282 } 1283 out, err := s.d.Cmd(args...) 1284 if err == nil || !strings.Contains(out, errCaUnknown) { 1285 c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out) 1286 } 1287 } 1288 1289 func pingContainers(c *testing.T, d *daemon.Daemon, expectFailure bool) { 1290 var dargs []string 1291 if d != nil { 1292 dargs = []string{"--host", d.Sock()} 1293 } 1294 1295 args := append(dargs, "run", "-d", "--name", "container1", "busybox", "top") 1296 cli.DockerCmd(c, args...) 1297 1298 args = append(dargs, "run", "--rm", "--link", "container1:alias1", "busybox", "sh", "-c") 1299 pingCmd := "ping -c 1 %s -W 1" 1300 args = append(args, fmt.Sprintf(pingCmd, "alias1")) 1301 _, _, err := dockerCmdWithError(args...) 1302 1303 if expectFailure { 1304 assert.ErrorContains(c, err, "") 1305 } else { 1306 assert.NilError(c, err) 1307 } 1308 1309 args = append(dargs, "rm", "-f", "container1") 1310 cli.DockerCmd(c, args...) 1311 } 1312 1313 func (s *DockerDaemonSuite) TestDaemonRestartWithSocketAsVolume(c *testing.T) { 1314 s.d.StartWithBusybox(testutil.GetContext(c), c) 1315 1316 socket := filepath.Join(s.d.Folder, "docker.sock") 1317 1318 out, err := s.d.Cmd("run", "--restart=always", "-v", socket+":/sock", "busybox") 1319 assert.NilError(c, err, "Output: %s", out) 1320 s.d.Restart(c) 1321 } 1322 1323 // os.Kill should kill daemon ungracefully, leaving behind container mounts. 1324 // A subsequent daemon restart should clean up said mounts. 1325 func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonAndContainerKill(c *testing.T) { 1326 d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 1327 d.StartWithBusybox(testutil.GetContext(c), c) 1328 1329 out, err := d.Cmd("run", "-d", "busybox", "top") 1330 assert.NilError(c, err, "Output: %s", out) 1331 1332 id := strings.TrimSpace(out) 1333 1334 // If there are no mounts with container id visible from the host 1335 // (as those are in container's own mount ns), there is nothing 1336 // to check here and the test should be skipped. 1337 mountOut, err := os.ReadFile("/proc/self/mountinfo") 1338 assert.NilError(c, err, "Output: %s", mountOut) 1339 if !strings.Contains(string(mountOut), id) { 1340 d.Stop(c) 1341 c.Skip("no container mounts visible in host ns") 1342 } 1343 1344 // kill the daemon 1345 assert.NilError(c, d.Kill()) 1346 1347 // kill the container 1348 icmd.RunCommand(ctrBinary, "--address", containerdSocket, 1349 "--namespace", d.ContainersNamespace(), "tasks", "kill", id).Assert(c, icmd.Success) 1350 1351 // restart daemon. 1352 d.Restart(c) 1353 1354 // Now, container mounts should be gone. 1355 mountOut, err = os.ReadFile("/proc/self/mountinfo") 1356 assert.NilError(c, err, "Output: %s", mountOut) 1357 assert.Assert(c, !strings.Contains(string(mountOut), id), "%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, d.Root, mountOut) 1358 1359 d.Stop(c) 1360 } 1361 1362 // os.Interrupt should perform a graceful daemon shutdown and hence cleanup mounts. 1363 func (s *DockerDaemonSuite) TestCleanupMountsAfterGracefulShutdown(c *testing.T) { 1364 d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 1365 d.StartWithBusybox(testutil.GetContext(c), c) 1366 1367 out, err := d.Cmd("run", "-d", "busybox", "top") 1368 assert.NilError(c, err, "Output: %s", out) 1369 id := strings.TrimSpace(out) 1370 1371 // Send SIGINT and daemon should clean up 1372 assert.NilError(c, d.Signal(os.Interrupt)) 1373 // Wait for the daemon to stop. 1374 assert.NilError(c, <-d.Wait) 1375 1376 mountOut, err := os.ReadFile("/proc/self/mountinfo") 1377 assert.NilError(c, err, "Output: %s", mountOut) 1378 1379 assert.Assert(c, !strings.Contains(string(mountOut), id), "%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, d.Root, mountOut) 1380 } 1381 1382 func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *testing.T) { 1383 s.d.StartWithBusybox(testutil.GetContext(t), t) 1384 if out, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top"); err != nil { 1385 t.Fatal(out, err) 1386 } 1387 1388 s.d.Restart(t) 1389 // Container 'test' should be removed without error 1390 if out, err := s.d.Cmd("rm", "test"); err != nil { 1391 t.Fatal(out, err) 1392 } 1393 } 1394 1395 func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *testing.T) { 1396 s.d.StartWithBusybox(testutil.GetContext(c), c) 1397 out, err := s.d.Cmd("run", "--name", "netns", "-d", "busybox", "top") 1398 if err != nil { 1399 c.Fatal(out, err) 1400 } 1401 1402 // Get sandbox key via inspect 1403 out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.SandboxKey}}'", "netns") 1404 if err != nil { 1405 c.Fatalf("Error inspecting container: %s, %v", out, err) 1406 } 1407 fileName := strings.Trim(out, " \r\n'") 1408 1409 if out, err := s.d.Cmd("stop", "netns"); err != nil { 1410 c.Fatal(out, err) 1411 } 1412 1413 // Test if the file still exists 1414 icmd.RunCommand("stat", "-c", "%n", fileName).Assert(c, icmd.Expected{ 1415 Out: fileName, 1416 }) 1417 1418 // Remove the container and restart the daemon 1419 if out, err := s.d.Cmd("rm", "netns"); err != nil { 1420 c.Fatal(out, err) 1421 } 1422 1423 s.d.Restart(c) 1424 1425 // Test again and see now the netns file does not exist 1426 icmd.RunCommand("stat", "-c", "%n", fileName).Assert(c, icmd.Expected{ 1427 Err: "No such file or directory", 1428 ExitCode: 1, 1429 }) 1430 } 1431 1432 // tests regression detailed in #13964 where DOCKER_TLS_VERIFY env is ignored 1433 func (s *DockerDaemonSuite) TestDaemonTLSVerifyIssue13964(c *testing.T) { 1434 host := "tcp://localhost:4271" 1435 s.d.Start(c, "-H", host) 1436 icmd.RunCmd(icmd.Cmd{ 1437 Command: []string{dockerBinary, "-H", host, "info"}, 1438 Env: []string{"DOCKER_TLS_VERIFY=1", "DOCKER_CERT_PATH=fixtures/https"}, 1439 }).Assert(c, icmd.Expected{ 1440 ExitCode: 1, 1441 Err: "error during connect", 1442 }) 1443 } 1444 1445 func setupV6(c *testing.T) { 1446 // Hack to get the right IPv6 address on docker0, which has already been created 1447 result := icmd.RunCommand("ip", "addr", "add", "fe80::1/64", "dev", "docker0") 1448 result.Assert(c, icmd.Success) 1449 } 1450 1451 func teardownV6(c *testing.T) { 1452 result := icmd.RunCommand("ip", "addr", "del", "fe80::1/64", "dev", "docker0") 1453 result.Assert(c, icmd.Success) 1454 } 1455 1456 func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlways(c *testing.T) { 1457 s.d.StartWithBusybox(testutil.GetContext(c), c) 1458 1459 out, err := s.d.Cmd("run", "-d", "--restart", "always", "busybox", "top") 1460 assert.NilError(c, err, out) 1461 id := strings.TrimSpace(out) 1462 1463 out, err = s.d.Cmd("stop", id) 1464 assert.NilError(c, err, out) 1465 out, err = s.d.Cmd("wait", id) 1466 assert.NilError(c, err, out) 1467 1468 out, err = s.d.Cmd("ps", "-q") 1469 assert.NilError(c, err, out) 1470 assert.Equal(c, out, "") 1471 1472 s.d.Restart(c) 1473 1474 out, err = s.d.Cmd("ps", "-q") 1475 assert.NilError(c, err, out) 1476 assert.Equal(c, strings.TrimSpace(out), id[:12]) 1477 } 1478 1479 func (s *DockerDaemonSuite) TestDaemonWideLogConfig(c *testing.T) { 1480 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-opt=max-size=1k") 1481 name := "logtest" 1482 out, err := s.d.Cmd("run", "-d", "--log-opt=max-file=5", "--name", name, "busybox", "top") 1483 assert.NilError(c, err, "Output: %s, err: %v", out, err) 1484 1485 out, err = s.d.Cmd("inspect", "-f", "{{ .HostConfig.LogConfig.Config }}", name) 1486 assert.NilError(c, err, "Output: %s", out) 1487 assert.Assert(c, strings.Contains(out, "max-size:1k")) 1488 assert.Assert(c, strings.Contains(out, "max-file:5")) 1489 1490 out, err = s.d.Cmd("inspect", "-f", "{{ .HostConfig.LogConfig.Type }}", name) 1491 assert.NilError(c, err, "Output: %s", out) 1492 assert.Equal(c, strings.TrimSpace(out), "json-file") 1493 } 1494 1495 func (s *DockerDaemonSuite) TestDaemonRestartWithPausedContainer(c *testing.T) { 1496 s.d.StartWithBusybox(testutil.GetContext(c), c) 1497 if out, err := s.d.Cmd("run", "-i", "-d", "--name", "test", "busybox", "top"); err != nil { 1498 c.Fatal(err, out) 1499 } 1500 if out, err := s.d.Cmd("pause", "test"); err != nil { 1501 c.Fatal(err, out) 1502 } 1503 s.d.Restart(c) 1504 1505 errchan := make(chan error, 1) 1506 go func() { 1507 out, err := s.d.Cmd("start", "test") 1508 if err != nil { 1509 errchan <- fmt.Errorf("%v:\n%s", err, out) 1510 return 1511 } 1512 name := strings.TrimSpace(out) 1513 if name != "test" { 1514 errchan <- fmt.Errorf("Paused container start error on docker daemon restart, expected 'test' but got '%s'", name) 1515 return 1516 } 1517 close(errchan) 1518 }() 1519 1520 select { 1521 case <-time.After(5 * time.Second): 1522 c.Fatal("Waiting on start a container timed out") 1523 case err := <-errchan: 1524 if err != nil { 1525 c.Fatal(err) 1526 } 1527 } 1528 } 1529 1530 func (s *DockerDaemonSuite) TestDaemonRestartRmVolumeInUse(c *testing.T) { 1531 s.d.StartWithBusybox(testutil.GetContext(c), c) 1532 1533 out, err := s.d.Cmd("create", "-v", "test:/foo", "busybox") 1534 assert.NilError(c, err, out) 1535 1536 s.d.Restart(c) 1537 1538 out, err = s.d.Cmd("volume", "rm", "test") 1539 assert.Assert(c, err != nil, "should not be able to remove in use volume after daemon restart") 1540 assert.Assert(c, strings.Contains(out, "in use")) 1541 } 1542 1543 func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *testing.T) { 1544 s.d.Start(c) 1545 1546 out, err := s.d.Cmd("volume", "create", "test") 1547 assert.NilError(c, err, out) 1548 s.d.Restart(c) 1549 1550 out, err = s.d.Cmd("volume", "inspect", "test") 1551 assert.NilError(c, err, out) 1552 } 1553 1554 // FIXME(vdemeester) Use a new daemon instance instead of the Suite one 1555 func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *testing.T) { 1556 s.d.UseDefaultHost = true 1557 defer func() { 1558 s.d.UseDefaultHost = false 1559 }() 1560 s.d.Start(c) 1561 } 1562 1563 // FIXME(vdemeester) Use a new daemon instance instead of the Suite one 1564 func (s *DockerDaemonSuite) TestDaemonStartWithDefaultTLSHost(c *testing.T) { 1565 s.d.UseDefaultTLSHost = true 1566 defer func() { 1567 s.d.UseDefaultTLSHost = false 1568 }() 1569 s.d.Start(c, 1570 "--tlsverify", 1571 "--tlscacert", "fixtures/https/ca.pem", 1572 "--tlscert", "fixtures/https/server-cert.pem", 1573 "--tlskey", "fixtures/https/server-key.pem") 1574 1575 // The client with --tlsverify should also use default host localhost:2376 1576 c.Setenv("DOCKER_HOST", "") 1577 1578 out := cli.DockerCmd(c, 1579 "--tlsverify", 1580 "--tlscacert", "fixtures/https/ca.pem", 1581 "--tlscert", "fixtures/https/client-cert.pem", 1582 "--tlskey", "fixtures/https/client-key.pem", 1583 "version", 1584 ).Stdout() 1585 if !strings.Contains(out, "Server") { 1586 c.Fatalf("docker version should return information of server side") 1587 } 1588 1589 // ensure when connecting to the server that only a single acceptable CA is requested 1590 contents, err := os.ReadFile("fixtures/https/ca.pem") 1591 assert.NilError(c, err) 1592 rootCert, err := helpers.ParseCertificatePEM(contents) 1593 assert.NilError(c, err) 1594 rootPool := x509.NewCertPool() 1595 rootPool.AddCert(rootCert) 1596 1597 var certRequestInfo *tls.CertificateRequestInfo 1598 conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort), &tls.Config{ 1599 RootCAs: rootPool, 1600 GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) { 1601 certRequestInfo = cri 1602 cert, err := tls.LoadX509KeyPair("fixtures/https/client-cert.pem", "fixtures/https/client-key.pem") 1603 if err != nil { 1604 return nil, err 1605 } 1606 return &cert, nil 1607 }, 1608 }) 1609 assert.NilError(c, err) 1610 conn.Close() 1611 1612 assert.Assert(c, certRequestInfo != nil) 1613 assert.Equal(c, len(certRequestInfo.AcceptableCAs), 1) 1614 assert.DeepEqual(c, certRequestInfo.AcceptableCAs[0], rootCert.RawSubject) 1615 } 1616 1617 func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *testing.T) { 1618 defaultNetworkBridge := "docker0" 1619 deleteInterface(c, defaultNetworkBridge) 1620 1621 bridgeIP := "192.169.1.1" 1622 bridgeRange := bridgeIP + "/30" 1623 1624 s.d.StartWithBusybox(testutil.GetContext(c), c, "--bip", bridgeRange) 1625 defer s.d.Restart(c) 1626 1627 var cont int 1628 for { 1629 contName := fmt.Sprintf("container%d", cont) 1630 _, err := s.d.Cmd("run", "--name", contName, "-d", "busybox", "/bin/sleep", "2") 1631 if err != nil { 1632 // pool exhausted 1633 break 1634 } 1635 ip, err := s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.IPAddress}}'", contName) 1636 assert.Assert(c, err == nil, ip) 1637 1638 assert.Assert(c, ip != bridgeIP) 1639 cont++ 1640 } 1641 } 1642 1643 // Test daemon for no space left on device error 1644 func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *testing.T) { 1645 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, Network) 1646 1647 testDir, err := os.MkdirTemp("", "no-space-left-on-device-test") 1648 assert.NilError(c, err) 1649 defer os.RemoveAll(testDir) 1650 assert.Assert(c, mount.MakeRShared(testDir) == nil) 1651 defer mount.Unmount(testDir) 1652 1653 // create a 3MiB image (with a 2MiB ext4 fs) and mount it as storage root 1654 storageFS := filepath.Join(testDir, "testfs.img") 1655 icmd.RunCommand("dd", "of="+storageFS, "bs=1M", "seek=3", "count=0").Assert(c, icmd.Success) 1656 icmd.RunCommand("mkfs.ext4", "-F", storageFS).Assert(c, icmd.Success) 1657 1658 testMount, err := os.MkdirTemp(testDir, "test-mount") 1659 assert.NilError(c, err) 1660 icmd.RunCommand("mount", "-n", "-t", "ext4", storageFS, testMount).Assert(c, icmd.Success) 1661 defer mount.Unmount(testMount) 1662 1663 driver := "vfs" 1664 if testEnv.UsingSnapshotter() { 1665 driver = "native" 1666 } 1667 1668 s.d.Start(c, 1669 "--data-root", testMount, 1670 "--storage-driver", driver, 1671 1672 // Pass empty containerd socket to force daemon to create a new 1673 // supervised containerd daemon, otherwise the global containerd daemon 1674 // will be used and its data won't be stored in the specified data-root. 1675 "--containerd", "", 1676 ) 1677 defer s.d.Stop(c) 1678 1679 // pull a repository large enough to overfill the mounted filesystem 1680 pullOut, err := s.d.Cmd("pull", "debian:bookworm-slim") 1681 assert.Check(c, err != nil) 1682 assert.Check(c, is.Contains(pullOut, "no space left on device")) 1683 } 1684 1685 // Test daemon restart with container links + auto restart 1686 func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *testing.T) { 1687 s.d.StartWithBusybox(testutil.GetContext(c), c) 1688 1689 var parent1Args []string 1690 var parent2Args []string 1691 wg := sync.WaitGroup{} 1692 maxChildren := 10 1693 chErr := make(chan error, maxChildren) 1694 1695 for i := 0; i < maxChildren; i++ { 1696 wg.Add(1) 1697 name := fmt.Sprintf("test%d", i) 1698 1699 if i < maxChildren/2 { 1700 parent1Args = append(parent1Args, []string{"--link", name}...) 1701 } else { 1702 parent2Args = append(parent2Args, []string{"--link", name}...) 1703 } 1704 1705 go func() { 1706 _, err := s.d.Cmd("run", "-d", "--name", name, "--restart=always", "busybox", "top") 1707 chErr <- err 1708 wg.Done() 1709 }() 1710 } 1711 1712 wg.Wait() 1713 close(chErr) 1714 for err := range chErr { 1715 assert.NilError(c, err) 1716 } 1717 1718 parent1Args = append([]string{"run", "-d"}, parent1Args...) 1719 parent1Args = append(parent1Args, []string{"--name=parent1", "--restart=always", "busybox", "top"}...) 1720 parent2Args = append([]string{"run", "-d"}, parent2Args...) 1721 parent2Args = append(parent2Args, []string{"--name=parent2", "--restart=always", "busybox", "top"}...) 1722 1723 _, err := s.d.Cmd(parent1Args...) 1724 assert.NilError(c, err) 1725 _, err = s.d.Cmd(parent2Args...) 1726 assert.NilError(c, err) 1727 1728 s.d.Stop(c) 1729 // clear the log file -- we don't need any of it but may for the next part 1730 // can ignore the error here, this is just a cleanup 1731 os.Truncate(s.d.LogFileName(), 0) 1732 s.d.Start(c) 1733 1734 for _, num := range []string{"1", "2"} { 1735 out, err := s.d.Cmd("inspect", "-f", "{{ .State.Running }}", "parent"+num) 1736 assert.NilError(c, err) 1737 if strings.TrimSpace(out) != "true" { 1738 log, _ := os.ReadFile(s.d.LogFileName()) 1739 c.Fatalf("parent container is not running\n%s", string(log)) 1740 } 1741 } 1742 } 1743 1744 func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *testing.T) { 1745 testRequires(c, DaemonIsLinux) 1746 1747 cgroupParent := "test" 1748 name := "cgroup-test" 1749 1750 s.d.StartWithBusybox(testutil.GetContext(c), c, "--cgroup-parent", cgroupParent) 1751 defer s.d.Restart(c) 1752 1753 out, err := s.d.Cmd("run", "--name", name, "busybox", "cat", "/proc/self/cgroup") 1754 assert.NilError(c, err) 1755 cgroupPaths := ParseCgroupPaths(out) 1756 assert.Assert(c, len(cgroupPaths) != 0, "unexpected output - %q", out) 1757 out, err = s.d.Cmd("inspect", "-f", "{{.Id}}", name) 1758 assert.NilError(c, err) 1759 id := strings.TrimSpace(out) 1760 expectedCgroup := path.Join(cgroupParent, id) 1761 found := false 1762 for _, p := range cgroupPaths { 1763 if strings.HasSuffix(p, expectedCgroup) { 1764 found = true 1765 break 1766 } 1767 } 1768 assert.Assert(c, found, "Cgroup path for container (%s) doesn't found in cgroups file: %s", expectedCgroup, cgroupPaths) 1769 } 1770 1771 func (s *DockerDaemonSuite) TestDaemonRestartWithLinks(c *testing.T) { 1772 testRequires(c, DaemonIsLinux) // Windows does not support links 1773 s.d.StartWithBusybox(testutil.GetContext(c), c) 1774 1775 out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top") 1776 assert.NilError(c, err, out) 1777 1778 out, err = s.d.Cmd("run", "--name=test2", "--link", "test:abc", "busybox", "sh", "-c", "ping -c 1 -w 1 abc") 1779 assert.NilError(c, err, out) 1780 1781 s.d.Restart(c) 1782 1783 // should fail since test is not running yet 1784 out, err = s.d.Cmd("start", "test2") 1785 assert.ErrorContains(c, err, "", out) 1786 1787 out, err = s.d.Cmd("start", "test") 1788 assert.NilError(c, err, out) 1789 out, err = s.d.Cmd("start", "-a", "test2") 1790 assert.NilError(c, err, out) 1791 assert.Equal(c, strings.Contains(out, "1 packets transmitted, 1 packets received"), true, out) 1792 } 1793 1794 func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *testing.T) { 1795 testRequires(c, DaemonIsLinux) // Windows does not support links 1796 s.d.StartWithBusybox(testutil.GetContext(c), c) 1797 1798 out, err := s.d.Cmd("create", "--name=test", "busybox") 1799 assert.NilError(c, err, out) 1800 1801 out, err = s.d.Cmd("run", "-d", "--name=test2", "busybox", "top") 1802 assert.NilError(c, err, out) 1803 test2ID := strings.TrimSpace(out) 1804 1805 out, err = s.d.Cmd("run", "-d", "--name=test3", "--link", "test2:abc", "busybox", "top") 1806 assert.NilError(c, err) 1807 test3ID := strings.TrimSpace(out) 1808 1809 s.d.Restart(c) 1810 1811 _, err = s.d.Cmd("create", "--name=test", "busybox") 1812 assert.ErrorContains(c, err, "", "expected error trying to create container with duplicate name") 1813 // this one is no longer needed, removing simplifies the remainder of the test 1814 out, err = s.d.Cmd("rm", "-f", "test") 1815 assert.NilError(c, err, out) 1816 1817 out, err = s.d.Cmd("ps", "-a", "--no-trunc") 1818 assert.NilError(c, err, out) 1819 1820 lines := strings.Split(strings.TrimSpace(out), "\n")[1:] 1821 1822 test2validated := false 1823 test3validated := false 1824 for _, line := range lines { 1825 fields := strings.Fields(line) 1826 names := fields[len(fields)-1] 1827 switch fields[0] { 1828 case test2ID: 1829 assert.Equal(c, names, "test2,test3/abc") 1830 test2validated = true 1831 case test3ID: 1832 assert.Equal(c, names, "test3") 1833 test3validated = true 1834 } 1835 } 1836 1837 assert.Assert(c, test2validated) 1838 assert.Assert(c, test3validated) 1839 } 1840 1841 // TestDaemonRestartWithKilledRunningContainer requires live restore of running containers 1842 func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *testing.T) { 1843 testRequires(t, DaemonIsLinux) 1844 s.d.StartWithBusybox(testutil.GetContext(t), t) 1845 1846 cid, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top") 1847 defer s.d.Stop(t) 1848 if err != nil { 1849 t.Fatal(cid, err) 1850 } 1851 cid = strings.TrimSpace(cid) 1852 1853 pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid) 1854 assert.NilError(t, err) 1855 pid = strings.TrimSpace(pid) 1856 1857 // Kill the daemon 1858 if err := s.d.Kill(); err != nil { 1859 t.Fatal(err) 1860 } 1861 1862 // kill the container 1863 icmd.RunCommand(ctrBinary, "--address", containerdSocket, 1864 "--namespace", s.d.ContainersNamespace(), "tasks", "kill", cid).Assert(t, icmd.Success) 1865 1866 // Give time to containerd to process the command if we don't 1867 // the exit event might be received after we do the inspect 1868 result := icmd.RunCommand("kill", "-0", pid) 1869 for result.ExitCode == 0 { 1870 time.Sleep(1 * time.Second) 1871 // FIXME(vdemeester) should we check it doesn't error out ? 1872 result = icmd.RunCommand("kill", "-0", pid) 1873 } 1874 1875 // restart the daemon 1876 s.d.Start(t) 1877 1878 // Check that we've got the correct exit code 1879 out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", cid) 1880 assert.NilError(t, err) 1881 1882 out = strings.TrimSpace(out) 1883 if out != "143" { 1884 t.Fatalf("Expected exit code '%s' got '%s' for container '%s'\n", "143", out, cid) 1885 } 1886 } 1887 1888 // os.Kill should kill daemon ungracefully, leaving behind live containers. 1889 // The live containers should be known to the restarted daemon. Stopping 1890 // them now, should remove the mounts. 1891 func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *testing.T) { 1892 testRequires(c, DaemonIsLinux) 1893 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 1894 1895 out, err := s.d.Cmd("run", "-d", "busybox", "top") 1896 assert.NilError(c, err, "Output: %s", out) 1897 id := strings.TrimSpace(out) 1898 1899 // kill the daemon 1900 assert.Assert(c, s.d.Kill() == nil) 1901 1902 // Check if there are mounts with container id visible from the host. 1903 // If not, those mounts exist in container's own mount ns, and so 1904 // the following check for mounts being cleared is pointless. 1905 skipMountCheck := false 1906 mountOut, err := os.ReadFile("/proc/self/mountinfo") 1907 assert.Assert(c, err == nil, "Output: %s", mountOut) 1908 if !strings.Contains(string(mountOut), id) { 1909 skipMountCheck = true 1910 } 1911 1912 // restart daemon. 1913 s.d.Start(c, "--live-restore") 1914 1915 // container should be running. 1916 out, err = s.d.Cmd("inspect", "--format={{.State.Running}}", id) 1917 assert.NilError(c, err, "Output: %s", out) 1918 out = strings.TrimSpace(out) 1919 if out != "true" { 1920 c.Fatalf("Container %s expected to stay alive after daemon restart", id) 1921 } 1922 1923 // 'docker stop' should work. 1924 out, err = s.d.Cmd("stop", id) 1925 assert.NilError(c, err, "Output: %s", out) 1926 1927 if skipMountCheck { 1928 return 1929 } 1930 // Now, container mounts should be gone. 1931 mountOut, err = os.ReadFile("/proc/self/mountinfo") 1932 assert.Assert(c, err == nil, "Output: %s", mountOut) 1933 comment := fmt.Sprintf("%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, s.d.Root, mountOut) 1934 assert.Equal(c, strings.Contains(string(mountOut), id), false, comment) 1935 } 1936 1937 // TestDaemonRestartWithUnpausedRunningContainer requires live restore of running containers. 1938 func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *testing.T) { 1939 testRequires(t, DaemonIsLinux) 1940 s.d.StartWithBusybox(testutil.GetContext(t), t, "--live-restore") 1941 1942 cid, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top") 1943 defer s.d.Stop(t) 1944 if err != nil { 1945 t.Fatal(cid, err) 1946 } 1947 cid = strings.TrimSpace(cid) 1948 1949 pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid) 1950 assert.NilError(t, err) 1951 1952 // pause the container 1953 if _, err := s.d.Cmd("pause", cid); err != nil { 1954 t.Fatal(cid, err) 1955 } 1956 1957 // Kill the daemon 1958 if err := s.d.Kill(); err != nil { 1959 t.Fatal(err) 1960 } 1961 1962 // resume the container 1963 result := icmd.RunCommand( 1964 ctrBinary, 1965 "--address", containerdSocket, 1966 "--namespace", s.d.ContainersNamespace(), 1967 "tasks", "resume", cid) 1968 result.Assert(t, icmd.Success) 1969 1970 // Give time to containerd to process the command if we don't 1971 // the resume event might be received after we do the inspect 1972 poll.WaitOn(t, pollCheck(t, func(*testing.T) (interface{}, string) { 1973 result := icmd.RunCommand("kill", "-0", strings.TrimSpace(pid)) 1974 return result.ExitCode, "" 1975 }, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout)) 1976 1977 // restart the daemon 1978 s.d.Start(t, "--live-restore") 1979 1980 // Check that we've got the correct status 1981 out, err := s.d.Cmd("inspect", "-f", "{{.State.Status}}", cid) 1982 assert.NilError(t, err) 1983 1984 out = strings.TrimSpace(out) 1985 if out != "running" { 1986 t.Fatalf("Expected exit code '%s' got '%s' for container '%s'\n", "running", out, cid) 1987 } 1988 if _, err := s.d.Cmd("kill", cid); err != nil { 1989 t.Fatal(err) 1990 } 1991 } 1992 1993 // TestRunLinksChanged checks that creating a new container with the same name does not update links 1994 // this ensures that the old, pre gh#16032 functionality continues on 1995 func (s *DockerDaemonSuite) TestRunLinksChanged(c *testing.T) { 1996 testRequires(c, DaemonIsLinux) // Windows does not support links 1997 s.d.StartWithBusybox(testutil.GetContext(c), c) 1998 1999 out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top") 2000 assert.NilError(c, err, out) 2001 2002 out, err = s.d.Cmd("run", "--name=test2", "--link=test:abc", "busybox", "sh", "-c", "ping -c 1 abc") 2003 assert.NilError(c, err, out) 2004 assert.Assert(c, strings.Contains(out, "1 packets transmitted, 1 packets received")) 2005 out, err = s.d.Cmd("rm", "-f", "test") 2006 assert.NilError(c, err, out) 2007 2008 out, err = s.d.Cmd("run", "-d", "--name=test", "busybox", "top") 2009 assert.NilError(c, err, out) 2010 out, err = s.d.Cmd("start", "-a", "test2") 2011 assert.ErrorContains(c, err, "", out) 2012 assert.Assert(c, !strings.Contains(out, "1 packets transmitted, 1 packets received")) 2013 s.d.Restart(c) 2014 out, err = s.d.Cmd("start", "-a", "test2") 2015 assert.ErrorContains(c, err, "", out) 2016 assert.Assert(c, !strings.Contains(out, "1 packets transmitted, 1 packets received")) 2017 } 2018 2019 func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *testing.T) { 2020 testRequires(c, DaemonIsLinux) 2021 2022 infoLog := "\x1b[36mINFO\x1b" 2023 2024 b := bytes.NewBuffer(nil) 2025 done := make(chan bool) 2026 2027 p, tty, err := pty.Open() 2028 assert.NilError(c, err) 2029 defer func() { 2030 tty.Close() 2031 p.Close() 2032 }() 2033 2034 go func() { 2035 io.Copy(b, p) 2036 done <- true 2037 }() 2038 2039 // Enable coloring explicitly 2040 s.d.StartWithLogFile(tty, "--raw-logs=false") 2041 s.d.Stop(c) 2042 // Wait for io.Copy() before checking output 2043 <-done 2044 assert.Assert(c, strings.Contains(b.String(), infoLog)) 2045 b.Reset() 2046 2047 // "tty" is already closed in prev s.d.Stop(), 2048 // we have to close the other side "p" and open another pair of 2049 // pty for the next test. 2050 p.Close() 2051 p, tty, err = pty.Open() 2052 assert.NilError(c, err) 2053 2054 go func() { 2055 io.Copy(b, p) 2056 done <- true 2057 }() 2058 2059 // Disable coloring explicitly 2060 s.d.StartWithLogFile(tty, "--raw-logs=true") 2061 s.d.Stop(c) 2062 // Wait for io.Copy() before checking output 2063 <-done 2064 assert.Assert(c, b.String() != "") 2065 assert.Assert(c, !strings.Contains(b.String(), infoLog)) 2066 } 2067 2068 func (s *DockerDaemonSuite) TestDaemonDebugLog(c *testing.T) { 2069 testRequires(c, DaemonIsLinux) 2070 2071 debugLog := "\x1b[37mDEBU\x1b" 2072 2073 p, tty, err := pty.Open() 2074 assert.NilError(c, err) 2075 defer func() { 2076 tty.Close() 2077 p.Close() 2078 }() 2079 2080 b := bytes.NewBuffer(nil) 2081 go io.Copy(b, p) 2082 2083 s.d.StartWithLogFile(tty, "--debug") 2084 s.d.Stop(c) 2085 assert.Assert(c, strings.Contains(b.String(), debugLog)) 2086 } 2087 2088 // Test for #21956 2089 func (s *DockerDaemonSuite) TestDaemonLogOptions(c *testing.T) { 2090 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=syslog", "--log-opt=syslog-address=udp://127.0.0.1:514") 2091 2092 out, err := s.d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "top") 2093 assert.NilError(c, err, out) 2094 id := strings.TrimSpace(out) 2095 2096 out, err = s.d.Cmd("inspect", "--format='{{.HostConfig.LogConfig}}'", id) 2097 assert.NilError(c, err, out) 2098 assert.Assert(c, strings.Contains(out, "{json-file map[]}")) 2099 } 2100 2101 // Test case for #20936, #22443 2102 func (s *DockerDaemonSuite) TestDaemonMaxConcurrency(c *testing.T) { 2103 skip.If(c, testEnv.UsingSnapshotter, "max concurrency is not implemented (yet) with containerd snapshotters https://github.com/moby/moby/issues/46610") 2104 2105 s.d.Start(c, "--max-concurrent-uploads=6", "--max-concurrent-downloads=8") 2106 2107 expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 6"` 2108 expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 8"` 2109 content, err := s.d.ReadLogFile() 2110 assert.NilError(c, err) 2111 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2112 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2113 } 2114 2115 // Test case for #20936, #22443 2116 func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFile(c *testing.T) { 2117 skip.If(c, testEnv.UsingSnapshotter, "max concurrency is not implemented (yet) with containerd snapshotters https://github.com/moby/moby/issues/46610") 2118 2119 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2120 2121 // daemon config file 2122 const configFilePath = "test-daemon.json" 2123 err := os.WriteFile(configFilePath, []byte(`{ "max-concurrent-downloads" : 8 }`), 0666) 2124 assert.NilError(c, err) 2125 defer os.Remove(configFilePath) 2126 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 2127 2128 expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 5"` 2129 expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 8"` 2130 content, err := s.d.ReadLogFile() 2131 assert.NilError(c, err) 2132 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2133 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2134 err = os.WriteFile(configFilePath, []byte(`{ "max-concurrent-uploads" : 7, "max-concurrent-downloads" : 9 }`), 0666) 2135 assert.NilError(c, err) 2136 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2137 // unix.Kill(s.d.cmd.Process.Pid, unix.SIGHUP) 2138 2139 time.Sleep(3 * time.Second) 2140 2141 expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 7"` 2142 expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 9"` 2143 content, err = s.d.ReadLogFile() 2144 assert.NilError(c, err) 2145 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2146 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2147 } 2148 2149 // Test case for #20936, #22443 2150 func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *testing.T) { 2151 skip.If(c, testEnv.UsingSnapshotter, "max concurrency is not implemented (yet) with containerd snapshotters https://github.com/moby/moby/issues/46610") 2152 2153 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2154 2155 // daemon config file 2156 const configFilePath = "test-daemon.json" 2157 err := os.WriteFile(configFilePath, []byte(`{ "max-concurrent-uploads" : null }`), 0666) 2158 assert.NilError(c, err) 2159 defer os.Remove(configFilePath) 2160 2161 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 2162 2163 expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 5"` 2164 expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 3"` 2165 content, err := s.d.ReadLogFile() 2166 assert.NilError(c, err) 2167 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2168 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2169 err = os.WriteFile(configFilePath, []byte(`{ "max-concurrent-uploads" : 1, "max-concurrent-downloads" : null }`), 0666) 2170 assert.NilError(c, err) 2171 2172 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2173 // unix.Kill(s.d.cmd.Process.Pid, unix.SIGHUP) 2174 2175 time.Sleep(3 * time.Second) 2176 2177 expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 1"` 2178 expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 3"` 2179 content, err = s.d.ReadLogFile() 2180 assert.NilError(c, err) 2181 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2182 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2183 err = os.WriteFile(configFilePath, []byte(`{ "labels":["foo=bar"] }`), 0666) 2184 assert.NilError(c, err) 2185 2186 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2187 2188 time.Sleep(3 * time.Second) 2189 2190 expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 5"` 2191 expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 3"` 2192 content, err = s.d.ReadLogFile() 2193 assert.NilError(c, err) 2194 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2195 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2196 } 2197 2198 func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *testing.T) { 2199 s.d.StartWithBusybox(testutil.GetContext(c), c, "-b=none", "--iptables=false") 2200 2201 result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d), 2202 build.WithDockerfile(` 2203 FROM busybox 2204 RUN cat /etc/hosts`), 2205 build.WithoutCache, 2206 ) 2207 comment := fmt.Sprintf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error) 2208 assert.Assert(c, result.Error == nil, comment) 2209 assert.Equal(c, result.ExitCode, 0, comment) 2210 } 2211 2212 // Test case for #21976 2213 func (s *DockerDaemonSuite) TestDaemonDNSFlagsInHostMode(c *testing.T) { 2214 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2215 2216 s.d.StartWithBusybox(testutil.GetContext(c), c, "--dns", "1.2.3.4", "--dns-search", "example.com", "--dns-opt", "timeout:3") 2217 2218 expectedOutput := "nameserver 1.2.3.4" 2219 out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf") 2220 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 2221 expectedOutput = "search example.com" 2222 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 2223 expectedOutput = "options timeout:3" 2224 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 2225 } 2226 2227 func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *testing.T) { 2228 conf, err := os.CreateTemp("", "config-file-") 2229 assert.NilError(c, err) 2230 configName := conf.Name() 2231 conf.Close() 2232 defer os.Remove(configName) 2233 2234 config := ` 2235 { 2236 "runtimes": { 2237 "oci": { 2238 "path": "runc" 2239 }, 2240 "vm": { 2241 "path": "/usr/local/bin/vm-manager", 2242 "runtimeArgs": [ 2243 "--debug" 2244 ] 2245 } 2246 } 2247 } 2248 ` 2249 os.WriteFile(configName, []byte(config), 0o644) 2250 s.d.StartWithBusybox(testutil.GetContext(c), c, "--config-file", configName) 2251 2252 // Run with default runtime 2253 out, err := s.d.Cmd("run", "--rm", "busybox", "ls") 2254 assert.NilError(c, err, out) 2255 2256 // Run with default runtime explicitly 2257 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2258 assert.NilError(c, err, out) 2259 2260 // Run with oci (same path as default) but keep it around 2261 out, err = s.d.Cmd("run", "--name", "oci-runtime-ls", "--runtime=oci", "busybox", "ls") 2262 assert.NilError(c, err, out) 2263 2264 // Run with "vm" 2265 out, err = s.d.Cmd("run", "--rm", "--runtime=vm", "busybox", "ls") 2266 assert.ErrorContains(c, err, "", out) 2267 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2268 // Reset config to only have the default 2269 config = ` 2270 { 2271 "runtimes": { 2272 } 2273 } 2274 ` 2275 os.WriteFile(configName, []byte(config), 0o644) 2276 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2277 // Give daemon time to reload config 2278 <-time.After(1 * time.Second) 2279 2280 // Run with default runtime 2281 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2282 assert.NilError(c, err, out) 2283 2284 // Run with "oci" 2285 out, err = s.d.Cmd("run", "--rm", "--runtime=oci", "busybox", "ls") 2286 assert.ErrorContains(c, err, "", out) 2287 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2288 // Start previously created container with oci 2289 out, err = s.d.Cmd("start", "oci-runtime-ls") 2290 assert.ErrorContains(c, err, "", out) 2291 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2292 // Check that we can't override the default runtime 2293 config = ` 2294 { 2295 "runtimes": { 2296 "runc": { 2297 "path": "my-runc" 2298 } 2299 } 2300 } 2301 ` 2302 os.WriteFile(configName, []byte(config), 0o644) 2303 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2304 // Give daemon time to reload config 2305 <-time.After(1 * time.Second) 2306 2307 content, err := s.d.ReadLogFile() 2308 assert.NilError(c, err) 2309 assert.Assert(c, is.Contains(string(content), `runtime name 'runc' is reserved`)) 2310 // Check that we can select a default runtime 2311 config = ` 2312 { 2313 "default-runtime": "vm", 2314 "runtimes": { 2315 "oci": { 2316 "path": "runc" 2317 }, 2318 "vm": { 2319 "path": "/usr/local/bin/vm-manager", 2320 "runtimeArgs": [ 2321 "--debug" 2322 ] 2323 } 2324 } 2325 } 2326 ` 2327 os.WriteFile(configName, []byte(config), 0o644) 2328 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2329 // Give daemon time to reload config 2330 <-time.After(1 * time.Second) 2331 2332 out, err = s.d.Cmd("run", "--rm", "busybox", "ls") 2333 assert.ErrorContains(c, err, "", out) 2334 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2335 // Run with default runtime explicitly 2336 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2337 assert.NilError(c, err, out) 2338 } 2339 2340 func (s *DockerDaemonSuite) TestRunWithRuntimeFromCommandLine(c *testing.T) { 2341 s.d.StartWithBusybox(testutil.GetContext(c), c, "--add-runtime", "oci=runc", "--add-runtime", "vm=/usr/local/bin/vm-manager") 2342 2343 // Run with default runtime 2344 out, err := s.d.Cmd("run", "--rm", "busybox", "ls") 2345 assert.NilError(c, err, out) 2346 2347 // Run with default runtime explicitly 2348 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2349 assert.NilError(c, err, out) 2350 2351 // Run with oci (same path as default) but keep it around 2352 out, err = s.d.Cmd("run", "--name", "oci-runtime-ls", "--runtime=oci", "busybox", "ls") 2353 assert.NilError(c, err, out) 2354 2355 // Run with "vm" 2356 out, err = s.d.Cmd("run", "--rm", "--runtime=vm", "busybox", "ls") 2357 assert.ErrorContains(c, err, "", out) 2358 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2359 // Start a daemon without any extra runtimes 2360 s.d.Stop(c) 2361 s.d.StartWithBusybox(testutil.GetContext(c), c) 2362 2363 // Run with default runtime 2364 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2365 assert.NilError(c, err, out) 2366 2367 // Run with "oci" 2368 out, err = s.d.Cmd("run", "--rm", "--runtime=oci", "busybox", "ls") 2369 assert.ErrorContains(c, err, "", out) 2370 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2371 // Start previously created container with oci 2372 out, err = s.d.Cmd("start", "oci-runtime-ls") 2373 assert.ErrorContains(c, err, "", out) 2374 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2375 // Check that we can't override the default runtime 2376 s.d.Stop(c) 2377 assert.Assert(c, s.d.StartWithError("--add-runtime", "runc=my-runc") != nil) 2378 2379 content, err := s.d.ReadLogFile() 2380 assert.NilError(c, err) 2381 assert.Assert(c, is.Contains(string(content), `runtime name 'runc' is reserved`)) 2382 // Check that we can select a default runtime 2383 s.d.Stop(c) 2384 s.d.StartWithBusybox(testutil.GetContext(c), c, "--default-runtime=vm", "--add-runtime", "oci=runc", "--add-runtime", "vm=/usr/local/bin/vm-manager") 2385 2386 out, err = s.d.Cmd("run", "--rm", "busybox", "ls") 2387 assert.ErrorContains(c, err, "", out) 2388 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2389 // Run with default runtime explicitly 2390 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2391 assert.NilError(c, err, out) 2392 } 2393 2394 func (s *DockerDaemonSuite) TestDaemonRestartWithAutoRemoveContainer(c *testing.T) { 2395 s.d.StartWithBusybox(testutil.GetContext(c), c) 2396 2397 // top1 will exist after daemon restarts 2398 out, err := s.d.Cmd("run", "-d", "--name", "top1", "busybox:latest", "top") 2399 assert.Assert(c, err == nil, "run top1: %v", out) 2400 // top2 will be removed after daemon restarts 2401 out, err = s.d.Cmd("run", "-d", "--rm", "--name", "top2", "busybox:latest", "top") 2402 assert.Assert(c, err == nil, "run top2: %v", out) 2403 2404 out, err = s.d.Cmd("ps") 2405 assert.NilError(c, err) 2406 assert.Assert(c, strings.Contains(out, "top1"), "top1 should be running") 2407 assert.Assert(c, strings.Contains(out, "top2"), "top2 should be running") 2408 // now restart daemon gracefully 2409 s.d.Restart(c) 2410 2411 out, err = s.d.Cmd("ps", "-a") 2412 assert.NilError(c, err, "out: %v", out) 2413 assert.Assert(c, strings.Contains(out, "top1"), "top1 should exist after daemon restarts") 2414 assert.Assert(c, !strings.Contains(out, "top2"), "top2 should be removed after daemon restarts") 2415 } 2416 2417 func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *testing.T) { 2418 s.d.StartWithBusybox(testutil.GetContext(c), c) 2419 2420 containerName := "error-values" 2421 // Make a container with both a non 0 exit code and an error message 2422 // We explicitly disable `--init` for this test, because `--init` is enabled by default 2423 // on "experimental". Enabling `--init` results in a different behavior; because the "init" 2424 // process itself is PID1, the container does not fail on _startup_ (i.e., `docker-init` starting), 2425 // but directly after. The exit code of the container is still 127, but the Error Message is not 2426 // captured, so `.State.Error` is empty. 2427 // See the discussion on https://github.com/docker/docker/pull/30227#issuecomment-274161426, 2428 // and https://github.com/docker/docker/pull/26061#r78054578 for more information. 2429 _, err := s.d.Cmd("run", "--name", containerName, "--init=false", "busybox", "toto") 2430 assert.ErrorContains(c, err, "") 2431 2432 // Check that those values were saved on disk 2433 out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName) 2434 out = strings.TrimSpace(out) 2435 assert.NilError(c, err) 2436 assert.Equal(c, out, "127") 2437 2438 errMsg1, err := s.d.Cmd("inspect", "-f", "{{.State.Error}}", containerName) 2439 errMsg1 = strings.TrimSpace(errMsg1) 2440 assert.NilError(c, err) 2441 assert.Assert(c, strings.Contains(errMsg1, "executable file not found")) 2442 // now restart daemon 2443 s.d.Restart(c) 2444 2445 // Check that those values are still around 2446 out, err = s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName) 2447 out = strings.TrimSpace(out) 2448 assert.NilError(c, err) 2449 assert.Equal(c, out, "127") 2450 2451 out, err = s.d.Cmd("inspect", "-f", "{{.State.Error}}", containerName) 2452 out = strings.TrimSpace(out) 2453 assert.NilError(c, err) 2454 assert.Equal(c, out, errMsg1) 2455 } 2456 2457 func (s *DockerDaemonSuite) TestDaemonWithUserlandProxyPath(c *testing.T) { 2458 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2459 ctx := context.TODO() 2460 2461 dockerProxyPath, err := exec.LookPath("docker-proxy") 2462 assert.NilError(c, err) 2463 tmpDir, err := os.MkdirTemp("", "test-docker-proxy") 2464 assert.NilError(c, err) 2465 2466 newProxyPath := filepath.Join(tmpDir, "docker-proxy") 2467 cmd := exec.Command("cp", dockerProxyPath, newProxyPath) 2468 assert.NilError(c, cmd.Run()) 2469 2470 // custom one 2471 s.d.StartWithBusybox(testutil.GetContext(c), c, "--userland-proxy-path", newProxyPath) 2472 out, err := s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true") 2473 assert.NilError(c, err, out) 2474 2475 // try with the original one 2476 s.d.Restart(c, "--userland-proxy-path", dockerProxyPath) 2477 out, err = s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true") 2478 assert.NilError(c, err, out) 2479 2480 // not exist 2481 s.d.Stop(c) 2482 err = s.d.StartWithError("--userland-proxy-path", "/does/not/exist") 2483 assert.ErrorContains(c, err, "", "daemon should fail to start") 2484 expected := "invalid userland-proxy-path" 2485 ok, _ := s.d.ScanLogsT(ctx, c, testdaemon.ScanLogsMatchString(expected)) 2486 assert.Assert(c, ok, "logs did not contain: %s", expected) 2487 2488 // not an absolute path 2489 s.d.Stop(c) 2490 err = s.d.StartWithError("--userland-proxy-path", "docker-proxy") 2491 assert.ErrorContains(c, err, "", "daemon should fail to start") 2492 expected = "invalid userland-proxy-path: must be an absolute path: docker-proxy" 2493 ok, _ = s.d.ScanLogsT(ctx, c, testdaemon.ScanLogsMatchString(expected)) 2494 assert.Assert(c, ok, "logs did not contain: %s", expected) 2495 } 2496 2497 // Test case for #22471 2498 func (s *DockerDaemonSuite) TestDaemonShutdownTimeout(c *testing.T) { 2499 testRequires(c, testEnv.IsLocalDaemon) 2500 s.d.StartWithBusybox(testutil.GetContext(c), c, "--shutdown-timeout=3") 2501 2502 _, err := s.d.Cmd("run", "-d", "busybox", "top") 2503 assert.NilError(c, err) 2504 2505 assert.Assert(c, s.d.Signal(unix.SIGINT) == nil) 2506 2507 select { 2508 case <-s.d.Wait: 2509 case <-time.After(5 * time.Second): 2510 } 2511 2512 expectedMessage := `level=debug msg="daemon configured with a 3 seconds minimum shutdown timeout"` 2513 content, err := s.d.ReadLogFile() 2514 assert.NilError(c, err) 2515 assert.Assert(c, strings.Contains(string(content), expectedMessage)) 2516 } 2517 2518 // Test case for #22471 2519 func (s *DockerDaemonSuite) TestDaemonShutdownTimeoutWithConfigFile(c *testing.T) { 2520 testRequires(c, testEnv.IsLocalDaemon) 2521 2522 // daemon config file 2523 const configFilePath = "test-daemon.json" 2524 err := os.WriteFile(configFilePath, []byte(`{ "shutdown-timeout" : 8 }`), 0666) 2525 assert.NilError(c, err) 2526 defer os.Remove(configFilePath) 2527 2528 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 2529 2530 err = os.WriteFile(configFilePath, []byte(`{ "shutdown-timeout" : 5 }`), 0666) 2531 assert.NilError(c, err) 2532 2533 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2534 2535 select { 2536 case <-s.d.Wait: 2537 case <-time.After(3 * time.Second): 2538 } 2539 2540 expectedMessage := `level=debug msg="Reset Shutdown Timeout: 5"` 2541 content, err := s.d.ReadLogFile() 2542 assert.NilError(c, err) 2543 assert.Assert(c, strings.Contains(string(content), expectedMessage)) 2544 } 2545 2546 // Test case for 29342 2547 func (s *DockerDaemonSuite) TestExecWithUserAfterLiveRestore(c *testing.T) { 2548 testRequires(c, DaemonIsLinux) 2549 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 2550 2551 out, err := s.d.Cmd("run", "--init", "-d", "--name=top", "busybox", "sh", "-c", "addgroup -S testgroup && adduser -S -G testgroup test -D -s /bin/sh && touch /adduser_end && exec top") 2552 assert.NilError(c, err, "Output: %s", out) 2553 2554 s.d.WaitRun("top") 2555 2556 // Wait for shell command to be completed 2557 _, err = s.d.Cmd("exec", "top", "sh", "-c", `for i in $(seq 1 5); do if [ -e /adduser_end ]; then rm -f /adduser_end && break; else sleep 1 && false; fi; done`) 2558 assert.Assert(c, err == nil, "Timeout waiting for shell command to be completed") 2559 2560 out1, err := s.d.Cmd("exec", "-u", "test", "top", "id") 2561 // uid=100(test) gid=101(testgroup) groups=101(testgroup) 2562 assert.Assert(c, err == nil, "Output: %s", out1) 2563 2564 // restart daemon. 2565 s.d.Restart(c, "--live-restore") 2566 2567 out2, err := s.d.Cmd("exec", "-u", "test", "top", "id") 2568 assert.Assert(c, err == nil, "Output: %s", out2) 2569 assert.Equal(c, out2, out1, fmt.Sprintf("Output: before restart '%s', after restart '%s'", out1, out2)) 2570 2571 out, err = s.d.Cmd("stop", "top") 2572 assert.NilError(c, err, "Output: %s", out) 2573 } 2574 2575 func (s *DockerDaemonSuite) TestRemoveContainerAfterLiveRestore(c *testing.T) { 2576 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 2577 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 2578 out, err := s.d.Cmd("run", "-d", "--name=top", "busybox", "top") 2579 assert.NilError(c, err, "Output: %s", out) 2580 2581 s.d.WaitRun("top") 2582 2583 // restart daemon. 2584 s.d.Restart(c, "--live-restore") 2585 2586 out, err = s.d.Cmd("stop", "top") 2587 assert.NilError(c, err, "Output: %s", out) 2588 2589 // test if the rootfs mountpoint still exist 2590 mountpoint, err := s.d.InspectField("top", ".GraphDriver.Data.MergedDir") 2591 assert.NilError(c, err) 2592 f, err := os.Open("/proc/self/mountinfo") 2593 assert.NilError(c, err) 2594 defer f.Close() 2595 sc := bufio.NewScanner(f) 2596 for sc.Scan() { 2597 line := sc.Text() 2598 if strings.Contains(line, mountpoint) { 2599 c.Fatalf("mountinfo should not include the mountpoint of stop container") 2600 } 2601 } 2602 2603 out, err = s.d.Cmd("rm", "top") 2604 assert.NilError(c, err, "Output: %s", out) 2605 } 2606 2607 // #29598 2608 func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *testing.T) { 2609 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 2610 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 2611 2612 out, err := s.d.Cmd("run", "-d", "--restart", "always", "busybox", "top") 2613 assert.NilError(c, err, "Output: %s", out) 2614 id := strings.TrimSpace(out) 2615 2616 type state struct { 2617 Running bool 2618 StartedAt time.Time 2619 } 2620 out, err = s.d.Cmd("inspect", "-f", "{{json .State}}", id) 2621 assert.Assert(c, err == nil, "output: %s", out) 2622 2623 var origState state 2624 err = json.Unmarshal([]byte(strings.TrimSpace(out)), &origState) 2625 assert.NilError(c, err) 2626 2627 s.d.Restart(c, "--live-restore") 2628 2629 pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", id) 2630 assert.NilError(c, err) 2631 pidint, err := strconv.Atoi(strings.TrimSpace(pid)) 2632 assert.NilError(c, err) 2633 assert.Assert(c, pidint > 0) 2634 assert.NilError(c, unix.Kill(pidint, unix.SIGKILL)) 2635 2636 ticker := time.NewTicker(50 * time.Millisecond) 2637 timeout := time.After(10 * time.Second) 2638 2639 for range ticker.C { 2640 select { 2641 case <-timeout: 2642 c.Fatal("timeout waiting for container restart") 2643 default: 2644 } 2645 2646 out, err := s.d.Cmd("inspect", "-f", "{{json .State}}", id) 2647 assert.Assert(c, err == nil, "output: %s", out) 2648 2649 var newState state 2650 err = json.Unmarshal([]byte(strings.TrimSpace(out)), &newState) 2651 assert.NilError(c, err) 2652 2653 if !newState.Running { 2654 continue 2655 } 2656 if newState.StartedAt.After(origState.StartedAt) { 2657 break 2658 } 2659 } 2660 2661 out, err = s.d.Cmd("stop", id) 2662 assert.NilError(c, err, "Output: %s", out) 2663 } 2664 2665 func (s *DockerDaemonSuite) TestShmSize(c *testing.T) { 2666 testRequires(c, DaemonIsLinux) 2667 2668 size := 67108864 * 2 2669 pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024)) 2670 2671 s.d.StartWithBusybox(testutil.GetContext(c), c, "--default-shm-size", fmt.Sprintf("%v", size)) 2672 2673 name := "shm1" 2674 out, err := s.d.Cmd("run", "--name", name, "busybox", "mount") 2675 assert.NilError(c, err, "Output: %s", out) 2676 assert.Assert(c, pattern.MatchString(out)) 2677 out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name) 2678 assert.NilError(c, err, "Output: %s", out) 2679 assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size)) 2680 } 2681 2682 func (s *DockerDaemonSuite) TestShmSizeReload(c *testing.T) { 2683 testRequires(c, DaemonIsLinux) 2684 2685 configPath, err := os.MkdirTemp("", "test-daemon-shm-size-reload-config") 2686 assert.Assert(c, err == nil, "could not create temp file for config reload") 2687 defer os.RemoveAll(configPath) // clean up 2688 configFile := filepath.Join(configPath, "config.json") 2689 2690 size := 67108864 * 2 2691 configData := []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024)) 2692 assert.Assert(c, os.WriteFile(configFile, configData, 0o666) == nil, "could not write temp file for config reload") 2693 pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024)) 2694 2695 s.d.StartWithBusybox(testutil.GetContext(c), c, "--config-file", configFile) 2696 2697 name := "shm1" 2698 out, err := s.d.Cmd("run", "--name", name, "busybox", "mount") 2699 assert.NilError(c, err, "Output: %s", out) 2700 assert.Assert(c, pattern.MatchString(out)) 2701 out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name) 2702 assert.NilError(c, err, "Output: %s", out) 2703 assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size)) 2704 2705 size = 67108864 * 3 2706 configData = []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024)) 2707 assert.Assert(c, os.WriteFile(configFile, configData, 0o666) == nil, "could not write temp file for config reload") 2708 pattern = regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024)) 2709 2710 err = s.d.ReloadConfig() 2711 assert.Assert(c, err == nil, "error reloading daemon config") 2712 2713 name = "shm2" 2714 out, err = s.d.Cmd("run", "--name", name, "busybox", "mount") 2715 assert.NilError(c, err, "Output: %s", out) 2716 assert.Assert(c, pattern.MatchString(out)) 2717 out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name) 2718 assert.NilError(c, err, "Output: %s", out) 2719 assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size)) 2720 } 2721 2722 func testDaemonStartIpcMode(c *testing.T, from, mode string, valid bool) { 2723 d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 2724 c.Logf("Checking IpcMode %s set from %s\n", mode, from) 2725 var serr error 2726 switch from { 2727 case "config": 2728 f, err := os.CreateTemp("", "test-daemon-ipc-config") 2729 assert.NilError(c, err) 2730 defer os.Remove(f.Name()) 2731 config := `{"default-ipc-mode": "` + mode + `"}` 2732 _, err = f.WriteString(config) 2733 assert.NilError(c, f.Close()) 2734 assert.NilError(c, err) 2735 2736 serr = d.StartWithError("--config-file", f.Name()) 2737 case "cli": 2738 serr = d.StartWithError("--default-ipc-mode", mode) 2739 default: 2740 c.Fatalf("testDaemonStartIpcMode: invalid 'from' argument") 2741 } 2742 if serr == nil { 2743 d.Stop(c) 2744 } 2745 2746 if valid { 2747 assert.NilError(c, serr) 2748 } else { 2749 assert.ErrorContains(c, serr, "") 2750 icmd.RunCommand("grep", "-E", "IPC .* is (invalid|not supported)", d.LogFileName()).Assert(c, icmd.Success) 2751 } 2752 } 2753 2754 // TestDaemonStartWithIpcModes checks that daemon starts fine given correct 2755 // arguments for default IPC mode, and bails out with incorrect ones. 2756 // Both CLI option (--default-ipc-mode) and config parameter are tested. 2757 func (s *DockerDaemonSuite) TestDaemonStartWithIpcModes(c *testing.T) { 2758 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 2759 2760 ipcModes := []struct { 2761 mode string 2762 valid bool 2763 }{ 2764 {"private", true}, 2765 {"shareable", true}, 2766 2767 {"host", false}, 2768 {"container:123", false}, 2769 {"nosuchmode", false}, 2770 } 2771 2772 for _, from := range []string{"config", "cli"} { 2773 for _, m := range ipcModes { 2774 testDaemonStartIpcMode(c, from, m.mode, m.valid) 2775 } 2776 } 2777 } 2778 2779 // TestFailedPluginRemove makes sure that a failed plugin remove does not block 2780 // the daemon from starting 2781 func (s *DockerDaemonSuite) TestFailedPluginRemove(c *testing.T) { 2782 testRequires(c, DaemonIsLinux, IsAmd64, testEnv.IsLocalDaemon) 2783 d := daemon.New(c, dockerBinary, dockerdBinary) 2784 d.Start(c) 2785 apiClient := d.NewClientT(c) 2786 2787 ctx, cancel := context.WithTimeout(testutil.GetContext(c), 300*time.Second) 2788 defer cancel() 2789 2790 name := "test-plugin-rm-fail" 2791 out, err := apiClient.PluginInstall(ctx, name, types.PluginInstallOptions{ 2792 Disabled: true, 2793 AcceptAllPermissions: true, 2794 RemoteRef: "cpuguy83/docker-logdriver-test", 2795 }) 2796 assert.NilError(c, err) 2797 defer out.Close() 2798 io.Copy(io.Discard, out) 2799 2800 ctx, cancel = context.WithTimeout(testutil.GetContext(c), 30*time.Second) 2801 defer cancel() 2802 p, _, err := apiClient.PluginInspectWithRaw(ctx, name) 2803 assert.NilError(c, err) 2804 2805 // simulate a bad/partial removal by removing the plugin config. 2806 configPath := filepath.Join(d.Root, "plugins", p.ID, "config.json") 2807 assert.NilError(c, os.Remove(configPath)) 2808 2809 d.Restart(c) 2810 ctx, cancel = context.WithTimeout(testutil.GetContext(c), 30*time.Second) 2811 defer cancel() 2812 _, err = apiClient.Ping(ctx) 2813 assert.NilError(c, err) 2814 2815 _, _, err = apiClient.PluginInspectWithRaw(ctx, name) 2816 // plugin should be gone since the config.json is gone 2817 assert.ErrorContains(c, err, "") 2818 }