github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/integration/exec_test.go (about) 1 package integration 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "reflect" 11 "strconv" 12 "strings" 13 "syscall" 14 "testing" 15 16 "github.com/opencontainers/runc/libcontainer" 17 "github.com/opencontainers/runc/libcontainer/cgroups/systemd" 18 "github.com/opencontainers/runc/libcontainer/configs" 19 ) 20 21 func TestExecPS(t *testing.T) { 22 testExecPS(t, false) 23 } 24 25 func TestUsernsExecPS(t *testing.T) { 26 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { 27 t.Skip("userns is unsupported") 28 } 29 testExecPS(t, true) 30 } 31 32 func testExecPS(t *testing.T, userns bool) { 33 if testing.Short() { 34 return 35 } 36 rootfs, err := newRootfs() 37 ok(t, err) 38 defer remove(rootfs) 39 config := newTemplateConfig(rootfs) 40 if userns { 41 config.UidMappings = []configs.IDMap{{0, 0, 1000}} 42 config.GidMappings = []configs.IDMap{{0, 0, 1000}} 43 config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER}) 44 } 45 46 buffers, exitCode, err := runContainer(config, "", "ps") 47 if err != nil { 48 t.Fatalf("%s: %s", buffers, err) 49 } 50 if exitCode != 0 { 51 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 52 } 53 lines := strings.Split(buffers.Stdout.String(), "\n") 54 if len(lines) < 2 { 55 t.Fatalf("more than one process running for output %q", buffers.Stdout.String()) 56 } 57 expected := `1 root ps` 58 actual := strings.Trim(lines[1], "\n ") 59 if actual != expected { 60 t.Fatalf("expected output %q but received %q", expected, actual) 61 } 62 } 63 64 func TestIPCPrivate(t *testing.T) { 65 if testing.Short() { 66 return 67 } 68 69 rootfs, err := newRootfs() 70 ok(t, err) 71 defer remove(rootfs) 72 73 l, err := os.Readlink("/proc/1/ns/ipc") 74 ok(t, err) 75 76 config := newTemplateConfig(rootfs) 77 buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") 78 ok(t, err) 79 80 if exitCode != 0 { 81 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 82 } 83 84 if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual == l { 85 t.Fatalf("ipc link should be private to the container but equals host %q %q", actual, l) 86 } 87 } 88 89 func TestIPCHost(t *testing.T) { 90 if testing.Short() { 91 return 92 } 93 94 rootfs, err := newRootfs() 95 ok(t, err) 96 defer remove(rootfs) 97 98 l, err := os.Readlink("/proc/1/ns/ipc") 99 ok(t, err) 100 101 config := newTemplateConfig(rootfs) 102 config.Namespaces.Remove(configs.NEWIPC) 103 buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") 104 ok(t, err) 105 106 if exitCode != 0 { 107 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 108 } 109 110 if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual != l { 111 t.Fatalf("ipc link not equal to host link %q %q", actual, l) 112 } 113 } 114 115 func TestIPCJoinPath(t *testing.T) { 116 if testing.Short() { 117 return 118 } 119 120 rootfs, err := newRootfs() 121 ok(t, err) 122 defer remove(rootfs) 123 124 l, err := os.Readlink("/proc/1/ns/ipc") 125 ok(t, err) 126 127 config := newTemplateConfig(rootfs) 128 config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc") 129 130 buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") 131 ok(t, err) 132 133 if exitCode != 0 { 134 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 135 } 136 137 if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual != l { 138 t.Fatalf("ipc link not equal to host link %q %q", actual, l) 139 } 140 } 141 142 func TestIPCBadPath(t *testing.T) { 143 if testing.Short() { 144 return 145 } 146 147 rootfs, err := newRootfs() 148 ok(t, err) 149 defer remove(rootfs) 150 151 config := newTemplateConfig(rootfs) 152 config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipcc") 153 154 _, _, err = runContainer(config, "", "true") 155 if err == nil { 156 t.Fatal("container succeeded with bad ipc path") 157 } 158 } 159 160 func TestRlimit(t *testing.T) { 161 testRlimit(t, false) 162 } 163 164 func TestUsernsRlimit(t *testing.T) { 165 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { 166 t.Skip("userns is unsupported") 167 } 168 169 testRlimit(t, true) 170 } 171 172 func testRlimit(t *testing.T, userns bool) { 173 if testing.Short() { 174 return 175 } 176 177 rootfs, err := newRootfs() 178 ok(t, err) 179 defer remove(rootfs) 180 181 config := newTemplateConfig(rootfs) 182 if userns { 183 config.UidMappings = []configs.IDMap{{0, 0, 1000}} 184 config.GidMappings = []configs.IDMap{{0, 0, 1000}} 185 config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER}) 186 } 187 188 // ensure limit is lower than what the config requests to test that in a user namespace 189 // the Setrlimit call happens early enough that we still have permissions to raise the limit. 190 ok(t, syscall.Setrlimit(syscall.RLIMIT_NOFILE, &syscall.Rlimit{ 191 Max: 1024, 192 Cur: 1024, 193 })) 194 195 out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n") 196 ok(t, err) 197 if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" { 198 t.Fatalf("expected rlimit to be 1025, got %s", limit) 199 } 200 } 201 202 func TestEnter(t *testing.T) { 203 if testing.Short() { 204 return 205 } 206 root, err := newTestRoot() 207 ok(t, err) 208 defer os.RemoveAll(root) 209 210 rootfs, err := newRootfs() 211 ok(t, err) 212 defer remove(rootfs) 213 214 config := newTemplateConfig(rootfs) 215 216 container, err := factory.Create("test", config) 217 ok(t, err) 218 defer container.Destroy() 219 220 // Execute a first process in the container 221 stdinR, stdinW, err := os.Pipe() 222 ok(t, err) 223 224 var stdout, stdout2 bytes.Buffer 225 226 pconfig := libcontainer.Process{ 227 Cwd: "/", 228 Args: []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"}, 229 Env: standardEnvironment, 230 Stdin: stdinR, 231 Stdout: &stdout, 232 } 233 err = container.Run(&pconfig) 234 stdinR.Close() 235 defer stdinW.Close() 236 ok(t, err) 237 pid, err := pconfig.Pid() 238 ok(t, err) 239 240 // Execute another process in the container 241 stdinR2, stdinW2, err := os.Pipe() 242 ok(t, err) 243 pconfig2 := libcontainer.Process{ 244 Cwd: "/", 245 Env: standardEnvironment, 246 } 247 pconfig2.Args = []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"} 248 pconfig2.Stdin = stdinR2 249 pconfig2.Stdout = &stdout2 250 251 err = container.Run(&pconfig2) 252 stdinR2.Close() 253 defer stdinW2.Close() 254 ok(t, err) 255 256 pid2, err := pconfig2.Pid() 257 ok(t, err) 258 259 processes, err := container.Processes() 260 ok(t, err) 261 262 n := 0 263 for i := range processes { 264 if processes[i] == pid || processes[i] == pid2 { 265 n++ 266 } 267 } 268 if n != 2 { 269 t.Fatal("unexpected number of processes", processes, pid, pid2) 270 } 271 272 // Wait processes 273 stdinW2.Close() 274 waitProcess(&pconfig2, t) 275 276 stdinW.Close() 277 waitProcess(&pconfig, t) 278 279 // Check that both processes live in the same pidns 280 pidns := string(stdout.Bytes()) 281 ok(t, err) 282 283 pidns2 := string(stdout2.Bytes()) 284 ok(t, err) 285 286 if pidns != pidns2 { 287 t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2) 288 } 289 } 290 291 func TestProcessEnv(t *testing.T) { 292 if testing.Short() { 293 return 294 } 295 root, err := newTestRoot() 296 ok(t, err) 297 defer os.RemoveAll(root) 298 299 rootfs, err := newRootfs() 300 ok(t, err) 301 defer remove(rootfs) 302 303 config := newTemplateConfig(rootfs) 304 305 container, err := factory.Create("test", config) 306 ok(t, err) 307 defer container.Destroy() 308 309 var stdout bytes.Buffer 310 pconfig := libcontainer.Process{ 311 Cwd: "/", 312 Args: []string{"sh", "-c", "env"}, 313 Env: []string{ 314 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 315 "HOSTNAME=integration", 316 "TERM=xterm", 317 "FOO=BAR", 318 }, 319 Stdin: nil, 320 Stdout: &stdout, 321 } 322 err = container.Run(&pconfig) 323 ok(t, err) 324 325 // Wait for process 326 waitProcess(&pconfig, t) 327 328 outputEnv := string(stdout.Bytes()) 329 330 // Check that the environment has the key/value pair we added 331 if !strings.Contains(outputEnv, "FOO=BAR") { 332 t.Fatal("Environment doesn't have the expected FOO=BAR key/value pair: ", outputEnv) 333 } 334 335 // Make sure that HOME is set 336 if !strings.Contains(outputEnv, "HOME=/root") { 337 t.Fatal("Environment doesn't have HOME set: ", outputEnv) 338 } 339 } 340 341 func TestProcessCaps(t *testing.T) { 342 if testing.Short() { 343 return 344 } 345 root, err := newTestRoot() 346 ok(t, err) 347 defer os.RemoveAll(root) 348 349 rootfs, err := newRootfs() 350 ok(t, err) 351 defer remove(rootfs) 352 353 config := newTemplateConfig(rootfs) 354 355 container, err := factory.Create("test", config) 356 ok(t, err) 357 defer container.Destroy() 358 359 processCaps := append(config.Capabilities, "CAP_NET_ADMIN") 360 361 var stdout bytes.Buffer 362 pconfig := libcontainer.Process{ 363 Cwd: "/", 364 Args: []string{"sh", "-c", "cat /proc/self/status"}, 365 Env: standardEnvironment, 366 Capabilities: processCaps, 367 Stdin: nil, 368 Stdout: &stdout, 369 } 370 err = container.Run(&pconfig) 371 ok(t, err) 372 373 // Wait for process 374 waitProcess(&pconfig, t) 375 376 outputStatus := string(stdout.Bytes()) 377 378 lines := strings.Split(outputStatus, "\n") 379 380 effectiveCapsLine := "" 381 for _, l := range lines { 382 line := strings.TrimSpace(l) 383 if strings.Contains(line, "CapEff:") { 384 effectiveCapsLine = line 385 break 386 } 387 } 388 389 if effectiveCapsLine == "" { 390 t.Fatal("Couldn't find effective caps: ", outputStatus) 391 } 392 393 parts := strings.Split(effectiveCapsLine, ":") 394 effectiveCapsStr := strings.TrimSpace(parts[1]) 395 396 effectiveCaps, err := strconv.ParseUint(effectiveCapsStr, 16, 64) 397 if err != nil { 398 t.Fatal("Could not parse effective caps", err) 399 } 400 401 var netAdminMask uint64 402 var netAdminBit uint 403 netAdminBit = 12 // from capability.h 404 netAdminMask = 1 << netAdminBit 405 if effectiveCaps&netAdminMask != netAdminMask { 406 t.Fatal("CAP_NET_ADMIN is not set as expected") 407 } 408 } 409 410 func TestAdditionalGroups(t *testing.T) { 411 if testing.Short() { 412 return 413 } 414 root, err := newTestRoot() 415 ok(t, err) 416 defer os.RemoveAll(root) 417 418 rootfs, err := newRootfs() 419 ok(t, err) 420 defer remove(rootfs) 421 422 config := newTemplateConfig(rootfs) 423 424 factory, err := libcontainer.New(root, libcontainer.Cgroupfs) 425 ok(t, err) 426 427 container, err := factory.Create("test", config) 428 ok(t, err) 429 defer container.Destroy() 430 431 var stdout bytes.Buffer 432 pconfig := libcontainer.Process{ 433 Cwd: "/", 434 Args: []string{"sh", "-c", "id", "-Gn"}, 435 Env: standardEnvironment, 436 Stdin: nil, 437 Stdout: &stdout, 438 AdditionalGroups: []string{"plugdev", "audio"}, 439 } 440 err = container.Run(&pconfig) 441 ok(t, err) 442 443 // Wait for process 444 waitProcess(&pconfig, t) 445 446 outputGroups := string(stdout.Bytes()) 447 448 // Check that the groups output has the groups that we specified 449 if !strings.Contains(outputGroups, "audio") { 450 t.Fatalf("Listed groups do not contain the audio group as expected: %v", outputGroups) 451 } 452 453 if !strings.Contains(outputGroups, "plugdev") { 454 t.Fatalf("Listed groups do not contain the plugdev group as expected: %v", outputGroups) 455 } 456 } 457 458 func TestFreeze(t *testing.T) { 459 testFreeze(t, false) 460 } 461 462 func TestSystemdFreeze(t *testing.T) { 463 if !systemd.UseSystemd() { 464 t.Skip("Systemd is unsupported") 465 } 466 testFreeze(t, true) 467 } 468 469 func testFreeze(t *testing.T, systemd bool) { 470 if testing.Short() { 471 return 472 } 473 root, err := newTestRoot() 474 ok(t, err) 475 defer os.RemoveAll(root) 476 477 rootfs, err := newRootfs() 478 ok(t, err) 479 defer remove(rootfs) 480 481 config := newTemplateConfig(rootfs) 482 f := factory 483 if systemd { 484 f = systemdFactory 485 } 486 487 container, err := f.Create("test", config) 488 ok(t, err) 489 defer container.Destroy() 490 491 stdinR, stdinW, err := os.Pipe() 492 ok(t, err) 493 494 pconfig := &libcontainer.Process{ 495 Cwd: "/", 496 Args: []string{"cat"}, 497 Env: standardEnvironment, 498 Stdin: stdinR, 499 } 500 err = container.Run(pconfig) 501 stdinR.Close() 502 defer stdinW.Close() 503 ok(t, err) 504 505 err = container.Pause() 506 ok(t, err) 507 state, err := container.Status() 508 ok(t, err) 509 err = container.Resume() 510 ok(t, err) 511 if state != libcontainer.Paused { 512 t.Fatal("Unexpected state: ", state) 513 } 514 515 stdinW.Close() 516 waitProcess(pconfig, t) 517 } 518 519 func TestCpuShares(t *testing.T) { 520 testCpuShares(t, false) 521 } 522 523 func TestCpuSharesSystemd(t *testing.T) { 524 if !systemd.UseSystemd() { 525 t.Skip("Systemd is unsupported") 526 } 527 testCpuShares(t, true) 528 } 529 530 func testCpuShares(t *testing.T, systemd bool) { 531 if testing.Short() { 532 return 533 } 534 rootfs, err := newRootfs() 535 ok(t, err) 536 defer remove(rootfs) 537 538 config := newTemplateConfig(rootfs) 539 if systemd { 540 config.Cgroups.Parent = "system.slice" 541 } 542 config.Cgroups.Resources.CpuShares = 1 543 544 _, _, err = runContainer(config, "", "ps") 545 if err == nil { 546 t.Fatalf("runContainer should failed with invalid CpuShares") 547 } 548 } 549 550 func TestPids(t *testing.T) { 551 testPids(t, false) 552 } 553 554 func TestPidsSystemd(t *testing.T) { 555 if !systemd.UseSystemd() { 556 t.Skip("Systemd is unsupported") 557 } 558 testPids(t, true) 559 } 560 561 func testPids(t *testing.T, systemd bool) { 562 if testing.Short() { 563 return 564 } 565 566 rootfs, err := newRootfs() 567 ok(t, err) 568 defer remove(rootfs) 569 570 config := newTemplateConfig(rootfs) 571 if systemd { 572 config.Cgroups.Parent = "system.slice" 573 } 574 config.Cgroups.Resources.PidsLimit = -1 575 576 // Running multiple processes. 577 _, ret, err := runContainer(config, "", "/bin/sh", "-c", "/bin/true | /bin/true | /bin/true | /bin/true") 578 if err != nil && strings.Contains(err.Error(), "no such directory for pids.max") { 579 t.Skip("PIDs cgroup is unsupported") 580 } 581 ok(t, err) 582 583 if ret != 0 { 584 t.Fatalf("expected fork() to succeed with no pids limit") 585 } 586 587 // Enforce a permissive limit. This needs to be fairly hand-wavey due to the 588 // issues with running Go binaries with pids restrictions (see below). 589 config.Cgroups.Resources.PidsLimit = 64 590 _, ret, err = runContainer(config, "", "/bin/sh", "-c", ` 591 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 592 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 593 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 594 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true`) 595 if err != nil && strings.Contains(err.Error(), "no such directory for pids.max") { 596 t.Skip("PIDs cgroup is unsupported") 597 } 598 ok(t, err) 599 600 if ret != 0 { 601 t.Fatalf("expected fork() to succeed with permissive pids limit") 602 } 603 604 // Enforce a restrictive limit. 64 * /bin/true + 1 * shell should cause this 605 // to fail reliability. 606 config.Cgroups.Resources.PidsLimit = 64 607 out, _, err := runContainer(config, "", "/bin/sh", "-c", ` 608 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 609 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 610 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 611 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 612 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 613 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 614 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true | 615 /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | /bin/true | bin/true | /bin/true`) 616 if err != nil && strings.Contains(err.Error(), "no such directory for pids.max") { 617 t.Skip("PIDs cgroup is unsupported") 618 } 619 if err != nil && !strings.Contains(out.String(), "sh: can't fork") { 620 ok(t, err) 621 } 622 623 if err == nil { 624 t.Fatalf("expected fork() to fail with restrictive pids limit") 625 } 626 627 // Minimal restrictions are not really supported, due to quirks in using Go 628 // due to the fact that it spawns random processes. While we do our best with 629 // late setting cgroup values, it's just too unreliable with very small pids.max. 630 // As such, we don't test that case. YMMV. 631 } 632 633 func TestRunWithKernelMemory(t *testing.T) { 634 testRunWithKernelMemory(t, false) 635 } 636 637 func TestRunWithKernelMemorySystemd(t *testing.T) { 638 if !systemd.UseSystemd() { 639 t.Skip("Systemd is unsupported") 640 } 641 testRunWithKernelMemory(t, true) 642 } 643 644 func testRunWithKernelMemory(t *testing.T, systemd bool) { 645 if testing.Short() { 646 return 647 } 648 rootfs, err := newRootfs() 649 ok(t, err) 650 defer remove(rootfs) 651 652 config := newTemplateConfig(rootfs) 653 if systemd { 654 config.Cgroups.Parent = "system.slice" 655 } 656 config.Cgroups.Resources.KernelMemory = 52428800 657 658 _, _, err = runContainer(config, "", "ps") 659 if err != nil { 660 t.Fatalf("runContainer failed with kernel memory limit: %v", err) 661 } 662 } 663 664 func TestContainerState(t *testing.T) { 665 if testing.Short() { 666 return 667 } 668 root, err := newTestRoot() 669 if err != nil { 670 t.Fatal(err) 671 } 672 defer os.RemoveAll(root) 673 674 rootfs, err := newRootfs() 675 if err != nil { 676 t.Fatal(err) 677 } 678 defer remove(rootfs) 679 680 l, err := os.Readlink("/proc/1/ns/ipc") 681 if err != nil { 682 t.Fatal(err) 683 } 684 685 config := newTemplateConfig(rootfs) 686 config.Namespaces = configs.Namespaces([]configs.Namespace{ 687 {Type: configs.NEWNS}, 688 {Type: configs.NEWUTS}, 689 // host for IPC 690 //{Type: configs.NEWIPC}, 691 {Type: configs.NEWPID}, 692 {Type: configs.NEWNET}, 693 }) 694 695 container, err := factory.Create("test", config) 696 if err != nil { 697 t.Fatal(err) 698 } 699 defer container.Destroy() 700 701 stdinR, stdinW, err := os.Pipe() 702 if err != nil { 703 t.Fatal(err) 704 } 705 p := &libcontainer.Process{ 706 Cwd: "/", 707 Args: []string{"cat"}, 708 Env: standardEnvironment, 709 Stdin: stdinR, 710 } 711 err = container.Run(p) 712 if err != nil { 713 t.Fatal(err) 714 } 715 stdinR.Close() 716 defer stdinW.Close() 717 718 st, err := container.State() 719 if err != nil { 720 t.Fatal(err) 721 } 722 723 l1, err := os.Readlink(st.NamespacePaths[configs.NEWIPC]) 724 if err != nil { 725 t.Fatal(err) 726 } 727 if l1 != l { 728 t.Fatal("Container using non-host ipc namespace") 729 } 730 stdinW.Close() 731 waitProcess(p, t) 732 } 733 734 func TestPassExtraFiles(t *testing.T) { 735 if testing.Short() { 736 return 737 } 738 739 rootfs, err := newRootfs() 740 if err != nil { 741 t.Fatal(err) 742 } 743 defer remove(rootfs) 744 745 config := newTemplateConfig(rootfs) 746 747 container, err := factory.Create("test", config) 748 if err != nil { 749 t.Fatal(err) 750 } 751 defer container.Destroy() 752 753 var stdout bytes.Buffer 754 pipeout1, pipein1, err := os.Pipe() 755 pipeout2, pipein2, err := os.Pipe() 756 process := libcontainer.Process{ 757 Cwd: "/", 758 Args: []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"}, 759 Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, 760 ExtraFiles: []*os.File{pipein1, pipein2}, 761 Stdin: nil, 762 Stdout: &stdout, 763 } 764 err = container.Run(&process) 765 if err != nil { 766 t.Fatal(err) 767 } 768 769 waitProcess(&process, t) 770 771 out := string(stdout.Bytes()) 772 // fd 5 is the directory handle for /proc/$$/fd 773 if out != "0 1 2 3 4 5" { 774 t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to init, got '%s'", out) 775 } 776 var buf = []byte{0} 777 _, err = pipeout1.Read(buf) 778 if err != nil { 779 t.Fatal(err) 780 } 781 out1 := string(buf) 782 if out1 != "1" { 783 t.Fatalf("expected first pipe to receive '1', got '%s'", out1) 784 } 785 786 _, err = pipeout2.Read(buf) 787 if err != nil { 788 t.Fatal(err) 789 } 790 out2 := string(buf) 791 if out2 != "2" { 792 t.Fatalf("expected second pipe to receive '2', got '%s'", out2) 793 } 794 } 795 796 func TestMountCmds(t *testing.T) { 797 if testing.Short() { 798 return 799 } 800 root, err := newTestRoot() 801 if err != nil { 802 t.Fatal(err) 803 } 804 defer os.RemoveAll(root) 805 806 rootfs, err := newRootfs() 807 if err != nil { 808 t.Fatal(err) 809 } 810 defer remove(rootfs) 811 812 tmpDir, err := ioutil.TempDir("", "tmpdir") 813 if err != nil { 814 t.Fatal(err) 815 } 816 defer os.RemoveAll(tmpDir) 817 818 config := newTemplateConfig(rootfs) 819 config.Mounts = append(config.Mounts, &configs.Mount{ 820 Source: tmpDir, 821 Destination: "/tmp", 822 Device: "bind", 823 Flags: syscall.MS_BIND | syscall.MS_REC, 824 PremountCmds: []configs.Command{ 825 {Path: "touch", Args: []string{filepath.Join(tmpDir, "hello")}}, 826 {Path: "touch", Args: []string{filepath.Join(tmpDir, "world")}}, 827 }, 828 PostmountCmds: []configs.Command{ 829 {Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "hello"), filepath.Join(rootfs, "tmp", "hello-backup")}}, 830 {Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "world"), filepath.Join(rootfs, "tmp", "world-backup")}}, 831 }, 832 }) 833 834 container, err := factory.Create("test", config) 835 if err != nil { 836 t.Fatal(err) 837 } 838 defer container.Destroy() 839 840 pconfig := libcontainer.Process{ 841 Cwd: "/", 842 Args: []string{"sh", "-c", "env"}, 843 Env: standardEnvironment, 844 } 845 err = container.Run(&pconfig) 846 if err != nil { 847 t.Fatal(err) 848 } 849 850 // Wait for process 851 waitProcess(&pconfig, t) 852 853 entries, err := ioutil.ReadDir(tmpDir) 854 if err != nil { 855 t.Fatal(err) 856 } 857 expected := []string{"hello", "hello-backup", "world", "world-backup"} 858 for i, e := range entries { 859 if e.Name() != expected[i] { 860 t.Errorf("Got(%s), expect %s", e.Name(), expected[i]) 861 } 862 } 863 } 864 865 func TestSysctl(t *testing.T) { 866 if testing.Short() { 867 return 868 } 869 root, err := newTestRoot() 870 ok(t, err) 871 defer os.RemoveAll(root) 872 873 rootfs, err := newRootfs() 874 ok(t, err) 875 defer remove(rootfs) 876 877 config := newTemplateConfig(rootfs) 878 config.Sysctl = map[string]string{ 879 "kernel.shmmni": "8192", 880 } 881 882 container, err := factory.Create("test", config) 883 ok(t, err) 884 defer container.Destroy() 885 886 var stdout bytes.Buffer 887 pconfig := libcontainer.Process{ 888 Cwd: "/", 889 Args: []string{"sh", "-c", "cat /proc/sys/kernel/shmmni"}, 890 Env: standardEnvironment, 891 Stdin: nil, 892 Stdout: &stdout, 893 } 894 err = container.Run(&pconfig) 895 ok(t, err) 896 897 // Wait for process 898 waitProcess(&pconfig, t) 899 900 shmmniOutput := strings.TrimSpace(string(stdout.Bytes())) 901 if shmmniOutput != "8192" { 902 t.Fatalf("kernel.shmmni property expected to be 8192, but is %s", shmmniOutput) 903 } 904 } 905 906 func TestMountCgroupRO(t *testing.T) { 907 if testing.Short() { 908 return 909 } 910 rootfs, err := newRootfs() 911 ok(t, err) 912 defer remove(rootfs) 913 config := newTemplateConfig(rootfs) 914 915 config.Mounts = append(config.Mounts, &configs.Mount{ 916 Destination: "/sys/fs/cgroup", 917 Device: "cgroup", 918 Flags: defaultMountFlags | syscall.MS_RDONLY, 919 }) 920 921 buffers, exitCode, err := runContainer(config, "", "mount") 922 if err != nil { 923 t.Fatalf("%s: %s", buffers, err) 924 } 925 if exitCode != 0 { 926 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 927 } 928 mountInfo := buffers.Stdout.String() 929 lines := strings.Split(mountInfo, "\n") 930 for _, l := range lines { 931 if strings.HasPrefix(l, "tmpfs on /sys/fs/cgroup") { 932 if !strings.Contains(l, "ro") || 933 !strings.Contains(l, "nosuid") || 934 !strings.Contains(l, "nodev") || 935 !strings.Contains(l, "noexec") { 936 t.Fatalf("Mode expected to contain 'ro,nosuid,nodev,noexec': %s", l) 937 } 938 if !strings.Contains(l, "mode=755") { 939 t.Fatalf("Mode expected to contain 'mode=755': %s", l) 940 } 941 continue 942 } 943 if !strings.HasPrefix(l, "cgroup") { 944 continue 945 } 946 if !strings.Contains(l, "ro") || 947 !strings.Contains(l, "nosuid") || 948 !strings.Contains(l, "nodev") || 949 !strings.Contains(l, "noexec") { 950 t.Fatalf("Mode expected to contain 'ro,nosuid,nodev,noexec': %s", l) 951 } 952 } 953 } 954 955 func TestMountCgroupRW(t *testing.T) { 956 if testing.Short() { 957 return 958 } 959 rootfs, err := newRootfs() 960 ok(t, err) 961 defer remove(rootfs) 962 config := newTemplateConfig(rootfs) 963 964 config.Mounts = append(config.Mounts, &configs.Mount{ 965 Destination: "/sys/fs/cgroup", 966 Device: "cgroup", 967 Flags: defaultMountFlags, 968 }) 969 970 buffers, exitCode, err := runContainer(config, "", "mount") 971 if err != nil { 972 t.Fatalf("%s: %s", buffers, err) 973 } 974 if exitCode != 0 { 975 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 976 } 977 mountInfo := buffers.Stdout.String() 978 lines := strings.Split(mountInfo, "\n") 979 for _, l := range lines { 980 if strings.HasPrefix(l, "tmpfs on /sys/fs/cgroup") { 981 if !strings.Contains(l, "rw") || 982 !strings.Contains(l, "nosuid") || 983 !strings.Contains(l, "nodev") || 984 !strings.Contains(l, "noexec") { 985 t.Fatalf("Mode expected to contain 'rw,nosuid,nodev,noexec': %s", l) 986 } 987 if !strings.Contains(l, "mode=755") { 988 t.Fatalf("Mode expected to contain 'mode=755': %s", l) 989 } 990 continue 991 } 992 if !strings.HasPrefix(l, "cgroup") { 993 continue 994 } 995 if !strings.Contains(l, "rw") || 996 !strings.Contains(l, "nosuid") || 997 !strings.Contains(l, "nodev") || 998 !strings.Contains(l, "noexec") { 999 t.Fatalf("Mode expected to contain 'rw,nosuid,nodev,noexec': %s", l) 1000 } 1001 } 1002 } 1003 1004 func TestOomScoreAdj(t *testing.T) { 1005 if testing.Short() { 1006 return 1007 } 1008 root, err := newTestRoot() 1009 ok(t, err) 1010 defer os.RemoveAll(root) 1011 1012 rootfs, err := newRootfs() 1013 ok(t, err) 1014 defer remove(rootfs) 1015 1016 config := newTemplateConfig(rootfs) 1017 config.OomScoreAdj = 200 1018 1019 factory, err := libcontainer.New(root, libcontainer.Cgroupfs) 1020 ok(t, err) 1021 1022 container, err := factory.Create("test", config) 1023 ok(t, err) 1024 defer container.Destroy() 1025 1026 var stdout bytes.Buffer 1027 pconfig := libcontainer.Process{ 1028 Cwd: "/", 1029 Args: []string{"sh", "-c", "cat /proc/self/oom_score_adj"}, 1030 Env: standardEnvironment, 1031 Stdin: nil, 1032 Stdout: &stdout, 1033 } 1034 err = container.Run(&pconfig) 1035 ok(t, err) 1036 1037 // Wait for process 1038 waitProcess(&pconfig, t) 1039 outputOomScoreAdj := strings.TrimSpace(string(stdout.Bytes())) 1040 1041 // Check that the oom_score_adj matches the value that was set as part of config. 1042 if outputOomScoreAdj != strconv.Itoa(config.OomScoreAdj) { 1043 t.Fatalf("Expected oom_score_adj %d; got %q", config.OomScoreAdj, outputOomScoreAdj) 1044 } 1045 } 1046 1047 func TestHook(t *testing.T) { 1048 if testing.Short() { 1049 return 1050 } 1051 root, err := newTestRoot() 1052 ok(t, err) 1053 defer os.RemoveAll(root) 1054 1055 rootfs, err := newRootfs() 1056 ok(t, err) 1057 defer remove(rootfs) 1058 1059 config := newTemplateConfig(rootfs) 1060 expectedBundlePath := "/path/to/bundle/path" 1061 config.Labels = append(config.Labels, fmt.Sprintf("bundle=%s", expectedBundlePath)) 1062 config.Hooks = &configs.Hooks{ 1063 Prestart: []configs.Hook{ 1064 configs.NewFunctionHook(func(s configs.HookState) error { 1065 if s.BundlePath != expectedBundlePath { 1066 t.Fatalf("Expected prestart hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath) 1067 } 1068 1069 f, err := os.Create(filepath.Join(s.Root, "test")) 1070 if err != nil { 1071 return err 1072 } 1073 return f.Close() 1074 }), 1075 }, 1076 Poststart: []configs.Hook{ 1077 configs.NewFunctionHook(func(s configs.HookState) error { 1078 if s.BundlePath != expectedBundlePath { 1079 t.Fatalf("Expected poststart hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath) 1080 } 1081 1082 return ioutil.WriteFile(filepath.Join(s.Root, "test"), []byte("hello world"), 0755) 1083 }), 1084 }, 1085 Poststop: []configs.Hook{ 1086 configs.NewFunctionHook(func(s configs.HookState) error { 1087 if s.BundlePath != expectedBundlePath { 1088 t.Fatalf("Expected poststop hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath) 1089 } 1090 1091 return os.RemoveAll(filepath.Join(s.Root, "test")) 1092 }), 1093 }, 1094 } 1095 container, err := factory.Create("test", config) 1096 ok(t, err) 1097 1098 var stdout bytes.Buffer 1099 pconfig := libcontainer.Process{ 1100 Cwd: "/", 1101 Args: []string{"sh", "-c", "ls /test"}, 1102 Env: standardEnvironment, 1103 Stdin: nil, 1104 Stdout: &stdout, 1105 } 1106 err = container.Run(&pconfig) 1107 ok(t, err) 1108 1109 // Wait for process 1110 waitProcess(&pconfig, t) 1111 1112 outputLs := string(stdout.Bytes()) 1113 1114 // Check that the ls output has the expected file touched by the prestart hook 1115 if !strings.Contains(outputLs, "/test") { 1116 container.Destroy() 1117 t.Fatalf("ls output doesn't have the expected file: %s", outputLs) 1118 } 1119 1120 // Check that the file is written by the poststart hook 1121 testFilePath := filepath.Join(rootfs, "test") 1122 contents, err := ioutil.ReadFile(testFilePath) 1123 if err != nil { 1124 t.Fatalf("cannot read file '%s': %s", testFilePath, err) 1125 } 1126 if string(contents) != "hello world" { 1127 t.Fatalf("Expected test file to contain 'hello world'; got '%s'", string(contents)) 1128 } 1129 1130 if err := container.Destroy(); err != nil { 1131 t.Fatalf("container destroy %s", err) 1132 } 1133 fi, err := os.Stat(filepath.Join(rootfs, "test")) 1134 if err == nil || !os.IsNotExist(err) { 1135 t.Fatalf("expected file to not exist, got %s", fi.Name()) 1136 } 1137 } 1138 1139 func TestSTDIOPermissions(t *testing.T) { 1140 if testing.Short() { 1141 return 1142 } 1143 1144 rootfs, err := newRootfs() 1145 ok(t, err) 1146 defer remove(rootfs) 1147 config := newTemplateConfig(rootfs) 1148 buffers, exitCode, err := runContainer(config, "", "sh", "-c", "echo hi > /dev/stderr") 1149 ok(t, err) 1150 if exitCode != 0 { 1151 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 1152 } 1153 1154 if actual := strings.Trim(buffers.Stderr.String(), "\n"); actual != "hi" { 1155 t.Fatalf("stderr should equal be equal %q %q", actual, "hi") 1156 } 1157 } 1158 1159 func unmountOp(path string) error { 1160 if err := syscall.Unmount(path, syscall.MNT_DETACH); err != nil { 1161 return err 1162 } 1163 return nil 1164 } 1165 1166 // Launch container with rootfsPropagation in rslave mode. Also 1167 // bind mount a volume /mnt1host at /mnt1cont at the time of launch. Now do 1168 // another mount on host (/mnt1host/mnt2host) and this new mount should 1169 // propagate to container (/mnt1cont/mnt2host) 1170 func TestRootfsPropagationSlaveMount(t *testing.T) { 1171 var mountPropagated bool 1172 var dir1cont string 1173 var dir2cont string 1174 1175 dir1cont = "/root/mnt1cont" 1176 1177 if testing.Short() { 1178 return 1179 } 1180 rootfs, err := newRootfs() 1181 ok(t, err) 1182 defer remove(rootfs) 1183 config := newTemplateConfig(rootfs) 1184 1185 config.RootPropagation = syscall.MS_SLAVE | syscall.MS_REC 1186 1187 // Bind mount a volume 1188 dir1host, err := ioutil.TempDir("", "mnt1host") 1189 ok(t, err) 1190 defer os.RemoveAll(dir1host) 1191 1192 // Make this dir a "shared" mount point. This will make sure a 1193 // slave relationship can be established in container. 1194 err = syscall.Mount(dir1host, dir1host, "bind", syscall.MS_BIND|syscall.MS_REC, "") 1195 ok(t, err) 1196 err = syscall.Mount("", dir1host, "", syscall.MS_SHARED|syscall.MS_REC, "") 1197 ok(t, err) 1198 defer unmountOp(dir1host) 1199 1200 config.Mounts = append(config.Mounts, &configs.Mount{ 1201 Source: dir1host, 1202 Destination: dir1cont, 1203 Device: "bind", 1204 Flags: syscall.MS_BIND | syscall.MS_REC}) 1205 1206 // TODO: systemd specific processing 1207 f := factory 1208 1209 container, err := f.Create("testSlaveMount", config) 1210 ok(t, err) 1211 defer container.Destroy() 1212 1213 stdinR, stdinW, err := os.Pipe() 1214 ok(t, err) 1215 1216 pconfig := &libcontainer.Process{ 1217 Cwd: "/", 1218 Args: []string{"cat"}, 1219 Env: standardEnvironment, 1220 Stdin: stdinR, 1221 } 1222 1223 err = container.Run(pconfig) 1224 stdinR.Close() 1225 defer stdinW.Close() 1226 ok(t, err) 1227 1228 // Create mnt1host/mnt2host and bind mount itself on top of it. This 1229 // should be visible in container. 1230 dir2host, err := ioutil.TempDir(dir1host, "mnt2host") 1231 ok(t, err) 1232 defer os.RemoveAll(dir2host) 1233 1234 err = syscall.Mount(dir2host, dir2host, "bind", syscall.MS_BIND, "") 1235 defer unmountOp(dir2host) 1236 ok(t, err) 1237 1238 // Run "cat /proc/self/mountinfo" in container and look at mount points. 1239 var stdout2 bytes.Buffer 1240 1241 stdinR2, stdinW2, err := os.Pipe() 1242 ok(t, err) 1243 1244 pconfig2 := &libcontainer.Process{ 1245 Cwd: "/", 1246 Args: []string{"cat", "/proc/self/mountinfo"}, 1247 Env: standardEnvironment, 1248 Stdin: stdinR2, 1249 Stdout: &stdout2, 1250 } 1251 1252 err = container.Run(pconfig2) 1253 stdinR2.Close() 1254 defer stdinW2.Close() 1255 ok(t, err) 1256 1257 stdinW2.Close() 1258 waitProcess(pconfig2, t) 1259 stdinW.Close() 1260 waitProcess(pconfig, t) 1261 1262 mountPropagated = false 1263 dir2cont = filepath.Join(dir1cont, filepath.Base(dir2host)) 1264 1265 propagationInfo := string(stdout2.Bytes()) 1266 lines := strings.Split(propagationInfo, "\n") 1267 for _, l := range lines { 1268 linefields := strings.Split(l, " ") 1269 if len(linefields) < 5 { 1270 continue 1271 } 1272 1273 if linefields[4] == dir2cont { 1274 mountPropagated = true 1275 break 1276 } 1277 } 1278 1279 if mountPropagated != true { 1280 t.Fatalf("Mount on host %s did not propagate in container at %s\n", dir2host, dir2cont) 1281 } 1282 } 1283 1284 // Launch container with rootfsPropagation 0 so no propagation flags are 1285 // applied. Also bind mount a volume /mnt1host at /mnt1cont at the time of 1286 // launch. Now do a mount in container (/mnt1cont/mnt2cont) and this new 1287 // mount should propagate to host (/mnt1host/mnt2cont) 1288 1289 func TestRootfsPropagationSharedMount(t *testing.T) { 1290 var dir1cont string 1291 var dir2cont string 1292 1293 dir1cont = "/root/mnt1cont" 1294 1295 if testing.Short() { 1296 return 1297 } 1298 rootfs, err := newRootfs() 1299 ok(t, err) 1300 defer remove(rootfs) 1301 config := newTemplateConfig(rootfs) 1302 config.RootPropagation = syscall.MS_PRIVATE 1303 1304 // Bind mount a volume 1305 dir1host, err := ioutil.TempDir("", "mnt1host") 1306 ok(t, err) 1307 defer os.RemoveAll(dir1host) 1308 1309 // Make this dir a "shared" mount point. This will make sure a 1310 // shared relationship can be established in container. 1311 err = syscall.Mount(dir1host, dir1host, "bind", syscall.MS_BIND|syscall.MS_REC, "") 1312 ok(t, err) 1313 err = syscall.Mount("", dir1host, "", syscall.MS_SHARED|syscall.MS_REC, "") 1314 ok(t, err) 1315 defer unmountOp(dir1host) 1316 1317 config.Mounts = append(config.Mounts, &configs.Mount{ 1318 Source: dir1host, 1319 Destination: dir1cont, 1320 Device: "bind", 1321 Flags: syscall.MS_BIND | syscall.MS_REC}) 1322 1323 // TODO: systemd specific processing 1324 f := factory 1325 1326 container, err := f.Create("testSharedMount", config) 1327 ok(t, err) 1328 defer container.Destroy() 1329 1330 stdinR, stdinW, err := os.Pipe() 1331 ok(t, err) 1332 1333 pconfig := &libcontainer.Process{ 1334 Cwd: "/", 1335 Args: []string{"cat"}, 1336 Env: standardEnvironment, 1337 Stdin: stdinR, 1338 } 1339 1340 err = container.Run(pconfig) 1341 stdinR.Close() 1342 defer stdinW.Close() 1343 ok(t, err) 1344 1345 // Create mnt1host/mnt2cont. This will become visible inside container 1346 // at mnt1cont/mnt2cont. Bind mount itself on top of it. This 1347 // should be visible on host now. 1348 dir2host, err := ioutil.TempDir(dir1host, "mnt2cont") 1349 ok(t, err) 1350 defer os.RemoveAll(dir2host) 1351 1352 dir2cont = filepath.Join(dir1cont, filepath.Base(dir2host)) 1353 1354 // Mount something in container and see if it is visible on host. 1355 var stdout2 bytes.Buffer 1356 1357 stdinR2, stdinW2, err := os.Pipe() 1358 ok(t, err) 1359 1360 // Provide CAP_SYS_ADMIN 1361 processCaps := append(config.Capabilities, "CAP_SYS_ADMIN") 1362 1363 pconfig2 := &libcontainer.Process{ 1364 Cwd: "/", 1365 Args: []string{"mount", "--bind", dir2cont, dir2cont}, 1366 Env: standardEnvironment, 1367 Stdin: stdinR2, 1368 Stdout: &stdout2, 1369 Capabilities: processCaps, 1370 } 1371 1372 err = container.Run(pconfig2) 1373 stdinR2.Close() 1374 defer stdinW2.Close() 1375 ok(t, err) 1376 1377 // Wait for process 1378 stdinW2.Close() 1379 waitProcess(pconfig2, t) 1380 stdinW.Close() 1381 waitProcess(pconfig, t) 1382 1383 defer unmountOp(dir2host) 1384 1385 // Check if mount is visible on host or not. 1386 out, err := exec.Command("findmnt", "-n", "-f", "-oTARGET", dir2host).CombinedOutput() 1387 outtrim := strings.TrimSpace(string(out)) 1388 if err != nil { 1389 t.Logf("findmnt error %q: %q", err, outtrim) 1390 } 1391 1392 if string(outtrim) != dir2host { 1393 t.Fatalf("Mount in container on %s did not propagate to host on %s. finmnt output=%s", dir2cont, dir2host, outtrim) 1394 } 1395 } 1396 1397 func TestPIDHost(t *testing.T) { 1398 if testing.Short() { 1399 return 1400 } 1401 1402 rootfs, err := newRootfs() 1403 ok(t, err) 1404 defer remove(rootfs) 1405 1406 l, err := os.Readlink("/proc/1/ns/pid") 1407 ok(t, err) 1408 1409 config := newTemplateConfig(rootfs) 1410 config.Namespaces.Remove(configs.NEWPID) 1411 buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/pid") 1412 ok(t, err) 1413 1414 if exitCode != 0 { 1415 t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) 1416 } 1417 1418 if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual != l { 1419 t.Fatalf("ipc link not equal to host link %q %q", actual, l) 1420 } 1421 } 1422 1423 func TestInitJoinPID(t *testing.T) { 1424 if testing.Short() { 1425 return 1426 } 1427 rootfs, err := newRootfs() 1428 ok(t, err) 1429 defer remove(rootfs) 1430 1431 // Execute a long-running container 1432 container1, err := newContainer(newTemplateConfig(rootfs)) 1433 ok(t, err) 1434 defer container1.Destroy() 1435 1436 stdinR1, stdinW1, err := os.Pipe() 1437 ok(t, err) 1438 init1 := &libcontainer.Process{ 1439 Cwd: "/", 1440 Args: []string{"cat"}, 1441 Env: standardEnvironment, 1442 Stdin: stdinR1, 1443 } 1444 err = container1.Run(init1) 1445 stdinR1.Close() 1446 defer stdinW1.Close() 1447 ok(t, err) 1448 1449 // get the state of the first container 1450 state1, err := container1.State() 1451 ok(t, err) 1452 pidns1 := state1.NamespacePaths[configs.NEWPID] 1453 1454 // Run a container inside the existing pidns but with different cgroups 1455 config2 := newTemplateConfig(rootfs) 1456 config2.Namespaces.Add(configs.NEWPID, pidns1) 1457 config2.Cgroups.Path = "integration/test2" 1458 container2, err := newContainerWithName("testCT2", config2) 1459 ok(t, err) 1460 defer container2.Destroy() 1461 1462 stdinR2, stdinW2, err := os.Pipe() 1463 ok(t, err) 1464 init2 := &libcontainer.Process{ 1465 Cwd: "/", 1466 Args: []string{"cat"}, 1467 Env: standardEnvironment, 1468 Stdin: stdinR2, 1469 } 1470 err = container2.Run(init2) 1471 stdinR2.Close() 1472 defer stdinW2.Close() 1473 ok(t, err) 1474 // get the state of the second container 1475 state2, err := container2.State() 1476 ok(t, err) 1477 1478 ns1, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", state1.InitProcessPid)) 1479 ok(t, err) 1480 ns2, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", state2.InitProcessPid)) 1481 ok(t, err) 1482 if ns1 != ns2 { 1483 t.Errorf("pidns(%s), wanted %s", ns2, ns1) 1484 } 1485 1486 // check that namespaces are not the same 1487 if reflect.DeepEqual(state2.NamespacePaths, state1.NamespacePaths) { 1488 t.Errorf("Namespaces(%v), original %v", state2.NamespacePaths, 1489 state1.NamespacePaths) 1490 } 1491 // check that pidns is joined correctly. The initial container process list 1492 // should contain the second container's init process 1493 buffers := newStdBuffers() 1494 ps := &libcontainer.Process{ 1495 Cwd: "/", 1496 Args: []string{"ps"}, 1497 Env: standardEnvironment, 1498 Stdout: buffers.Stdout, 1499 } 1500 err = container1.Run(ps) 1501 ok(t, err) 1502 waitProcess(ps, t) 1503 1504 // Stop init processes one by one. Stop the second container should 1505 // not stop the first. 1506 stdinW2.Close() 1507 waitProcess(init2, t) 1508 stdinW1.Close() 1509 waitProcess(init1, t) 1510 1511 out := strings.TrimSpace(buffers.Stdout.String()) 1512 // output of ps inside the initial PID namespace should have 1513 // 1 line of header, 1514 // 2 lines of init processes, 1515 // 1 line of ps process 1516 if len(strings.Split(out, "\n")) != 4 { 1517 t.Errorf("unexpected running process, output %q", out) 1518 } 1519 } 1520 1521 func TestInitJoinNetworkAndUser(t *testing.T) { 1522 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { 1523 t.Skip("userns is unsupported") 1524 } 1525 if testing.Short() { 1526 return 1527 } 1528 rootfs, err := newRootfs() 1529 ok(t, err) 1530 defer remove(rootfs) 1531 1532 // Execute a long-running container 1533 config1 := newTemplateConfig(rootfs) 1534 config1.UidMappings = []configs.IDMap{{0, 0, 1000}} 1535 config1.GidMappings = []configs.IDMap{{0, 0, 1000}} 1536 config1.Namespaces = append(config1.Namespaces, configs.Namespace{Type: configs.NEWUSER}) 1537 container1, err := newContainer(config1) 1538 ok(t, err) 1539 defer container1.Destroy() 1540 1541 stdinR1, stdinW1, err := os.Pipe() 1542 ok(t, err) 1543 init1 := &libcontainer.Process{ 1544 Cwd: "/", 1545 Args: []string{"cat"}, 1546 Env: standardEnvironment, 1547 Stdin: stdinR1, 1548 } 1549 err = container1.Run(init1) 1550 stdinR1.Close() 1551 defer stdinW1.Close() 1552 ok(t, err) 1553 1554 // get the state of the first container 1555 state1, err := container1.State() 1556 ok(t, err) 1557 netns1 := state1.NamespacePaths[configs.NEWNET] 1558 userns1 := state1.NamespacePaths[configs.NEWUSER] 1559 1560 // Run a container inside the existing pidns but with different cgroups 1561 rootfs2, err := newRootfs() 1562 ok(t, err) 1563 defer remove(rootfs2) 1564 1565 config2 := newTemplateConfig(rootfs2) 1566 config2.UidMappings = []configs.IDMap{{0, 0, 1000}} 1567 config2.GidMappings = []configs.IDMap{{0, 0, 1000}} 1568 config2.Namespaces.Add(configs.NEWNET, netns1) 1569 config2.Namespaces.Add(configs.NEWUSER, userns1) 1570 config2.Cgroups.Path = "integration/test2" 1571 container2, err := newContainerWithName("testCT2", config2) 1572 ok(t, err) 1573 defer container2.Destroy() 1574 1575 stdinR2, stdinW2, err := os.Pipe() 1576 ok(t, err) 1577 init2 := &libcontainer.Process{ 1578 Cwd: "/", 1579 Args: []string{"cat"}, 1580 Env: standardEnvironment, 1581 Stdin: stdinR2, 1582 } 1583 err = container2.Run(init2) 1584 stdinR2.Close() 1585 defer stdinW2.Close() 1586 ok(t, err) 1587 1588 // get the state of the second container 1589 state2, err := container2.State() 1590 ok(t, err) 1591 1592 for _, ns := range []string{"net", "user"} { 1593 ns1, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/%s", state1.InitProcessPid, ns)) 1594 ok(t, err) 1595 ns2, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/%s", state2.InitProcessPid, ns)) 1596 ok(t, err) 1597 if ns1 != ns2 { 1598 t.Errorf("%s(%s), wanted %s", ns, ns2, ns1) 1599 } 1600 } 1601 1602 // check that namespaces are not the same 1603 if reflect.DeepEqual(state2.NamespacePaths, state1.NamespacePaths) { 1604 t.Errorf("Namespaces(%v), original %v", state2.NamespacePaths, 1605 state1.NamespacePaths) 1606 } 1607 // Stop init processes one by one. Stop the second container should 1608 // not stop the first. 1609 stdinW2.Close() 1610 waitProcess(init2, t) 1611 stdinW1.Close() 1612 waitProcess(init1, t) 1613 } 1614 1615 func TestTmpfsCopyUp(t *testing.T) { 1616 if testing.Short() { 1617 return 1618 } 1619 root, err := newTestRoot() 1620 ok(t, err) 1621 defer os.RemoveAll(root) 1622 1623 rootfs, err := newRootfs() 1624 ok(t, err) 1625 defer remove(rootfs) 1626 1627 config := newTemplateConfig(rootfs) 1628 1629 config.Mounts = append(config.Mounts, &configs.Mount{ 1630 Source: "tmpfs", 1631 Destination: "/etc", 1632 Device: "tmpfs", 1633 Extensions: configs.EXT_COPYUP, 1634 }) 1635 1636 factory, err := libcontainer.New(root, libcontainer.Cgroupfs) 1637 ok(t, err) 1638 1639 container, err := factory.Create("test", config) 1640 ok(t, err) 1641 defer container.Destroy() 1642 1643 var stdout bytes.Buffer 1644 pconfig := libcontainer.Process{ 1645 Args: []string{"ls", "/etc/passwd"}, 1646 Env: standardEnvironment, 1647 Stdin: nil, 1648 Stdout: &stdout, 1649 } 1650 err = container.Run(&pconfig) 1651 ok(t, err) 1652 1653 // Wait for process 1654 waitProcess(&pconfig, t) 1655 1656 outputLs := string(stdout.Bytes()) 1657 1658 // Check that the ls output has /etc/passwd 1659 if !strings.Contains(outputLs, "/etc/passwd") { 1660 t.Fatalf("/etc/passwd not copied up as expected: %v", outputLs) 1661 } 1662 }