github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/container/multi_container_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package container 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "math" 21 "os" 22 "path" 23 "path/filepath" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/cenkalti/backoff" 29 specs "github.com/opencontainers/runtime-spec/specs-go" 30 "golang.org/x/sys/unix" 31 "github.com/SagerNet/gvisor/pkg/cleanup" 32 "github.com/SagerNet/gvisor/pkg/sentry/control" 33 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 34 "github.com/SagerNet/gvisor/pkg/sync" 35 "github.com/SagerNet/gvisor/pkg/test/testutil" 36 "github.com/SagerNet/gvisor/runsc/boot" 37 "github.com/SagerNet/gvisor/runsc/config" 38 "github.com/SagerNet/gvisor/runsc/specutils" 39 ) 40 41 func createSpecs(cmds ...[]string) ([]*specs.Spec, []string) { 42 var specs []*specs.Spec 43 var ids []string 44 rootID := testutil.RandomContainerID() 45 46 for i, cmd := range cmds { 47 spec := testutil.NewSpecWithArgs(cmd...) 48 if i == 0 { 49 spec.Annotations = map[string]string{ 50 specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeSandbox, 51 } 52 ids = append(ids, rootID) 53 } else { 54 spec.Annotations = map[string]string{ 55 specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeContainer, 56 specutils.ContainerdSandboxIDAnnotation: rootID, 57 } 58 ids = append(ids, testutil.RandomContainerID()) 59 } 60 specs = append(specs, spec) 61 } 62 return specs, ids 63 } 64 65 func startContainers(conf *config.Config, specs []*specs.Spec, ids []string) ([]*Container, func(), error) { 66 if len(conf.RootDir) == 0 { 67 panic("conf.RootDir not set. Call testutil.SetupRootDir() to set.") 68 } 69 70 cu := cleanup.Cleanup{} 71 defer cu.Clean() 72 73 var containers []*Container 74 for i, spec := range specs { 75 bundleDir, cleanup, err := testutil.SetupBundleDir(spec) 76 if err != nil { 77 return nil, nil, fmt.Errorf("error setting up container: %v", err) 78 } 79 cu.Add(cleanup) 80 81 args := Args{ 82 ID: ids[i], 83 Spec: spec, 84 BundleDir: bundleDir, 85 } 86 cont, err := New(conf, args) 87 if err != nil { 88 return nil, nil, fmt.Errorf("error creating container: %v", err) 89 } 90 cu.Add(func() { cont.Destroy() }) 91 containers = append(containers, cont) 92 93 if err := cont.Start(conf); err != nil { 94 return nil, nil, fmt.Errorf("error starting container: %v", err) 95 } 96 } 97 98 return containers, cu.Release(), nil 99 } 100 101 type execDesc struct { 102 c *Container 103 cmd []string 104 want int 105 name string 106 } 107 108 func execMany(t *testing.T, execs []execDesc) { 109 for _, exec := range execs { 110 t.Run(exec.name, func(t *testing.T) { 111 args := &control.ExecArgs{Argv: exec.cmd} 112 if ws, err := exec.c.executeSync(args); err != nil { 113 t.Errorf("error executing %+v: %v", args, err) 114 } else if ws.ExitStatus() != exec.want { 115 t.Errorf("%q: exec %q got exit status: %d, want: %d", exec.name, exec.cmd, ws.ExitStatus(), exec.want) 116 } 117 }) 118 } 119 } 120 121 func createSharedMount(mount specs.Mount, name string, pod ...*specs.Spec) { 122 for _, spec := range pod { 123 spec.Annotations[boot.MountPrefix+name+".source"] = mount.Source 124 spec.Annotations[boot.MountPrefix+name+".type"] = mount.Type 125 spec.Annotations[boot.MountPrefix+name+".share"] = "pod" 126 if len(mount.Options) > 0 { 127 spec.Annotations[boot.MountPrefix+name+".options"] = strings.Join(mount.Options, ",") 128 } 129 } 130 } 131 132 // TestMultiContainerSanity checks that it is possible to run 2 dead-simple 133 // containers in the same sandbox. 134 func TestMultiContainerSanity(t *testing.T) { 135 for name, conf := range configs(t, all...) { 136 t.Run(name, func(t *testing.T) { 137 rootDir, cleanup, err := testutil.SetupRootDir() 138 if err != nil { 139 t.Fatalf("error creating root dir: %v", err) 140 } 141 defer cleanup() 142 conf.RootDir = rootDir 143 144 // Setup the containers. 145 sleep := []string{"sleep", "100"} 146 specs, ids := createSpecs(sleep, sleep) 147 containers, cleanup, err := startContainers(conf, specs, ids) 148 if err != nil { 149 t.Fatalf("error starting containers: %v", err) 150 } 151 defer cleanup() 152 153 // Check via ps that multiple processes are running. 154 expectedPL := []*control.Process{ 155 newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(), 156 } 157 if err := waitForProcessList(containers[0], expectedPL); err != nil { 158 t.Errorf("failed to wait for sleep to start: %v", err) 159 } 160 expectedPL = []*control.Process{ 161 newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(), 162 } 163 if err := waitForProcessList(containers[1], expectedPL); err != nil { 164 t.Errorf("failed to wait for sleep to start: %v", err) 165 } 166 }) 167 } 168 } 169 170 // TestMultiPIDNS checks that it is possible to run 2 dead-simple containers in 171 // the same sandbox with different pidns. 172 func TestMultiPIDNS(t *testing.T) { 173 for name, conf := range configs(t, all...) { 174 t.Run(name, func(t *testing.T) { 175 rootDir, cleanup, err := testutil.SetupRootDir() 176 if err != nil { 177 t.Fatalf("error creating root dir: %v", err) 178 } 179 defer cleanup() 180 conf.RootDir = rootDir 181 182 // Setup the containers. 183 sleep := []string{"sleep", "100"} 184 testSpecs, ids := createSpecs(sleep, sleep) 185 testSpecs[1].Linux = &specs.Linux{ 186 Namespaces: []specs.LinuxNamespace{ 187 { 188 Type: "pid", 189 }, 190 }, 191 } 192 193 containers, cleanup, err := startContainers(conf, testSpecs, ids) 194 if err != nil { 195 t.Fatalf("error starting containers: %v", err) 196 } 197 defer cleanup() 198 199 // Check via ps that multiple processes are running. 200 expectedPL := []*control.Process{ 201 newProcessBuilder().PID(1).Cmd("sleep").Process(), 202 } 203 if err := waitForProcessList(containers[0], expectedPL); err != nil { 204 t.Errorf("failed to wait for sleep to start: %v", err) 205 } 206 expectedPL = []*control.Process{ 207 newProcessBuilder().PID(2).Cmd("sleep").Process(), 208 } 209 if err := waitForProcessList(containers[1], expectedPL); err != nil { 210 t.Errorf("failed to wait for sleep to start: %v", err) 211 } 212 213 // Root container runs in the root PID namespace and can see all 214 // processes. 215 expectedPL = []*control.Process{ 216 newProcessBuilder().PID(1).Cmd("sleep").Process(), 217 newProcessBuilder().PID(2).Cmd("sleep").Process(), 218 newProcessBuilder().Cmd("ps").Process(), 219 } 220 got, err := execPS(containers[0]) 221 if err != nil { 222 t.Fatal(err) 223 } 224 if !procListsEqual(got, expectedPL) { 225 t.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(expectedPL)) 226 } 227 228 expectedPL = []*control.Process{ 229 newProcessBuilder().PID(1).Cmd("sleep").Process(), 230 newProcessBuilder().Cmd("ps").Process(), 231 } 232 got, err = execPS(containers[1]) 233 if err != nil { 234 t.Fatal(err) 235 } 236 if !procListsEqual(got, expectedPL) { 237 t.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(expectedPL)) 238 } 239 }) 240 } 241 } 242 243 // TestMultiPIDNSPath checks the pidns path. 244 func TestMultiPIDNSPath(t *testing.T) { 245 for name, conf := range configs(t, all...) { 246 t.Run(name, func(t *testing.T) { 247 rootDir, cleanup, err := testutil.SetupRootDir() 248 if err != nil { 249 t.Fatalf("error creating root dir: %v", err) 250 } 251 defer cleanup() 252 conf.RootDir = rootDir 253 254 // Setup the containers. 255 sleep := []string{"sleep", "100"} 256 testSpecs, ids := createSpecs(sleep, sleep, sleep) 257 testSpecs[0].Linux = &specs.Linux{ 258 Namespaces: []specs.LinuxNamespace{ 259 { 260 Type: "pid", 261 Path: "/proc/1/ns/pid", 262 }, 263 }, 264 } 265 testSpecs[1].Linux = &specs.Linux{ 266 Namespaces: []specs.LinuxNamespace{ 267 { 268 Type: "pid", 269 Path: "/proc/1/ns/pid", 270 }, 271 }, 272 } 273 testSpecs[2].Linux = &specs.Linux{ 274 Namespaces: []specs.LinuxNamespace{ 275 { 276 Type: "pid", 277 Path: "/proc/2/ns/pid", 278 }, 279 }, 280 } 281 282 containers, cleanup, err := startContainers(conf, testSpecs, ids) 283 if err != nil { 284 t.Fatalf("error starting containers: %v", err) 285 } 286 defer cleanup() 287 288 // Check via ps that multiple processes are running. 289 expectedPL := []*control.Process{ 290 newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(), 291 } 292 if err := waitForProcessList(containers[0], expectedPL); err != nil { 293 t.Errorf("failed to wait for sleep to start: %v", err) 294 } 295 expectedPL = []*control.Process{ 296 newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(), 297 } 298 if err := waitForProcessList(containers[1], expectedPL); err != nil { 299 t.Errorf("failed to wait for sleep to start: %v", err) 300 } 301 expectedPL = []*control.Process{ 302 newProcessBuilder().PID(3).PPID(0).Cmd("sleep").Process(), 303 } 304 if err := waitForProcessList(containers[2], expectedPL); err != nil { 305 t.Errorf("failed to wait for sleep to start: %v", err) 306 } 307 308 // Root container runs in the root PID namespace and can see all 309 // processes. 310 expectedPL = []*control.Process{ 311 newProcessBuilder().PID(1).Cmd("sleep").Process(), 312 newProcessBuilder().PID(2).Cmd("sleep").Process(), 313 newProcessBuilder().PID(3).Cmd("sleep").Process(), 314 newProcessBuilder().Cmd("ps").Process(), 315 } 316 got, err := execPS(containers[0]) 317 if err != nil { 318 t.Fatal(err) 319 } 320 if !procListsEqual(got, expectedPL) { 321 t.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(expectedPL)) 322 } 323 324 // Container 1 runs in the same PID namespace as the root container. 325 expectedPL = []*control.Process{ 326 newProcessBuilder().PID(1).Cmd("sleep").Process(), 327 newProcessBuilder().PID(2).Cmd("sleep").Process(), 328 newProcessBuilder().PID(3).Cmd("sleep").Process(), 329 newProcessBuilder().Cmd("ps").Process(), 330 } 331 got, err = execPS(containers[1]) 332 if err != nil { 333 t.Fatal(err) 334 } 335 if !procListsEqual(got, expectedPL) { 336 t.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(expectedPL)) 337 } 338 339 // Container 2 runs on its own namespace. 340 expectedPL = []*control.Process{ 341 newProcessBuilder().PID(1).Cmd("sleep").Process(), 342 newProcessBuilder().Cmd("ps").Process(), 343 } 344 got, err = execPS(containers[2]) 345 if err != nil { 346 t.Fatal(err) 347 } 348 if !procListsEqual(got, expectedPL) { 349 t.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(expectedPL)) 350 } 351 }) 352 } 353 } 354 355 // TestMultiPIDNSKill kills processes using PID when containers are using 356 // different PID namespaces to ensure PID is taken from the root namespace. 357 func TestMultiPIDNSKill(t *testing.T) { 358 app, err := testutil.FindFile("test/cmd/test_app/test_app") 359 if err != nil { 360 t.Fatal("error finding test_app:", err) 361 } 362 363 for name, conf := range configs(t, all...) { 364 t.Run(name, func(t *testing.T) { 365 rootDir, cleanup, err := testutil.SetupRootDir() 366 if err != nil { 367 t.Fatalf("error creating root dir: %v", err) 368 } 369 defer cleanup() 370 conf.RootDir = rootDir 371 372 // Setup the containers. 373 cmd := []string{app, "task-tree", "--depth=1", "--width=2", "--pause=true"} 374 const processes = 3 375 testSpecs, ids := createSpecs(cmd, cmd) 376 377 testSpecs[1].Linux = &specs.Linux{ 378 Namespaces: []specs.LinuxNamespace{ 379 { 380 Type: "pid", 381 }, 382 }, 383 } 384 385 containers, cleanup, err := startContainers(conf, testSpecs, ids) 386 if err != nil { 387 t.Fatalf("error starting containers: %v", err) 388 } 389 defer cleanup() 390 391 // Wait until all processes are created. 392 for _, c := range containers { 393 if err := waitForProcessCount(c, processes); err != nil { 394 t.Fatalf("error waitting for processes: %v", err) 395 } 396 } 397 398 for i, c := range containers { 399 // First, kill a process that belongs to the container. 400 procs, err := c.Processes() 401 if err != nil { 402 t.Fatalf("container.Processes(): %v", err) 403 } 404 t.Logf("Container %q procs: %s", c.ID, procListToString(procs)) 405 pidToKill := procs[processes-1].PID 406 t.Logf("PID to kill: %d", pidToKill) 407 if err := c.SignalProcess(unix.SIGKILL, int32(pidToKill)); err != nil { 408 t.Errorf("container.SignalProcess: %v", err) 409 } 410 // Wait for the process to get killed. 411 if err := waitForProcessCount(c, processes-1); err != nil { 412 t.Fatalf("error waitting for processes: %v", err) 413 } 414 procs, err = c.Processes() 415 if err != nil { 416 t.Fatalf("container.Processes(): %v", err) 417 } 418 t.Logf("Container %q procs after kill: %s", c.ID, procListToString(procs)) 419 for _, proc := range procs { 420 if proc.PID == pidToKill { 421 t.Errorf("process %d not killed: %+v", pidToKill, proc) 422 } 423 } 424 425 // Next, attempt to kill a process from another container and check that 426 // it fails. 427 other := containers[(i+1)%len(containers)] 428 procs, err = other.Processes() 429 if err != nil { 430 t.Fatalf("container.Processes(): %v", err) 431 } 432 t.Logf("Other container %q procs: %s", other.ID, procListToString(procs)) 433 434 pidToKill = procs[len(procs)-1].PID 435 t.Logf("PID that should not be killed: %d", pidToKill) 436 err = c.SignalProcess(unix.SIGKILL, int32(pidToKill)) 437 if err == nil { 438 t.Fatalf("killing another container's process should fail") 439 } 440 if !strings.Contains(err.Error(), "belongs to a different container") { 441 t.Errorf("wrong error message from killing another container's: %v", err) 442 } 443 } 444 }) 445 } 446 } 447 448 func TestMultiContainerWait(t *testing.T) { 449 rootDir, cleanup, err := testutil.SetupRootDir() 450 if err != nil { 451 t.Fatalf("error creating root dir: %v", err) 452 } 453 defer cleanup() 454 455 conf := testutil.TestConfig(t) 456 conf.RootDir = rootDir 457 458 // The first container should run the entire duration of the test. 459 cmd1 := []string{"sleep", "100"} 460 // We'll wait on the second container, which is much shorter lived. 461 cmd2 := []string{"sleep", "1"} 462 specs, ids := createSpecs(cmd1, cmd2) 463 464 containers, cleanup, err := startContainers(conf, specs, ids) 465 if err != nil { 466 t.Fatalf("error starting containers: %v", err) 467 } 468 defer cleanup() 469 470 // Check that we can wait for the sub-container. 471 c := containers[1] 472 if ws, err := c.Wait(); err != nil { 473 t.Errorf("failed to wait for process %s: %v", c.Spec.Process.Args, err) 474 } else if es := ws.ExitStatus(); es != 0 { 475 t.Errorf("process %s exited with non-zero status %d", c.Spec.Process.Args, es) 476 } 477 if _, err := c.Wait(); err != nil { 478 t.Errorf("wait for stopped container %s shouldn't fail: %v", c.Spec.Process.Args, err) 479 } 480 481 // After Wait returns, ensure that the root container is running and 482 // the child has finished. 483 expectedPL := []*control.Process{ 484 newProcessBuilder().Cmd("sleep").PID(1).Process(), 485 } 486 if err := waitForProcessList(containers[0], expectedPL); err != nil { 487 t.Errorf("failed to wait for %q to start: %v", strings.Join(containers[0].Spec.Process.Args, " "), err) 488 } 489 } 490 491 // TestExecWait ensures what we can wait on containers and individual processes 492 // in the sandbox that have already exited. 493 func TestExecWait(t *testing.T) { 494 rootDir, cleanup, err := testutil.SetupRootDir() 495 if err != nil { 496 t.Fatalf("error creating root dir: %v", err) 497 } 498 defer cleanup() 499 500 conf := testutil.TestConfig(t) 501 conf.RootDir = rootDir 502 503 // The first container should run the entire duration of the test. 504 cmd1 := []string{"sleep", "100"} 505 // We'll wait on the second container, which is much shorter lived. 506 cmd2 := []string{"sleep", "1"} 507 specs, ids := createSpecs(cmd1, cmd2) 508 containers, cleanup, err := startContainers(conf, specs, ids) 509 if err != nil { 510 t.Fatalf("error starting containers: %v", err) 511 } 512 defer cleanup() 513 514 // Check via ps that process is running. 515 expectedPL := []*control.Process{ 516 newProcessBuilder().Cmd("sleep").Process(), 517 } 518 if err := waitForProcessList(containers[1], expectedPL); err != nil { 519 t.Fatalf("failed to wait for sleep to start: %v", err) 520 } 521 522 // Wait for the second container to finish. 523 if err := waitForProcessCount(containers[1], 0); err != nil { 524 t.Fatalf("failed to wait for second container to stop: %v", err) 525 } 526 527 // Get the second container exit status. 528 if ws, err := containers[1].Wait(); err != nil { 529 t.Fatalf("failed to wait for process %s: %v", containers[1].Spec.Process.Args, err) 530 } else if es := ws.ExitStatus(); es != 0 { 531 t.Fatalf("process %s exited with non-zero status %d", containers[1].Spec.Process.Args, es) 532 } 533 if _, err := containers[1].Wait(); err != nil { 534 t.Fatalf("wait for stopped container %s shouldn't fail: %v", containers[1].Spec.Process.Args, err) 535 } 536 537 // Execute another process in the first container. 538 args := &control.ExecArgs{ 539 Filename: "/bin/sleep", 540 Argv: []string{"/bin/sleep", "1"}, 541 WorkingDirectory: "/", 542 KUID: 0, 543 } 544 pid, err := containers[0].Execute(args) 545 if err != nil { 546 t.Fatalf("error executing: %v", err) 547 } 548 549 // Wait for the exec'd process to exit. 550 expectedPL = []*control.Process{ 551 newProcessBuilder().PID(1).Cmd("sleep").Process(), 552 } 553 if err := waitForProcessList(containers[0], expectedPL); err != nil { 554 t.Fatalf("failed to wait for second container to stop: %v", err) 555 } 556 557 // Get the exit status from the exec'd process. 558 if ws, err := containers[0].WaitPID(pid); err != nil { 559 t.Fatalf("failed to wait for process %+v with pid %d: %v", args, pid, err) 560 } else if es := ws.ExitStatus(); es != 0 { 561 t.Fatalf("process %+v exited with non-zero status %d", args, es) 562 } 563 if _, err := containers[0].WaitPID(pid); err == nil { 564 t.Fatalf("wait for stopped process %+v should fail", args) 565 } 566 } 567 568 // TestMultiContainerMount tests that bind mounts can be used with multiple 569 // containers. 570 func TestMultiContainerMount(t *testing.T) { 571 cmd1 := []string{"sleep", "100"} 572 573 // 'src != dst' ensures that 'dst' doesn't exist in the host and must be 574 // properly mapped inside the container to work. 575 src, err := ioutil.TempDir(testutil.TmpDir(), "container") 576 if err != nil { 577 t.Fatal("ioutil.TempDir failed:", err) 578 } 579 dst := src + ".dst" 580 cmd2 := []string{"touch", filepath.Join(dst, "file")} 581 582 sps, ids := createSpecs(cmd1, cmd2) 583 sps[1].Mounts = append(sps[1].Mounts, specs.Mount{ 584 Source: src, 585 Destination: dst, 586 Type: "bind", 587 }) 588 589 // Setup the containers. 590 rootDir, cleanup, err := testutil.SetupRootDir() 591 if err != nil { 592 t.Fatalf("error creating root dir: %v", err) 593 } 594 defer cleanup() 595 596 conf := testutil.TestConfig(t) 597 conf.RootDir = rootDir 598 599 containers, cleanup, err := startContainers(conf, sps, ids) 600 if err != nil { 601 t.Fatalf("error starting containers: %v", err) 602 } 603 defer cleanup() 604 605 ws, err := containers[1].Wait() 606 if err != nil { 607 t.Error("error waiting on container:", err) 608 } 609 if !ws.Exited() || ws.ExitStatus() != 0 { 610 t.Error("container failed, waitStatus:", ws) 611 } 612 } 613 614 // TestMultiContainerSignal checks that it is possible to signal individual 615 // containers without killing the entire sandbox. 616 func TestMultiContainerSignal(t *testing.T) { 617 for name, conf := range configs(t, all...) { 618 t.Run(name, func(t *testing.T) { 619 rootDir, cleanup, err := testutil.SetupRootDir() 620 if err != nil { 621 t.Fatalf("error creating root dir: %v", err) 622 } 623 defer cleanup() 624 conf.RootDir = rootDir 625 626 // Setup the containers. 627 sleep := []string{"sleep", "100"} 628 specs, ids := createSpecs(sleep, sleep) 629 containers, cleanup, err := startContainers(conf, specs, ids) 630 if err != nil { 631 t.Fatalf("error starting containers: %v", err) 632 } 633 defer cleanup() 634 635 // Check via ps that container 1 process is running. 636 expectedPL := []*control.Process{ 637 newProcessBuilder().Cmd("sleep").Process(), 638 } 639 if err := waitForProcessList(containers[1], expectedPL); err != nil { 640 t.Errorf("failed to wait for sleep to start: %v", err) 641 } 642 643 // Kill process 2. 644 if err := containers[1].SignalContainer(unix.SIGKILL, false); err != nil { 645 t.Errorf("failed to kill process 2: %v", err) 646 } 647 648 // Make sure process 1 is still running. 649 expectedPL = []*control.Process{ 650 newProcessBuilder().PID(1).Cmd("sleep").Process(), 651 } 652 if err := waitForProcessList(containers[0], expectedPL); err != nil { 653 t.Errorf("failed to wait for sleep to start: %v", err) 654 } 655 656 // goferPid is reset when container is destroyed. 657 goferPid := containers[1].GoferPid 658 659 // Destroy container and ensure container's gofer process has exited. 660 if err := containers[1].Destroy(); err != nil { 661 t.Errorf("failed to destroy container: %v", err) 662 } 663 _, _, err = specutils.RetryEintr(func() (uintptr, uintptr, error) { 664 cpid, err := unix.Wait4(goferPid, nil, 0, nil) 665 return uintptr(cpid), 0, err 666 }) 667 if err != unix.ECHILD { 668 t.Errorf("error waiting for gofer to exit: %v", err) 669 } 670 // Make sure process 1 is still running. 671 if err := waitForProcessList(containers[0], expectedPL); err != nil { 672 t.Errorf("failed to wait for sleep to start: %v", err) 673 } 674 675 // Now that process 2 is gone, ensure we get an error trying to 676 // signal it again. 677 if err := containers[1].SignalContainer(unix.SIGKILL, false); err == nil { 678 t.Errorf("container %q shouldn't exist, but we were able to signal it", containers[1].ID) 679 } 680 681 // Kill process 1. 682 if err := containers[0].SignalContainer(unix.SIGKILL, false); err != nil { 683 t.Errorf("failed to kill process 1: %v", err) 684 } 685 686 // Ensure that container's gofer and sandbox process are no more. 687 err = blockUntilWaitable(containers[0].GoferPid) 688 if err != nil && err != unix.ECHILD { 689 t.Errorf("error waiting for gofer to exit: %v", err) 690 } 691 692 err = blockUntilWaitable(containers[0].Sandbox.Pid) 693 if err != nil && err != unix.ECHILD { 694 t.Errorf("error waiting for sandbox to exit: %v", err) 695 } 696 697 // The sentry should be gone, so signaling should yield an error. 698 if err := containers[0].SignalContainer(unix.SIGKILL, false); err == nil { 699 t.Errorf("sandbox %q shouldn't exist, but we were able to signal it", containers[0].Sandbox.ID) 700 } 701 702 if err := containers[0].Destroy(); err != nil { 703 t.Errorf("failed to destroy container: %v", err) 704 } 705 }) 706 } 707 } 708 709 // TestMultiContainerDestroy checks that container are properly cleaned-up when 710 // they are destroyed. 711 func TestMultiContainerDestroy(t *testing.T) { 712 app, err := testutil.FindFile("test/cmd/test_app/test_app") 713 if err != nil { 714 t.Fatal("error finding test_app:", err) 715 } 716 717 for name, conf := range configs(t, all...) { 718 t.Run(name, func(t *testing.T) { 719 rootDir, cleanup, err := testutil.SetupRootDir() 720 if err != nil { 721 t.Fatalf("error creating root dir: %v", err) 722 } 723 defer cleanup() 724 conf.RootDir = rootDir 725 726 // First container will remain intact while the second container is killed. 727 podSpecs, ids := createSpecs( 728 []string{"sleep", "100"}, 729 []string{app, "fork-bomb"}) 730 731 // Run the fork bomb in a PID namespace to prevent processes to be 732 // re-parented to PID=1 in the root container. 733 podSpecs[1].Linux = &specs.Linux{ 734 Namespaces: []specs.LinuxNamespace{{Type: "pid"}}, 735 } 736 containers, cleanup, err := startContainers(conf, podSpecs, ids) 737 if err != nil { 738 t.Fatalf("error starting containers: %v", err) 739 } 740 defer cleanup() 741 742 // Exec more processes to ensure signal all works for exec'd processes too. 743 args := &control.ExecArgs{ 744 Filename: app, 745 Argv: []string{app, "fork-bomb"}, 746 } 747 if _, err := containers[1].Execute(args); err != nil { 748 t.Fatalf("error exec'ing: %v", err) 749 } 750 751 // Let it brew... 752 time.Sleep(500 * time.Millisecond) 753 754 if err := containers[1].Destroy(); err != nil { 755 t.Fatalf("error destroying container: %v", err) 756 } 757 758 // Check that destroy killed all processes belonging to the container and 759 // waited for them to exit before returning. 760 pss, err := containers[0].Sandbox.Processes("") 761 if err != nil { 762 t.Fatalf("error getting process data from sandbox: %v", err) 763 } 764 expectedPL := []*control.Process{ 765 newProcessBuilder().PID(1).Cmd("sleep").Process(), 766 } 767 if !procListsEqual(pss, expectedPL) { 768 t.Errorf("container got process list: %s, want: %s: error: %v", 769 procListToString(pss), procListToString(expectedPL), err) 770 } 771 772 // Check that cont.Destroy is safe to call multiple times. 773 if err := containers[1].Destroy(); err != nil { 774 t.Errorf("error destroying container: %v", err) 775 } 776 }) 777 } 778 } 779 780 func TestMultiContainerProcesses(t *testing.T) { 781 rootDir, cleanup, err := testutil.SetupRootDir() 782 if err != nil { 783 t.Fatalf("error creating root dir: %v", err) 784 } 785 defer cleanup() 786 787 conf := testutil.TestConfig(t) 788 conf.RootDir = rootDir 789 790 // Note: use curly braces to keep 'sh' process around. Otherwise, shell 791 // will just execve into 'sleep' and both containers will look the 792 // same. 793 specs, ids := createSpecs( 794 []string{"sleep", "100"}, 795 []string{"sh", "-c", "{ sleep 100; }"}) 796 containers, cleanup, err := startContainers(conf, specs, ids) 797 if err != nil { 798 t.Fatalf("error starting containers: %v", err) 799 } 800 defer cleanup() 801 802 // Check root's container process list doesn't include other containers. 803 expectedPL0 := []*control.Process{ 804 newProcessBuilder().PID(1).Cmd("sleep").Process(), 805 } 806 if err := waitForProcessList(containers[0], expectedPL0); err != nil { 807 t.Errorf("failed to wait for process to start: %v", err) 808 } 809 810 // Same for the other container. 811 expectedPL1 := []*control.Process{ 812 newProcessBuilder().PID(2).Cmd("sh").Process(), 813 newProcessBuilder().PID(3).PPID(2).Cmd("sleep").Process(), 814 } 815 if err := waitForProcessList(containers[1], expectedPL1); err != nil { 816 t.Errorf("failed to wait for process to start: %v", err) 817 } 818 819 // Now exec into the second container and verify it shows up in the container. 820 args := &control.ExecArgs{ 821 Filename: "/bin/sleep", 822 Argv: []string{"/bin/sleep", "100"}, 823 } 824 if _, err := containers[1].Execute(args); err != nil { 825 t.Fatalf("error exec'ing: %v", err) 826 } 827 expectedPL1 = append(expectedPL1, newProcessBuilder().PID(4).Cmd("sleep").Process()) 828 if err := waitForProcessList(containers[1], expectedPL1); err != nil { 829 t.Errorf("failed to wait for process to start: %v", err) 830 } 831 // Root container should remain unchanged. 832 if err := waitForProcessList(containers[0], expectedPL0); err != nil { 833 t.Errorf("failed to wait for process to start: %v", err) 834 } 835 } 836 837 // TestMultiContainerKillAll checks that all process that belong to a container 838 // are killed when SIGKILL is sent to *all* processes in that container. 839 func TestMultiContainerKillAll(t *testing.T) { 840 rootDir, cleanup, err := testutil.SetupRootDir() 841 if err != nil { 842 t.Fatalf("error creating root dir: %v", err) 843 } 844 defer cleanup() 845 846 conf := testutil.TestConfig(t) 847 conf.RootDir = rootDir 848 849 for _, tc := range []struct { 850 killContainer bool 851 }{ 852 {killContainer: true}, 853 {killContainer: false}, 854 } { 855 app, err := testutil.FindFile("test/cmd/test_app/test_app") 856 if err != nil { 857 t.Fatal("error finding test_app:", err) 858 } 859 860 // First container will remain intact while the second container is killed. 861 specs, ids := createSpecs( 862 []string{app, "task-tree", "--depth=2", "--width=2"}, 863 []string{app, "task-tree", "--depth=4", "--width=2"}) 864 containers, cleanup, err := startContainers(conf, specs, ids) 865 if err != nil { 866 t.Fatalf("error starting containers: %v", err) 867 } 868 defer cleanup() 869 870 // Wait until all processes are created. 871 rootProcCount := int(math.Pow(2, 3) - 1) 872 if err := waitForProcessCount(containers[0], rootProcCount); err != nil { 873 t.Fatalf("error waitting for processes: %v", err) 874 } 875 procCount := int(math.Pow(2, 5) - 1) 876 if err := waitForProcessCount(containers[1], procCount); err != nil { 877 t.Fatalf("error waiting for processes: %v", err) 878 } 879 880 // Exec more processes to ensure signal works for exec'd processes too. 881 args := &control.ExecArgs{ 882 Filename: app, 883 Argv: []string{app, "task-tree", "--depth=2", "--width=2"}, 884 } 885 if _, err := containers[1].Execute(args); err != nil { 886 t.Fatalf("error exec'ing: %v", err) 887 } 888 // Wait for these new processes to start. 889 procCount += int(math.Pow(2, 3) - 1) 890 if err := waitForProcessCount(containers[1], procCount); err != nil { 891 t.Fatalf("error waiting for processes: %v", err) 892 } 893 894 if tc.killContainer { 895 // First kill the init process to make the container be stopped with 896 // processes still running inside. 897 containers[1].SignalContainer(unix.SIGKILL, false) 898 op := func() error { 899 c, err := Load(conf.RootDir, FullID{ContainerID: ids[1]}, LoadOpts{}) 900 if err != nil { 901 return err 902 } 903 if c.Status != Stopped { 904 return fmt.Errorf("container is not stopped") 905 } 906 return nil 907 } 908 if err := testutil.Poll(op, 5*time.Second); err != nil { 909 t.Fatalf("container did not stop %q: %v", containers[1].ID, err) 910 } 911 } 912 913 c, err := Load(conf.RootDir, FullID{ContainerID: ids[1]}, LoadOpts{}) 914 if err != nil { 915 t.Fatalf("failed to load child container %q: %v", c.ID, err) 916 } 917 // Kill'Em All 918 if err := c.SignalContainer(unix.SIGKILL, true); err != nil { 919 t.Fatalf("failed to send SIGKILL to container %q: %v", c.ID, err) 920 } 921 922 // Check that all processes are gone. 923 if err := waitForProcessCount(containers[1], 0); err != nil { 924 t.Fatalf("error waiting for processes: %v", err) 925 } 926 // Check that root container was not affected. 927 if err := waitForProcessCount(containers[0], rootProcCount); err != nil { 928 t.Fatalf("error waiting for processes: %v", err) 929 } 930 } 931 } 932 933 func TestMultiContainerDestroyNotStarted(t *testing.T) { 934 specs, ids := createSpecs( 935 []string{"/bin/sleep", "100"}, 936 []string{"/bin/sleep", "100"}) 937 938 conf := testutil.TestConfig(t) 939 _, bundleDir, cleanup, err := testutil.SetupContainer(specs[0], conf) 940 if err != nil { 941 t.Fatalf("error setting up container: %v", err) 942 } 943 defer cleanup() 944 945 rootArgs := Args{ 946 ID: ids[0], 947 Spec: specs[0], 948 BundleDir: bundleDir, 949 } 950 root, err := New(conf, rootArgs) 951 if err != nil { 952 t.Fatalf("error creating root container: %v", err) 953 } 954 defer root.Destroy() 955 if err := root.Start(conf); err != nil { 956 t.Fatalf("error starting root container: %v", err) 957 } 958 959 // Create and destroy sub-container. 960 bundleDir, cleanupSub, err := testutil.SetupBundleDir(specs[1]) 961 if err != nil { 962 t.Fatalf("error setting up container: %v", err) 963 } 964 defer cleanupSub() 965 966 args := Args{ 967 ID: ids[1], 968 Spec: specs[1], 969 BundleDir: bundleDir, 970 } 971 cont, err := New(conf, args) 972 if err != nil { 973 t.Fatalf("error creating container: %v", err) 974 } 975 976 // Check that container can be destroyed. 977 if err := cont.Destroy(); err != nil { 978 t.Fatalf("deleting non-started container failed: %v", err) 979 } 980 } 981 982 // TestMultiContainerDestroyStarting attempts to force a race between start 983 // and destroy. 984 func TestMultiContainerDestroyStarting(t *testing.T) { 985 cmds := make([][]string, 10) 986 for i := range cmds { 987 cmds[i] = []string{"/bin/sleep", "100"} 988 } 989 specs, ids := createSpecs(cmds...) 990 991 conf := testutil.TestConfig(t) 992 rootDir, bundleDir, cleanup, err := testutil.SetupContainer(specs[0], conf) 993 if err != nil { 994 t.Fatalf("error setting up container: %v", err) 995 } 996 defer cleanup() 997 998 rootArgs := Args{ 999 ID: ids[0], 1000 Spec: specs[0], 1001 BundleDir: bundleDir, 1002 } 1003 root, err := New(conf, rootArgs) 1004 if err != nil { 1005 t.Fatalf("error creating root container: %v", err) 1006 } 1007 defer root.Destroy() 1008 if err := root.Start(conf); err != nil { 1009 t.Fatalf("error starting root container: %v", err) 1010 } 1011 1012 wg := sync.WaitGroup{} 1013 for i := range cmds { 1014 if i == 0 { 1015 continue // skip root container 1016 } 1017 1018 bundleDir, cleanup, err := testutil.SetupBundleDir(specs[i]) 1019 if err != nil { 1020 t.Fatalf("error setting up container: %v", err) 1021 } 1022 defer cleanup() 1023 1024 rootArgs := Args{ 1025 ID: ids[i], 1026 Spec: specs[i], 1027 BundleDir: bundleDir, 1028 } 1029 cont, err := New(conf, rootArgs) 1030 if err != nil { 1031 t.Fatalf("error creating container: %v", err) 1032 } 1033 1034 // Container is not thread safe, so load another instance to run in 1035 // concurrently. 1036 startCont, err := Load(rootDir, FullID{ContainerID: ids[i]}, LoadOpts{}) 1037 if err != nil { 1038 t.Fatalf("error loading container: %v", err) 1039 } 1040 wg.Add(1) 1041 go func() { 1042 defer wg.Done() 1043 startCont.Start(conf) // ignore failures, start can fail if destroy runs first. 1044 }() 1045 1046 wg.Add(1) 1047 go func() { 1048 defer wg.Done() 1049 if err := cont.Destroy(); err != nil { 1050 t.Errorf("deleting non-started container failed: %v", err) 1051 } 1052 }() 1053 } 1054 wg.Wait() 1055 } 1056 1057 // TestMultiContainerDifferentFilesystems tests that different containers have 1058 // different root filesystems. 1059 func TestMultiContainerDifferentFilesystems(t *testing.T) { 1060 filename := "/foo" 1061 // Root container will create file and then sleep. 1062 cmdRoot := []string{"sh", "-c", fmt.Sprintf("touch %q && sleep 100", filename)} 1063 1064 // Child containers will assert that the file does not exist, and will 1065 // then create it. 1066 script := fmt.Sprintf("if [ -f %q ]; then exit 1; else touch %q; fi", filename, filename) 1067 cmd := []string{"sh", "-c", script} 1068 1069 rootDir, cleanup, err := testutil.SetupRootDir() 1070 if err != nil { 1071 t.Fatalf("error creating root dir: %v", err) 1072 } 1073 defer cleanup() 1074 1075 conf := testutil.TestConfig(t) 1076 conf.RootDir = rootDir 1077 1078 // Make sure overlay is enabled, and none of the root filesystems are 1079 // read-only, otherwise we won't be able to create the file. 1080 conf.Overlay = true 1081 specs, ids := createSpecs(cmdRoot, cmd, cmd) 1082 for _, s := range specs { 1083 s.Root.Readonly = false 1084 } 1085 1086 containers, cleanup, err := startContainers(conf, specs, ids) 1087 if err != nil { 1088 t.Fatalf("error starting containers: %v", err) 1089 } 1090 defer cleanup() 1091 1092 // Both child containers should exit successfully. 1093 for i, c := range containers { 1094 if i == 0 { 1095 // Don't wait on the root. 1096 continue 1097 } 1098 if ws, err := c.Wait(); err != nil { 1099 t.Errorf("failed to wait for process %s: %v", c.Spec.Process.Args, err) 1100 } else if es := ws.ExitStatus(); es != 0 { 1101 t.Errorf("process %s exited with non-zero status %d", c.Spec.Process.Args, es) 1102 } 1103 } 1104 } 1105 1106 // TestMultiContainerContainerDestroyStress tests that IO operations continue 1107 // to work after containers have been stopped and gofers killed. 1108 func TestMultiContainerContainerDestroyStress(t *testing.T) { 1109 app, err := testutil.FindFile("test/cmd/test_app/test_app") 1110 if err != nil { 1111 t.Fatal("error finding test_app:", err) 1112 } 1113 1114 // Setup containers. Root container just reaps children, while the others 1115 // perform some IOs. Children are executed in 3 batches of 10. Within the 1116 // batch there is overlap between containers starting and being destroyed. In 1117 // between batches all containers stop before starting another batch. 1118 cmds := [][]string{{app, "reaper"}} 1119 const batchSize = 10 1120 for i := 0; i < 3*batchSize; i++ { 1121 dir, err := ioutil.TempDir(testutil.TmpDir(), "gofer-stop-test") 1122 if err != nil { 1123 t.Fatal("ioutil.TempDir failed:", err) 1124 } 1125 defer os.RemoveAll(dir) 1126 1127 cmd := "find /bin -type f | head | xargs -I SRC cp SRC " + dir 1128 cmds = append(cmds, []string{"sh", "-c", cmd}) 1129 } 1130 allSpecs, allIDs := createSpecs(cmds...) 1131 1132 // Split up the specs and IDs. 1133 rootSpec := allSpecs[0] 1134 rootID := allIDs[0] 1135 childrenSpecs := allSpecs[1:] 1136 childrenIDs := allIDs[1:] 1137 1138 conf := testutil.TestConfig(t) 1139 _, bundleDir, cleanup, err := testutil.SetupContainer(rootSpec, conf) 1140 if err != nil { 1141 t.Fatalf("error setting up container: %v", err) 1142 } 1143 defer cleanup() 1144 1145 // Start root container. 1146 rootArgs := Args{ 1147 ID: rootID, 1148 Spec: rootSpec, 1149 BundleDir: bundleDir, 1150 } 1151 root, err := New(conf, rootArgs) 1152 if err != nil { 1153 t.Fatalf("error creating root container: %v", err) 1154 } 1155 if err := root.Start(conf); err != nil { 1156 t.Fatalf("error starting root container: %v", err) 1157 } 1158 defer root.Destroy() 1159 1160 // Run batches. Each batch starts containers in parallel, then wait and 1161 // destroy them before starting another batch. 1162 for i := 0; i < len(childrenSpecs); i += batchSize { 1163 t.Logf("Starting batch from %d to %d", i, i+batchSize) 1164 specs := childrenSpecs[i : i+batchSize] 1165 ids := childrenIDs[i : i+batchSize] 1166 1167 var children []*Container 1168 for j, spec := range specs { 1169 bundleDir, cleanup, err := testutil.SetupBundleDir(spec) 1170 if err != nil { 1171 t.Fatalf("error setting up container: %v", err) 1172 } 1173 defer cleanup() 1174 1175 args := Args{ 1176 ID: ids[j], 1177 Spec: spec, 1178 BundleDir: bundleDir, 1179 } 1180 child, err := New(conf, args) 1181 if err != nil { 1182 t.Fatalf("error creating container: %v", err) 1183 } 1184 children = append(children, child) 1185 1186 if err := child.Start(conf); err != nil { 1187 t.Fatalf("error starting container: %v", err) 1188 } 1189 1190 // Give a small gap between containers. 1191 time.Sleep(50 * time.Millisecond) 1192 } 1193 for _, child := range children { 1194 ws, err := child.Wait() 1195 if err != nil { 1196 t.Fatalf("waiting for container: %v", err) 1197 } 1198 if !ws.Exited() || ws.ExitStatus() != 0 { 1199 t.Fatalf("container failed, waitStatus: %x (%d)", ws, ws.ExitStatus()) 1200 } 1201 if err := child.Destroy(); err != nil { 1202 t.Fatalf("error destroying container: %v", err) 1203 } 1204 } 1205 } 1206 } 1207 1208 // Test that pod shared mounts are properly mounted in 2 containers and that 1209 // changes from one container is reflected in the other. 1210 func TestMultiContainerSharedMount(t *testing.T) { 1211 for name, conf := range configs(t, all...) { 1212 t.Run(name, func(t *testing.T) { 1213 rootDir, cleanup, err := testutil.SetupRootDir() 1214 if err != nil { 1215 t.Fatalf("error creating root dir: %v", err) 1216 } 1217 defer cleanup() 1218 conf.RootDir = rootDir 1219 1220 // Setup the containers. 1221 sleep := []string{"sleep", "100"} 1222 podSpec, ids := createSpecs(sleep, sleep) 1223 mnt0 := specs.Mount{ 1224 Destination: "/mydir/test", 1225 Source: "/some/dir", 1226 Type: "tmpfs", 1227 Options: nil, 1228 } 1229 podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0) 1230 1231 mnt1 := mnt0 1232 mnt1.Destination = "/mydir2/test2" 1233 podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1) 1234 1235 createSharedMount(mnt0, "test-mount", podSpec...) 1236 1237 containers, cleanup, err := startContainers(conf, podSpec, ids) 1238 if err != nil { 1239 t.Fatalf("error starting containers: %v", err) 1240 } 1241 defer cleanup() 1242 1243 file0 := path.Join(mnt0.Destination, "abc") 1244 file1 := path.Join(mnt1.Destination, "abc") 1245 execs := []execDesc{ 1246 { 1247 c: containers[0], 1248 cmd: []string{"/usr/bin/test", "-d", mnt0.Destination}, 1249 name: "directory is mounted in container0", 1250 }, 1251 { 1252 c: containers[1], 1253 cmd: []string{"/usr/bin/test", "-d", mnt1.Destination}, 1254 name: "directory is mounted in container1", 1255 }, 1256 { 1257 c: containers[0], 1258 cmd: []string{"/bin/touch", file0}, 1259 name: "create file in container0", 1260 }, 1261 { 1262 c: containers[0], 1263 cmd: []string{"/usr/bin/test", "-f", file0}, 1264 name: "file appears in container0", 1265 }, 1266 { 1267 c: containers[1], 1268 cmd: []string{"/usr/bin/test", "-f", file1}, 1269 name: "file appears in container1", 1270 }, 1271 { 1272 c: containers[1], 1273 cmd: []string{"/bin/rm", file1}, 1274 name: "remove file from container1", 1275 }, 1276 { 1277 c: containers[0], 1278 cmd: []string{"/usr/bin/test", "!", "-f", file0}, 1279 name: "file removed from container0", 1280 }, 1281 { 1282 c: containers[1], 1283 cmd: []string{"/usr/bin/test", "!", "-f", file1}, 1284 name: "file removed from container1", 1285 }, 1286 { 1287 c: containers[1], 1288 cmd: []string{"/bin/mkdir", file1}, 1289 name: "create directory in container1", 1290 }, 1291 { 1292 c: containers[0], 1293 cmd: []string{"/usr/bin/test", "-d", file0}, 1294 name: "dir appears in container0", 1295 }, 1296 { 1297 c: containers[1], 1298 cmd: []string{"/usr/bin/test", "-d", file1}, 1299 name: "dir appears in container1", 1300 }, 1301 { 1302 c: containers[0], 1303 cmd: []string{"/bin/rmdir", file0}, 1304 name: "remove directory from container0", 1305 }, 1306 { 1307 c: containers[0], 1308 cmd: []string{"/usr/bin/test", "!", "-d", file0}, 1309 name: "dir removed from container0", 1310 }, 1311 { 1312 c: containers[1], 1313 cmd: []string{"/usr/bin/test", "!", "-d", file1}, 1314 name: "dir removed from container1", 1315 }, 1316 } 1317 execMany(t, execs) 1318 }) 1319 } 1320 } 1321 1322 // Test that pod mounts are mounted as readonly when requested. 1323 func TestMultiContainerSharedMountReadonly(t *testing.T) { 1324 for name, conf := range configs(t, all...) { 1325 t.Run(name, func(t *testing.T) { 1326 rootDir, cleanup, err := testutil.SetupRootDir() 1327 if err != nil { 1328 t.Fatalf("error creating root dir: %v", err) 1329 } 1330 defer cleanup() 1331 conf.RootDir = rootDir 1332 1333 // Setup the containers. 1334 sleep := []string{"sleep", "100"} 1335 podSpec, ids := createSpecs(sleep, sleep) 1336 mnt0 := specs.Mount{ 1337 Destination: "/mydir/test", 1338 Source: "/some/dir", 1339 Type: "tmpfs", 1340 Options: []string{"ro"}, 1341 } 1342 podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0) 1343 1344 mnt1 := mnt0 1345 mnt1.Destination = "/mydir2/test2" 1346 podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1) 1347 1348 createSharedMount(mnt0, "test-mount", podSpec...) 1349 1350 containers, cleanup, err := startContainers(conf, podSpec, ids) 1351 if err != nil { 1352 t.Fatalf("error starting containers: %v", err) 1353 } 1354 defer cleanup() 1355 1356 file0 := path.Join(mnt0.Destination, "abc") 1357 file1 := path.Join(mnt1.Destination, "abc") 1358 execs := []execDesc{ 1359 { 1360 c: containers[0], 1361 cmd: []string{"/usr/bin/test", "-d", mnt0.Destination}, 1362 name: "directory is mounted in container0", 1363 }, 1364 { 1365 c: containers[1], 1366 cmd: []string{"/usr/bin/test", "-d", mnt1.Destination}, 1367 name: "directory is mounted in container1", 1368 }, 1369 { 1370 c: containers[0], 1371 cmd: []string{"/bin/touch", file0}, 1372 want: 1, 1373 name: "fails to write to container0", 1374 }, 1375 { 1376 c: containers[1], 1377 cmd: []string{"/bin/touch", file1}, 1378 want: 1, 1379 name: "fails to write to container1", 1380 }, 1381 } 1382 execMany(t, execs) 1383 }) 1384 } 1385 } 1386 1387 // Test that shared pod mounts continue to work after container is restarted. 1388 func TestMultiContainerSharedMountRestart(t *testing.T) { 1389 for name, conf := range configs(t, all...) { 1390 t.Run(name, func(t *testing.T) { 1391 rootDir, cleanup, err := testutil.SetupRootDir() 1392 if err != nil { 1393 t.Fatalf("error creating root dir: %v", err) 1394 } 1395 defer cleanup() 1396 conf.RootDir = rootDir 1397 1398 // Setup the containers. 1399 sleep := []string{"sleep", "100"} 1400 podSpec, ids := createSpecs(sleep, sleep) 1401 mnt0 := specs.Mount{ 1402 Destination: "/mydir/test", 1403 Source: "/some/dir", 1404 Type: "tmpfs", 1405 Options: nil, 1406 } 1407 podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0) 1408 1409 mnt1 := mnt0 1410 mnt1.Destination = "/mydir2/test2" 1411 podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1) 1412 1413 createSharedMount(mnt0, "test-mount", podSpec...) 1414 1415 containers, cleanup, err := startContainers(conf, podSpec, ids) 1416 if err != nil { 1417 t.Fatalf("error starting containers: %v", err) 1418 } 1419 defer cleanup() 1420 1421 file0 := path.Join(mnt0.Destination, "abc") 1422 file1 := path.Join(mnt1.Destination, "abc") 1423 execs := []execDesc{ 1424 { 1425 c: containers[0], 1426 cmd: []string{"/bin/touch", file0}, 1427 name: "create file in container0", 1428 }, 1429 { 1430 c: containers[0], 1431 cmd: []string{"/usr/bin/test", "-f", file0}, 1432 name: "file appears in container0", 1433 }, 1434 { 1435 c: containers[1], 1436 cmd: []string{"/usr/bin/test", "-f", file1}, 1437 name: "file appears in container1", 1438 }, 1439 } 1440 execMany(t, execs) 1441 1442 containers[1].Destroy() 1443 1444 bundleDir, cleanup, err := testutil.SetupBundleDir(podSpec[1]) 1445 if err != nil { 1446 t.Fatalf("error restarting container: %v", err) 1447 } 1448 defer cleanup() 1449 1450 args := Args{ 1451 ID: ids[1], 1452 Spec: podSpec[1], 1453 BundleDir: bundleDir, 1454 } 1455 containers[1], err = New(conf, args) 1456 if err != nil { 1457 t.Fatalf("error creating container: %v", err) 1458 } 1459 if err := containers[1].Start(conf); err != nil { 1460 t.Fatalf("error starting container: %v", err) 1461 } 1462 1463 execs = []execDesc{ 1464 { 1465 c: containers[0], 1466 cmd: []string{"/usr/bin/test", "-f", file0}, 1467 name: "file is still in container0", 1468 }, 1469 { 1470 c: containers[1], 1471 cmd: []string{"/usr/bin/test", "-f", file1}, 1472 name: "file is still in container1", 1473 }, 1474 { 1475 c: containers[1], 1476 cmd: []string{"/bin/rm", file1}, 1477 name: "file removed from container1", 1478 }, 1479 { 1480 c: containers[0], 1481 cmd: []string{"/usr/bin/test", "!", "-f", file0}, 1482 name: "file removed from container0", 1483 }, 1484 { 1485 c: containers[1], 1486 cmd: []string{"/usr/bin/test", "!", "-f", file1}, 1487 name: "file removed from container1", 1488 }, 1489 } 1490 execMany(t, execs) 1491 }) 1492 } 1493 } 1494 1495 // Test that unsupported pod mounts options are ignored when matching master and 1496 // replica mounts. 1497 func TestMultiContainerSharedMountUnsupportedOptions(t *testing.T) { 1498 for name, conf := range configs(t, all...) { 1499 t.Run(name, func(t *testing.T) { 1500 rootDir, cleanup, err := testutil.SetupRootDir() 1501 if err != nil { 1502 t.Fatalf("error creating root dir: %v", err) 1503 } 1504 defer cleanup() 1505 conf.RootDir = rootDir 1506 1507 // Setup the containers. 1508 sleep := []string{"/bin/sleep", "100"} 1509 podSpec, ids := createSpecs(sleep, sleep) 1510 mnt0 := specs.Mount{ 1511 Destination: "/mydir/test", 1512 Source: "/some/dir", 1513 Type: "tmpfs", 1514 Options: []string{"rw", "relatime"}, 1515 } 1516 podSpec[0].Mounts = append(podSpec[0].Mounts, mnt0) 1517 1518 mnt1 := mnt0 1519 mnt1.Destination = "/mydir2/test2" 1520 mnt1.Options = []string{"rw", "nosuid"} 1521 podSpec[1].Mounts = append(podSpec[1].Mounts, mnt1) 1522 1523 createSharedMount(mnt0, "test-mount", podSpec...) 1524 1525 containers, cleanup, err := startContainers(conf, podSpec, ids) 1526 if err != nil { 1527 t.Fatalf("error starting containers: %v", err) 1528 } 1529 defer cleanup() 1530 1531 execs := []execDesc{ 1532 { 1533 c: containers[0], 1534 cmd: []string{"/usr/bin/test", "-d", mnt0.Destination}, 1535 name: "directory is mounted in container0", 1536 }, 1537 { 1538 c: containers[1], 1539 cmd: []string{"/usr/bin/test", "-d", mnt1.Destination}, 1540 name: "directory is mounted in container1", 1541 }, 1542 } 1543 execMany(t, execs) 1544 }) 1545 } 1546 } 1547 1548 // Test that one container can send an FD to another container, even though 1549 // they have distinct MountNamespaces. 1550 func TestMultiContainerMultiRootCanHandleFDs(t *testing.T) { 1551 app, err := testutil.FindFile("test/cmd/test_app/test_app") 1552 if err != nil { 1553 t.Fatal("error finding test_app:", err) 1554 } 1555 1556 // We set up two containers with one shared mount that is used for a 1557 // shared socket. The first container will send an FD over the socket 1558 // to the second container. The FD corresponds to a file in the first 1559 // container's mount namespace that is not part of the second 1560 // container's mount namespace. However, the second container still 1561 // should be able to read the FD. 1562 1563 // Create a shared mount where we will put the socket. 1564 sharedMnt := specs.Mount{ 1565 Destination: "/mydir/test", 1566 Type: "tmpfs", 1567 // Shared mounts need a Source, even for tmpfs. It is only used 1568 // to match up different shared mounts inside the pod. 1569 Source: "/some/dir", 1570 } 1571 socketPath := filepath.Join(sharedMnt.Destination, "socket") 1572 1573 // Create a writeable tmpfs mount where the FD sender app will create 1574 // files to send. This will only be mounted in the FD sender. 1575 writeableMnt := specs.Mount{ 1576 Destination: "/tmp", 1577 Type: "tmpfs", 1578 } 1579 1580 rootDir, cleanup, err := testutil.SetupRootDir() 1581 if err != nil { 1582 t.Fatalf("error creating root dir: %v", err) 1583 } 1584 defer cleanup() 1585 1586 conf := testutil.TestConfig(t) 1587 conf.RootDir = rootDir 1588 1589 // Create the specs. 1590 specs, ids := createSpecs( 1591 []string{"sleep", "1000"}, 1592 []string{app, "fd_sender", "--socket", socketPath}, 1593 []string{app, "fd_receiver", "--socket", socketPath}, 1594 ) 1595 createSharedMount(sharedMnt, "shared-mount", specs...) 1596 specs[1].Mounts = append(specs[2].Mounts, sharedMnt, writeableMnt) 1597 specs[2].Mounts = append(specs[1].Mounts, sharedMnt) 1598 1599 containers, cleanup, err := startContainers(conf, specs, ids) 1600 if err != nil { 1601 t.Fatalf("error starting containers: %v", err) 1602 } 1603 defer cleanup() 1604 1605 // Both containers should exit successfully. 1606 for _, c := range containers[1:] { 1607 if ws, err := c.Wait(); err != nil { 1608 t.Errorf("failed to wait for process %s: %v", c.Spec.Process.Args, err) 1609 } else if es := ws.ExitStatus(); es != 0 { 1610 t.Errorf("process %s exited with non-zero status %d", c.Spec.Process.Args, es) 1611 } 1612 } 1613 } 1614 1615 // Test that container is destroyed when Gofer is killed. 1616 func TestMultiContainerGoferKilled(t *testing.T) { 1617 rootDir, cleanup, err := testutil.SetupRootDir() 1618 if err != nil { 1619 t.Fatalf("error creating root dir: %v", err) 1620 } 1621 defer cleanup() 1622 1623 conf := testutil.TestConfig(t) 1624 conf.RootDir = rootDir 1625 1626 sleep := []string{"sleep", "100"} 1627 specs, ids := createSpecs(sleep, sleep, sleep) 1628 containers, cleanup, err := startContainers(conf, specs, ids) 1629 if err != nil { 1630 t.Fatalf("error starting containers: %v", err) 1631 } 1632 defer cleanup() 1633 1634 // Ensure container is running 1635 c := containers[2] 1636 expectedPL := []*control.Process{ 1637 newProcessBuilder().PID(3).Cmd("sleep").Process(), 1638 } 1639 if err := waitForProcessList(c, expectedPL); err != nil { 1640 t.Errorf("failed to wait for sleep to start: %v", err) 1641 } 1642 1643 // Kill container's gofer. 1644 if err := unix.Kill(c.GoferPid, unix.SIGKILL); err != nil { 1645 t.Fatalf("unix.Kill(%d, SIGKILL)=%v", c.GoferPid, err) 1646 } 1647 1648 // Wait until container stops. 1649 if err := waitForProcessList(c, nil); err != nil { 1650 t.Errorf("Container %q was not stopped after gofer death: %v", c.ID, err) 1651 } 1652 1653 // Check that container isn't running anymore. 1654 if _, err := execute(c, "/bin/true"); err == nil { 1655 t.Fatalf("Container %q was not stopped after gofer death", c.ID) 1656 } 1657 1658 // Check that other containers are unaffected. 1659 for i, c := range containers { 1660 if i == 2 { 1661 continue // container[2] has been killed. 1662 } 1663 pl := []*control.Process{ 1664 newProcessBuilder().PID(kernel.ThreadID(i + 1)).Cmd("sleep").Process(), 1665 } 1666 if err := waitForProcessList(c, pl); err != nil { 1667 t.Errorf("Container %q was affected by another container: %v", c.ID, err) 1668 } 1669 if _, err := execute(c, "/bin/true"); err != nil { 1670 t.Fatalf("Container %q was affected by another container: %v", c.ID, err) 1671 } 1672 } 1673 1674 // Kill root container's gofer to bring entire sandbox down. 1675 c = containers[0] 1676 if err := unix.Kill(c.GoferPid, unix.SIGKILL); err != nil { 1677 t.Fatalf("unix.Kill(%d, SIGKILL)=%v", c.GoferPid, err) 1678 } 1679 1680 // Wait until sandbox stops. waitForProcessList will loop until sandbox exits 1681 // and RPC errors out. 1682 impossiblePL := []*control.Process{ 1683 newProcessBuilder().Cmd("non-existent-process").Process(), 1684 } 1685 if err := waitForProcessList(c, impossiblePL); err == nil { 1686 t.Fatalf("Sandbox was not killed after gofer death") 1687 } 1688 1689 // Check that entire sandbox isn't running anymore. 1690 for _, c := range containers { 1691 if _, err := execute(c, "/bin/true"); err == nil { 1692 t.Fatalf("Container %q was not stopped after gofer death", c.ID) 1693 } 1694 } 1695 } 1696 1697 func TestMultiContainerLoadSandbox(t *testing.T) { 1698 sleep := []string{"sleep", "100"} 1699 specs, ids := createSpecs(sleep, sleep, sleep) 1700 1701 rootDir, cleanup, err := testutil.SetupRootDir() 1702 if err != nil { 1703 t.Fatalf("error creating root dir: %v", err) 1704 } 1705 defer cleanup() 1706 1707 conf := testutil.TestConfig(t) 1708 conf.RootDir = rootDir 1709 1710 // Create containers for the sandbox. 1711 wants, cleanup, err := startContainers(conf, specs, ids) 1712 if err != nil { 1713 t.Fatalf("error starting containers: %v", err) 1714 } 1715 defer cleanup() 1716 1717 // Then create unrelated containers. 1718 for i := 0; i < 3; i++ { 1719 specs, ids = createSpecs(sleep, sleep, sleep) 1720 _, cleanup, err = startContainers(conf, specs, ids) 1721 if err != nil { 1722 t.Fatalf("error starting containers: %v", err) 1723 } 1724 defer cleanup() 1725 } 1726 1727 // Create an unrelated directory under root. 1728 dir := filepath.Join(conf.RootDir, "not-a-container") 1729 if err := os.MkdirAll(dir, 0755); err != nil { 1730 t.Fatalf("os.MkdirAll(%q)=%v", dir, err) 1731 } 1732 1733 // Create a valid but empty container directory. 1734 randomCID := testutil.RandomContainerID() 1735 dir = filepath.Join(conf.RootDir, randomCID) 1736 if err := os.MkdirAll(dir, 0755); err != nil { 1737 t.Fatalf("os.MkdirAll(%q)=%v", dir, err) 1738 } 1739 1740 // Load the sandbox and check that the correct containers were returned. 1741 id := wants[0].Sandbox.ID 1742 gots, err := loadSandbox(conf.RootDir, id) 1743 if err != nil { 1744 t.Fatalf("loadSandbox()=%v", err) 1745 } 1746 wantIDs := make(map[string]struct{}) 1747 for _, want := range wants { 1748 wantIDs[want.ID] = struct{}{} 1749 } 1750 for _, got := range gots { 1751 if got.Sandbox.ID != id { 1752 t.Errorf("wrong sandbox ID, got: %v, want: %v", got.Sandbox.ID, id) 1753 } 1754 if _, ok := wantIDs[got.ID]; !ok { 1755 t.Errorf("wrong container ID, got: %v, wants: %v", got.ID, wantIDs) 1756 } 1757 delete(wantIDs, got.ID) 1758 } 1759 if len(wantIDs) != 0 { 1760 t.Errorf("containers not found: %v", wantIDs) 1761 } 1762 } 1763 1764 // TestMultiContainerRunNonRoot checks that child container can be configured 1765 // when running as non-privileged user. 1766 func TestMultiContainerRunNonRoot(t *testing.T) { 1767 cmdRoot := []string{"/bin/sleep", "100"} 1768 cmdSub := []string{"/bin/true"} 1769 podSpecs, ids := createSpecs(cmdRoot, cmdSub) 1770 1771 // User running inside container can't list '$TMP/blocked' and would fail to 1772 // mount it. 1773 blocked, err := ioutil.TempDir(testutil.TmpDir(), "blocked") 1774 if err != nil { 1775 t.Fatalf("ioutil.TempDir() failed: %v", err) 1776 } 1777 if err := os.Chmod(blocked, 0700); err != nil { 1778 t.Fatalf("os.MkDir(%q) failed: %v", blocked, err) 1779 } 1780 dir := path.Join(blocked, "test") 1781 if err := os.Mkdir(dir, 0755); err != nil { 1782 t.Fatalf("os.MkDir(%q) failed: %v", dir, err) 1783 } 1784 1785 src, err := ioutil.TempDir(testutil.TmpDir(), "src") 1786 if err != nil { 1787 t.Fatalf("ioutil.TempDir() failed: %v", err) 1788 } 1789 1790 // Set a random user/group with no access to "blocked" dir. 1791 podSpecs[1].Process.User.UID = 343 1792 podSpecs[1].Process.User.GID = 2401 1793 podSpecs[1].Process.Capabilities = nil 1794 1795 podSpecs[1].Mounts = append(podSpecs[1].Mounts, specs.Mount{ 1796 Destination: dir, 1797 Source: src, 1798 Type: "bind", 1799 }) 1800 1801 rootDir, cleanup, err := testutil.SetupRootDir() 1802 if err != nil { 1803 t.Fatalf("error creating root dir: %v", err) 1804 } 1805 defer cleanup() 1806 1807 conf := testutil.TestConfig(t) 1808 conf.RootDir = rootDir 1809 1810 pod, cleanup, err := startContainers(conf, podSpecs, ids) 1811 if err != nil { 1812 t.Fatalf("error starting containers: %v", err) 1813 } 1814 defer cleanup() 1815 1816 // Once all containers are started, wait for the child container to exit. 1817 // This means that the volume was mounted properly. 1818 ws, err := pod[1].Wait() 1819 if err != nil { 1820 t.Fatalf("running child container: %v", err) 1821 } 1822 if !ws.Exited() || ws.ExitStatus() != 0 { 1823 t.Fatalf("child container failed, waitStatus: %v", ws) 1824 } 1825 } 1826 1827 // TestMultiContainerHomeEnvDir tests that the HOME environment variable is set 1828 // for root containers, sub-containers, and exec'ed processes. 1829 func TestMultiContainerHomeEnvDir(t *testing.T) { 1830 // NOTE: Don't use overlay since we need changes to persist to the temp dir 1831 // outside the sandbox. 1832 for testName, conf := range configs(t, noOverlay...) { 1833 t.Run(testName, func(t *testing.T) { 1834 1835 rootDir, cleanup, err := testutil.SetupRootDir() 1836 if err != nil { 1837 t.Fatalf("error creating root dir: %v", err) 1838 } 1839 defer cleanup() 1840 conf.RootDir = rootDir 1841 1842 // Create temp files we can write the value of $HOME to. 1843 homeDirs := map[string]*os.File{} 1844 for _, name := range []string{"root", "sub", "exec"} { 1845 homeFile, err := ioutil.TempFile(testutil.TmpDir(), name) 1846 if err != nil { 1847 t.Fatalf("creating temp file: %v", err) 1848 } 1849 homeDirs[name] = homeFile 1850 } 1851 1852 // We will sleep in the root container in order to ensure that the root 1853 //container doesn't terminate before sub containers can be created. 1854 rootCmd := []string{"/bin/sh", "-c", fmt.Sprintf(`printf "$HOME" > %s; sleep 1000`, homeDirs["root"].Name())} 1855 subCmd := []string{"/bin/sh", "-c", fmt.Sprintf(`printf "$HOME" > %s`, homeDirs["sub"].Name())} 1856 execCmd := fmt.Sprintf(`printf "$HOME" > %s`, homeDirs["exec"].Name()) 1857 1858 // Setup the containers, a root container and sub container. 1859 specConfig, ids := createSpecs(rootCmd, subCmd) 1860 containers, cleanup, err := startContainers(conf, specConfig, ids) 1861 if err != nil { 1862 t.Fatalf("error starting containers: %v", err) 1863 } 1864 defer cleanup() 1865 1866 // Exec into the root container synchronously. 1867 if _, err := execute(containers[0], "/bin/sh", "-c", execCmd); err != nil { 1868 t.Errorf("error executing %+v: %v", execCmd, err) 1869 } 1870 1871 // Wait for the subcontainer to finish. 1872 _, err = containers[1].Wait() 1873 if err != nil { 1874 t.Errorf("wait on child container: %v", err) 1875 } 1876 1877 // Wait until after `env` has executed. 1878 expectedProc := newProcessBuilder().Cmd("sleep").Process() 1879 if err := waitForProcess(containers[0], expectedProc); err != nil { 1880 t.Errorf("failed to wait for sleep to start: %v", err) 1881 } 1882 1883 // Check the written files. 1884 for name, tmpFile := range homeDirs { 1885 dirBytes, err := ioutil.ReadAll(tmpFile) 1886 if err != nil { 1887 t.Fatalf("reading %s temp file: %v", name, err) 1888 } 1889 got := string(dirBytes) 1890 1891 want := "/" 1892 if got != want { 1893 t.Errorf("%s $HOME incorrect: got: %q, want: %q", name, got, want) 1894 } 1895 } 1896 1897 }) 1898 } 1899 } 1900 1901 func TestMultiContainerEvent(t *testing.T) { 1902 conf := testutil.TestConfig(t) 1903 rootDir, cleanup, err := testutil.SetupRootDir() 1904 if err != nil { 1905 t.Fatalf("error creating root dir: %v", err) 1906 } 1907 defer cleanup() 1908 conf.RootDir = rootDir 1909 1910 // Setup the containers. 1911 sleep := []string{"/bin/sleep", "100"} 1912 busy := []string{"/bin/bash", "-c", "i=0 ; while true ; do (( i += 1 )) ; done"} 1913 quick := []string{"/bin/true"} 1914 podSpec, ids := createSpecs(sleep, busy, quick) 1915 containers, cleanup, err := startContainers(conf, podSpec, ids) 1916 if err != nil { 1917 t.Fatalf("error starting containers: %v", err) 1918 } 1919 defer cleanup() 1920 1921 t.Logf("Running container sleep %s", containers[0].ID) 1922 t.Logf("Running container busy %s", containers[1].ID) 1923 t.Logf("Running container quick %s", containers[2].ID) 1924 1925 // Wait for last container to stabilize the process count that is 1926 // checked further below. 1927 if ws, err := containers[2].Wait(); err != nil || ws != 0 { 1928 t.Fatalf("Container.Wait, status: %v, err: %v", ws, err) 1929 } 1930 expectedPL := []*control.Process{ 1931 newProcessBuilder().Cmd("sleep").Process(), 1932 } 1933 if err := waitForProcessList(containers[0], expectedPL); err != nil { 1934 t.Errorf("failed to wait for sleep to start: %v", err) 1935 } 1936 expectedPL = []*control.Process{ 1937 newProcessBuilder().Cmd("bash").Process(), 1938 } 1939 if err := waitForProcessList(containers[1], expectedPL); err != nil { 1940 t.Errorf("failed to wait for bash to start: %v", err) 1941 } 1942 1943 // Check events for running containers. 1944 for _, cont := range containers[:2] { 1945 ret, err := cont.Event() 1946 if err != nil { 1947 t.Errorf("Container.Event(%q): %v", cont.ID, err) 1948 } 1949 evt := ret.Event 1950 if want := "stats"; evt.Type != want { 1951 t.Errorf("Wrong event type, cid: %q, want: %s, got: %s", cont.ID, want, evt.Type) 1952 } 1953 if cont.ID != evt.ID { 1954 t.Errorf("Wrong container ID, want: %s, got: %s", cont.ID, evt.ID) 1955 } 1956 // One process per remaining container. 1957 if got, want := evt.Data.Pids.Current, uint64(2); got != want { 1958 t.Errorf("Wrong number of PIDs, cid: %q, want: %d, got: %d", cont.ID, want, got) 1959 } 1960 1961 // The exited container should always have a usage of zero. 1962 if exited := ret.ContainerUsage[containers[2].ID]; exited != 0 { 1963 t.Errorf("Exited container should report 0 CPU usage, got: %d", exited) 1964 } 1965 } 1966 1967 // Check that CPU reported by busy container is higher than sleep. 1968 cb := func() error { 1969 sleepEvt, err := containers[0].Event() 1970 if err != nil { 1971 return &backoff.PermanentError{Err: err} 1972 } 1973 sleepUsage := sleepEvt.Event.Data.CPU.Usage.Total 1974 1975 busyEvt, err := containers[1].Event() 1976 if err != nil { 1977 return &backoff.PermanentError{Err: err} 1978 } 1979 busyUsage := busyEvt.Event.Data.CPU.Usage.Total 1980 1981 if busyUsage <= sleepUsage { 1982 t.Logf("Busy container usage lower than sleep (busy: %d, sleep: %d), retrying...", busyUsage, sleepUsage) 1983 return fmt.Errorf("Busy container should have higher usage than sleep, busy: %d, sleep: %d", busyUsage, sleepUsage) 1984 } 1985 return nil 1986 } 1987 // Give time for busy container to run and use more CPU than sleep. 1988 if err := testutil.Poll(cb, 10*time.Second); err != nil { 1989 t.Fatal(err) 1990 } 1991 1992 // Check that stopped and destroyed containers return error. 1993 if err := containers[1].Destroy(); err != nil { 1994 t.Fatalf("container.Destroy: %v", err) 1995 } 1996 for _, cont := range containers[1:] { 1997 if _, err := cont.Event(); err == nil { 1998 t.Errorf("Container.Event() should have failed, cid: %q, state: %v", cont.ID, cont.Status) 1999 } 2000 } 2001 } 2002 2003 // Tests that duplicate variables in the spec are merged into a single one. 2004 func TestDuplicateEnvVariable(t *testing.T) { 2005 conf := testutil.TestConfig(t) 2006 2007 rootDir, cleanup, err := testutil.SetupRootDir() 2008 if err != nil { 2009 t.Fatalf("error creating root dir: %v", err) 2010 } 2011 defer cleanup() 2012 conf.RootDir = rootDir 2013 2014 // Create files to dump `env` output. 2015 files := [3]*os.File{} 2016 for i := 0; i < len(files); i++ { 2017 var err error 2018 files[i], err = ioutil.TempFile(testutil.TmpDir(), "env-var-test") 2019 if err != nil { 2020 t.Fatalf("creating temp file: %v", err) 2021 } 2022 defer files[i].Close() 2023 defer os.Remove(files[i].Name()) 2024 } 2025 2026 // Setup the containers. Use root container to test exec too. 2027 cmd1 := fmt.Sprintf("env > %q; sleep 1000", files[0].Name()) 2028 cmd2 := fmt.Sprintf("env > %q", files[1].Name()) 2029 cmdExec := fmt.Sprintf("env > %q", files[2].Name()) 2030 testSpecs, ids := createSpecs([]string{"/bin/sh", "-c", cmd1}, []string{"/bin/sh", "-c", cmd2}) 2031 testSpecs[0].Process.Env = append(testSpecs[0].Process.Env, "VAR=foo", "VAR=bar") 2032 testSpecs[1].Process.Env = append(testSpecs[1].Process.Env, "VAR=foo", "VAR=bar") 2033 2034 containers, cleanup, err := startContainers(conf, testSpecs, ids) 2035 if err != nil { 2036 t.Fatalf("error starting containers: %v", err) 2037 } 2038 defer cleanup() 2039 2040 // Wait until after `env` has executed. 2041 expectedProc := newProcessBuilder().Cmd("sleep").Process() 2042 if err := waitForProcess(containers[0], expectedProc); err != nil { 2043 t.Errorf("failed to wait for sleep to start: %v", err) 2044 } 2045 if ws, err := containers[1].Wait(); err != nil { 2046 t.Errorf("failed to wait container 1: %v", err) 2047 } else if es := ws.ExitStatus(); es != 0 { 2048 t.Errorf("container %s exited with non-zero status: %v", containers[1].ID, es) 2049 } 2050 2051 execArgs := &control.ExecArgs{ 2052 Filename: "/bin/sh", 2053 Argv: []string{"/bin/sh", "-c", cmdExec}, 2054 Envv: []string{"VAR=foo", "VAR=bar"}, 2055 } 2056 if ws, err := containers[0].executeSync(execArgs); err != nil || ws.ExitStatus() != 0 { 2057 t.Fatalf("exec failed, ws: %v, err: %v", ws, err) 2058 } 2059 2060 // Now read and check that none of the env has repeated values. 2061 for _, file := range files { 2062 out, err := ioutil.ReadAll(file) 2063 if err != nil { 2064 t.Fatal(err) 2065 } 2066 t.Logf("Checking env %q:\n%s", file.Name(), out) 2067 envs := make(map[string]string) 2068 for _, line := range strings.Split(string(out), "\n") { 2069 if len(line) == 0 { 2070 continue 2071 } 2072 envVar := strings.SplitN(line, "=", 2) 2073 if len(envVar) != 2 { 2074 t.Fatalf("invalid env variable: %s", line) 2075 } 2076 key := envVar[0] 2077 if val, ok := envs[key]; ok { 2078 t.Errorf("env variable %q is duplicated: %q and %q", key, val, envVar[1]) 2079 } 2080 envs[key] = envVar[1] 2081 } 2082 if _, ok := envs["VAR"]; !ok { 2083 t.Errorf("variable VAR missing: %v", envs) 2084 } 2085 } 2086 }