github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/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 cols := strings.Fields(out) 811 812 expected := []string{"0", "0", "DROP", "all", "--", bridgeName, bridgeName, "anywhere", "anywhere"} 813 assert.DeepEqual(c, cols, expected) 814 815 // Pinging another container must fail with --icc=false 816 pingContainers(c, d, true) 817 818 const cidr = "192.171.1.1/24" 819 ip, _, _ := net.ParseCIDR(cidr) 820 const ifName = "icc-dummy" 821 822 createInterface(c, "dummy", ifName, cidr) 823 defer deleteInterface(c, ifName) 824 825 // But, Pinging external or a Host interface must succeed 826 pingCmd := fmt.Sprintf("ping -c 1 %s -W 1", ip.String()) 827 runArgs := []string{"run", "--rm", "busybox", "sh", "-c", pingCmd} 828 out, err := d.Cmd(runArgs...) 829 assert.NilError(c, err, out) 830 } 831 832 func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *testing.T) { 833 d := s.d 834 835 // make sure the default docker0 bridge doesn't interfere with the test, 836 // which may happen if it was created with the same IP range. 837 deleteInterface(c, "docker0") 838 839 const bridgeName = "ext-bridge6" 840 const bridgeIP = "192.169.1.1/24" 841 842 createInterface(c, "bridge", bridgeName, bridgeIP) 843 defer deleteInterface(c, bridgeName) 844 845 d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName, "--icc=false") 846 defer d.Restart(c) 847 848 result := icmd.RunCommand("sh", "-c", "iptables -vL FORWARD | grep DROP") 849 result.Assert(c, icmd.Success) 850 851 // strip whitespace and newlines to verify we only found a single DROP 852 out := strings.TrimSpace(result.Stdout()) 853 assert.Assert(c, is.Equal(strings.Count(out, "\n"), 0), "only expected a single DROP rules") 854 855 // Column headers are stripped because of grep-ing, but should be: 856 // 857 // pkts bytes target prot opt in out source destination 858 // 0 0 DROP all -- ext-bridge6 ext-bridge6 anywhere anywhere 859 cols := strings.Fields(out) 860 861 expected := []string{"0", "0", "DROP", "all", "--", bridgeName, bridgeName, "anywhere", "anywhere"} 862 assert.DeepEqual(c, cols, expected) 863 864 out, err := d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567") 865 assert.NilError(c, err, out) 866 867 out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567") 868 assert.NilError(c, err, out) 869 } 870 871 func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *testing.T) { 872 // make sure the default docker0 bridge doesn't interfere with the test, 873 // which may happen if it was created with the same IP range. 874 deleteInterface(c, "docker0") 875 876 bridgeName := "ext-bridge7" 877 bridgeIP := "192.169.1.1/24" 878 879 createInterface(c, "bridge", bridgeName, bridgeIP) 880 defer deleteInterface(c, bridgeName) 881 882 s.d.StartWithBusybox(testutil.GetContext(c), c, "--bridge", bridgeName, "--icc=false") 883 defer s.d.Restart(c) 884 885 out, err := s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top") 886 assert.NilError(c, err, out) 887 out, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top") 888 assert.NilError(c, err, out) 889 890 childIP := s.d.FindContainerIP(c, "child") 891 parentIP := s.d.FindContainerIP(c, "parent") 892 893 sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"} 894 destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"} 895 iptable := iptables.GetIptable(iptables.IPv4) 896 if !iptable.Exists("filter", "DOCKER", sourceRule...) || !iptable.Exists("filter", "DOCKER", destinationRule...) { 897 c.Fatal("Iptables rules not found") 898 } 899 900 s.d.Cmd("rm", "--link", "parent/http") 901 if iptable.Exists("filter", "DOCKER", sourceRule...) || iptable.Exists("filter", "DOCKER", destinationRule...) { 902 c.Fatal("Iptables rules should be removed when unlink") 903 } 904 905 s.d.Cmd("kill", "child") 906 s.d.Cmd("kill", "parent") 907 } 908 909 func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *testing.T) { 910 s.d.StartWithBusybox(testutil.GetContext(c), c, "--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024") 911 912 out, err := s.d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -u)") 913 if err != nil { 914 c.Fatal(err, out) 915 } 916 917 outArr := strings.Split(out, "\n") 918 if len(outArr) < 2 { 919 c.Fatalf("got unexpected output: %s", out) 920 } 921 nofile := strings.TrimSpace(outArr[0]) 922 nproc := strings.TrimSpace(outArr[1]) 923 924 if nofile != "42" { 925 c.Fatalf("expected `ulimit -n` to be `42`, got: %s", nofile) 926 } 927 if nproc != "2048" { 928 c.Fatalf("expected `ulimit -u` to be 2048, got: %s", nproc) 929 } 930 931 // Now restart daemon with a new default 932 s.d.Restart(c, "--default-ulimit", "nofile=43") 933 934 out, err = s.d.Cmd("start", "-a", "test") 935 if err != nil { 936 c.Fatal(err, out) 937 } 938 939 outArr = strings.Split(out, "\n") 940 if len(outArr) < 2 { 941 c.Fatalf("got unexpected output: %s", out) 942 } 943 nofile = strings.TrimSpace(outArr[0]) 944 nproc = strings.TrimSpace(outArr[1]) 945 946 if nofile != "43" { 947 c.Fatalf("expected `ulimit -n` to be `43`, got: %s", nofile) 948 } 949 if nproc != "2048" { 950 c.Fatalf("expected `ulimit -u` to be 2048, got: %s", nproc) 951 } 952 } 953 954 // #11315 955 func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *testing.T) { 956 s.d.StartWithBusybox(testutil.GetContext(c), c) 957 958 if out, err := s.d.Cmd("run", "--name=test", "busybox"); err != nil { 959 c.Fatal(err, out) 960 } 961 962 if out, err := s.d.Cmd("rename", "test", "test2"); err != nil { 963 c.Fatal(err, out) 964 } 965 966 s.d.Restart(c) 967 968 if out, err := s.d.Cmd("start", "test2"); err != nil { 969 c.Fatal(err, out) 970 } 971 } 972 973 func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *testing.T) { 974 s.d.StartWithBusybox(testutil.GetContext(c), c) 975 976 out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline") 977 assert.NilError(c, err, out) 978 id, err := s.d.GetIDByName("test") 979 assert.NilError(c, err) 980 981 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 982 983 if _, err := os.Stat(logPath); err != nil { 984 c.Fatal(err) 985 } 986 f, err := os.Open(logPath) 987 if err != nil { 988 c.Fatal(err) 989 } 990 defer f.Close() 991 992 var res struct { 993 Log string `json:"log"` 994 Stream string `json:"stream"` 995 Time time.Time `json:"time"` 996 } 997 if err := json.NewDecoder(f).Decode(&res); err != nil { 998 c.Fatal(err) 999 } 1000 if res.Log != "testline\n" { 1001 c.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n") 1002 } 1003 if res.Stream != "stdout" { 1004 c.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout") 1005 } 1006 if !time.Now().After(res.Time) { 1007 c.Fatalf("Log time %v in future", res.Time) 1008 } 1009 } 1010 1011 func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *testing.T) { 1012 s.d.StartWithBusybox(testutil.GetContext(c), c) 1013 1014 out, err := s.d.Cmd("run", "--name=test", "--log-driver=none", "busybox", "echo", "testline") 1015 if err != nil { 1016 c.Fatal(out, err) 1017 } 1018 id, err := s.d.GetIDByName("test") 1019 assert.NilError(c, err) 1020 1021 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 1022 1023 if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) { 1024 c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err) 1025 } 1026 } 1027 1028 func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *testing.T) { 1029 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=none") 1030 1031 out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline") 1032 if err != nil { 1033 c.Fatal(out, err) 1034 } 1035 id, err := s.d.GetIDByName("test") 1036 assert.NilError(c, err) 1037 1038 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 1039 1040 if _, err := os.Stat(logPath); err == nil || !os.IsNotExist(err) { 1041 c.Fatalf("%s shouldn't exits, error on Stat: %s", logPath, err) 1042 } 1043 } 1044 1045 func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *testing.T) { 1046 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=none") 1047 1048 out, err := s.d.Cmd("run", "--name=test", "--log-driver=json-file", "busybox", "echo", "testline") 1049 if err != nil { 1050 c.Fatal(out, err) 1051 } 1052 id, err := s.d.GetIDByName("test") 1053 assert.NilError(c, err) 1054 1055 logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log") 1056 1057 if _, err := os.Stat(logPath); err != nil { 1058 c.Fatal(err) 1059 } 1060 f, err := os.Open(logPath) 1061 if err != nil { 1062 c.Fatal(err) 1063 } 1064 defer f.Close() 1065 1066 var res struct { 1067 Log string `json:"log"` 1068 Stream string `json:"stream"` 1069 Time time.Time `json:"time"` 1070 } 1071 if err := json.NewDecoder(f).Decode(&res); err != nil { 1072 c.Fatal(err) 1073 } 1074 if res.Log != "testline\n" { 1075 c.Fatalf("Unexpected log line: %q, expected: %q", res.Log, "testline\n") 1076 } 1077 if res.Stream != "stdout" { 1078 c.Fatalf("Unexpected stream: %q, expected: %q", res.Stream, "stdout") 1079 } 1080 if !time.Now().After(res.Time) { 1081 c.Fatalf("Log time %v in future", res.Time) 1082 } 1083 } 1084 1085 func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *testing.T) { 1086 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=none") 1087 1088 out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline") 1089 assert.NilError(c, err, out) 1090 1091 out, err = s.d.Cmd("logs", "test") 1092 assert.Assert(c, err != nil, "Logs should fail with 'none' driver") 1093 expected := `configured logging driver does not support reading` 1094 assert.Assert(c, strings.Contains(out, expected)) 1095 } 1096 1097 func (s *DockerDaemonSuite) TestDaemonLoggingDriverShouldBeIgnoredForBuild(c *testing.T) { 1098 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=splunk") 1099 1100 result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d), 1101 build.WithDockerfile(` 1102 FROM busybox 1103 RUN echo foo`), 1104 build.WithoutCache, 1105 ) 1106 comment := fmt.Sprintf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error) 1107 assert.Assert(c, result.Error == nil, comment) 1108 assert.Equal(c, result.ExitCode, 0, comment) 1109 assert.Assert(c, strings.Contains(result.Combined(), "foo"), comment) 1110 } 1111 1112 func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *testing.T) { 1113 dir, err := os.MkdirTemp("", "socket-cleanup-test") 1114 if err != nil { 1115 c.Fatal(err) 1116 } 1117 defer os.RemoveAll(dir) 1118 1119 sockPath := filepath.Join(dir, "docker.sock") 1120 s.d.Start(c, "--host", "unix://"+sockPath) 1121 1122 if _, err := os.Stat(sockPath); err != nil { 1123 c.Fatal("socket does not exist") 1124 } 1125 1126 s.d.Stop(c) 1127 1128 if _, err := os.Stat(sockPath); err == nil || !os.IsNotExist(err) { 1129 c.Fatal("unix socket is not cleaned up") 1130 } 1131 } 1132 1133 func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *testing.T) { 1134 s.d.StartWithBusybox(testutil.GetContext(c), c) 1135 1136 out, err := s.d.Cmd("run", "-id", "busybox", "/bin/cat") 1137 if err != nil { 1138 c.Fatalf("Could not run /bin/cat: err=%v\n%s", err, out) 1139 } 1140 containerID := strings.TrimSpace(out) 1141 1142 if out, err := s.d.Cmd("kill", containerID); err != nil { 1143 c.Fatalf("Could not kill %s: err=%v\n%s", containerID, err, out) 1144 } 1145 1146 s.d.Restart(c) 1147 1148 errchan := make(chan error, 1) 1149 go func() { 1150 if out, err := s.d.Cmd("wait", containerID); err != nil { 1151 errchan <- fmt.Errorf("%v:\n%s", err, out) 1152 } 1153 close(errchan) 1154 }() 1155 1156 select { 1157 case <-time.After(5 * time.Second): 1158 c.Fatal("Waiting on a stopped (killed) container timed out") 1159 case err := <-errchan: 1160 if err != nil { 1161 c.Fatal(err) 1162 } 1163 } 1164 } 1165 1166 // TestHTTPSInfo connects via two-way authenticated HTTPS to the info endpoint 1167 func (s *DockerDaemonSuite) TestHTTPSInfo(c *testing.T) { 1168 const ( 1169 testDaemonHTTPSAddr = "tcp://localhost:4271" 1170 ) 1171 1172 s.d.Start(c, 1173 "--tlsverify", 1174 "--tlscacert", "fixtures/https/ca.pem", 1175 "--tlscert", "fixtures/https/server-cert.pem", 1176 "--tlskey", "fixtures/https/server-key.pem", 1177 "-H", testDaemonHTTPSAddr) 1178 1179 args := []string{ 1180 "--host", testDaemonHTTPSAddr, 1181 "--tlsverify", 1182 "--tlscacert", "fixtures/https/ca.pem", 1183 "--tlscert", "fixtures/https/client-cert.pem", 1184 "--tlskey", "fixtures/https/client-key.pem", 1185 "info", 1186 } 1187 out, err := s.d.Cmd(args...) 1188 if err != nil { 1189 c.Fatalf("Error Occurred: %s and output: %s", err, out) 1190 } 1191 } 1192 1193 // TestHTTPSRun connects via two-way authenticated HTTPS to the create, attach, start, and wait endpoints. 1194 // https://github.com/docker/docker/issues/19280 1195 func (s *DockerDaemonSuite) TestHTTPSRun(c *testing.T) { 1196 const ( 1197 testDaemonHTTPSAddr = "tcp://localhost:4271" 1198 ) 1199 1200 s.d.StartWithBusybox(testutil.GetContext(c), c, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", 1201 "--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHTTPSAddr) 1202 1203 args := []string{ 1204 "--host", testDaemonHTTPSAddr, 1205 "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", 1206 "--tlscert", "fixtures/https/client-cert.pem", 1207 "--tlskey", "fixtures/https/client-key.pem", 1208 "run", "busybox", "echo", "TLS response", 1209 } 1210 out, err := s.d.Cmd(args...) 1211 if err != nil { 1212 c.Fatalf("Error Occurred: %s and output: %s", err, out) 1213 } 1214 1215 if !strings.Contains(out, "TLS response") { 1216 c.Fatalf("expected output to include `TLS response`, got %v", out) 1217 } 1218 } 1219 1220 // TestTLSVerify verifies that --tlsverify=false turns on tls 1221 func (s *DockerDaemonSuite) TestTLSVerify(c *testing.T) { 1222 out, err := exec.Command(dockerdBinary, "--tlsverify=false").CombinedOutput() 1223 if err == nil || !strings.Contains(string(out), "could not load X509 key pair") { 1224 c.Fatalf("Daemon should not have started due to missing certs: %v\n%s", err, string(out)) 1225 } 1226 } 1227 1228 // TestHTTPSInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint 1229 // by using a rogue client certificate and checks that it fails with the expected error. 1230 func (s *DockerDaemonSuite) TestHTTPSInfoRogueCert(c *testing.T) { 1231 const ( 1232 errBadCertificate = "bad certificate" 1233 testDaemonHTTPSAddr = "tcp://localhost:4271" 1234 ) 1235 1236 s.d.Start(c, 1237 "--tlsverify", 1238 "--tlscacert", "fixtures/https/ca.pem", 1239 "--tlscert", "fixtures/https/server-cert.pem", 1240 "--tlskey", "fixtures/https/server-key.pem", 1241 "-H", testDaemonHTTPSAddr) 1242 1243 args := []string{ 1244 "--host", testDaemonHTTPSAddr, 1245 "--tlsverify", 1246 "--tlscacert", "fixtures/https/ca.pem", 1247 "--tlscert", "fixtures/https/client-rogue-cert.pem", 1248 "--tlskey", "fixtures/https/client-rogue-key.pem", 1249 "info", 1250 } 1251 out, err := s.d.Cmd(args...) 1252 if err == nil || !strings.Contains(out, errBadCertificate) { 1253 c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out) 1254 } 1255 } 1256 1257 // TestHTTPSInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint 1258 // which provides a rogue server certificate and checks that it fails with the expected error 1259 func (s *DockerDaemonSuite) TestHTTPSInfoRogueServerCert(c *testing.T) { 1260 const ( 1261 errCaUnknown = "x509: certificate signed by unknown authority" 1262 testDaemonRogueHTTPSAddr = "tcp://localhost:4272" 1263 ) 1264 s.d.Start(c, 1265 "--tlsverify", 1266 "--tlscacert", "fixtures/https/ca.pem", 1267 "--tlscert", "fixtures/https/server-rogue-cert.pem", 1268 "--tlskey", "fixtures/https/server-rogue-key.pem", 1269 "-H", testDaemonRogueHTTPSAddr) 1270 1271 args := []string{ 1272 "--host", testDaemonRogueHTTPSAddr, 1273 "--tlsverify", 1274 "--tlscacert", "fixtures/https/ca.pem", 1275 "--tlscert", "fixtures/https/client-rogue-cert.pem", 1276 "--tlskey", "fixtures/https/client-rogue-key.pem", 1277 "info", 1278 } 1279 out, err := s.d.Cmd(args...) 1280 if err == nil || !strings.Contains(out, errCaUnknown) { 1281 c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out) 1282 } 1283 } 1284 1285 func pingContainers(c *testing.T, d *daemon.Daemon, expectFailure bool) { 1286 var dargs []string 1287 if d != nil { 1288 dargs = []string{"--host", d.Sock()} 1289 } 1290 1291 args := append(dargs, "run", "-d", "--name", "container1", "busybox", "top") 1292 cli.DockerCmd(c, args...) 1293 1294 args = append(dargs, "run", "--rm", "--link", "container1:alias1", "busybox", "sh", "-c") 1295 pingCmd := "ping -c 1 %s -W 1" 1296 args = append(args, fmt.Sprintf(pingCmd, "alias1")) 1297 _, _, err := dockerCmdWithError(args...) 1298 1299 if expectFailure { 1300 assert.ErrorContains(c, err, "") 1301 } else { 1302 assert.NilError(c, err) 1303 } 1304 1305 args = append(dargs, "rm", "-f", "container1") 1306 cli.DockerCmd(c, args...) 1307 } 1308 1309 func (s *DockerDaemonSuite) TestDaemonRestartWithSocketAsVolume(c *testing.T) { 1310 s.d.StartWithBusybox(testutil.GetContext(c), c) 1311 1312 socket := filepath.Join(s.d.Folder, "docker.sock") 1313 1314 out, err := s.d.Cmd("run", "--restart=always", "-v", socket+":/sock", "busybox") 1315 assert.NilError(c, err, "Output: %s", out) 1316 s.d.Restart(c) 1317 } 1318 1319 // os.Kill should kill daemon ungracefully, leaving behind container mounts. 1320 // A subsequent daemon restart should clean up said mounts. 1321 func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonAndContainerKill(c *testing.T) { 1322 d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 1323 d.StartWithBusybox(testutil.GetContext(c), c) 1324 1325 out, err := d.Cmd("run", "-d", "busybox", "top") 1326 assert.NilError(c, err, "Output: %s", out) 1327 1328 id := strings.TrimSpace(out) 1329 1330 // If there are no mounts with container id visible from the host 1331 // (as those are in container's own mount ns), there is nothing 1332 // to check here and the test should be skipped. 1333 mountOut, err := os.ReadFile("/proc/self/mountinfo") 1334 assert.NilError(c, err, "Output: %s", mountOut) 1335 if !strings.Contains(string(mountOut), id) { 1336 d.Stop(c) 1337 c.Skip("no container mounts visible in host ns") 1338 } 1339 1340 // kill the daemon 1341 assert.NilError(c, d.Kill()) 1342 1343 // kill the container 1344 icmd.RunCommand(ctrBinary, "--address", containerdSocket, 1345 "--namespace", d.ContainersNamespace(), "tasks", "kill", id).Assert(c, icmd.Success) 1346 1347 // restart daemon. 1348 d.Restart(c) 1349 1350 // Now, container mounts should be gone. 1351 mountOut, err = os.ReadFile("/proc/self/mountinfo") 1352 assert.NilError(c, err, "Output: %s", mountOut) 1353 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) 1354 1355 d.Stop(c) 1356 } 1357 1358 // os.Interrupt should perform a graceful daemon shutdown and hence cleanup mounts. 1359 func (s *DockerDaemonSuite) TestCleanupMountsAfterGracefulShutdown(c *testing.T) { 1360 d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 1361 d.StartWithBusybox(testutil.GetContext(c), c) 1362 1363 out, err := d.Cmd("run", "-d", "busybox", "top") 1364 assert.NilError(c, err, "Output: %s", out) 1365 id := strings.TrimSpace(out) 1366 1367 // Send SIGINT and daemon should clean up 1368 assert.NilError(c, d.Signal(os.Interrupt)) 1369 // Wait for the daemon to stop. 1370 assert.NilError(c, <-d.Wait) 1371 1372 mountOut, err := os.ReadFile("/proc/self/mountinfo") 1373 assert.NilError(c, err, "Output: %s", mountOut) 1374 1375 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) 1376 } 1377 1378 func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *testing.T) { 1379 s.d.StartWithBusybox(testutil.GetContext(t), t) 1380 if out, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top"); err != nil { 1381 t.Fatal(out, err) 1382 } 1383 1384 s.d.Restart(t) 1385 // Container 'test' should be removed without error 1386 if out, err := s.d.Cmd("rm", "test"); err != nil { 1387 t.Fatal(out, err) 1388 } 1389 } 1390 1391 func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *testing.T) { 1392 s.d.StartWithBusybox(testutil.GetContext(c), c) 1393 out, err := s.d.Cmd("run", "--name", "netns", "-d", "busybox", "top") 1394 if err != nil { 1395 c.Fatal(out, err) 1396 } 1397 1398 // Get sandbox key via inspect 1399 out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.SandboxKey}}'", "netns") 1400 if err != nil { 1401 c.Fatalf("Error inspecting container: %s, %v", out, err) 1402 } 1403 fileName := strings.Trim(out, " \r\n'") 1404 1405 if out, err := s.d.Cmd("stop", "netns"); err != nil { 1406 c.Fatal(out, err) 1407 } 1408 1409 // Test if the file still exists 1410 icmd.RunCommand("stat", "-c", "%n", fileName).Assert(c, icmd.Expected{ 1411 Out: fileName, 1412 }) 1413 1414 // Remove the container and restart the daemon 1415 if out, err := s.d.Cmd("rm", "netns"); err != nil { 1416 c.Fatal(out, err) 1417 } 1418 1419 s.d.Restart(c) 1420 1421 // Test again and see now the netns file does not exist 1422 icmd.RunCommand("stat", "-c", "%n", fileName).Assert(c, icmd.Expected{ 1423 Err: "No such file or directory", 1424 ExitCode: 1, 1425 }) 1426 } 1427 1428 // tests regression detailed in #13964 where DOCKER_TLS_VERIFY env is ignored 1429 func (s *DockerDaemonSuite) TestDaemonTLSVerifyIssue13964(c *testing.T) { 1430 host := "tcp://localhost:4271" 1431 s.d.Start(c, "-H", host) 1432 icmd.RunCmd(icmd.Cmd{ 1433 Command: []string{dockerBinary, "-H", host, "info"}, 1434 Env: []string{"DOCKER_TLS_VERIFY=1", "DOCKER_CERT_PATH=fixtures/https"}, 1435 }).Assert(c, icmd.Expected{ 1436 ExitCode: 1, 1437 Err: "error during connect", 1438 }) 1439 } 1440 1441 func setupV6(c *testing.T) { 1442 // Hack to get the right IPv6 address on docker0, which has already been created 1443 result := icmd.RunCommand("ip", "addr", "add", "fe80::1/64", "dev", "docker0") 1444 result.Assert(c, icmd.Success) 1445 } 1446 1447 func teardownV6(c *testing.T) { 1448 result := icmd.RunCommand("ip", "addr", "del", "fe80::1/64", "dev", "docker0") 1449 result.Assert(c, icmd.Success) 1450 } 1451 1452 func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlways(c *testing.T) { 1453 s.d.StartWithBusybox(testutil.GetContext(c), c) 1454 1455 out, err := s.d.Cmd("run", "-d", "--restart", "always", "busybox", "top") 1456 assert.NilError(c, err, out) 1457 id := strings.TrimSpace(out) 1458 1459 out, err = s.d.Cmd("stop", id) 1460 assert.NilError(c, err, out) 1461 out, err = s.d.Cmd("wait", id) 1462 assert.NilError(c, err, out) 1463 1464 out, err = s.d.Cmd("ps", "-q") 1465 assert.NilError(c, err, out) 1466 assert.Equal(c, out, "") 1467 1468 s.d.Restart(c) 1469 1470 out, err = s.d.Cmd("ps", "-q") 1471 assert.NilError(c, err, out) 1472 assert.Equal(c, strings.TrimSpace(out), id[:12]) 1473 } 1474 1475 func (s *DockerDaemonSuite) TestDaemonWideLogConfig(c *testing.T) { 1476 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-opt=max-size=1k") 1477 name := "logtest" 1478 out, err := s.d.Cmd("run", "-d", "--log-opt=max-file=5", "--name", name, "busybox", "top") 1479 assert.NilError(c, err, "Output: %s, err: %v", out, err) 1480 1481 out, err = s.d.Cmd("inspect", "-f", "{{ .HostConfig.LogConfig.Config }}", name) 1482 assert.NilError(c, err, "Output: %s", out) 1483 assert.Assert(c, strings.Contains(out, "max-size:1k")) 1484 assert.Assert(c, strings.Contains(out, "max-file:5")) 1485 1486 out, err = s.d.Cmd("inspect", "-f", "{{ .HostConfig.LogConfig.Type }}", name) 1487 assert.NilError(c, err, "Output: %s", out) 1488 assert.Equal(c, strings.TrimSpace(out), "json-file") 1489 } 1490 1491 func (s *DockerDaemonSuite) TestDaemonRestartWithPausedContainer(c *testing.T) { 1492 s.d.StartWithBusybox(testutil.GetContext(c), c) 1493 if out, err := s.d.Cmd("run", "-i", "-d", "--name", "test", "busybox", "top"); err != nil { 1494 c.Fatal(err, out) 1495 } 1496 if out, err := s.d.Cmd("pause", "test"); err != nil { 1497 c.Fatal(err, out) 1498 } 1499 s.d.Restart(c) 1500 1501 errchan := make(chan error, 1) 1502 go func() { 1503 out, err := s.d.Cmd("start", "test") 1504 if err != nil { 1505 errchan <- fmt.Errorf("%v:\n%s", err, out) 1506 return 1507 } 1508 name := strings.TrimSpace(out) 1509 if name != "test" { 1510 errchan <- fmt.Errorf("Paused container start error on docker daemon restart, expected 'test' but got '%s'", name) 1511 return 1512 } 1513 close(errchan) 1514 }() 1515 1516 select { 1517 case <-time.After(5 * time.Second): 1518 c.Fatal("Waiting on start a container timed out") 1519 case err := <-errchan: 1520 if err != nil { 1521 c.Fatal(err) 1522 } 1523 } 1524 } 1525 1526 func (s *DockerDaemonSuite) TestDaemonRestartRmVolumeInUse(c *testing.T) { 1527 s.d.StartWithBusybox(testutil.GetContext(c), c) 1528 1529 out, err := s.d.Cmd("create", "-v", "test:/foo", "busybox") 1530 assert.NilError(c, err, out) 1531 1532 s.d.Restart(c) 1533 1534 out, err = s.d.Cmd("volume", "rm", "test") 1535 assert.Assert(c, err != nil, "should not be able to remove in use volume after daemon restart") 1536 assert.Assert(c, strings.Contains(out, "in use")) 1537 } 1538 1539 func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *testing.T) { 1540 s.d.Start(c) 1541 1542 out, err := s.d.Cmd("volume", "create", "test") 1543 assert.NilError(c, err, out) 1544 s.d.Restart(c) 1545 1546 out, err = s.d.Cmd("volume", "inspect", "test") 1547 assert.NilError(c, err, out) 1548 } 1549 1550 // FIXME(vdemeester) Use a new daemon instance instead of the Suite one 1551 func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *testing.T) { 1552 s.d.UseDefaultHost = true 1553 defer func() { 1554 s.d.UseDefaultHost = false 1555 }() 1556 s.d.Start(c) 1557 } 1558 1559 // FIXME(vdemeester) Use a new daemon instance instead of the Suite one 1560 func (s *DockerDaemonSuite) TestDaemonStartWithDefaultTLSHost(c *testing.T) { 1561 s.d.UseDefaultTLSHost = true 1562 defer func() { 1563 s.d.UseDefaultTLSHost = false 1564 }() 1565 s.d.Start(c, 1566 "--tlsverify", 1567 "--tlscacert", "fixtures/https/ca.pem", 1568 "--tlscert", "fixtures/https/server-cert.pem", 1569 "--tlskey", "fixtures/https/server-key.pem") 1570 1571 // The client with --tlsverify should also use default host localhost:2376 1572 c.Setenv("DOCKER_HOST", "") 1573 1574 out := cli.DockerCmd(c, 1575 "--tlsverify", 1576 "--tlscacert", "fixtures/https/ca.pem", 1577 "--tlscert", "fixtures/https/client-cert.pem", 1578 "--tlskey", "fixtures/https/client-key.pem", 1579 "version", 1580 ).Stdout() 1581 if !strings.Contains(out, "Server") { 1582 c.Fatalf("docker version should return information of server side") 1583 } 1584 1585 // ensure when connecting to the server that only a single acceptable CA is requested 1586 contents, err := os.ReadFile("fixtures/https/ca.pem") 1587 assert.NilError(c, err) 1588 rootCert, err := helpers.ParseCertificatePEM(contents) 1589 assert.NilError(c, err) 1590 rootPool := x509.NewCertPool() 1591 rootPool.AddCert(rootCert) 1592 1593 var certRequestInfo *tls.CertificateRequestInfo 1594 conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort), &tls.Config{ 1595 RootCAs: rootPool, 1596 GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) { 1597 certRequestInfo = cri 1598 cert, err := tls.LoadX509KeyPair("fixtures/https/client-cert.pem", "fixtures/https/client-key.pem") 1599 if err != nil { 1600 return nil, err 1601 } 1602 return &cert, nil 1603 }, 1604 }) 1605 assert.NilError(c, err) 1606 conn.Close() 1607 1608 assert.Assert(c, certRequestInfo != nil) 1609 assert.Equal(c, len(certRequestInfo.AcceptableCAs), 1) 1610 assert.DeepEqual(c, certRequestInfo.AcceptableCAs[0], rootCert.RawSubject) 1611 } 1612 1613 func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *testing.T) { 1614 defaultNetworkBridge := "docker0" 1615 deleteInterface(c, defaultNetworkBridge) 1616 1617 bridgeIP := "192.169.1.1" 1618 bridgeRange := bridgeIP + "/30" 1619 1620 s.d.StartWithBusybox(testutil.GetContext(c), c, "--bip", bridgeRange) 1621 defer s.d.Restart(c) 1622 1623 var cont int 1624 for { 1625 contName := fmt.Sprintf("container%d", cont) 1626 _, err := s.d.Cmd("run", "--name", contName, "-d", "busybox", "/bin/sleep", "2") 1627 if err != nil { 1628 // pool exhausted 1629 break 1630 } 1631 ip, err := s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.IPAddress}}'", contName) 1632 assert.Assert(c, err == nil, ip) 1633 1634 assert.Assert(c, ip != bridgeIP) 1635 cont++ 1636 } 1637 } 1638 1639 // Test daemon for no space left on device error 1640 func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *testing.T) { 1641 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, Network) 1642 1643 testDir, err := os.MkdirTemp("", "no-space-left-on-device-test") 1644 assert.NilError(c, err) 1645 defer os.RemoveAll(testDir) 1646 assert.Assert(c, mount.MakeRShared(testDir) == nil) 1647 defer mount.Unmount(testDir) 1648 1649 // create a 3MiB image (with a 2MiB ext4 fs) and mount it as storage root 1650 storageFS := filepath.Join(testDir, "testfs.img") 1651 icmd.RunCommand("dd", "of="+storageFS, "bs=1M", "seek=3", "count=0").Assert(c, icmd.Success) 1652 icmd.RunCommand("mkfs.ext4", "-F", storageFS).Assert(c, icmd.Success) 1653 1654 testMount, err := os.MkdirTemp(testDir, "test-mount") 1655 assert.NilError(c, err) 1656 icmd.RunCommand("mount", "-n", "-t", "ext4", storageFS, testMount).Assert(c, icmd.Success) 1657 defer mount.Unmount(testMount) 1658 1659 driver := "vfs" 1660 if testEnv.UsingSnapshotter() { 1661 driver = "native" 1662 } 1663 1664 s.d.Start(c, 1665 "--data-root", testMount, 1666 "--storage-driver", driver, 1667 1668 // Pass empty containerd socket to force daemon to create a new 1669 // supervised containerd daemon, otherwise the global containerd daemon 1670 // will be used and its data won't be stored in the specified data-root. 1671 "--containerd", "", 1672 ) 1673 defer s.d.Stop(c) 1674 1675 // pull a repository large enough to overfill the mounted filesystem 1676 pullOut, err := s.d.Cmd("pull", "debian:bookworm-slim") 1677 assert.Check(c, err != nil) 1678 assert.Check(c, is.Contains(pullOut, "no space left on device")) 1679 } 1680 1681 // Test daemon restart with container links + auto restart 1682 func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *testing.T) { 1683 s.d.StartWithBusybox(testutil.GetContext(c), c) 1684 1685 var parent1Args []string 1686 var parent2Args []string 1687 wg := sync.WaitGroup{} 1688 maxChildren := 10 1689 chErr := make(chan error, maxChildren) 1690 1691 for i := 0; i < maxChildren; i++ { 1692 wg.Add(1) 1693 name := fmt.Sprintf("test%d", i) 1694 1695 if i < maxChildren/2 { 1696 parent1Args = append(parent1Args, []string{"--link", name}...) 1697 } else { 1698 parent2Args = append(parent2Args, []string{"--link", name}...) 1699 } 1700 1701 go func() { 1702 _, err := s.d.Cmd("run", "-d", "--name", name, "--restart=always", "busybox", "top") 1703 chErr <- err 1704 wg.Done() 1705 }() 1706 } 1707 1708 wg.Wait() 1709 close(chErr) 1710 for err := range chErr { 1711 assert.NilError(c, err) 1712 } 1713 1714 parent1Args = append([]string{"run", "-d"}, parent1Args...) 1715 parent1Args = append(parent1Args, []string{"--name=parent1", "--restart=always", "busybox", "top"}...) 1716 parent2Args = append([]string{"run", "-d"}, parent2Args...) 1717 parent2Args = append(parent2Args, []string{"--name=parent2", "--restart=always", "busybox", "top"}...) 1718 1719 _, err := s.d.Cmd(parent1Args...) 1720 assert.NilError(c, err) 1721 _, err = s.d.Cmd(parent2Args...) 1722 assert.NilError(c, err) 1723 1724 s.d.Stop(c) 1725 // clear the log file -- we don't need any of it but may for the next part 1726 // can ignore the error here, this is just a cleanup 1727 os.Truncate(s.d.LogFileName(), 0) 1728 s.d.Start(c) 1729 1730 for _, num := range []string{"1", "2"} { 1731 out, err := s.d.Cmd("inspect", "-f", "{{ .State.Running }}", "parent"+num) 1732 assert.NilError(c, err) 1733 if strings.TrimSpace(out) != "true" { 1734 log, _ := os.ReadFile(s.d.LogFileName()) 1735 c.Fatalf("parent container is not running\n%s", string(log)) 1736 } 1737 } 1738 } 1739 1740 func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *testing.T) { 1741 testRequires(c, DaemonIsLinux) 1742 1743 cgroupParent := "test" 1744 name := "cgroup-test" 1745 1746 s.d.StartWithBusybox(testutil.GetContext(c), c, "--cgroup-parent", cgroupParent) 1747 defer s.d.Restart(c) 1748 1749 out, err := s.d.Cmd("run", "--name", name, "busybox", "cat", "/proc/self/cgroup") 1750 assert.NilError(c, err) 1751 cgroupPaths := ParseCgroupPaths(out) 1752 assert.Assert(c, len(cgroupPaths) != 0, "unexpected output - %q", out) 1753 out, err = s.d.Cmd("inspect", "-f", "{{.Id}}", name) 1754 assert.NilError(c, err) 1755 id := strings.TrimSpace(out) 1756 expectedCgroup := path.Join(cgroupParent, id) 1757 found := false 1758 for _, p := range cgroupPaths { 1759 if strings.HasSuffix(p, expectedCgroup) { 1760 found = true 1761 break 1762 } 1763 } 1764 assert.Assert(c, found, "Cgroup path for container (%s) doesn't found in cgroups file: %s", expectedCgroup, cgroupPaths) 1765 } 1766 1767 func (s *DockerDaemonSuite) TestDaemonRestartWithLinks(c *testing.T) { 1768 testRequires(c, DaemonIsLinux) // Windows does not support links 1769 s.d.StartWithBusybox(testutil.GetContext(c), c) 1770 1771 out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top") 1772 assert.NilError(c, err, out) 1773 1774 out, err = s.d.Cmd("run", "--name=test2", "--link", "test:abc", "busybox", "sh", "-c", "ping -c 1 -w 1 abc") 1775 assert.NilError(c, err, out) 1776 1777 s.d.Restart(c) 1778 1779 // should fail since test is not running yet 1780 out, err = s.d.Cmd("start", "test2") 1781 assert.ErrorContains(c, err, "", out) 1782 1783 out, err = s.d.Cmd("start", "test") 1784 assert.NilError(c, err, out) 1785 out, err = s.d.Cmd("start", "-a", "test2") 1786 assert.NilError(c, err, out) 1787 assert.Equal(c, strings.Contains(out, "1 packets transmitted, 1 packets received"), true, out) 1788 } 1789 1790 func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *testing.T) { 1791 testRequires(c, DaemonIsLinux) // Windows does not support links 1792 s.d.StartWithBusybox(testutil.GetContext(c), c) 1793 1794 out, err := s.d.Cmd("create", "--name=test", "busybox") 1795 assert.NilError(c, err, out) 1796 1797 out, err = s.d.Cmd("run", "-d", "--name=test2", "busybox", "top") 1798 assert.NilError(c, err, out) 1799 test2ID := strings.TrimSpace(out) 1800 1801 out, err = s.d.Cmd("run", "-d", "--name=test3", "--link", "test2:abc", "busybox", "top") 1802 assert.NilError(c, err) 1803 test3ID := strings.TrimSpace(out) 1804 1805 s.d.Restart(c) 1806 1807 _, err = s.d.Cmd("create", "--name=test", "busybox") 1808 assert.ErrorContains(c, err, "", "expected error trying to create container with duplicate name") 1809 // this one is no longer needed, removing simplifies the remainder of the test 1810 out, err = s.d.Cmd("rm", "-f", "test") 1811 assert.NilError(c, err, out) 1812 1813 out, err = s.d.Cmd("ps", "-a", "--no-trunc") 1814 assert.NilError(c, err, out) 1815 1816 lines := strings.Split(strings.TrimSpace(out), "\n")[1:] 1817 1818 test2validated := false 1819 test3validated := false 1820 for _, line := range lines { 1821 fields := strings.Fields(line) 1822 names := fields[len(fields)-1] 1823 switch fields[0] { 1824 case test2ID: 1825 assert.Equal(c, names, "test2,test3/abc") 1826 test2validated = true 1827 case test3ID: 1828 assert.Equal(c, names, "test3") 1829 test3validated = true 1830 } 1831 } 1832 1833 assert.Assert(c, test2validated) 1834 assert.Assert(c, test3validated) 1835 } 1836 1837 // TestDaemonRestartWithKilledRunningContainer requires live restore of running containers 1838 func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *testing.T) { 1839 testRequires(t, DaemonIsLinux) 1840 s.d.StartWithBusybox(testutil.GetContext(t), t) 1841 1842 cid, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top") 1843 defer s.d.Stop(t) 1844 if err != nil { 1845 t.Fatal(cid, err) 1846 } 1847 cid = strings.TrimSpace(cid) 1848 1849 pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid) 1850 assert.NilError(t, err) 1851 pid = strings.TrimSpace(pid) 1852 1853 // Kill the daemon 1854 if err := s.d.Kill(); err != nil { 1855 t.Fatal(err) 1856 } 1857 1858 // kill the container 1859 icmd.RunCommand(ctrBinary, "--address", containerdSocket, 1860 "--namespace", s.d.ContainersNamespace(), "tasks", "kill", cid).Assert(t, icmd.Success) 1861 1862 // Give time to containerd to process the command if we don't 1863 // the exit event might be received after we do the inspect 1864 result := icmd.RunCommand("kill", "-0", pid) 1865 for result.ExitCode == 0 { 1866 time.Sleep(1 * time.Second) 1867 // FIXME(vdemeester) should we check it doesn't error out ? 1868 result = icmd.RunCommand("kill", "-0", pid) 1869 } 1870 1871 // restart the daemon 1872 s.d.Start(t) 1873 1874 // Check that we've got the correct exit code 1875 out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", cid) 1876 assert.NilError(t, err) 1877 1878 out = strings.TrimSpace(out) 1879 if out != "143" { 1880 t.Fatalf("Expected exit code '%s' got '%s' for container '%s'\n", "143", out, cid) 1881 } 1882 } 1883 1884 // os.Kill should kill daemon ungracefully, leaving behind live containers. 1885 // The live containers should be known to the restarted daemon. Stopping 1886 // them now, should remove the mounts. 1887 func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *testing.T) { 1888 testRequires(c, DaemonIsLinux) 1889 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 1890 1891 out, err := s.d.Cmd("run", "-d", "busybox", "top") 1892 assert.NilError(c, err, "Output: %s", out) 1893 id := strings.TrimSpace(out) 1894 1895 // kill the daemon 1896 assert.Assert(c, s.d.Kill() == nil) 1897 1898 // Check if there are mounts with container id visible from the host. 1899 // If not, those mounts exist in container's own mount ns, and so 1900 // the following check for mounts being cleared is pointless. 1901 skipMountCheck := false 1902 mountOut, err := os.ReadFile("/proc/self/mountinfo") 1903 assert.Assert(c, err == nil, "Output: %s", mountOut) 1904 if !strings.Contains(string(mountOut), id) { 1905 skipMountCheck = true 1906 } 1907 1908 // restart daemon. 1909 s.d.Start(c, "--live-restore") 1910 1911 // container should be running. 1912 out, err = s.d.Cmd("inspect", "--format={{.State.Running}}", id) 1913 assert.NilError(c, err, "Output: %s", out) 1914 out = strings.TrimSpace(out) 1915 if out != "true" { 1916 c.Fatalf("Container %s expected to stay alive after daemon restart", id) 1917 } 1918 1919 // 'docker stop' should work. 1920 out, err = s.d.Cmd("stop", id) 1921 assert.NilError(c, err, "Output: %s", out) 1922 1923 if skipMountCheck { 1924 return 1925 } 1926 // Now, container mounts should be gone. 1927 mountOut, err = os.ReadFile("/proc/self/mountinfo") 1928 assert.Assert(c, err == nil, "Output: %s", mountOut) 1929 comment := fmt.Sprintf("%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, s.d.Root, mountOut) 1930 assert.Equal(c, strings.Contains(string(mountOut), id), false, comment) 1931 } 1932 1933 // TestDaemonRestartWithUnpausedRunningContainer requires live restore of running containers. 1934 func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *testing.T) { 1935 testRequires(t, DaemonIsLinux) 1936 s.d.StartWithBusybox(testutil.GetContext(t), t, "--live-restore") 1937 1938 cid, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top") 1939 defer s.d.Stop(t) 1940 if err != nil { 1941 t.Fatal(cid, err) 1942 } 1943 cid = strings.TrimSpace(cid) 1944 1945 pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid) 1946 assert.NilError(t, err) 1947 1948 // pause the container 1949 if _, err := s.d.Cmd("pause", cid); err != nil { 1950 t.Fatal(cid, err) 1951 } 1952 1953 // Kill the daemon 1954 if err := s.d.Kill(); err != nil { 1955 t.Fatal(err) 1956 } 1957 1958 // resume the container 1959 result := icmd.RunCommand( 1960 ctrBinary, 1961 "--address", containerdSocket, 1962 "--namespace", s.d.ContainersNamespace(), 1963 "tasks", "resume", cid) 1964 result.Assert(t, icmd.Success) 1965 1966 // Give time to containerd to process the command if we don't 1967 // the resume event might be received after we do the inspect 1968 poll.WaitOn(t, pollCheck(t, func(*testing.T) (interface{}, string) { 1969 result := icmd.RunCommand("kill", "-0", strings.TrimSpace(pid)) 1970 return result.ExitCode, "" 1971 }, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout)) 1972 1973 // restart the daemon 1974 s.d.Start(t, "--live-restore") 1975 1976 // Check that we've got the correct status 1977 out, err := s.d.Cmd("inspect", "-f", "{{.State.Status}}", cid) 1978 assert.NilError(t, err) 1979 1980 out = strings.TrimSpace(out) 1981 if out != "running" { 1982 t.Fatalf("Expected exit code '%s' got '%s' for container '%s'\n", "running", out, cid) 1983 } 1984 if _, err := s.d.Cmd("kill", cid); err != nil { 1985 t.Fatal(err) 1986 } 1987 } 1988 1989 // TestRunLinksChanged checks that creating a new container with the same name does not update links 1990 // this ensures that the old, pre gh#16032 functionality continues on 1991 func (s *DockerDaemonSuite) TestRunLinksChanged(c *testing.T) { 1992 testRequires(c, DaemonIsLinux) // Windows does not support links 1993 s.d.StartWithBusybox(testutil.GetContext(c), c) 1994 1995 out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top") 1996 assert.NilError(c, err, out) 1997 1998 out, err = s.d.Cmd("run", "--name=test2", "--link=test:abc", "busybox", "sh", "-c", "ping -c 1 abc") 1999 assert.NilError(c, err, out) 2000 assert.Assert(c, strings.Contains(out, "1 packets transmitted, 1 packets received")) 2001 out, err = s.d.Cmd("rm", "-f", "test") 2002 assert.NilError(c, err, out) 2003 2004 out, err = s.d.Cmd("run", "-d", "--name=test", "busybox", "top") 2005 assert.NilError(c, err, out) 2006 out, err = s.d.Cmd("start", "-a", "test2") 2007 assert.ErrorContains(c, err, "", out) 2008 assert.Assert(c, !strings.Contains(out, "1 packets transmitted, 1 packets received")) 2009 s.d.Restart(c) 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 } 2014 2015 func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *testing.T) { 2016 testRequires(c, DaemonIsLinux) 2017 2018 infoLog := "\x1b[36mINFO\x1b" 2019 2020 b := bytes.NewBuffer(nil) 2021 done := make(chan bool) 2022 2023 p, tty, err := pty.Open() 2024 assert.NilError(c, err) 2025 defer func() { 2026 tty.Close() 2027 p.Close() 2028 }() 2029 2030 go func() { 2031 io.Copy(b, p) 2032 done <- true 2033 }() 2034 2035 // Enable coloring explicitly 2036 s.d.StartWithLogFile(tty, "--raw-logs=false") 2037 s.d.Stop(c) 2038 // Wait for io.Copy() before checking output 2039 <-done 2040 assert.Assert(c, strings.Contains(b.String(), infoLog)) 2041 b.Reset() 2042 2043 // "tty" is already closed in prev s.d.Stop(), 2044 // we have to close the other side "p" and open another pair of 2045 // pty for the next test. 2046 p.Close() 2047 p, tty, err = pty.Open() 2048 assert.NilError(c, err) 2049 2050 go func() { 2051 io.Copy(b, p) 2052 done <- true 2053 }() 2054 2055 // Disable coloring explicitly 2056 s.d.StartWithLogFile(tty, "--raw-logs=true") 2057 s.d.Stop(c) 2058 // Wait for io.Copy() before checking output 2059 <-done 2060 assert.Assert(c, b.String() != "") 2061 assert.Assert(c, !strings.Contains(b.String(), infoLog)) 2062 } 2063 2064 func (s *DockerDaemonSuite) TestDaemonDebugLog(c *testing.T) { 2065 testRequires(c, DaemonIsLinux) 2066 2067 debugLog := "\x1b[37mDEBU\x1b" 2068 2069 p, tty, err := pty.Open() 2070 assert.NilError(c, err) 2071 defer func() { 2072 tty.Close() 2073 p.Close() 2074 }() 2075 2076 b := bytes.NewBuffer(nil) 2077 go io.Copy(b, p) 2078 2079 s.d.StartWithLogFile(tty, "--debug") 2080 s.d.Stop(c) 2081 assert.Assert(c, strings.Contains(b.String(), debugLog)) 2082 } 2083 2084 // Test for #21956 2085 func (s *DockerDaemonSuite) TestDaemonLogOptions(c *testing.T) { 2086 s.d.StartWithBusybox(testutil.GetContext(c), c, "--log-driver=syslog", "--log-opt=syslog-address=udp://127.0.0.1:514") 2087 2088 out, err := s.d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "top") 2089 assert.NilError(c, err, out) 2090 id := strings.TrimSpace(out) 2091 2092 out, err = s.d.Cmd("inspect", "--format='{{.HostConfig.LogConfig}}'", id) 2093 assert.NilError(c, err, out) 2094 assert.Assert(c, strings.Contains(out, "{json-file map[]}")) 2095 } 2096 2097 // Test case for #20936, #22443 2098 func (s *DockerDaemonSuite) TestDaemonMaxConcurrency(c *testing.T) { 2099 skip.If(c, testEnv.UsingSnapshotter, "max concurrency is not implemented (yet) with containerd snapshotters https://github.com/moby/moby/issues/46610") 2100 2101 s.d.Start(c, "--max-concurrent-uploads=6", "--max-concurrent-downloads=8") 2102 2103 expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 6"` 2104 expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 8"` 2105 content, err := s.d.ReadLogFile() 2106 assert.NilError(c, err) 2107 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2108 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2109 } 2110 2111 // Test case for #20936, #22443 2112 func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFile(c *testing.T) { 2113 skip.If(c, testEnv.UsingSnapshotter, "max concurrency is not implemented (yet) with containerd snapshotters https://github.com/moby/moby/issues/46610") 2114 2115 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2116 2117 // daemon config file 2118 const configFilePath = "test-daemon.json" 2119 err := os.WriteFile(configFilePath, []byte(`{ "max-concurrent-downloads" : 8 }`), 0666) 2120 assert.NilError(c, err) 2121 defer os.Remove(configFilePath) 2122 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 2123 2124 expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 5"` 2125 expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 8"` 2126 content, err := s.d.ReadLogFile() 2127 assert.NilError(c, err) 2128 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2129 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2130 err = os.WriteFile(configFilePath, []byte(`{ "max-concurrent-uploads" : 7, "max-concurrent-downloads" : 9 }`), 0666) 2131 assert.NilError(c, err) 2132 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2133 // unix.Kill(s.d.cmd.Process.Pid, unix.SIGHUP) 2134 2135 time.Sleep(3 * time.Second) 2136 2137 expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 7"` 2138 expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 9"` 2139 content, err = s.d.ReadLogFile() 2140 assert.NilError(c, err) 2141 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2142 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2143 } 2144 2145 // Test case for #20936, #22443 2146 func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *testing.T) { 2147 skip.If(c, testEnv.UsingSnapshotter, "max concurrency is not implemented (yet) with containerd snapshotters https://github.com/moby/moby/issues/46610") 2148 2149 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2150 2151 // daemon config file 2152 const configFilePath = "test-daemon.json" 2153 err := os.WriteFile(configFilePath, []byte(`{ "max-concurrent-uploads" : null }`), 0666) 2154 assert.NilError(c, err) 2155 defer os.Remove(configFilePath) 2156 2157 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 2158 2159 expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 5"` 2160 expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 3"` 2161 content, err := s.d.ReadLogFile() 2162 assert.NilError(c, err) 2163 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2164 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2165 err = os.WriteFile(configFilePath, []byte(`{ "max-concurrent-uploads" : 1, "max-concurrent-downloads" : null }`), 0666) 2166 assert.NilError(c, err) 2167 2168 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2169 // unix.Kill(s.d.cmd.Process.Pid, unix.SIGHUP) 2170 2171 time.Sleep(3 * time.Second) 2172 2173 expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 1"` 2174 expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 3"` 2175 content, err = s.d.ReadLogFile() 2176 assert.NilError(c, err) 2177 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2178 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2179 err = os.WriteFile(configFilePath, []byte(`{ "labels":["foo=bar"] }`), 0666) 2180 assert.NilError(c, err) 2181 2182 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2183 2184 time.Sleep(3 * time.Second) 2185 2186 expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 5"` 2187 expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 3"` 2188 content, err = s.d.ReadLogFile() 2189 assert.NilError(c, err) 2190 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads)) 2191 assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads)) 2192 } 2193 2194 func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *testing.T) { 2195 s.d.StartWithBusybox(testutil.GetContext(c), c, "-b=none", "--iptables=false") 2196 2197 result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d), 2198 build.WithDockerfile(` 2199 FROM busybox 2200 RUN cat /etc/hosts`), 2201 build.WithoutCache, 2202 ) 2203 comment := fmt.Sprintf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error) 2204 assert.Assert(c, result.Error == nil, comment) 2205 assert.Equal(c, result.ExitCode, 0, comment) 2206 } 2207 2208 // Test case for #21976 2209 func (s *DockerDaemonSuite) TestDaemonDNSFlagsInHostMode(c *testing.T) { 2210 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2211 2212 s.d.StartWithBusybox(testutil.GetContext(c), c, "--dns", "1.2.3.4", "--dns-search", "example.com", "--dns-opt", "timeout:3") 2213 2214 expectedOutput := "nameserver 1.2.3.4" 2215 out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf") 2216 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 2217 expectedOutput = "search example.com" 2218 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 2219 expectedOutput = "options timeout:3" 2220 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 2221 } 2222 2223 func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *testing.T) { 2224 conf, err := os.CreateTemp("", "config-file-") 2225 assert.NilError(c, err) 2226 configName := conf.Name() 2227 conf.Close() 2228 defer os.Remove(configName) 2229 2230 config := ` 2231 { 2232 "runtimes": { 2233 "oci": { 2234 "path": "runc" 2235 }, 2236 "vm": { 2237 "path": "/usr/local/bin/vm-manager", 2238 "runtimeArgs": [ 2239 "--debug" 2240 ] 2241 } 2242 } 2243 } 2244 ` 2245 os.WriteFile(configName, []byte(config), 0o644) 2246 s.d.StartWithBusybox(testutil.GetContext(c), c, "--config-file", configName) 2247 2248 // Run with default runtime 2249 out, err := s.d.Cmd("run", "--rm", "busybox", "ls") 2250 assert.NilError(c, err, out) 2251 2252 // Run with default runtime explicitly 2253 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2254 assert.NilError(c, err, out) 2255 2256 // Run with oci (same path as default) but keep it around 2257 out, err = s.d.Cmd("run", "--name", "oci-runtime-ls", "--runtime=oci", "busybox", "ls") 2258 assert.NilError(c, err, out) 2259 2260 // Run with "vm" 2261 out, err = s.d.Cmd("run", "--rm", "--runtime=vm", "busybox", "ls") 2262 assert.ErrorContains(c, err, "", out) 2263 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2264 // Reset config to only have the default 2265 config = ` 2266 { 2267 "runtimes": { 2268 } 2269 } 2270 ` 2271 os.WriteFile(configName, []byte(config), 0o644) 2272 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2273 // Give daemon time to reload config 2274 <-time.After(1 * time.Second) 2275 2276 // Run with default runtime 2277 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2278 assert.NilError(c, err, out) 2279 2280 // Run with "oci" 2281 out, err = s.d.Cmd("run", "--rm", "--runtime=oci", "busybox", "ls") 2282 assert.ErrorContains(c, err, "", out) 2283 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2284 // Start previously created container with oci 2285 out, err = s.d.Cmd("start", "oci-runtime-ls") 2286 assert.ErrorContains(c, err, "", out) 2287 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2288 // Check that we can't override the default runtime 2289 config = ` 2290 { 2291 "runtimes": { 2292 "runc": { 2293 "path": "my-runc" 2294 } 2295 } 2296 } 2297 ` 2298 os.WriteFile(configName, []byte(config), 0o644) 2299 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2300 // Give daemon time to reload config 2301 <-time.After(1 * time.Second) 2302 2303 content, err := s.d.ReadLogFile() 2304 assert.NilError(c, err) 2305 assert.Assert(c, is.Contains(string(content), `runtime name 'runc' is reserved`)) 2306 // Check that we can select a default runtime 2307 config = ` 2308 { 2309 "default-runtime": "vm", 2310 "runtimes": { 2311 "oci": { 2312 "path": "runc" 2313 }, 2314 "vm": { 2315 "path": "/usr/local/bin/vm-manager", 2316 "runtimeArgs": [ 2317 "--debug" 2318 ] 2319 } 2320 } 2321 } 2322 ` 2323 os.WriteFile(configName, []byte(config), 0o644) 2324 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2325 // Give daemon time to reload config 2326 <-time.After(1 * time.Second) 2327 2328 out, err = s.d.Cmd("run", "--rm", "busybox", "ls") 2329 assert.ErrorContains(c, err, "", out) 2330 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2331 // Run with default runtime explicitly 2332 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2333 assert.NilError(c, err, out) 2334 } 2335 2336 func (s *DockerDaemonSuite) TestRunWithRuntimeFromCommandLine(c *testing.T) { 2337 s.d.StartWithBusybox(testutil.GetContext(c), c, "--add-runtime", "oci=runc", "--add-runtime", "vm=/usr/local/bin/vm-manager") 2338 2339 // Run with default runtime 2340 out, err := s.d.Cmd("run", "--rm", "busybox", "ls") 2341 assert.NilError(c, err, out) 2342 2343 // Run with default runtime explicitly 2344 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2345 assert.NilError(c, err, out) 2346 2347 // Run with oci (same path as default) but keep it around 2348 out, err = s.d.Cmd("run", "--name", "oci-runtime-ls", "--runtime=oci", "busybox", "ls") 2349 assert.NilError(c, err, out) 2350 2351 // Run with "vm" 2352 out, err = s.d.Cmd("run", "--rm", "--runtime=vm", "busybox", "ls") 2353 assert.ErrorContains(c, err, "", out) 2354 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2355 // Start a daemon without any extra runtimes 2356 s.d.Stop(c) 2357 s.d.StartWithBusybox(testutil.GetContext(c), c) 2358 2359 // Run with default runtime 2360 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2361 assert.NilError(c, err, out) 2362 2363 // Run with "oci" 2364 out, err = s.d.Cmd("run", "--rm", "--runtime=oci", "busybox", "ls") 2365 assert.ErrorContains(c, err, "", out) 2366 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2367 // Start previously created container with oci 2368 out, err = s.d.Cmd("start", "oci-runtime-ls") 2369 assert.ErrorContains(c, err, "", out) 2370 assert.Assert(c, is.Contains(out, "unknown or invalid runtime name: oci")) 2371 // Check that we can't override the default runtime 2372 s.d.Stop(c) 2373 assert.Assert(c, s.d.StartWithError("--add-runtime", "runc=my-runc") != nil) 2374 2375 content, err := s.d.ReadLogFile() 2376 assert.NilError(c, err) 2377 assert.Assert(c, is.Contains(string(content), `runtime name 'runc' is reserved`)) 2378 // Check that we can select a default runtime 2379 s.d.Stop(c) 2380 s.d.StartWithBusybox(testutil.GetContext(c), c, "--default-runtime=vm", "--add-runtime", "oci=runc", "--add-runtime", "vm=/usr/local/bin/vm-manager") 2381 2382 out, err = s.d.Cmd("run", "--rm", "busybox", "ls") 2383 assert.ErrorContains(c, err, "", out) 2384 assert.Assert(c, is.Contains(out, "/usr/local/bin/vm-manager: no such file or directory")) 2385 // Run with default runtime explicitly 2386 out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls") 2387 assert.NilError(c, err, out) 2388 } 2389 2390 func (s *DockerDaemonSuite) TestDaemonRestartWithAutoRemoveContainer(c *testing.T) { 2391 s.d.StartWithBusybox(testutil.GetContext(c), c) 2392 2393 // top1 will exist after daemon restarts 2394 out, err := s.d.Cmd("run", "-d", "--name", "top1", "busybox:latest", "top") 2395 assert.Assert(c, err == nil, "run top1: %v", out) 2396 // top2 will be removed after daemon restarts 2397 out, err = s.d.Cmd("run", "-d", "--rm", "--name", "top2", "busybox:latest", "top") 2398 assert.Assert(c, err == nil, "run top2: %v", out) 2399 2400 out, err = s.d.Cmd("ps") 2401 assert.NilError(c, err) 2402 assert.Assert(c, strings.Contains(out, "top1"), "top1 should be running") 2403 assert.Assert(c, strings.Contains(out, "top2"), "top2 should be running") 2404 // now restart daemon gracefully 2405 s.d.Restart(c) 2406 2407 out, err = s.d.Cmd("ps", "-a") 2408 assert.NilError(c, err, "out: %v", out) 2409 assert.Assert(c, strings.Contains(out, "top1"), "top1 should exist after daemon restarts") 2410 assert.Assert(c, !strings.Contains(out, "top2"), "top2 should be removed after daemon restarts") 2411 } 2412 2413 func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *testing.T) { 2414 s.d.StartWithBusybox(testutil.GetContext(c), c) 2415 2416 containerName := "error-values" 2417 // Make a container with both a non 0 exit code and an error message 2418 // We explicitly disable `--init` for this test, because `--init` is enabled by default 2419 // on "experimental". Enabling `--init` results in a different behavior; because the "init" 2420 // process itself is PID1, the container does not fail on _startup_ (i.e., `docker-init` starting), 2421 // but directly after. The exit code of the container is still 127, but the Error Message is not 2422 // captured, so `.State.Error` is empty. 2423 // See the discussion on https://github.com/docker/docker/pull/30227#issuecomment-274161426, 2424 // and https://github.com/docker/docker/pull/26061#r78054578 for more information. 2425 _, err := s.d.Cmd("run", "--name", containerName, "--init=false", "busybox", "toto") 2426 assert.ErrorContains(c, err, "") 2427 2428 // Check that those values were saved on disk 2429 out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName) 2430 out = strings.TrimSpace(out) 2431 assert.NilError(c, err) 2432 assert.Equal(c, out, "127") 2433 2434 errMsg1, err := s.d.Cmd("inspect", "-f", "{{.State.Error}}", containerName) 2435 errMsg1 = strings.TrimSpace(errMsg1) 2436 assert.NilError(c, err) 2437 assert.Assert(c, strings.Contains(errMsg1, "executable file not found")) 2438 // now restart daemon 2439 s.d.Restart(c) 2440 2441 // Check that those values are still around 2442 out, err = s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName) 2443 out = strings.TrimSpace(out) 2444 assert.NilError(c, err) 2445 assert.Equal(c, out, "127") 2446 2447 out, err = s.d.Cmd("inspect", "-f", "{{.State.Error}}", containerName) 2448 out = strings.TrimSpace(out) 2449 assert.NilError(c, err) 2450 assert.Equal(c, out, errMsg1) 2451 } 2452 2453 func (s *DockerDaemonSuite) TestDaemonWithUserlandProxyPath(c *testing.T) { 2454 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 2455 ctx := context.TODO() 2456 2457 dockerProxyPath, err := exec.LookPath("docker-proxy") 2458 assert.NilError(c, err) 2459 tmpDir, err := os.MkdirTemp("", "test-docker-proxy") 2460 assert.NilError(c, err) 2461 2462 newProxyPath := filepath.Join(tmpDir, "docker-proxy") 2463 cmd := exec.Command("cp", dockerProxyPath, newProxyPath) 2464 assert.NilError(c, cmd.Run()) 2465 2466 // custom one 2467 s.d.StartWithBusybox(testutil.GetContext(c), c, "--userland-proxy-path", newProxyPath) 2468 out, err := s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true") 2469 assert.NilError(c, err, out) 2470 2471 // try with the original one 2472 s.d.Restart(c, "--userland-proxy-path", dockerProxyPath) 2473 out, err = s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true") 2474 assert.NilError(c, err, out) 2475 2476 // not exist 2477 s.d.Stop(c) 2478 err = s.d.StartWithError("--userland-proxy-path", "/does/not/exist") 2479 assert.ErrorContains(c, err, "", "daemon should fail to start") 2480 expected := "invalid userland-proxy-path" 2481 ok, _ := s.d.ScanLogsT(ctx, c, testdaemon.ScanLogsMatchString(expected)) 2482 assert.Assert(c, ok, "logs did not contain: %s", expected) 2483 2484 // not an absolute path 2485 s.d.Stop(c) 2486 err = s.d.StartWithError("--userland-proxy-path", "docker-proxy") 2487 assert.ErrorContains(c, err, "", "daemon should fail to start") 2488 expected = "invalid userland-proxy-path: must be an absolute path: docker-proxy" 2489 ok, _ = s.d.ScanLogsT(ctx, c, testdaemon.ScanLogsMatchString(expected)) 2490 assert.Assert(c, ok, "logs did not contain: %s", expected) 2491 } 2492 2493 // Test case for #22471 2494 func (s *DockerDaemonSuite) TestDaemonShutdownTimeout(c *testing.T) { 2495 testRequires(c, testEnv.IsLocalDaemon) 2496 s.d.StartWithBusybox(testutil.GetContext(c), c, "--shutdown-timeout=3") 2497 2498 _, err := s.d.Cmd("run", "-d", "busybox", "top") 2499 assert.NilError(c, err) 2500 2501 assert.Assert(c, s.d.Signal(unix.SIGINT) == nil) 2502 2503 select { 2504 case <-s.d.Wait: 2505 case <-time.After(5 * time.Second): 2506 } 2507 2508 expectedMessage := `level=debug msg="daemon configured with a 3 seconds minimum shutdown timeout"` 2509 content, err := s.d.ReadLogFile() 2510 assert.NilError(c, err) 2511 assert.Assert(c, strings.Contains(string(content), expectedMessage)) 2512 } 2513 2514 // Test case for #22471 2515 func (s *DockerDaemonSuite) TestDaemonShutdownTimeoutWithConfigFile(c *testing.T) { 2516 testRequires(c, testEnv.IsLocalDaemon) 2517 2518 // daemon config file 2519 const configFilePath = "test-daemon.json" 2520 err := os.WriteFile(configFilePath, []byte(`{ "shutdown-timeout" : 8 }`), 0666) 2521 assert.NilError(c, err) 2522 defer os.Remove(configFilePath) 2523 2524 s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath)) 2525 2526 err = os.WriteFile(configFilePath, []byte(`{ "shutdown-timeout" : 5 }`), 0666) 2527 assert.NilError(c, err) 2528 2529 assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil) 2530 2531 select { 2532 case <-s.d.Wait: 2533 case <-time.After(3 * time.Second): 2534 } 2535 2536 expectedMessage := `level=debug msg="Reset Shutdown Timeout: 5"` 2537 content, err := s.d.ReadLogFile() 2538 assert.NilError(c, err) 2539 assert.Assert(c, strings.Contains(string(content), expectedMessage)) 2540 } 2541 2542 // Test case for 29342 2543 func (s *DockerDaemonSuite) TestExecWithUserAfterLiveRestore(c *testing.T) { 2544 testRequires(c, DaemonIsLinux) 2545 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 2546 2547 out, err := s.d.Cmd("run", "--init", "-d", "--name=top", "busybox", "sh", "-c", "addgroup -S test && adduser -S -G test test -D -s /bin/sh && touch /adduser_end && exec top") 2548 assert.NilError(c, err, "Output: %s", out) 2549 2550 s.d.WaitRun("top") 2551 2552 // Wait for shell command to be completed 2553 _, 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`) 2554 assert.Assert(c, err == nil, "Timeout waiting for shell command to be completed") 2555 2556 out1, err := s.d.Cmd("exec", "-u", "test", "top", "id") 2557 // uid=100(test) gid=101(test) groups=101(test) 2558 assert.Assert(c, err == nil, "Output: %s", out1) 2559 2560 // restart daemon. 2561 s.d.Restart(c, "--live-restore") 2562 2563 out2, err := s.d.Cmd("exec", "-u", "test", "top", "id") 2564 assert.Assert(c, err == nil, "Output: %s", out2) 2565 assert.Equal(c, out2, out1, fmt.Sprintf("Output: before restart '%s', after restart '%s'", out1, out2)) 2566 2567 out, err = s.d.Cmd("stop", "top") 2568 assert.NilError(c, err, "Output: %s", out) 2569 } 2570 2571 func (s *DockerDaemonSuite) TestRemoveContainerAfterLiveRestore(c *testing.T) { 2572 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 2573 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 2574 out, err := s.d.Cmd("run", "-d", "--name=top", "busybox", "top") 2575 assert.NilError(c, err, "Output: %s", out) 2576 2577 s.d.WaitRun("top") 2578 2579 // restart daemon. 2580 s.d.Restart(c, "--live-restore") 2581 2582 out, err = s.d.Cmd("stop", "top") 2583 assert.NilError(c, err, "Output: %s", out) 2584 2585 // test if the rootfs mountpoint still exist 2586 mountpoint, err := s.d.InspectField("top", ".GraphDriver.Data.MergedDir") 2587 assert.NilError(c, err) 2588 f, err := os.Open("/proc/self/mountinfo") 2589 assert.NilError(c, err) 2590 defer f.Close() 2591 sc := bufio.NewScanner(f) 2592 for sc.Scan() { 2593 line := sc.Text() 2594 if strings.Contains(line, mountpoint) { 2595 c.Fatalf("mountinfo should not include the mountpoint of stop container") 2596 } 2597 } 2598 2599 out, err = s.d.Cmd("rm", "top") 2600 assert.NilError(c, err, "Output: %s", out) 2601 } 2602 2603 // #29598 2604 func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *testing.T) { 2605 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 2606 s.d.StartWithBusybox(testutil.GetContext(c), c, "--live-restore") 2607 2608 out, err := s.d.Cmd("run", "-d", "--restart", "always", "busybox", "top") 2609 assert.NilError(c, err, "Output: %s", out) 2610 id := strings.TrimSpace(out) 2611 2612 type state struct { 2613 Running bool 2614 StartedAt time.Time 2615 } 2616 out, err = s.d.Cmd("inspect", "-f", "{{json .State}}", id) 2617 assert.Assert(c, err == nil, "output: %s", out) 2618 2619 var origState state 2620 err = json.Unmarshal([]byte(strings.TrimSpace(out)), &origState) 2621 assert.NilError(c, err) 2622 2623 s.d.Restart(c, "--live-restore") 2624 2625 pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", id) 2626 assert.NilError(c, err) 2627 pidint, err := strconv.Atoi(strings.TrimSpace(pid)) 2628 assert.NilError(c, err) 2629 assert.Assert(c, pidint > 0) 2630 assert.NilError(c, unix.Kill(pidint, unix.SIGKILL)) 2631 2632 ticker := time.NewTicker(50 * time.Millisecond) 2633 timeout := time.After(10 * time.Second) 2634 2635 for range ticker.C { 2636 select { 2637 case <-timeout: 2638 c.Fatal("timeout waiting for container restart") 2639 default: 2640 } 2641 2642 out, err := s.d.Cmd("inspect", "-f", "{{json .State}}", id) 2643 assert.Assert(c, err == nil, "output: %s", out) 2644 2645 var newState state 2646 err = json.Unmarshal([]byte(strings.TrimSpace(out)), &newState) 2647 assert.NilError(c, err) 2648 2649 if !newState.Running { 2650 continue 2651 } 2652 if newState.StartedAt.After(origState.StartedAt) { 2653 break 2654 } 2655 } 2656 2657 out, err = s.d.Cmd("stop", id) 2658 assert.NilError(c, err, "Output: %s", out) 2659 } 2660 2661 func (s *DockerDaemonSuite) TestShmSize(c *testing.T) { 2662 testRequires(c, DaemonIsLinux) 2663 2664 size := 67108864 * 2 2665 pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024)) 2666 2667 s.d.StartWithBusybox(testutil.GetContext(c), c, "--default-shm-size", fmt.Sprintf("%v", size)) 2668 2669 name := "shm1" 2670 out, err := s.d.Cmd("run", "--name", name, "busybox", "mount") 2671 assert.NilError(c, err, "Output: %s", out) 2672 assert.Assert(c, pattern.MatchString(out)) 2673 out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name) 2674 assert.NilError(c, err, "Output: %s", out) 2675 assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size)) 2676 } 2677 2678 func (s *DockerDaemonSuite) TestShmSizeReload(c *testing.T) { 2679 testRequires(c, DaemonIsLinux) 2680 2681 configPath, err := os.MkdirTemp("", "test-daemon-shm-size-reload-config") 2682 assert.Assert(c, err == nil, "could not create temp file for config reload") 2683 defer os.RemoveAll(configPath) // clean up 2684 configFile := filepath.Join(configPath, "config.json") 2685 2686 size := 67108864 * 2 2687 configData := []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024)) 2688 assert.Assert(c, os.WriteFile(configFile, configData, 0o666) == nil, "could not write temp file for config reload") 2689 pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024)) 2690 2691 s.d.StartWithBusybox(testutil.GetContext(c), c, "--config-file", configFile) 2692 2693 name := "shm1" 2694 out, err := s.d.Cmd("run", "--name", name, "busybox", "mount") 2695 assert.NilError(c, err, "Output: %s", out) 2696 assert.Assert(c, pattern.MatchString(out)) 2697 out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name) 2698 assert.NilError(c, err, "Output: %s", out) 2699 assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size)) 2700 2701 size = 67108864 * 3 2702 configData = []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024)) 2703 assert.Assert(c, os.WriteFile(configFile, configData, 0o666) == nil, "could not write temp file for config reload") 2704 pattern = regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024)) 2705 2706 err = s.d.ReloadConfig() 2707 assert.Assert(c, err == nil, "error reloading daemon config") 2708 2709 name = "shm2" 2710 out, err = s.d.Cmd("run", "--name", name, "busybox", "mount") 2711 assert.NilError(c, err, "Output: %s", out) 2712 assert.Assert(c, pattern.MatchString(out)) 2713 out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name) 2714 assert.NilError(c, err, "Output: %s", out) 2715 assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size)) 2716 } 2717 2718 func testDaemonStartIpcMode(c *testing.T, from, mode string, valid bool) { 2719 d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) 2720 c.Logf("Checking IpcMode %s set from %s\n", mode, from) 2721 var serr error 2722 switch from { 2723 case "config": 2724 f, err := os.CreateTemp("", "test-daemon-ipc-config") 2725 assert.NilError(c, err) 2726 defer os.Remove(f.Name()) 2727 config := `{"default-ipc-mode": "` + mode + `"}` 2728 _, err = f.WriteString(config) 2729 assert.NilError(c, f.Close()) 2730 assert.NilError(c, err) 2731 2732 serr = d.StartWithError("--config-file", f.Name()) 2733 case "cli": 2734 serr = d.StartWithError("--default-ipc-mode", mode) 2735 default: 2736 c.Fatalf("testDaemonStartIpcMode: invalid 'from' argument") 2737 } 2738 if serr == nil { 2739 d.Stop(c) 2740 } 2741 2742 if valid { 2743 assert.NilError(c, serr) 2744 } else { 2745 assert.ErrorContains(c, serr, "") 2746 icmd.RunCommand("grep", "-E", "IPC .* is (invalid|not supported)", d.LogFileName()).Assert(c, icmd.Success) 2747 } 2748 } 2749 2750 // TestDaemonStartWithIpcModes checks that daemon starts fine given correct 2751 // arguments for default IPC mode, and bails out with incorrect ones. 2752 // Both CLI option (--default-ipc-mode) and config parameter are tested. 2753 func (s *DockerDaemonSuite) TestDaemonStartWithIpcModes(c *testing.T) { 2754 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 2755 2756 ipcModes := []struct { 2757 mode string 2758 valid bool 2759 }{ 2760 {"private", true}, 2761 {"shareable", true}, 2762 2763 {"host", false}, 2764 {"container:123", false}, 2765 {"nosuchmode", false}, 2766 } 2767 2768 for _, from := range []string{"config", "cli"} { 2769 for _, m := range ipcModes { 2770 testDaemonStartIpcMode(c, from, m.mode, m.valid) 2771 } 2772 } 2773 } 2774 2775 // TestFailedPluginRemove makes sure that a failed plugin remove does not block 2776 // the daemon from starting 2777 func (s *DockerDaemonSuite) TestFailedPluginRemove(c *testing.T) { 2778 testRequires(c, DaemonIsLinux, IsAmd64, testEnv.IsLocalDaemon) 2779 d := daemon.New(c, dockerBinary, dockerdBinary) 2780 d.Start(c) 2781 apiClient := d.NewClientT(c) 2782 2783 ctx, cancel := context.WithTimeout(testutil.GetContext(c), 300*time.Second) 2784 defer cancel() 2785 2786 name := "test-plugin-rm-fail" 2787 out, err := apiClient.PluginInstall(ctx, name, types.PluginInstallOptions{ 2788 Disabled: true, 2789 AcceptAllPermissions: true, 2790 RemoteRef: "cpuguy83/docker-logdriver-test", 2791 }) 2792 assert.NilError(c, err) 2793 defer out.Close() 2794 io.Copy(io.Discard, out) 2795 2796 ctx, cancel = context.WithTimeout(testutil.GetContext(c), 30*time.Second) 2797 defer cancel() 2798 p, _, err := apiClient.PluginInspectWithRaw(ctx, name) 2799 assert.NilError(c, err) 2800 2801 // simulate a bad/partial removal by removing the plugin config. 2802 configPath := filepath.Join(d.Root, "plugins", p.ID, "config.json") 2803 assert.NilError(c, os.Remove(configPath)) 2804 2805 d.Restart(c) 2806 ctx, cancel = context.WithTimeout(testutil.GetContext(c), 30*time.Second) 2807 defer cancel() 2808 _, err = apiClient.Ping(ctx) 2809 assert.NilError(c, err) 2810 2811 _, _, err = apiClient.PluginInspectWithRaw(ctx, name) 2812 // plugin should be gone since the config.json is gone 2813 assert.ErrorContains(c, err, "") 2814 }