github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/container/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 "bytes" 19 "flag" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "math" 24 "os" 25 "path" 26 "path/filepath" 27 "reflect" 28 "strconv" 29 "strings" 30 "testing" 31 "time" 32 33 "github.com/cenkalti/backoff" 34 specs "github.com/opencontainers/runtime-spec/specs-go" 35 "golang.org/x/sys/unix" 36 "github.com/SagerNet/gvisor/pkg/abi/linux" 37 "github.com/SagerNet/gvisor/pkg/bits" 38 "github.com/SagerNet/gvisor/pkg/log" 39 "github.com/SagerNet/gvisor/pkg/sentry/control" 40 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 41 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 42 "github.com/SagerNet/gvisor/pkg/sync" 43 "github.com/SagerNet/gvisor/pkg/test/testutil" 44 "github.com/SagerNet/gvisor/pkg/urpc" 45 "github.com/SagerNet/gvisor/runsc/boot/platforms" 46 "github.com/SagerNet/gvisor/runsc/config" 47 "github.com/SagerNet/gvisor/runsc/specutils" 48 ) 49 50 func TestMain(m *testing.M) { 51 log.SetLevel(log.Debug) 52 flag.Parse() 53 if err := testutil.ConfigureExePath(); err != nil { 54 panic(err.Error()) 55 } 56 specutils.MaybeRunAsRoot() 57 os.Exit(m.Run()) 58 } 59 60 func execute(cont *Container, name string, arg ...string) (unix.WaitStatus, error) { 61 args := &control.ExecArgs{ 62 Filename: name, 63 Argv: append([]string{name}, arg...), 64 } 65 return cont.executeSync(args) 66 } 67 68 func executeCombinedOutput(cont *Container, name string, arg ...string) ([]byte, error) { 69 r, w, err := os.Pipe() 70 if err != nil { 71 return nil, err 72 } 73 defer r.Close() 74 75 args := &control.ExecArgs{ 76 Filename: name, 77 Argv: append([]string{name}, arg...), 78 FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}}, 79 } 80 ws, err := cont.executeSync(args) 81 w.Close() 82 if err != nil { 83 return nil, err 84 } 85 if ws != 0 { 86 return nil, fmt.Errorf("exec failed, status: %v", ws) 87 } 88 89 out, err := ioutil.ReadAll(r) 90 return out, err 91 } 92 93 // executeSync synchronously executes a new process. 94 func (c *Container) executeSync(args *control.ExecArgs) (unix.WaitStatus, error) { 95 pid, err := c.Execute(args) 96 if err != nil { 97 return 0, fmt.Errorf("error executing: %v", err) 98 } 99 ws, err := c.WaitPID(pid) 100 if err != nil { 101 return 0, fmt.Errorf("error waiting: %v", err) 102 } 103 return ws, nil 104 } 105 106 // waitForProcessList waits for the given process list to show up in the container. 107 func waitForProcessList(cont *Container, want []*control.Process) error { 108 cb := func() error { 109 got, err := cont.Processes() 110 if err != nil { 111 err = fmt.Errorf("error getting process data from container: %w", err) 112 return &backoff.PermanentError{Err: err} 113 } 114 if !procListsEqual(got, want) { 115 return fmt.Errorf("container got process list: %s, want: %s", procListToString(got), procListToString(want)) 116 } 117 return nil 118 } 119 // Gives plenty of time as tests can run slow under --race. 120 return testutil.Poll(cb, 30*time.Second) 121 } 122 123 // waitForProcess waits for the given process to show up in the container. 124 func waitForProcess(cont *Container, want *control.Process) error { 125 cb := func() error { 126 gots, err := cont.Processes() 127 if err != nil { 128 err = fmt.Errorf("error getting process data from container: %w", err) 129 return &backoff.PermanentError{Err: err} 130 } 131 for _, got := range gots { 132 if procEqual(got, want) { 133 return nil 134 } 135 } 136 return fmt.Errorf("container got process list: %s, want: %+v", procListToString(gots), want) 137 } 138 // Gives plenty of time as tests can run slow under --race. 139 return testutil.Poll(cb, 30*time.Second) 140 } 141 142 func waitForProcessCount(cont *Container, want int) error { 143 cb := func() error { 144 pss, err := cont.Processes() 145 if err != nil { 146 err = fmt.Errorf("error getting process data from container: %w", err) 147 return &backoff.PermanentError{Err: err} 148 } 149 if got := len(pss); got != want { 150 log.Infof("Waiting for process count to reach %d. Current: %d", want, got) 151 return fmt.Errorf("wrong process count, got: %d, want: %d", got, want) 152 } 153 return nil 154 } 155 // Gives plenty of time as tests can run slow under --race. 156 return testutil.Poll(cb, 30*time.Second) 157 } 158 159 func blockUntilWaitable(pid int) error { 160 _, _, err := specutils.RetryEintr(func() (uintptr, uintptr, error) { 161 var err error 162 _, _, err1 := unix.Syscall6(unix.SYS_WAITID, 1, uintptr(pid), 0, unix.WEXITED|unix.WNOWAIT, 0, 0) 163 if err1 != 0 { 164 err = err1 165 } 166 return 0, 0, err 167 }) 168 return err 169 } 170 171 // execPS executes `ps` inside the container and return the processes. 172 func execPS(c *Container) ([]*control.Process, error) { 173 out, err := executeCombinedOutput(c, "/bin/ps", "-e") 174 if err != nil { 175 return nil, err 176 } 177 lines := strings.Split(string(out), "\n") 178 if len(lines) < 1 { 179 return nil, fmt.Errorf("missing header: %q", lines) 180 } 181 procs := make([]*control.Process, 0, len(lines)-1) 182 for _, line := range lines[1:] { 183 if len(line) == 0 { 184 continue 185 } 186 fields := strings.Fields(line) 187 if len(fields) != 4 { 188 return nil, fmt.Errorf("malformed line: %s", line) 189 } 190 pid, err := strconv.Atoi(fields[0]) 191 if err != nil { 192 return nil, err 193 } 194 cmd := fields[3] 195 // Fill only the fields we need thus far. 196 procs = append(procs, &control.Process{ 197 PID: kernel.ThreadID(pid), 198 Cmd: cmd, 199 }) 200 } 201 return procs, nil 202 } 203 204 // procListsEqual is used to check whether 2 Process lists are equal. Fields 205 // set to -1 in wants are ignored. Timestamp and threads fields are always 206 // ignored. 207 func procListsEqual(gots, wants []*control.Process) bool { 208 if len(gots) != len(wants) { 209 return false 210 } 211 for i := range gots { 212 if !procEqual(gots[i], wants[i]) { 213 return false 214 } 215 } 216 return true 217 } 218 219 func procEqual(got, want *control.Process) bool { 220 if want.UID != math.MaxUint32 && want.UID != got.UID { 221 return false 222 } 223 if want.PID != -1 && want.PID != got.PID { 224 return false 225 } 226 if want.PPID != -1 && want.PPID != got.PPID { 227 return false 228 } 229 if len(want.TTY) != 0 && want.TTY != got.TTY { 230 return false 231 } 232 if len(want.Cmd) != 0 && want.Cmd != got.Cmd { 233 return false 234 } 235 return true 236 } 237 238 type processBuilder struct { 239 process control.Process 240 } 241 242 func newProcessBuilder() *processBuilder { 243 return &processBuilder{ 244 process: control.Process{ 245 UID: math.MaxUint32, 246 PID: -1, 247 PPID: -1, 248 }, 249 } 250 } 251 252 func (p *processBuilder) Cmd(cmd string) *processBuilder { 253 p.process.Cmd = cmd 254 return p 255 } 256 257 func (p *processBuilder) PID(pid kernel.ThreadID) *processBuilder { 258 p.process.PID = pid 259 return p 260 } 261 262 func (p *processBuilder) PPID(ppid kernel.ThreadID) *processBuilder { 263 p.process.PPID = ppid 264 return p 265 } 266 267 func (p *processBuilder) UID(uid auth.KUID) *processBuilder { 268 p.process.UID = uid 269 return p 270 } 271 272 func (p *processBuilder) Process() *control.Process { 273 return &p.process 274 } 275 276 func procListToString(pl []*control.Process) string { 277 strs := make([]string, 0, len(pl)) 278 for _, p := range pl { 279 strs = append(strs, fmt.Sprintf("%+v", p)) 280 } 281 return fmt.Sprintf("[%s]", strings.Join(strs, ",")) 282 } 283 284 // createWriteableOutputFile creates an output file that can be read and 285 // written to in the sandbox. 286 func createWriteableOutputFile(path string) (*os.File, error) { 287 outputFile, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666) 288 if err != nil { 289 return nil, fmt.Errorf("error creating file: %q, %v", path, err) 290 } 291 292 // Chmod to allow writing after umask. 293 if err := outputFile.Chmod(0666); err != nil { 294 return nil, fmt.Errorf("error chmoding file: %q, %v", path, err) 295 } 296 return outputFile, nil 297 } 298 299 func waitForFileNotEmpty(f *os.File) error { 300 op := func() error { 301 fi, err := f.Stat() 302 if err != nil { 303 return err 304 } 305 if fi.Size() == 0 { 306 return fmt.Errorf("file %q is empty", f.Name()) 307 } 308 return nil 309 } 310 311 return testutil.Poll(op, 30*time.Second) 312 } 313 314 func waitForFileExist(path string) error { 315 op := func() error { 316 if _, err := os.Stat(path); os.IsNotExist(err) { 317 return err 318 } 319 return nil 320 } 321 322 return testutil.Poll(op, 30*time.Second) 323 } 324 325 // readOutputNum reads a file at given filepath and returns the int at the 326 // requested position. 327 func readOutputNum(file string, position int) (int, error) { 328 f, err := os.Open(file) 329 if err != nil { 330 return 0, fmt.Errorf("error opening file: %q, %v", file, err) 331 } 332 333 // Ensure that there is content in output file. 334 if err := waitForFileNotEmpty(f); err != nil { 335 return 0, fmt.Errorf("error waiting for output file: %v", err) 336 } 337 338 b, err := ioutil.ReadAll(f) 339 if err != nil { 340 return 0, fmt.Errorf("error reading file: %v", err) 341 } 342 if len(b) == 0 { 343 return 0, fmt.Errorf("error no content was read") 344 } 345 346 // Strip leading null bytes caused by file offset not being 0 upon restore. 347 b = bytes.Trim(b, "\x00") 348 nums := strings.Split(string(b), "\n") 349 350 if position >= len(nums) { 351 return 0, fmt.Errorf("position %v is not within the length of content %v", position, nums) 352 } 353 if position == -1 { 354 // Expectation of newline at the end of last position. 355 position = len(nums) - 2 356 } 357 num, err := strconv.Atoi(nums[position]) 358 if err != nil { 359 return 0, fmt.Errorf("error getting number from file: %v", err) 360 } 361 return num, nil 362 } 363 364 // run starts the sandbox and waits for it to exit, checking that the 365 // application succeeded. 366 func run(spec *specs.Spec, conf *config.Config) error { 367 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 368 if err != nil { 369 return fmt.Errorf("error setting up container: %v", err) 370 } 371 defer cleanup() 372 373 // Create, start and wait for the container. 374 args := Args{ 375 ID: testutil.RandomContainerID(), 376 Spec: spec, 377 BundleDir: bundleDir, 378 Attached: true, 379 } 380 ws, err := Run(conf, args) 381 if err != nil { 382 return fmt.Errorf("running container: %v", err) 383 } 384 if !ws.Exited() || ws.ExitStatus() != 0 { 385 return fmt.Errorf("container failed, waitStatus: %v", ws) 386 } 387 return nil 388 } 389 390 type configOption int 391 392 const ( 393 overlay configOption = iota 394 ptrace 395 kvm 396 nonExclusiveFS 397 ) 398 399 var ( 400 noOverlay = append(platformOptions, nonExclusiveFS) 401 all = append(noOverlay, overlay) 402 ) 403 404 func configsHelper(t *testing.T, opts ...configOption) map[string]*config.Config { 405 // Always load the default config. 406 cs := make(map[string]*config.Config) 407 testutil.TestConfig(t) 408 for _, o := range opts { 409 c := testutil.TestConfig(t) 410 switch o { 411 case overlay: 412 c.Overlay = true 413 cs["overlay"] = c 414 case ptrace: 415 c.Platform = platforms.Ptrace 416 cs["ptrace"] = c 417 case kvm: 418 c.Platform = platforms.KVM 419 cs["kvm"] = c 420 case nonExclusiveFS: 421 c.FileAccess = config.FileAccessShared 422 cs["non-exclusive"] = c 423 default: 424 panic(fmt.Sprintf("unknown config option %v", o)) 425 } 426 } 427 return cs 428 } 429 430 // configs generates different configurations to run tests. 431 // 432 // TODO(github.com/SagerNet/issue/1624): Remove VFS1 dimension. 433 func configs(t *testing.T, opts ...configOption) map[string]*config.Config { 434 all := configsHelper(t, opts...) 435 for key, value := range configsHelper(t, opts...) { 436 value.VFS2 = true 437 all[key+"VFS2"] = value 438 } 439 return all 440 } 441 442 // TestLifecycle tests the basic Create/Start/Signal/Destroy container lifecycle. 443 // It verifies after each step that the container can be loaded from disk, and 444 // has the correct status. 445 func TestLifecycle(t *testing.T) { 446 // Start the child reaper. 447 childReaper := &testutil.Reaper{} 448 childReaper.Start() 449 defer childReaper.Stop() 450 451 for name, conf := range configs(t, all...) { 452 t.Run(name, func(t *testing.T) { 453 // The container will just sleep for a long time. We will kill it before 454 // it finishes sleeping. 455 spec := testutil.NewSpecWithArgs("sleep", "100") 456 457 rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 458 if err != nil { 459 t.Fatalf("error setting up container: %v", err) 460 } 461 defer cleanup() 462 463 // expectedPL lists the expected process state of the container. 464 expectedPL := []*control.Process{ 465 newProcessBuilder().Cmd("sleep").Process(), 466 } 467 // Create the container. 468 args := Args{ 469 ID: testutil.RandomContainerID(), 470 Spec: spec, 471 BundleDir: bundleDir, 472 } 473 c, err := New(conf, args) 474 if err != nil { 475 t.Fatalf("error creating container: %v", err) 476 } 477 defer c.Destroy() 478 479 // Load the container from disk and check the status. 480 c, err = Load(rootDir, FullID{ContainerID: args.ID}, LoadOpts{}) 481 if err != nil { 482 t.Fatalf("error loading container: %v", err) 483 } 484 if got, want := c.Status, Created; got != want { 485 t.Errorf("container status got %v, want %v", got, want) 486 } 487 488 // List should return the container id. 489 ids, err := List(rootDir) 490 if err != nil { 491 t.Fatalf("error listing containers: %v", err) 492 } 493 fullID := FullID{ 494 SandboxID: args.ID, 495 ContainerID: args.ID, 496 } 497 if got, want := ids, []FullID{fullID}; !reflect.DeepEqual(got, want) { 498 t.Errorf("container list got %v, want %v", got, want) 499 } 500 501 // Start the container. 502 if err := c.Start(conf); err != nil { 503 t.Fatalf("error starting container: %v", err) 504 } 505 506 // Load the container from disk and check the status. 507 c, err = Load(rootDir, fullID, LoadOpts{Exact: true}) 508 if err != nil { 509 t.Fatalf("error loading container: %v", err) 510 } 511 if got, want := c.Status, Running; got != want { 512 t.Errorf("container status got %v, want %v", got, want) 513 } 514 515 // Verify that "sleep 100" is running. 516 if err := waitForProcessList(c, expectedPL); err != nil { 517 t.Error(err) 518 } 519 520 // Wait on the container. 521 ch := make(chan error) 522 go func() { 523 ws, err := c.Wait() 524 if err != nil { 525 ch <- err 526 } 527 if got, want := ws.Signal(), unix.SIGTERM; got != want { 528 ch <- fmt.Errorf("got signal %v, want %v", got, want) 529 } 530 ch <- nil 531 }() 532 533 // Wait a bit to ensure that we've started waiting on 534 // the container before we signal. 535 time.Sleep(time.Second) 536 537 // Send the container a SIGTERM which will cause it to stop. 538 if err := c.SignalContainer(unix.SIGTERM, false); err != nil { 539 t.Fatalf("error sending signal %v to container: %v", unix.SIGTERM, err) 540 } 541 542 // Wait for it to die. 543 if err := <-ch; err != nil { 544 t.Fatalf("error waiting for container: %v", err) 545 } 546 547 // Load the container from disk and check the status. 548 c, err = Load(rootDir, fullID, LoadOpts{Exact: true}) 549 if err != nil { 550 t.Fatalf("error loading container: %v", err) 551 } 552 if got, want := c.Status, Stopped; got != want { 553 t.Errorf("container status got %v, want %v", got, want) 554 } 555 556 // Destroy the container. 557 if err := c.Destroy(); err != nil { 558 t.Fatalf("error destroying container: %v", err) 559 } 560 561 // List should not return the container id. 562 ids, err = List(rootDir) 563 if err != nil { 564 t.Fatalf("error listing containers: %v", err) 565 } 566 if len(ids) != 0 { 567 t.Errorf("expected container list to be empty, but got %v", ids) 568 } 569 570 // Loading the container by id should fail. 571 if _, err = Load(rootDir, fullID, LoadOpts{Exact: true}); err == nil { 572 t.Errorf("expected loading destroyed container to fail, but it did not") 573 } 574 }) 575 } 576 } 577 578 // Test the we can execute the application with different path formats. 579 func TestExePath(t *testing.T) { 580 // Create two directories that will be prepended to PATH. 581 firstPath, err := ioutil.TempDir(testutil.TmpDir(), "first") 582 if err != nil { 583 t.Fatalf("error creating temporary directory: %v", err) 584 } 585 defer os.RemoveAll(firstPath) 586 secondPath, err := ioutil.TempDir(testutil.TmpDir(), "second") 587 if err != nil { 588 t.Fatalf("error creating temporary directory: %v", err) 589 } 590 defer os.RemoveAll(secondPath) 591 592 // Create two minimal executables in the second path, two of which 593 // will be masked by files in first path. 594 for _, p := range []string{"unmasked", "masked1", "masked2"} { 595 path := filepath.Join(secondPath, p) 596 f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0777) 597 if err != nil { 598 t.Fatalf("error opening path: %v", err) 599 } 600 defer f.Close() 601 if _, err := io.WriteString(f, "#!/bin/true\n"); err != nil { 602 t.Fatalf("error writing contents: %v", err) 603 } 604 } 605 606 // Create a non-executable file in the first path which masks a healthy 607 // executable in the second. 608 nonExecutable := filepath.Join(firstPath, "masked1") 609 f2, err := os.OpenFile(nonExecutable, os.O_CREATE|os.O_EXCL, 0666) 610 if err != nil { 611 t.Fatalf("error opening file: %v", err) 612 } 613 f2.Close() 614 615 // Create a non-regular file in the first path which masks a healthy 616 // executable in the second. 617 nonRegular := filepath.Join(firstPath, "masked2") 618 if err := os.Mkdir(nonRegular, 0777); err != nil { 619 t.Fatalf("error making directory: %v", err) 620 } 621 622 for name, conf := range configs(t, all...) { 623 t.Run(name, func(t *testing.T) { 624 for _, test := range []struct { 625 path string 626 success bool 627 }{ 628 {path: "true", success: true}, 629 {path: "bin/true", success: true}, 630 {path: "/bin/true", success: true}, 631 {path: "thisfiledoesntexit", success: false}, 632 {path: "bin/thisfiledoesntexit", success: false}, 633 {path: "/bin/thisfiledoesntexit", success: false}, 634 635 {path: "unmasked", success: true}, 636 {path: filepath.Join(firstPath, "unmasked"), success: false}, 637 {path: filepath.Join(secondPath, "unmasked"), success: true}, 638 639 {path: "masked1", success: true}, 640 {path: filepath.Join(firstPath, "masked1"), success: false}, 641 {path: filepath.Join(secondPath, "masked1"), success: true}, 642 643 {path: "masked2", success: true}, 644 {path: filepath.Join(firstPath, "masked2"), success: false}, 645 {path: filepath.Join(secondPath, "masked2"), success: true}, 646 } { 647 t.Run(fmt.Sprintf("path=%s,success=%t", test.path, test.success), func(t *testing.T) { 648 spec := testutil.NewSpecWithArgs(test.path) 649 spec.Process.Env = []string{ 650 fmt.Sprintf("PATH=%s:%s:%s", firstPath, secondPath, os.Getenv("PATH")), 651 } 652 653 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 654 if err != nil { 655 t.Fatalf("exec: error setting up container: %v", err) 656 } 657 defer cleanup() 658 659 args := Args{ 660 ID: testutil.RandomContainerID(), 661 Spec: spec, 662 BundleDir: bundleDir, 663 Attached: true, 664 } 665 ws, err := Run(conf, args) 666 667 if test.success { 668 if err != nil { 669 t.Errorf("exec: error running container: %v", err) 670 } 671 if ws.ExitStatus() != 0 { 672 t.Errorf("exec: got exit status %v want %v", ws.ExitStatus(), 0) 673 } 674 } else { 675 if err == nil { 676 t.Errorf("exec: got: no error, want: error") 677 } 678 } 679 }) 680 } 681 }) 682 } 683 } 684 685 // Test the we can retrieve the application exit status from the container. 686 func TestAppExitStatus(t *testing.T) { 687 doAppExitStatus(t, false) 688 } 689 690 // This is TestAppExitStatus for VFSv2. 691 func TestAppExitStatusVFS2(t *testing.T) { 692 doAppExitStatus(t, true) 693 } 694 695 func doAppExitStatus(t *testing.T, vfs2 bool) { 696 // First container will succeed. 697 succSpec := testutil.NewSpecWithArgs("true") 698 conf := testutil.TestConfig(t) 699 conf.VFS2 = vfs2 700 _, bundleDir, cleanup, err := testutil.SetupContainer(succSpec, conf) 701 if err != nil { 702 t.Fatalf("error setting up container: %v", err) 703 } 704 defer cleanup() 705 706 args := Args{ 707 ID: testutil.RandomContainerID(), 708 Spec: succSpec, 709 BundleDir: bundleDir, 710 Attached: true, 711 } 712 ws, err := Run(conf, args) 713 if err != nil { 714 t.Fatalf("error running container: %v", err) 715 } 716 if ws.ExitStatus() != 0 { 717 t.Errorf("got exit status %v want %v", ws.ExitStatus(), 0) 718 } 719 720 // Second container exits with non-zero status. 721 wantStatus := 123 722 errSpec := testutil.NewSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus)) 723 724 _, bundleDir2, cleanup2, err := testutil.SetupContainer(errSpec, conf) 725 if err != nil { 726 t.Fatalf("error setting up container: %v", err) 727 } 728 defer cleanup2() 729 730 args2 := Args{ 731 ID: testutil.RandomContainerID(), 732 Spec: errSpec, 733 BundleDir: bundleDir2, 734 Attached: true, 735 } 736 ws, err = Run(conf, args2) 737 if err != nil { 738 t.Fatalf("error running container: %v", err) 739 } 740 if ws.ExitStatus() != wantStatus { 741 t.Errorf("got exit status %v want %v", ws.ExitStatus(), wantStatus) 742 } 743 } 744 745 // TestExec verifies that a container can exec a new program. 746 func TestExec(t *testing.T) { 747 for name, conf := range configs(t, all...) { 748 t.Run(name, func(t *testing.T) { 749 dir, err := ioutil.TempDir(testutil.TmpDir(), "exec-test") 750 if err != nil { 751 t.Fatalf("error creating temporary directory: %v", err) 752 } 753 // Note that some shells may exec the final command in a sequence as 754 // an optimization. We avoid this here by adding the exit 0. 755 cmd := fmt.Sprintf("ln -s /bin/true %q/symlink && sleep 100 && exit 0", dir) 756 spec := testutil.NewSpecWithArgs("sh", "-c", cmd) 757 758 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 759 if err != nil { 760 t.Fatalf("error setting up container: %v", err) 761 } 762 defer cleanup() 763 764 // Create and start the container. 765 args := Args{ 766 ID: testutil.RandomContainerID(), 767 Spec: spec, 768 BundleDir: bundleDir, 769 } 770 cont, err := New(conf, args) 771 if err != nil { 772 t.Fatalf("error creating container: %v", err) 773 } 774 defer cont.Destroy() 775 if err := cont.Start(conf); err != nil { 776 t.Fatalf("error starting container: %v", err) 777 } 778 779 // Wait until sleep is running to ensure the symlink was created. 780 expectedPL := []*control.Process{ 781 newProcessBuilder().Cmd("sh").Process(), 782 newProcessBuilder().Cmd("sleep").Process(), 783 } 784 if err := waitForProcessList(cont, expectedPL); err != nil { 785 t.Fatalf("waitForProcessList: %v", err) 786 } 787 788 for _, tc := range []struct { 789 name string 790 args control.ExecArgs 791 }{ 792 { 793 name: "complete", 794 args: control.ExecArgs{ 795 Filename: "/bin/true", 796 Argv: []string{"/bin/true"}, 797 }, 798 }, 799 { 800 name: "filename", 801 args: control.ExecArgs{ 802 Filename: "/bin/true", 803 }, 804 }, 805 { 806 name: "argv", 807 args: control.ExecArgs{ 808 Argv: []string{"/bin/true"}, 809 }, 810 }, 811 { 812 name: "filename resolution", 813 args: control.ExecArgs{ 814 Filename: "true", 815 Envv: []string{"PATH=/bin"}, 816 }, 817 }, 818 { 819 name: "argv resolution", 820 args: control.ExecArgs{ 821 Argv: []string{"true"}, 822 Envv: []string{"PATH=/bin"}, 823 }, 824 }, 825 { 826 name: "argv symlink", 827 args: control.ExecArgs{ 828 Argv: []string{filepath.Join(dir, "symlink")}, 829 }, 830 }, 831 { 832 name: "working dir", 833 args: control.ExecArgs{ 834 Argv: []string{"/bin/sh", "-c", `if [[ "${PWD}" != "/tmp" ]]; then exit 1; fi`}, 835 WorkingDirectory: "/tmp", 836 }, 837 }, 838 { 839 name: "user", 840 args: control.ExecArgs{ 841 Argv: []string{"/bin/sh", "-c", `if [[ "$(id -u)" != "343" ]]; then exit 1; fi`}, 842 KUID: 343, 843 }, 844 }, 845 { 846 name: "group", 847 args: control.ExecArgs{ 848 Argv: []string{"/bin/sh", "-c", `if [[ "$(id -g)" != "343" ]]; then exit 1; fi`}, 849 KGID: 343, 850 }, 851 }, 852 { 853 name: "env", 854 args: control.ExecArgs{ 855 Argv: []string{"/bin/sh", "-c", `if [[ "${FOO}" != "123" ]]; then exit 1; fi`}, 856 Envv: []string{"FOO=123"}, 857 }, 858 }, 859 } { 860 t.Run(tc.name, func(t *testing.T) { 861 // t.Parallel() 862 if ws, err := cont.executeSync(&tc.args); err != nil { 863 t.Fatalf("executeAsync(%+v): %v", tc.args, err) 864 } else if ws != 0 { 865 t.Fatalf("executeAsync(%+v) failed with exit: %v", tc.args, ws) 866 } 867 }) 868 } 869 870 // Test for exec failure with an non-existent file. 871 t.Run("nonexist", func(t *testing.T) { 872 // b/179114837 found by Syzkaller that causes nil pointer panic when 873 // trying to dec-ref an unix socket FD. 874 fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0) 875 if err != nil { 876 t.Fatal(err) 877 } 878 defer unix.Close(fds[0]) 879 880 _, err = cont.executeSync(&control.ExecArgs{ 881 Argv: []string{"/nonexist"}, 882 FilePayload: urpc.FilePayload{ 883 Files: []*os.File{os.NewFile(uintptr(fds[1]), "sock")}, 884 }, 885 }) 886 want := "failed to load /nonexist" 887 if err == nil || !strings.Contains(err.Error(), want) { 888 t.Errorf("executeSync: want err containing %q; got err = %q", want, err) 889 } 890 }) 891 }) 892 } 893 } 894 895 // TestExecProcList verifies that a container can exec a new program and it 896 // shows correcly in the process list. 897 func TestExecProcList(t *testing.T) { 898 for name, conf := range configs(t, all...) { 899 t.Run(name, func(t *testing.T) { 900 const uid = 343 901 spec := testutil.NewSpecWithArgs("sleep", "100") 902 903 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 904 if err != nil { 905 t.Fatalf("error setting up container: %v", err) 906 } 907 defer cleanup() 908 909 // Create and start the container. 910 args := Args{ 911 ID: testutil.RandomContainerID(), 912 Spec: spec, 913 BundleDir: bundleDir, 914 } 915 cont, err := New(conf, args) 916 if err != nil { 917 t.Fatalf("error creating container: %v", err) 918 } 919 defer cont.Destroy() 920 if err := cont.Start(conf); err != nil { 921 t.Fatalf("error starting container: %v", err) 922 } 923 924 execArgs := &control.ExecArgs{ 925 Filename: "/bin/sleep", 926 Argv: []string{"/bin/sleep", "5"}, 927 WorkingDirectory: "/", 928 KUID: uid, 929 } 930 931 // Verify that "sleep 100" and "sleep 5" are running after exec. First, 932 // start running exec (which blocks). 933 ch := make(chan error) 934 go func() { 935 exitStatus, err := cont.executeSync(execArgs) 936 if err != nil { 937 ch <- err 938 } else if exitStatus != 0 { 939 ch <- fmt.Errorf("failed with exit status: %v", exitStatus) 940 } else { 941 ch <- nil 942 } 943 }() 944 945 // expectedPL lists the expected process state of the container. 946 expectedPL := []*control.Process{ 947 newProcessBuilder().PID(1).PPID(0).Cmd("sleep").UID(0).Process(), 948 newProcessBuilder().PID(2).PPID(0).Cmd("sleep").UID(uid).Process(), 949 } 950 if err := waitForProcessList(cont, expectedPL); err != nil { 951 t.Fatalf("error waiting for processes: %v", err) 952 } 953 954 // Ensure that exec finished without error. 955 select { 956 case <-time.After(10 * time.Second): 957 t.Fatalf("container timed out waiting for exec to finish.") 958 case err := <-ch: 959 if err != nil { 960 t.Errorf("container failed to exec %v: %v", args, err) 961 } 962 } 963 }) 964 } 965 } 966 967 // TestKillPid verifies that we can signal individual exec'd processes. 968 func TestKillPid(t *testing.T) { 969 for name, conf := range configs(t, all...) { 970 t.Run(name, func(t *testing.T) { 971 app, err := testutil.FindFile("test/cmd/test_app/test_app") 972 if err != nil { 973 t.Fatal("error finding test_app:", err) 974 } 975 976 const nProcs = 4 977 spec := testutil.NewSpecWithArgs(app, "task-tree", "--depth", strconv.Itoa(nProcs-1), "--width=1", "--pause=true") 978 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 979 if err != nil { 980 t.Fatalf("error setting up container: %v", err) 981 } 982 defer cleanup() 983 984 // Create and start the container. 985 args := Args{ 986 ID: testutil.RandomContainerID(), 987 Spec: spec, 988 BundleDir: bundleDir, 989 } 990 cont, err := New(conf, args) 991 if err != nil { 992 t.Fatalf("error creating container: %v", err) 993 } 994 defer cont.Destroy() 995 if err := cont.Start(conf); err != nil { 996 t.Fatalf("error starting container: %v", err) 997 } 998 999 // Verify that all processes are running. 1000 if err := waitForProcessCount(cont, nProcs); err != nil { 1001 t.Fatalf("timed out waiting for processes to start: %v", err) 1002 } 1003 1004 // Kill the child process with the largest PID. 1005 procs, err := cont.Processes() 1006 if err != nil { 1007 t.Fatalf("failed to get process list: %v", err) 1008 } 1009 var pid int32 1010 for _, p := range procs { 1011 if pid < int32(p.PID) { 1012 pid = int32(p.PID) 1013 } 1014 } 1015 if err := cont.SignalProcess(unix.SIGKILL, pid); err != nil { 1016 t.Fatalf("failed to signal process %d: %v", pid, err) 1017 } 1018 1019 // Verify that one process is gone. 1020 if err := waitForProcessCount(cont, nProcs-1); err != nil { 1021 t.Fatalf("error waiting for processes: %v", err) 1022 } 1023 1024 procs, err = cont.Processes() 1025 if err != nil { 1026 t.Fatalf("failed to get process list: %v", err) 1027 } 1028 for _, p := range procs { 1029 if pid == int32(p.PID) { 1030 t.Fatalf("pid %d is still alive, which should be killed", pid) 1031 } 1032 } 1033 }) 1034 } 1035 } 1036 1037 // TestCheckpointRestore creates a container that continuously writes successive 1038 // integers to a file. To test checkpoint and restore functionality, the 1039 // container is checkpointed and the last number printed to the file is 1040 // recorded. Then, it is restored in two new containers and the first number 1041 // printed from these containers is checked. Both should be the next consecutive 1042 // number after the last number from the checkpointed container. 1043 func TestCheckpointRestore(t *testing.T) { 1044 // Skip overlay because test requires writing to host file. 1045 for name, conf := range configs(t, noOverlay...) { 1046 t.Run(name, func(t *testing.T) { 1047 dir, err := ioutil.TempDir(testutil.TmpDir(), "checkpoint-test") 1048 if err != nil { 1049 t.Fatalf("ioutil.TempDir failed: %v", err) 1050 } 1051 defer os.RemoveAll(dir) 1052 if err := os.Chmod(dir, 0777); err != nil { 1053 t.Fatalf("error chmoding file: %q, %v", dir, err) 1054 } 1055 1056 outputPath := filepath.Join(dir, "output") 1057 outputFile, err := createWriteableOutputFile(outputPath) 1058 if err != nil { 1059 t.Fatalf("error creating output file: %v", err) 1060 } 1061 defer outputFile.Close() 1062 1063 script := fmt.Sprintf("for ((i=0; ;i++)); do echo $i >> %q; sleep 1; done", outputPath) 1064 spec := testutil.NewSpecWithArgs("bash", "-c", script) 1065 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1066 if err != nil { 1067 t.Fatalf("error setting up container: %v", err) 1068 } 1069 defer cleanup() 1070 1071 // Create and start the container. 1072 args := Args{ 1073 ID: testutil.RandomContainerID(), 1074 Spec: spec, 1075 BundleDir: bundleDir, 1076 } 1077 cont, err := New(conf, args) 1078 if err != nil { 1079 t.Fatalf("error creating container: %v", err) 1080 } 1081 defer cont.Destroy() 1082 if err := cont.Start(conf); err != nil { 1083 t.Fatalf("error starting container: %v", err) 1084 } 1085 1086 // Set the image path, which is where the checkpoint image will be saved. 1087 imagePath := filepath.Join(dir, "test-image-file") 1088 1089 // Create the image file and open for writing. 1090 file, err := os.OpenFile(imagePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644) 1091 if err != nil { 1092 t.Fatalf("error opening new file at imagePath: %v", err) 1093 } 1094 defer file.Close() 1095 1096 // Wait until application has ran. 1097 if err := waitForFileNotEmpty(outputFile); err != nil { 1098 t.Fatalf("Failed to wait for output file: %v", err) 1099 } 1100 1101 // Checkpoint running container; save state into new file. 1102 if err := cont.Checkpoint(file); err != nil { 1103 t.Fatalf("error checkpointing container to empty file: %v", err) 1104 } 1105 defer os.RemoveAll(imagePath) 1106 1107 lastNum, err := readOutputNum(outputPath, -1) 1108 if err != nil { 1109 t.Fatalf("error with outputFile: %v", err) 1110 } 1111 1112 // Delete and recreate file before restoring. 1113 if err := os.Remove(outputPath); err != nil { 1114 t.Fatalf("error removing file") 1115 } 1116 outputFile2, err := createWriteableOutputFile(outputPath) 1117 if err != nil { 1118 t.Fatalf("error creating output file: %v", err) 1119 } 1120 defer outputFile2.Close() 1121 1122 // Restore into a new container. 1123 args2 := Args{ 1124 ID: testutil.RandomContainerID(), 1125 Spec: spec, 1126 BundleDir: bundleDir, 1127 } 1128 cont2, err := New(conf, args2) 1129 if err != nil { 1130 t.Fatalf("error creating container: %v", err) 1131 } 1132 defer cont2.Destroy() 1133 1134 if err := cont2.Restore(spec, conf, imagePath); err != nil { 1135 t.Fatalf("error restoring container: %v", err) 1136 } 1137 1138 // Wait until application has ran. 1139 if err := waitForFileNotEmpty(outputFile2); err != nil { 1140 t.Fatalf("Failed to wait for output file: %v", err) 1141 } 1142 1143 firstNum, err := readOutputNum(outputPath, 0) 1144 if err != nil { 1145 t.Fatalf("error with outputFile: %v", err) 1146 } 1147 1148 // Check that lastNum is one less than firstNum and that the container picks 1149 // up from where it left off. 1150 if lastNum+1 != firstNum { 1151 t.Errorf("error numbers not in order, previous: %d, next: %d", lastNum, firstNum) 1152 } 1153 cont2.Destroy() 1154 1155 // Restore into another container! 1156 // Delete and recreate file before restoring. 1157 if err := os.Remove(outputPath); err != nil { 1158 t.Fatalf("error removing file") 1159 } 1160 outputFile3, err := createWriteableOutputFile(outputPath) 1161 if err != nil { 1162 t.Fatalf("error creating output file: %v", err) 1163 } 1164 defer outputFile3.Close() 1165 1166 // Restore into a new container. 1167 args3 := Args{ 1168 ID: testutil.RandomContainerID(), 1169 Spec: spec, 1170 BundleDir: bundleDir, 1171 } 1172 cont3, err := New(conf, args3) 1173 if err != nil { 1174 t.Fatalf("error creating container: %v", err) 1175 } 1176 defer cont3.Destroy() 1177 1178 if err := cont3.Restore(spec, conf, imagePath); err != nil { 1179 t.Fatalf("error restoring container: %v", err) 1180 } 1181 1182 // Wait until application has ran. 1183 if err := waitForFileNotEmpty(outputFile3); err != nil { 1184 t.Fatalf("Failed to wait for output file: %v", err) 1185 } 1186 1187 firstNum2, err := readOutputNum(outputPath, 0) 1188 if err != nil { 1189 t.Fatalf("error with outputFile: %v", err) 1190 } 1191 1192 // Check that lastNum is one less than firstNum and that the container picks 1193 // up from where it left off. 1194 if lastNum+1 != firstNum2 { 1195 t.Errorf("error numbers not in order, previous: %d, next: %d", lastNum, firstNum2) 1196 } 1197 cont3.Destroy() 1198 }) 1199 } 1200 } 1201 1202 // TestUnixDomainSockets checks that Checkpoint/Restore works in cases 1203 // with filesystem Unix Domain Socket use. 1204 func TestUnixDomainSockets(t *testing.T) { 1205 // Skip overlay because test requires writing to host file. 1206 for name, conf := range configs(t, noOverlay...) { 1207 t.Run(name, func(t *testing.T) { 1208 // UDS path is limited to 108 chars for compatibility with older systems. 1209 // Use '/tmp' (instead of testutil.TmpDir) to ensure the size limit is 1210 // not exceeded. Assumes '/tmp' exists in the system. 1211 dir, err := ioutil.TempDir("/tmp", "uds-test") 1212 if err != nil { 1213 t.Fatalf("ioutil.TempDir failed: %v", err) 1214 } 1215 defer os.RemoveAll(dir) 1216 1217 outputPath := filepath.Join(dir, "uds_output") 1218 outputFile, err := os.OpenFile(outputPath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666) 1219 if err != nil { 1220 t.Fatalf("error creating output file: %v", err) 1221 } 1222 defer outputFile.Close() 1223 1224 app, err := testutil.FindFile("test/cmd/test_app/test_app") 1225 if err != nil { 1226 t.Fatal("error finding test_app:", err) 1227 } 1228 1229 socketPath := filepath.Join(dir, "uds_socket") 1230 defer os.Remove(socketPath) 1231 1232 spec := testutil.NewSpecWithArgs(app, "uds", "--file", outputPath, "--socket", socketPath) 1233 spec.Process.User = specs.User{ 1234 UID: uint32(os.Getuid()), 1235 GID: uint32(os.Getgid()), 1236 } 1237 spec.Mounts = []specs.Mount{{ 1238 Type: "bind", 1239 Destination: dir, 1240 Source: dir, 1241 }} 1242 1243 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1244 if err != nil { 1245 t.Fatalf("error setting up container: %v", err) 1246 } 1247 defer cleanup() 1248 1249 // Create and start the container. 1250 args := Args{ 1251 ID: testutil.RandomContainerID(), 1252 Spec: spec, 1253 BundleDir: bundleDir, 1254 } 1255 cont, err := New(conf, args) 1256 if err != nil { 1257 t.Fatalf("error creating container: %v", err) 1258 } 1259 defer cont.Destroy() 1260 if err := cont.Start(conf); err != nil { 1261 t.Fatalf("error starting container: %v", err) 1262 } 1263 1264 // Set the image path, the location where the checkpoint image will be saved. 1265 imagePath := filepath.Join(dir, "test-image-file") 1266 1267 // Create the image file and open for writing. 1268 file, err := os.OpenFile(imagePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644) 1269 if err != nil { 1270 t.Fatalf("error opening new file at imagePath: %v", err) 1271 } 1272 defer file.Close() 1273 defer os.RemoveAll(imagePath) 1274 1275 // Wait until application has ran. 1276 if err := waitForFileNotEmpty(outputFile); err != nil { 1277 t.Fatalf("Failed to wait for output file: %v", err) 1278 } 1279 1280 // Checkpoint running container; save state into new file. 1281 if err := cont.Checkpoint(file); err != nil { 1282 t.Fatalf("error checkpointing container to empty file: %v", err) 1283 } 1284 1285 // Read last number outputted before checkpoint. 1286 lastNum, err := readOutputNum(outputPath, -1) 1287 if err != nil { 1288 t.Fatalf("error with outputFile: %v", err) 1289 } 1290 1291 // Delete and recreate file before restoring. 1292 if err := os.Remove(outputPath); err != nil { 1293 t.Fatalf("error removing file") 1294 } 1295 outputFile2, err := os.OpenFile(outputPath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666) 1296 if err != nil { 1297 t.Fatalf("error creating output file: %v", err) 1298 } 1299 defer outputFile2.Close() 1300 1301 // Restore into a new container. 1302 argsRestore := Args{ 1303 ID: testutil.RandomContainerID(), 1304 Spec: spec, 1305 BundleDir: bundleDir, 1306 } 1307 contRestore, err := New(conf, argsRestore) 1308 if err != nil { 1309 t.Fatalf("error creating container: %v", err) 1310 } 1311 defer contRestore.Destroy() 1312 1313 if err := contRestore.Restore(spec, conf, imagePath); err != nil { 1314 t.Fatalf("error restoring container: %v", err) 1315 } 1316 1317 // Wait until application has ran. 1318 if err := waitForFileNotEmpty(outputFile2); err != nil { 1319 t.Fatalf("Failed to wait for output file: %v", err) 1320 } 1321 1322 // Read first number outputted after restore. 1323 firstNum, err := readOutputNum(outputPath, 0) 1324 if err != nil { 1325 t.Fatalf("error with outputFile: %v", err) 1326 } 1327 1328 // Check that lastNum is one less than firstNum. 1329 if lastNum+1 != firstNum { 1330 t.Errorf("error numbers not consecutive, previous: %d, next: %d", lastNum, firstNum) 1331 } 1332 contRestore.Destroy() 1333 }) 1334 } 1335 } 1336 1337 // TestPauseResume tests that we can successfully pause and resume a container. 1338 // The container will keep touching a file to indicate it's running. The test 1339 // pauses the container, removes the file, and checks that it doesn't get 1340 // recreated. Then it resumes the container, verify that the file gets created 1341 // again. 1342 func TestPauseResume(t *testing.T) { 1343 for name, conf := range configs(t, noOverlay...) { 1344 t.Run(name, func(t *testing.T) { 1345 tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "lock") 1346 if err != nil { 1347 t.Fatalf("error creating temp dir: %v", err) 1348 } 1349 defer os.RemoveAll(tmpDir) 1350 1351 running := path.Join(tmpDir, "running") 1352 script := fmt.Sprintf("while [[ true ]]; do touch %q; sleep 0.1; done", running) 1353 spec := testutil.NewSpecWithArgs("/bin/bash", "-c", script) 1354 1355 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1356 if err != nil { 1357 t.Fatalf("error setting up container: %v", err) 1358 } 1359 defer cleanup() 1360 1361 // Create and start the container. 1362 args := Args{ 1363 ID: testutil.RandomContainerID(), 1364 Spec: spec, 1365 BundleDir: bundleDir, 1366 } 1367 cont, err := New(conf, args) 1368 if err != nil { 1369 t.Fatalf("error creating container: %v", err) 1370 } 1371 defer cont.Destroy() 1372 if err := cont.Start(conf); err != nil { 1373 t.Fatalf("error starting container: %v", err) 1374 } 1375 1376 // Wait until container starts running, observed by the existence of running 1377 // file. 1378 if err := waitForFileExist(running); err != nil { 1379 t.Errorf("error waiting for container to start: %v", err) 1380 } 1381 1382 // Pause the running container. 1383 if err := cont.Pause(); err != nil { 1384 t.Errorf("error pausing container: %v", err) 1385 } 1386 if got, want := cont.Status, Paused; got != want { 1387 t.Errorf("container status got %v, want %v", got, want) 1388 } 1389 1390 if err := os.Remove(running); err != nil { 1391 t.Fatalf("os.Remove(%q) failed: %v", running, err) 1392 } 1393 // Script touches the file every 100ms. Give a bit a time for it to run to 1394 // catch the case that pause didn't work. 1395 time.Sleep(200 * time.Millisecond) 1396 if _, err := os.Stat(running); !os.IsNotExist(err) { 1397 t.Fatalf("container did not pause: file exist check: %v", err) 1398 } 1399 1400 // Resume the running container. 1401 if err := cont.Resume(); err != nil { 1402 t.Errorf("error pausing container: %v", err) 1403 } 1404 if got, want := cont.Status, Running; got != want { 1405 t.Errorf("container status got %v, want %v", got, want) 1406 } 1407 1408 // Verify that the file is once again created by container. 1409 if err := waitForFileExist(running); err != nil { 1410 t.Fatalf("error resuming container: file exist check: %v", err) 1411 } 1412 }) 1413 } 1414 } 1415 1416 // TestPauseResumeStatus makes sure that the statuses are set correctly 1417 // with calls to pause and resume and that pausing and resuming only 1418 // occurs given the correct state. 1419 func TestPauseResumeStatus(t *testing.T) { 1420 spec := testutil.NewSpecWithArgs("sleep", "20") 1421 conf := testutil.TestConfig(t) 1422 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1423 if err != nil { 1424 t.Fatalf("error setting up container: %v", err) 1425 } 1426 defer cleanup() 1427 1428 // Create and start the container. 1429 args := Args{ 1430 ID: testutil.RandomContainerID(), 1431 Spec: spec, 1432 BundleDir: bundleDir, 1433 } 1434 cont, err := New(conf, args) 1435 if err != nil { 1436 t.Fatalf("error creating container: %v", err) 1437 } 1438 defer cont.Destroy() 1439 if err := cont.Start(conf); err != nil { 1440 t.Fatalf("error starting container: %v", err) 1441 } 1442 1443 // Pause the running container. 1444 if err := cont.Pause(); err != nil { 1445 t.Errorf("error pausing container: %v", err) 1446 } 1447 if got, want := cont.Status, Paused; got != want { 1448 t.Errorf("container status got %v, want %v", got, want) 1449 } 1450 1451 // Try to Pause again. Should cause error. 1452 if err := cont.Pause(); err == nil { 1453 t.Errorf("error pausing container that was already paused: %v", err) 1454 } 1455 if got, want := cont.Status, Paused; got != want { 1456 t.Errorf("container status got %v, want %v", got, want) 1457 } 1458 1459 // Resume the running container. 1460 if err := cont.Resume(); err != nil { 1461 t.Errorf("error resuming container: %v", err) 1462 } 1463 if got, want := cont.Status, Running; got != want { 1464 t.Errorf("container status got %v, want %v", got, want) 1465 } 1466 1467 // Try to resume again. Should cause error. 1468 if err := cont.Resume(); err == nil { 1469 t.Errorf("error resuming container already running: %v", err) 1470 } 1471 if got, want := cont.Status, Running; got != want { 1472 t.Errorf("container status got %v, want %v", got, want) 1473 } 1474 } 1475 1476 // TestCapabilities verifies that: 1477 // - Running exec as non-root UID and GID will result in an error (because the 1478 // executable file can't be read). 1479 // - Running exec as non-root with CAP_DAC_OVERRIDE succeeds because it skips 1480 // this check. 1481 func TestCapabilities(t *testing.T) { 1482 // Pick uid/gid different than ours. 1483 uid := auth.KUID(os.Getuid() + 1) 1484 gid := auth.KGID(os.Getgid() + 1) 1485 1486 for name, conf := range configs(t, all...) { 1487 t.Run(name, func(t *testing.T) { 1488 spec := testutil.NewSpecWithArgs("sleep", "100") 1489 rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1490 if err != nil { 1491 t.Fatalf("error setting up container: %v", err) 1492 } 1493 defer cleanup() 1494 1495 // Create and start the container. 1496 args := Args{ 1497 ID: testutil.RandomContainerID(), 1498 Spec: spec, 1499 BundleDir: bundleDir, 1500 } 1501 cont, err := New(conf, args) 1502 if err != nil { 1503 t.Fatalf("error creating container: %v", err) 1504 } 1505 defer cont.Destroy() 1506 if err := cont.Start(conf); err != nil { 1507 t.Fatalf("error starting container: %v", err) 1508 } 1509 1510 // expectedPL lists the expected process state of the container. 1511 expectedPL := []*control.Process{ 1512 newProcessBuilder().Cmd("sleep").Process(), 1513 } 1514 if err := waitForProcessList(cont, expectedPL); err != nil { 1515 t.Fatalf("Failed to wait for sleep to start, err: %v", err) 1516 } 1517 1518 // Create an executable that can't be run with the specified UID:GID. 1519 // This shouldn't be callable within the container until we add the 1520 // CAP_DAC_OVERRIDE capability to skip the access check. 1521 exePath := filepath.Join(rootDir, "exe") 1522 if err := ioutil.WriteFile(exePath, []byte("#!/bin/sh\necho hello"), 0770); err != nil { 1523 t.Fatalf("couldn't create executable: %v", err) 1524 } 1525 defer os.Remove(exePath) 1526 1527 // Need to traverse the intermediate directory. 1528 os.Chmod(rootDir, 0755) 1529 1530 execArgs := &control.ExecArgs{ 1531 Filename: exePath, 1532 Argv: []string{exePath}, 1533 WorkingDirectory: "/", 1534 KUID: uid, 1535 KGID: gid, 1536 Capabilities: &auth.TaskCapabilities{}, 1537 } 1538 1539 // "exe" should fail because we don't have the necessary permissions. 1540 if _, err := cont.executeSync(execArgs); err == nil { 1541 t.Fatalf("container executed without error, but an error was expected") 1542 } 1543 1544 // Now we run with the capability enabled and should succeed. 1545 execArgs.Capabilities = &auth.TaskCapabilities{ 1546 EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), 1547 } 1548 // "exe" should not fail this time. 1549 if _, err := cont.executeSync(execArgs); err != nil { 1550 t.Fatalf("container failed to exec %v: %v", args, err) 1551 } 1552 }) 1553 } 1554 } 1555 1556 // TestRunNonRoot checks that sandbox can be configured when running as 1557 // non-privileged user. 1558 func TestRunNonRoot(t *testing.T) { 1559 for name, conf := range configs(t, noOverlay...) { 1560 t.Run(name, func(t *testing.T) { 1561 spec := testutil.NewSpecWithArgs("/bin/true") 1562 1563 // Set a random user/group with no access to "blocked" dir. 1564 spec.Process.User.UID = 343 1565 spec.Process.User.GID = 2401 1566 spec.Process.Capabilities = nil 1567 1568 // User running inside container can't list '$TMP/blocked' and would fail to 1569 // mount it. 1570 dir, err := ioutil.TempDir(testutil.TmpDir(), "blocked") 1571 if err != nil { 1572 t.Fatalf("ioutil.TempDir() failed: %v", err) 1573 } 1574 if err := os.Chmod(dir, 0700); err != nil { 1575 t.Fatalf("os.MkDir(%q) failed: %v", dir, err) 1576 } 1577 dir = path.Join(dir, "test") 1578 if err := os.Mkdir(dir, 0755); err != nil { 1579 t.Fatalf("os.MkDir(%q) failed: %v", dir, err) 1580 } 1581 1582 src, err := ioutil.TempDir(testutil.TmpDir(), "src") 1583 if err != nil { 1584 t.Fatalf("ioutil.TempDir() failed: %v", err) 1585 } 1586 1587 spec.Mounts = append(spec.Mounts, specs.Mount{ 1588 Destination: dir, 1589 Source: src, 1590 Type: "bind", 1591 }) 1592 1593 if err := run(spec, conf); err != nil { 1594 t.Fatalf("error running sandbox: %v", err) 1595 } 1596 }) 1597 } 1598 } 1599 1600 // TestMountNewDir checks that runsc will create destination directory if it 1601 // doesn't exit. 1602 func TestMountNewDir(t *testing.T) { 1603 for name, conf := range configs(t, all...) { 1604 t.Run(name, func(t *testing.T) { 1605 root, err := ioutil.TempDir(testutil.TmpDir(), "root") 1606 if err != nil { 1607 t.Fatal("ioutil.TempDir() failed:", err) 1608 } 1609 1610 srcDir := path.Join(root, "src", "dir", "anotherdir") 1611 if err := os.MkdirAll(srcDir, 0755); err != nil { 1612 t.Fatalf("os.MkDir(%q) failed: %v", srcDir, err) 1613 } 1614 1615 mountDir := path.Join(root, "dir", "anotherdir") 1616 1617 spec := testutil.NewSpecWithArgs("/bin/ls", mountDir) 1618 spec.Mounts = append(spec.Mounts, specs.Mount{ 1619 Destination: mountDir, 1620 Source: srcDir, 1621 Type: "bind", 1622 }) 1623 // Extra points for creating the mount with a readonly root. 1624 spec.Root.Readonly = true 1625 1626 if err := run(spec, conf); err != nil { 1627 t.Fatalf("error running sandbox: %v", err) 1628 } 1629 }) 1630 } 1631 } 1632 1633 func TestReadonlyRoot(t *testing.T) { 1634 for name, conf := range configs(t, all...) { 1635 t.Run(name, func(t *testing.T) { 1636 spec := testutil.NewSpecWithArgs("sleep", "100") 1637 spec.Root.Readonly = true 1638 1639 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1640 if err != nil { 1641 t.Fatalf("error setting up container: %v", err) 1642 } 1643 defer cleanup() 1644 1645 args := Args{ 1646 ID: testutil.RandomContainerID(), 1647 Spec: spec, 1648 BundleDir: bundleDir, 1649 } 1650 c, err := New(conf, args) 1651 if err != nil { 1652 t.Fatalf("error creating container: %v", err) 1653 } 1654 defer c.Destroy() 1655 if err := c.Start(conf); err != nil { 1656 t.Fatalf("error starting container: %v", err) 1657 } 1658 1659 // Read mounts to check that root is readonly. 1660 out, err := executeCombinedOutput(c, "/bin/sh", "-c", "mount | grep ' / ' | grep -o -e '(.*)'") 1661 if err != nil { 1662 t.Fatalf("exec failed: %v", err) 1663 } 1664 t.Logf("root mount options: %q", out) 1665 if !strings.Contains(string(out), "ro") { 1666 t.Errorf("root not mounted readonly: %q", out) 1667 } 1668 1669 // Check that file cannot be created. 1670 ws, err := execute(c, "/bin/touch", "/foo") 1671 if err != nil { 1672 t.Fatalf("touch file in ro mount: %v", err) 1673 } 1674 if !ws.Exited() || unix.Errno(ws.ExitStatus()) != unix.EPERM { 1675 t.Fatalf("wrong waitStatus: %v", ws) 1676 } 1677 }) 1678 } 1679 } 1680 1681 func TestReadonlyMount(t *testing.T) { 1682 for name, conf := range configs(t, all...) { 1683 t.Run(name, func(t *testing.T) { 1684 dir, err := ioutil.TempDir(testutil.TmpDir(), "ro-mount") 1685 if err != nil { 1686 t.Fatalf("ioutil.TempDir() failed: %v", err) 1687 } 1688 spec := testutil.NewSpecWithArgs("sleep", "100") 1689 spec.Mounts = append(spec.Mounts, specs.Mount{ 1690 Destination: dir, 1691 Source: dir, 1692 Type: "bind", 1693 Options: []string{"ro"}, 1694 }) 1695 spec.Root.Readonly = false 1696 1697 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1698 if err != nil { 1699 t.Fatalf("error setting up container: %v", err) 1700 } 1701 defer cleanup() 1702 1703 args := Args{ 1704 ID: testutil.RandomContainerID(), 1705 Spec: spec, 1706 BundleDir: bundleDir, 1707 } 1708 c, err := New(conf, args) 1709 if err != nil { 1710 t.Fatalf("error creating container: %v", err) 1711 } 1712 defer c.Destroy() 1713 if err := c.Start(conf); err != nil { 1714 t.Fatalf("error starting container: %v", err) 1715 } 1716 1717 // Read mounts to check that volume is readonly. 1718 cmd := fmt.Sprintf("mount | grep ' %s ' | grep -o -e '(.*)'", dir) 1719 out, err := executeCombinedOutput(c, "/bin/sh", "-c", cmd) 1720 if err != nil { 1721 t.Fatalf("exec failed, err: %v", err) 1722 } 1723 t.Logf("mount options: %q", out) 1724 if !strings.Contains(string(out), "ro") { 1725 t.Errorf("volume not mounted readonly: %q", out) 1726 } 1727 1728 // Check that file cannot be created. 1729 ws, err := execute(c, "/bin/touch", path.Join(dir, "file")) 1730 if err != nil { 1731 t.Fatalf("touch file in ro mount: %v", err) 1732 } 1733 if !ws.Exited() || unix.Errno(ws.ExitStatus()) != unix.EPERM { 1734 t.Fatalf("wrong WaitStatus: %v", ws) 1735 } 1736 }) 1737 } 1738 } 1739 1740 func TestUIDMap(t *testing.T) { 1741 for name, conf := range configs(t, noOverlay...) { 1742 t.Run(name, func(t *testing.T) { 1743 testDir, err := ioutil.TempDir(testutil.TmpDir(), "test-mount") 1744 if err != nil { 1745 t.Fatalf("ioutil.TempDir() failed: %v", err) 1746 } 1747 defer os.RemoveAll(testDir) 1748 testFile := path.Join(testDir, "testfile") 1749 1750 spec := testutil.NewSpecWithArgs("touch", "/tmp/testfile") 1751 uid := os.Getuid() 1752 gid := os.Getgid() 1753 spec.Linux = &specs.Linux{ 1754 Namespaces: []specs.LinuxNamespace{ 1755 {Type: specs.UserNamespace}, 1756 {Type: specs.PIDNamespace}, 1757 {Type: specs.MountNamespace}, 1758 }, 1759 UIDMappings: []specs.LinuxIDMapping{ 1760 { 1761 ContainerID: 0, 1762 HostID: uint32(uid), 1763 Size: 1, 1764 }, 1765 }, 1766 GIDMappings: []specs.LinuxIDMapping{ 1767 { 1768 ContainerID: 0, 1769 HostID: uint32(gid), 1770 Size: 1, 1771 }, 1772 }, 1773 } 1774 1775 spec.Mounts = append(spec.Mounts, specs.Mount{ 1776 Destination: "/tmp", 1777 Source: testDir, 1778 Type: "bind", 1779 }) 1780 1781 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1782 if err != nil { 1783 t.Fatalf("error setting up container: %v", err) 1784 } 1785 defer cleanup() 1786 1787 // Create, start and wait for the container. 1788 args := Args{ 1789 ID: testutil.RandomContainerID(), 1790 Spec: spec, 1791 BundleDir: bundleDir, 1792 } 1793 c, err := New(conf, args) 1794 if err != nil { 1795 t.Fatalf("error creating container: %v", err) 1796 } 1797 defer c.Destroy() 1798 if err := c.Start(conf); err != nil { 1799 t.Fatalf("error starting container: %v", err) 1800 } 1801 1802 ws, err := c.Wait() 1803 if err != nil { 1804 t.Fatalf("error waiting on container: %v", err) 1805 } 1806 if !ws.Exited() || ws.ExitStatus() != 0 { 1807 t.Fatalf("container failed, waitStatus: %v", ws) 1808 } 1809 st := unix.Stat_t{} 1810 if err := unix.Stat(testFile, &st); err != nil { 1811 t.Fatalf("error stat /testfile: %v", err) 1812 } 1813 1814 if st.Uid != uint32(uid) || st.Gid != uint32(gid) { 1815 t.Fatalf("UID: %d (%d) GID: %d (%d)", st.Uid, uid, st.Gid, gid) 1816 } 1817 }) 1818 } 1819 } 1820 1821 // TestAbbreviatedIDs checks that runsc supports using abbreviated container 1822 // IDs in place of full IDs. 1823 func TestAbbreviatedIDs(t *testing.T) { 1824 doAbbreviatedIDsTest(t, false) 1825 } 1826 1827 func TestAbbreviatedIDsVFS2(t *testing.T) { 1828 doAbbreviatedIDsTest(t, true) 1829 } 1830 1831 func doAbbreviatedIDsTest(t *testing.T, vfs2 bool) { 1832 rootDir, cleanup, err := testutil.SetupRootDir() 1833 if err != nil { 1834 t.Fatalf("error creating root dir: %v", err) 1835 } 1836 defer cleanup() 1837 1838 conf := testutil.TestConfig(t) 1839 conf.RootDir = rootDir 1840 conf.VFS2 = vfs2 1841 1842 cids := []string{ 1843 "foo-" + testutil.RandomContainerID(), 1844 "bar-" + testutil.RandomContainerID(), 1845 "baz-" + testutil.RandomContainerID(), 1846 } 1847 for _, cid := range cids { 1848 spec := testutil.NewSpecWithArgs("sleep", "100") 1849 bundleDir, cleanup, err := testutil.SetupBundleDir(spec) 1850 if err != nil { 1851 t.Fatalf("error setting up container: %v", err) 1852 } 1853 defer cleanup() 1854 1855 // Create and start the container. 1856 args := Args{ 1857 ID: cid, 1858 Spec: spec, 1859 BundleDir: bundleDir, 1860 } 1861 cont, err := New(conf, args) 1862 if err != nil { 1863 t.Fatalf("error creating container: %v", err) 1864 } 1865 defer cont.Destroy() 1866 } 1867 1868 // These should all be unambigious. 1869 unambiguous := map[string]string{ 1870 "f": cids[0], 1871 cids[0]: cids[0], 1872 "bar": cids[1], 1873 cids[1]: cids[1], 1874 "baz": cids[2], 1875 cids[2]: cids[2], 1876 } 1877 for shortid, longid := range unambiguous { 1878 if _, err := Load(rootDir, FullID{ContainerID: shortid}, LoadOpts{}); err != nil { 1879 t.Errorf("%q should resolve to %q: %v", shortid, longid, err) 1880 } 1881 } 1882 1883 // These should be ambiguous. 1884 ambiguous := []string{ 1885 "b", 1886 "ba", 1887 } 1888 for _, shortid := range ambiguous { 1889 if s, err := Load(rootDir, FullID{ContainerID: shortid}, LoadOpts{}); err == nil { 1890 t.Errorf("%q should be ambiguous, but resolved to %q", shortid, s.ID) 1891 } 1892 } 1893 } 1894 1895 func TestGoferExits(t *testing.T) { 1896 doGoferExitTest(t, false) 1897 } 1898 1899 func TestGoferExitsVFS2(t *testing.T) { 1900 doGoferExitTest(t, true) 1901 } 1902 1903 func doGoferExitTest(t *testing.T, vfs2 bool) { 1904 spec := testutil.NewSpecWithArgs("/bin/sleep", "10000") 1905 conf := testutil.TestConfig(t) 1906 conf.VFS2 = vfs2 1907 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1908 1909 if err != nil { 1910 t.Fatalf("error setting up container: %v", err) 1911 } 1912 defer cleanup() 1913 1914 // Create and start the container. 1915 args := Args{ 1916 ID: testutil.RandomContainerID(), 1917 Spec: spec, 1918 BundleDir: bundleDir, 1919 } 1920 c, err := New(conf, args) 1921 if err != nil { 1922 t.Fatalf("error creating container: %v", err) 1923 } 1924 defer c.Destroy() 1925 if err := c.Start(conf); err != nil { 1926 t.Fatalf("error starting container: %v", err) 1927 } 1928 1929 // Kill sandbox and expect gofer to exit on its own. 1930 sandboxProc, err := os.FindProcess(c.Sandbox.Pid) 1931 if err != nil { 1932 t.Fatalf("error finding sandbox process: %v", err) 1933 } 1934 if err := sandboxProc.Kill(); err != nil { 1935 t.Fatalf("error killing sandbox process: %v", err) 1936 } 1937 1938 err = blockUntilWaitable(c.GoferPid) 1939 if err != nil && err != unix.ECHILD { 1940 t.Errorf("error waiting for gofer to exit: %v", err) 1941 } 1942 } 1943 1944 func TestRootNotMount(t *testing.T) { 1945 appSym, err := testutil.FindFile("test/cmd/test_app/test_app") 1946 if err != nil { 1947 t.Fatal("error finding test_app:", err) 1948 } 1949 1950 app, err := filepath.EvalSymlinks(appSym) 1951 if err != nil { 1952 t.Fatalf("error resolving %q symlink: %v", appSym, err) 1953 } 1954 log.Infof("App path %q is a symlink to %q", appSym, app) 1955 1956 static, err := testutil.IsStatic(app) 1957 if err != nil { 1958 t.Fatalf("error reading application binary: %v", err) 1959 } 1960 if !static { 1961 // This happens during race builds; we cannot map in shared 1962 // libraries also, so we need to skip the test. 1963 t.Skip() 1964 } 1965 1966 root := filepath.Dir(app) 1967 exe := "/" + filepath.Base(app) 1968 log.Infof("Executing %q in %q", exe, root) 1969 1970 spec := testutil.NewSpecWithArgs(exe, "help") 1971 spec.Root.Path = root 1972 spec.Root.Readonly = true 1973 spec.Mounts = nil 1974 1975 conf := testutil.TestConfig(t) 1976 if err := run(spec, conf); err != nil { 1977 t.Fatalf("error running sandbox: %v", err) 1978 } 1979 } 1980 1981 func TestUserLog(t *testing.T) { 1982 app, err := testutil.FindFile("test/cmd/test_app/test_app") 1983 if err != nil { 1984 t.Fatal("error finding test_app:", err) 1985 } 1986 1987 // sched_rr_get_interval - not implemented in gvisor. 1988 num := strconv.Itoa(unix.SYS_SCHED_RR_GET_INTERVAL) 1989 spec := testutil.NewSpecWithArgs(app, "syscall", "--syscall="+num) 1990 conf := testutil.TestConfig(t) 1991 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 1992 if err != nil { 1993 t.Fatalf("error setting up container: %v", err) 1994 } 1995 defer cleanup() 1996 1997 dir, err := ioutil.TempDir(testutil.TmpDir(), "user_log_test") 1998 if err != nil { 1999 t.Fatalf("error creating tmp dir: %v", err) 2000 } 2001 userLog := filepath.Join(dir, "user.log") 2002 2003 // Create, start and wait for the container. 2004 args := Args{ 2005 ID: testutil.RandomContainerID(), 2006 Spec: spec, 2007 BundleDir: bundleDir, 2008 UserLog: userLog, 2009 Attached: true, 2010 } 2011 ws, err := Run(conf, args) 2012 if err != nil { 2013 t.Fatalf("error running container: %v", err) 2014 } 2015 if !ws.Exited() || ws.ExitStatus() != 0 { 2016 t.Fatalf("container failed, waitStatus: %v", ws) 2017 } 2018 2019 out, err := ioutil.ReadFile(userLog) 2020 if err != nil { 2021 t.Fatalf("error opening user log file %q: %v", userLog, err) 2022 } 2023 if want := "Unsupported syscall sched_rr_get_interval("; !strings.Contains(string(out), want) { 2024 t.Errorf("user log file doesn't contain %q, out: %s", want, string(out)) 2025 } 2026 } 2027 2028 func TestWaitOnExitedSandbox(t *testing.T) { 2029 for name, conf := range configs(t, all...) { 2030 t.Run(name, func(t *testing.T) { 2031 // Run a shell that sleeps for 1 second and then exits with a 2032 // non-zero code. 2033 const wantExit = 17 2034 cmd := fmt.Sprintf("sleep 1; exit %d", wantExit) 2035 spec := testutil.NewSpecWithArgs("/bin/sh", "-c", cmd) 2036 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2037 if err != nil { 2038 t.Fatalf("error setting up container: %v", err) 2039 } 2040 defer cleanup() 2041 2042 // Create and Start the container. 2043 args := Args{ 2044 ID: testutil.RandomContainerID(), 2045 Spec: spec, 2046 BundleDir: bundleDir, 2047 } 2048 c, err := New(conf, args) 2049 if err != nil { 2050 t.Fatalf("error creating container: %v", err) 2051 } 2052 defer c.Destroy() 2053 if err := c.Start(conf); err != nil { 2054 t.Fatalf("error starting container: %v", err) 2055 } 2056 2057 // Wait on the sandbox. This will make an RPC to the sandbox 2058 // and get the actual exit status of the application. 2059 ws, err := c.Wait() 2060 if err != nil { 2061 t.Fatalf("error waiting on container: %v", err) 2062 } 2063 if got := ws.ExitStatus(); got != wantExit { 2064 t.Errorf("got exit status %d, want %d", got, wantExit) 2065 } 2066 2067 // Now the sandbox has exited, but the zombie sandbox process 2068 // still exists. Calling Wait() now will return the sandbox 2069 // exit status. 2070 ws, err = c.Wait() 2071 if err != nil { 2072 t.Fatalf("error waiting on container: %v", err) 2073 } 2074 if got := ws.ExitStatus(); got != wantExit { 2075 t.Errorf("got exit status %d, want %d", got, wantExit) 2076 } 2077 }) 2078 } 2079 } 2080 2081 func TestDestroyNotStarted(t *testing.T) { 2082 doDestroyNotStartedTest(t, false) 2083 } 2084 2085 func TestDestroyNotStartedVFS2(t *testing.T) { 2086 doDestroyNotStartedTest(t, true) 2087 } 2088 2089 func doDestroyNotStartedTest(t *testing.T, vfs2 bool) { 2090 spec := testutil.NewSpecWithArgs("/bin/sleep", "100") 2091 conf := testutil.TestConfig(t) 2092 conf.VFS2 = vfs2 2093 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2094 if err != nil { 2095 t.Fatalf("error setting up container: %v", err) 2096 } 2097 defer cleanup() 2098 2099 // Create the container and check that it can be destroyed. 2100 args := Args{ 2101 ID: testutil.RandomContainerID(), 2102 Spec: spec, 2103 BundleDir: bundleDir, 2104 } 2105 c, err := New(conf, args) 2106 if err != nil { 2107 t.Fatalf("error creating container: %v", err) 2108 } 2109 if err := c.Destroy(); err != nil { 2110 t.Fatalf("deleting non-started container failed: %v", err) 2111 } 2112 } 2113 2114 // TestDestroyStarting attempts to force a race between start and destroy. 2115 func TestDestroyStarting(t *testing.T) { 2116 doDestroyStartingTest(t, false) 2117 } 2118 2119 func TestDestroyStartedVFS2(t *testing.T) { 2120 doDestroyStartingTest(t, true) 2121 } 2122 2123 func doDestroyStartingTest(t *testing.T, vfs2 bool) { 2124 for i := 0; i < 10; i++ { 2125 spec := testutil.NewSpecWithArgs("/bin/sleep", "100") 2126 conf := testutil.TestConfig(t) 2127 conf.VFS2 = vfs2 2128 rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2129 if err != nil { 2130 t.Fatalf("error setting up container: %v", err) 2131 } 2132 defer cleanup() 2133 2134 // Create the container and check that it can be destroyed. 2135 args := Args{ 2136 ID: testutil.RandomContainerID(), 2137 Spec: spec, 2138 BundleDir: bundleDir, 2139 } 2140 c, err := New(conf, args) 2141 if err != nil { 2142 t.Fatalf("error creating container: %v", err) 2143 } 2144 2145 // Container is not thread safe, so load another instance to run in 2146 // concurrently. 2147 startCont, err := Load(rootDir, FullID{ContainerID: args.ID}, LoadOpts{}) 2148 if err != nil { 2149 t.Fatalf("error loading container: %v", err) 2150 } 2151 wg := sync.WaitGroup{} 2152 wg.Add(1) 2153 go func() { 2154 defer wg.Done() 2155 // Ignore failures, start can fail if destroy runs first. 2156 startCont.Start(conf) 2157 }() 2158 2159 wg.Add(1) 2160 go func() { 2161 defer wg.Done() 2162 if err := c.Destroy(); err != nil { 2163 t.Errorf("deleting non-started container failed: %v", err) 2164 } 2165 }() 2166 wg.Wait() 2167 } 2168 } 2169 2170 func TestCreateWorkingDir(t *testing.T) { 2171 for name, conf := range configs(t, all...) { 2172 t.Run(name, func(t *testing.T) { 2173 tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "cwd-create") 2174 if err != nil { 2175 t.Fatalf("ioutil.TempDir() failed: %v", err) 2176 } 2177 dir := path.Join(tmpDir, "new/working/dir") 2178 2179 // touch will fail if the directory doesn't exist. 2180 spec := testutil.NewSpecWithArgs("/bin/touch", path.Join(dir, "file")) 2181 spec.Process.Cwd = dir 2182 spec.Root.Readonly = true 2183 2184 if err := run(spec, conf); err != nil { 2185 t.Fatalf("Error running container: %v", err) 2186 } 2187 }) 2188 } 2189 } 2190 2191 // TestMountPropagation verifies that mount propagates to slave but not to 2192 // private mounts. 2193 func TestMountPropagation(t *testing.T) { 2194 // Setup dir structure: 2195 // - src: is mounted as shared and is used as source for both private and 2196 // slave mounts 2197 // - dir: will be bind mounted inside src and should propagate to slave 2198 tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "mount") 2199 if err != nil { 2200 t.Fatalf("ioutil.TempDir() failed: %v", err) 2201 } 2202 src := filepath.Join(tmpDir, "src") 2203 srcMnt := filepath.Join(src, "mnt") 2204 dir := filepath.Join(tmpDir, "dir") 2205 for _, path := range []string{src, srcMnt, dir} { 2206 if err := os.MkdirAll(path, 0777); err != nil { 2207 t.Fatalf("MkdirAll(%q): %v", path, err) 2208 } 2209 } 2210 dirFile := filepath.Join(dir, "file") 2211 f, err := os.Create(dirFile) 2212 if err != nil { 2213 t.Fatalf("os.Create(%q): %v", dirFile, err) 2214 } 2215 f.Close() 2216 2217 // Setup src as a shared mount. 2218 if err := unix.Mount(src, src, "bind", unix.MS_BIND, ""); err != nil { 2219 t.Fatalf("mount(%q, %q, MS_BIND): %v", dir, srcMnt, err) 2220 } 2221 if err := unix.Mount("", src, "", unix.MS_SHARED, ""); err != nil { 2222 t.Fatalf("mount(%q, MS_SHARED): %v", srcMnt, err) 2223 } 2224 2225 spec := testutil.NewSpecWithArgs("sleep", "1000") 2226 2227 priv := filepath.Join(tmpDir, "priv") 2228 slave := filepath.Join(tmpDir, "slave") 2229 spec.Mounts = []specs.Mount{ 2230 { 2231 Source: src, 2232 Destination: priv, 2233 Type: "bind", 2234 Options: []string{"private"}, 2235 }, 2236 { 2237 Source: src, 2238 Destination: slave, 2239 Type: "bind", 2240 Options: []string{"slave"}, 2241 }, 2242 } 2243 2244 conf := testutil.TestConfig(t) 2245 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2246 if err != nil { 2247 t.Fatalf("error setting up container: %v", err) 2248 } 2249 defer cleanup() 2250 2251 args := Args{ 2252 ID: testutil.RandomContainerID(), 2253 Spec: spec, 2254 BundleDir: bundleDir, 2255 } 2256 cont, err := New(conf, args) 2257 if err != nil { 2258 t.Fatalf("creating container: %v", err) 2259 } 2260 defer cont.Destroy() 2261 2262 if err := cont.Start(conf); err != nil { 2263 t.Fatalf("starting container: %v", err) 2264 } 2265 2266 // After the container is started, mount dir inside source and check what 2267 // happens to both destinations. 2268 if err := unix.Mount(dir, srcMnt, "bind", unix.MS_BIND, ""); err != nil { 2269 t.Fatalf("mount(%q, %q, MS_BIND): %v", dir, srcMnt, err) 2270 } 2271 2272 // Check that mount didn't propagate to private mount. 2273 privFile := filepath.Join(priv, "mnt", "file") 2274 if ws, err := execute(cont, "/usr/bin/test", "!", "-f", privFile); err != nil || ws != 0 { 2275 t.Fatalf("exec: test ! -f %q, ws: %v, err: %v", privFile, ws, err) 2276 } 2277 2278 // Check that mount propagated to slave mount. 2279 slaveFile := filepath.Join(slave, "mnt", "file") 2280 if ws, err := execute(cont, "/usr/bin/test", "-f", slaveFile); err != nil || ws != 0 { 2281 t.Fatalf("exec: test -f %q, ws: %v, err: %v", privFile, ws, err) 2282 } 2283 } 2284 2285 func TestMountSymlink(t *testing.T) { 2286 for name, conf := range configs(t, all...) { 2287 t.Run(name, func(t *testing.T) { 2288 dir, err := ioutil.TempDir(testutil.TmpDir(), "mount-symlink") 2289 if err != nil { 2290 t.Fatalf("ioutil.TempDir() failed: %v", err) 2291 } 2292 defer os.RemoveAll(dir) 2293 2294 source := path.Join(dir, "source") 2295 target := path.Join(dir, "target") 2296 for _, path := range []string{source, target} { 2297 if err := os.MkdirAll(path, 0777); err != nil { 2298 t.Fatalf("os.MkdirAll(): %v", err) 2299 } 2300 } 2301 f, err := os.Create(path.Join(source, "file")) 2302 if err != nil { 2303 t.Fatalf("os.Create(): %v", err) 2304 } 2305 f.Close() 2306 2307 link := path.Join(dir, "link") 2308 if err := os.Symlink(target, link); err != nil { 2309 t.Fatalf("os.Symlink(%q, %q): %v", target, link, err) 2310 } 2311 2312 spec := testutil.NewSpecWithArgs("/bin/sleep", "1000") 2313 2314 // Mount to a symlink to ensure the mount code will follow it and mount 2315 // at the symlink target. 2316 spec.Mounts = append(spec.Mounts, specs.Mount{ 2317 Type: "bind", 2318 Destination: link, 2319 Source: source, 2320 }) 2321 2322 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2323 if err != nil { 2324 t.Fatalf("error setting up container: %v", err) 2325 } 2326 defer cleanup() 2327 2328 args := Args{ 2329 ID: testutil.RandomContainerID(), 2330 Spec: spec, 2331 BundleDir: bundleDir, 2332 } 2333 cont, err := New(conf, args) 2334 if err != nil { 2335 t.Fatalf("creating container: %v", err) 2336 } 2337 defer cont.Destroy() 2338 2339 if err := cont.Start(conf); err != nil { 2340 t.Fatalf("starting container: %v", err) 2341 } 2342 2343 // Check that symlink was resolved and mount was created where the symlink 2344 // is pointing to. 2345 file := path.Join(target, "file") 2346 if ws, err := execute(cont, "/usr/bin/test", "-f", file); err != nil || ws != 0 { 2347 t.Fatalf("exec: test -f %q, ws: %v, err: %v", file, ws, err) 2348 } 2349 }) 2350 } 2351 } 2352 2353 // Check that --net-raw disables the CAP_NET_RAW capability. 2354 func TestNetRaw(t *testing.T) { 2355 capNetRaw := strconv.FormatUint(bits.MaskOf64(int(linux.CAP_NET_RAW)), 10) 2356 app, err := testutil.FindFile("test/cmd/test_app/test_app") 2357 if err != nil { 2358 t.Fatal("error finding test_app:", err) 2359 } 2360 2361 for _, enableRaw := range []bool{true, false} { 2362 conf := testutil.TestConfig(t) 2363 conf.EnableRaw = enableRaw 2364 2365 test := "--enabled" 2366 if !enableRaw { 2367 test = "--disabled" 2368 } 2369 2370 spec := testutil.NewSpecWithArgs(app, "capability", test, capNetRaw) 2371 if err := run(spec, conf); err != nil { 2372 t.Fatalf("Error running container: %v", err) 2373 } 2374 } 2375 } 2376 2377 // TestTTYField checks TTY field returned by container.Processes(). 2378 func TestTTYField(t *testing.T) { 2379 stop := testutil.StartReaper() 2380 defer stop() 2381 2382 testApp, err := testutil.FindFile("test/cmd/test_app/test_app") 2383 if err != nil { 2384 t.Fatal("error finding test_app:", err) 2385 } 2386 2387 testCases := []struct { 2388 name string 2389 useTTY bool 2390 wantTTYField string 2391 }{ 2392 { 2393 name: "no tty", 2394 useTTY: false, 2395 wantTTYField: "?", 2396 }, 2397 { 2398 name: "tty used", 2399 useTTY: true, 2400 wantTTYField: "pts/0", 2401 }, 2402 } 2403 2404 for _, test := range testCases { 2405 for _, vfs2 := range []bool{false, true} { 2406 name := test.name 2407 if vfs2 { 2408 name += "-vfs2" 2409 } 2410 t.Run(name, func(t *testing.T) { 2411 conf := testutil.TestConfig(t) 2412 conf.VFS2 = vfs2 2413 2414 // We will run /bin/sleep, possibly with an open TTY. 2415 cmd := []string{"/bin/sleep", "10000"} 2416 if test.useTTY { 2417 // Run inside the "pty-runner". 2418 cmd = append([]string{testApp, "pty-runner"}, cmd...) 2419 } 2420 2421 spec := testutil.NewSpecWithArgs(cmd...) 2422 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2423 if err != nil { 2424 t.Fatalf("error setting up container: %v", err) 2425 } 2426 defer cleanup() 2427 2428 // Create and start the container. 2429 args := Args{ 2430 ID: testutil.RandomContainerID(), 2431 Spec: spec, 2432 BundleDir: bundleDir, 2433 } 2434 c, err := New(conf, args) 2435 if err != nil { 2436 t.Fatalf("error creating container: %v", err) 2437 } 2438 defer c.Destroy() 2439 if err := c.Start(conf); err != nil { 2440 t.Fatalf("error starting container: %v", err) 2441 } 2442 2443 // Wait for sleep to be running, and check the TTY 2444 // field. 2445 var gotTTYField string 2446 cb := func() error { 2447 ps, err := c.Processes() 2448 if err != nil { 2449 err = fmt.Errorf("error getting process data from container: %v", err) 2450 return &backoff.PermanentError{Err: err} 2451 } 2452 for _, p := range ps { 2453 if strings.Contains(p.Cmd, "sleep") { 2454 gotTTYField = p.TTY 2455 return nil 2456 } 2457 } 2458 return fmt.Errorf("sleep not running") 2459 } 2460 if err := testutil.Poll(cb, 30*time.Second); err != nil { 2461 t.Fatalf("error waiting for sleep process: %v", err) 2462 } 2463 2464 if gotTTYField != test.wantTTYField { 2465 t.Errorf("tty field got %q, want %q", gotTTYField, test.wantTTYField) 2466 } 2467 }) 2468 } 2469 } 2470 } 2471 2472 // Test that container can run even when there are corrupt state files in the 2473 // root directiry. 2474 func TestCreateWithCorruptedStateFile(t *testing.T) { 2475 conf := testutil.TestConfig(t) 2476 spec := testutil.NewSpecWithArgs("/bin/true") 2477 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2478 if err != nil { 2479 t.Fatalf("error setting up container: %v", err) 2480 } 2481 defer cleanup() 2482 2483 // Create corrupted state file. 2484 corruptID := testutil.RandomContainerID() 2485 corruptState := buildPath(conf.RootDir, FullID{SandboxID: corruptID, ContainerID: corruptID}, stateFileExtension) 2486 if err := ioutil.WriteFile(corruptState, []byte("this{file(is;not[valid.json"), 0777); err != nil { 2487 t.Fatalf("createCorruptStateFile(): %v", err) 2488 } 2489 defer os.Remove(corruptState) 2490 2491 if _, err := Load(conf.RootDir, FullID{ContainerID: corruptID}, LoadOpts{SkipCheck: true}); err == nil { 2492 t.Fatalf("loading corrupted state file should have failed") 2493 } 2494 2495 args := Args{ 2496 ID: testutil.RandomContainerID(), 2497 Spec: spec, 2498 BundleDir: bundleDir, 2499 Attached: true, 2500 } 2501 if ws, err := Run(conf, args); err != nil { 2502 t.Errorf("running container: %v", err) 2503 } else if !ws.Exited() || ws.ExitStatus() != 0 { 2504 t.Errorf("container failed, waitStatus: %v", ws) 2505 } 2506 } 2507 2508 func TestBindMountByOption(t *testing.T) { 2509 for name, conf := range configs(t, all...) { 2510 t.Run(name, func(t *testing.T) { 2511 dir, err := ioutil.TempDir(testutil.TmpDir(), "bind-mount") 2512 spec := testutil.NewSpecWithArgs("/bin/touch", path.Join(dir, "file")) 2513 if err != nil { 2514 t.Fatalf("ioutil.TempDir(): %v", err) 2515 } 2516 spec.Mounts = append(spec.Mounts, specs.Mount{ 2517 Destination: dir, 2518 Source: dir, 2519 Type: "none", 2520 Options: []string{"rw", "bind"}, 2521 }) 2522 if err := run(spec, conf); err != nil { 2523 t.Fatalf("error running sandbox: %v", err) 2524 } 2525 }) 2526 } 2527 } 2528 2529 // TestRlimits sets limit to number of open files and checks that the limit 2530 // is propagated to the container. 2531 func TestRlimits(t *testing.T) { 2532 file, err := ioutil.TempFile(testutil.TmpDir(), "ulimit") 2533 if err != nil { 2534 t.Fatal(err) 2535 } 2536 cmd := fmt.Sprintf("ulimit -n > %q", file.Name()) 2537 2538 spec := testutil.NewSpecWithArgs("sh", "-c", cmd) 2539 spec.Process.Rlimits = []specs.POSIXRlimit{ 2540 {Type: "RLIMIT_NOFILE", Hard: 1000, Soft: 100}, 2541 } 2542 2543 conf := testutil.TestConfig(t) 2544 if err := run(spec, conf); err != nil { 2545 t.Fatalf("Error running container: %v", err) 2546 } 2547 got, err := ioutil.ReadFile(file.Name()) 2548 if err != nil { 2549 t.Fatal(err) 2550 } 2551 if want := "100\n"; string(got) != want { 2552 t.Errorf("ulimit result, got: %q, want: %q", got, want) 2553 } 2554 } 2555 2556 // TestRlimitsExec sets limit to number of open files and checks that the limit 2557 // is propagated to exec'd processes. 2558 func TestRlimitsExec(t *testing.T) { 2559 spec := testutil.NewSpecWithArgs("sleep", "100") 2560 spec.Process.Rlimits = []specs.POSIXRlimit{ 2561 {Type: "RLIMIT_NOFILE", Hard: 1000, Soft: 100}, 2562 } 2563 2564 conf := testutil.TestConfig(t) 2565 _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) 2566 if err != nil { 2567 t.Fatalf("error setting up container: %v", err) 2568 } 2569 defer cleanup() 2570 2571 args := Args{ 2572 ID: testutil.RandomContainerID(), 2573 Spec: spec, 2574 BundleDir: bundleDir, 2575 } 2576 cont, err := New(conf, args) 2577 if err != nil { 2578 t.Fatalf("error creating container: %v", err) 2579 } 2580 defer cont.Destroy() 2581 if err := cont.Start(conf); err != nil { 2582 t.Fatalf("error starting container: %v", err) 2583 } 2584 2585 got, err := executeCombinedOutput(cont, "/bin/sh", "-c", "ulimit -n") 2586 if err != nil { 2587 t.Fatal(err) 2588 } 2589 if want := "100\n"; string(got) != want { 2590 t.Errorf("ulimit result, got: %q, want: %q", got, want) 2591 } 2592 }