github.com/demonoid81/containerd@v1.3.4/container_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package containerd 18 19 import ( 20 "bytes" 21 "context" 22 "io" 23 "io/ioutil" 24 "os" 25 "os/exec" 26 "runtime" 27 "strings" 28 "syscall" 29 "testing" 30 "time" 31 32 // Register the typeurl 33 "github.com/containerd/containerd/cio" 34 "github.com/containerd/containerd/containers" 35 "github.com/containerd/containerd/namespaces" 36 "github.com/containerd/containerd/oci" 37 "github.com/containerd/containerd/platforms" 38 _ "github.com/containerd/containerd/runtime" 39 "github.com/containerd/typeurl" 40 specs "github.com/opencontainers/runtime-spec/specs-go" 41 42 "github.com/containerd/containerd/errdefs" 43 gogotypes "github.com/gogo/protobuf/types" 44 ) 45 46 func empty() cio.Creator { 47 // TODO (@mlaventure) windows searches for pipes 48 // when none are provided 49 if runtime.GOOS == "windows" { 50 return cio.NewCreator(cio.WithStdio) 51 } 52 return cio.NullIO 53 } 54 55 func TestContainerList(t *testing.T) { 56 client, err := newClient(t, address) 57 if err != nil { 58 t.Fatal(err) 59 } 60 defer client.Close() 61 62 ctx, cancel := testContext(t) 63 defer cancel() 64 65 containers, err := client.Containers(ctx) 66 if err != nil { 67 t.Fatalf("container list returned error %v", err) 68 } 69 if len(containers) != 0 { 70 t.Errorf("expected 0 containers but received %d", len(containers)) 71 } 72 } 73 74 func TestNewContainer(t *testing.T) { 75 t.Parallel() 76 77 id := t.Name() 78 client, err := newClient(t, address) 79 if err != nil { 80 t.Fatal(err) 81 } 82 defer client.Close() 83 84 ctx, cancel := testContext(t) 85 defer cancel() 86 87 container, err := client.NewContainer(ctx, id, WithNewSpec()) 88 if err != nil { 89 t.Fatal(err) 90 } 91 defer container.Delete(ctx) 92 if container.ID() != id { 93 t.Errorf("expected container id %q but received %q", id, container.ID()) 94 } 95 if _, err = container.Spec(ctx); err != nil { 96 t.Fatal(err) 97 } 98 if err := container.Delete(ctx); err != nil { 99 t.Fatal(err) 100 } 101 } 102 103 func TestContainerStart(t *testing.T) { 104 t.Parallel() 105 106 client, err := newClient(t, address) 107 if err != nil { 108 t.Fatal(err) 109 } 110 defer client.Close() 111 112 var ( 113 image Image 114 ctx, cancel = testContext(t) 115 id = t.Name() 116 ) 117 defer cancel() 118 119 image, err = client.GetImage(ctx, testImage) 120 if err != nil { 121 t.Fatal(err) 122 } 123 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withExitStatus(7))) 124 if err != nil { 125 t.Fatal(err) 126 } 127 defer container.Delete(ctx, WithSnapshotCleanup) 128 129 task, err := container.NewTask(ctx, empty()) 130 if err != nil { 131 t.Fatal(err) 132 } 133 defer task.Delete(ctx) 134 135 statusC, err := task.Wait(ctx) 136 if err != nil { 137 t.Fatal(err) 138 } 139 140 if pid := task.Pid(); pid < 1 { 141 t.Errorf("invalid task pid %d", pid) 142 } 143 if err := task.Start(ctx); err != nil { 144 t.Error(err) 145 task.Delete(ctx) 146 return 147 } 148 status := <-statusC 149 code, _, err := status.Result() 150 if err != nil { 151 t.Fatal(err) 152 } 153 if code != 7 { 154 t.Errorf("expected status 7 from wait but received %d", code) 155 } 156 157 deleteStatus, err := task.Delete(ctx) 158 if err != nil { 159 t.Fatal(err) 160 } 161 if ec := deleteStatus.ExitCode(); ec != 7 { 162 t.Errorf("expected status 7 from delete but received %d", ec) 163 } 164 } 165 166 func TestContainerOutput(t *testing.T) { 167 t.Parallel() 168 169 client, err := newClient(t, address) 170 if err != nil { 171 t.Fatal(err) 172 } 173 defer client.Close() 174 175 var ( 176 image Image 177 ctx, cancel = testContext(t) 178 id = t.Name() 179 expected = "kingkoye" 180 ) 181 defer cancel() 182 183 image, err = client.GetImage(ctx, testImage) 184 if err != nil { 185 t.Fatal(err) 186 } 187 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("echo", expected))) 188 if err != nil { 189 t.Fatal(err) 190 } 191 defer container.Delete(ctx, WithSnapshotCleanup) 192 193 stdout := bytes.NewBuffer(nil) 194 task, err := container.NewTask(ctx, cio.NewCreator(withByteBuffers(stdout))) 195 if err != nil { 196 t.Fatal(err) 197 } 198 defer task.Delete(ctx) 199 200 statusC, err := task.Wait(ctx) 201 if err != nil { 202 t.Fatal(err) 203 } 204 205 if err := task.Start(ctx); err != nil { 206 t.Fatal(err) 207 } 208 209 status := <-statusC 210 code, _, err := status.Result() 211 if code != 0 { 212 t.Errorf("expected status 0 but received %d: %v", code, err) 213 } 214 if _, err := task.Delete(ctx); err != nil { 215 t.Fatal(err) 216 } 217 218 actual := stdout.String() 219 // echo adds a new line 220 expected = expected + newLine 221 if actual != expected { 222 t.Errorf("expected output %q but received %q", expected, actual) 223 } 224 } 225 226 func withByteBuffers(stdout io.Writer) cio.Opt { 227 // TODO: could this use ioutil.Discard? 228 return func(streams *cio.Streams) { 229 streams.Stdin = new(bytes.Buffer) 230 streams.Stdout = stdout 231 streams.Stderr = new(bytes.Buffer) 232 } 233 } 234 235 func TestContainerExec(t *testing.T) { 236 t.Parallel() 237 238 client, err := newClient(t, address) 239 if err != nil { 240 t.Fatal(err) 241 } 242 defer client.Close() 243 244 var ( 245 image Image 246 ctx, cancel = testContext(t) 247 id = t.Name() 248 ) 249 defer cancel() 250 251 image, err = client.GetImage(ctx, testImage) 252 if err != nil { 253 t.Fatal(err) 254 } 255 256 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 257 if err != nil { 258 t.Fatal(err) 259 } 260 defer container.Delete(ctx, WithSnapshotCleanup) 261 262 task, err := container.NewTask(ctx, empty()) 263 if err != nil { 264 t.Fatal(err) 265 } 266 defer task.Delete(ctx) 267 268 finishedC, err := task.Wait(ctx) 269 if err != nil { 270 t.Fatal(err) 271 } 272 273 if err := task.Start(ctx); err != nil { 274 t.Fatal(err) 275 } 276 spec, err := container.Spec(ctx) 277 if err != nil { 278 t.Fatal(err) 279 } 280 281 // start an exec process without running the original container process info 282 processSpec := spec.Process 283 withExecExitStatus(processSpec, 6) 284 execID := t.Name() + "_exec" 285 process, err := task.Exec(ctx, execID, processSpec, empty()) 286 if err != nil { 287 t.Fatal(err) 288 } 289 processStatusC, err := process.Wait(ctx) 290 if err != nil { 291 t.Fatal(err) 292 } 293 294 if err := process.Start(ctx); err != nil { 295 t.Fatal(err) 296 } 297 298 // wait for the exec to return 299 status := <-processStatusC 300 code, _, err := status.Result() 301 if err != nil { 302 t.Fatal(err) 303 } 304 305 if code != 6 { 306 t.Errorf("expected exec exit code 6 but received %d", code) 307 } 308 deleteStatus, err := process.Delete(ctx) 309 if err != nil { 310 t.Fatal(err) 311 } 312 if ec := deleteStatus.ExitCode(); ec != 6 { 313 t.Errorf("expected delete exit code 6 but received %d", ec) 314 } 315 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 316 t.Error(err) 317 } 318 <-finishedC 319 } 320 func TestContainerLargeExecArgs(t *testing.T) { 321 t.Parallel() 322 323 client, err := newClient(t, address) 324 if err != nil { 325 t.Fatal(err) 326 } 327 defer client.Close() 328 329 var ( 330 image Image 331 ctx, cancel = testContext(t) 332 id = t.Name() 333 ) 334 defer cancel() 335 336 image, err = client.GetImage(ctx, testImage) 337 if err != nil { 338 t.Fatal(err) 339 } 340 341 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 342 if err != nil { 343 t.Fatal(err) 344 } 345 defer container.Delete(ctx, WithSnapshotCleanup) 346 347 task, err := container.NewTask(ctx, empty()) 348 if err != nil { 349 t.Fatal(err) 350 } 351 defer task.Delete(ctx) 352 353 finishedC, err := task.Wait(ctx) 354 if err != nil { 355 t.Fatal(err) 356 } 357 358 if err := task.Start(ctx); err != nil { 359 t.Fatal(err) 360 } 361 spec, err := container.Spec(ctx) 362 if err != nil { 363 t.Fatal(err) 364 } 365 366 processSpec := spec.Process 367 withExecArgs(processSpec, "echo", strings.Repeat("a", 20000)) 368 execID := t.Name() + "_exec" 369 process, err := task.Exec(ctx, execID, processSpec, empty()) 370 if err != nil { 371 t.Fatal(err) 372 } 373 processStatusC, err := process.Wait(ctx) 374 if err != nil { 375 t.Fatal(err) 376 } 377 378 if err := process.Start(ctx); err != nil { 379 t.Fatal(err) 380 } 381 382 // wait for the exec to return 383 status := <-processStatusC 384 if _, _, err := status.Result(); err != nil { 385 t.Fatal(err) 386 } 387 if _, err := process.Delete(ctx); err != nil { 388 t.Fatal(err) 389 } 390 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 391 t.Error(err) 392 } 393 <-finishedC 394 } 395 396 func TestContainerPids(t *testing.T) { 397 t.Parallel() 398 399 client, err := newClient(t, address) 400 if err != nil { 401 t.Fatal(err) 402 } 403 defer client.Close() 404 405 var ( 406 image Image 407 ctx, cancel = testContext(t) 408 id = t.Name() 409 ) 410 defer cancel() 411 412 image, err = client.GetImage(ctx, testImage) 413 if err != nil { 414 t.Fatal(err) 415 } 416 417 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 418 if err != nil { 419 t.Fatal(err) 420 } 421 defer container.Delete(ctx, WithSnapshotCleanup) 422 423 task, err := container.NewTask(ctx, empty()) 424 if err != nil { 425 t.Fatal(err) 426 } 427 defer task.Delete(ctx) 428 429 statusC, err := task.Wait(ctx) 430 if err != nil { 431 t.Fatal(err) 432 } 433 434 if err := task.Start(ctx); err != nil { 435 t.Fatal(err) 436 } 437 438 pid := task.Pid() 439 if pid < 1 { 440 t.Errorf("invalid task pid %d", pid) 441 } 442 processes, err := task.Pids(ctx) 443 switch runtime.GOOS { 444 case "windows": 445 // TODO: This is currently not implemented on windows 446 default: 447 if err != nil { 448 t.Fatal(err) 449 } 450 if l := len(processes); l != 1 { 451 t.Errorf("expected 1 process but received %d", l) 452 } 453 if len(processes) > 0 { 454 actual := processes[0].Pid 455 if pid != actual { 456 t.Errorf("expected pid %d but received %d", pid, actual) 457 } 458 } 459 } 460 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 461 t.Error(err) 462 } 463 <-statusC 464 } 465 466 func TestContainerCloseIO(t *testing.T) { 467 t.Parallel() 468 469 client, err := newClient(t, address) 470 if err != nil { 471 t.Fatal(err) 472 } 473 defer client.Close() 474 475 var ( 476 image Image 477 ctx, cancel = testContext(t) 478 id = t.Name() 479 ) 480 defer cancel() 481 482 image, err = client.GetImage(ctx, testImage) 483 if err != nil { 484 t.Fatal(err) 485 } 486 487 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withCat())) 488 if err != nil { 489 t.Fatal(err) 490 } 491 defer container.Delete(ctx, WithSnapshotCleanup) 492 493 stdout := bytes.NewBuffer(nil) 494 495 r, w, err := os.Pipe() 496 if err != nil { 497 t.Fatal(err) 498 } 499 500 task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStreams(r, stdout, ioutil.Discard))) 501 if err != nil { 502 t.Fatal(err) 503 } 504 defer task.Delete(ctx) 505 506 statusC, err := task.Wait(ctx) 507 if err != nil { 508 t.Fatal(err) 509 } 510 511 if err := task.Start(ctx); err != nil { 512 t.Fatal(err) 513 } 514 w.Close() 515 if err := task.CloseIO(ctx, WithStdinCloser); err != nil { 516 t.Error(err) 517 } 518 519 <-statusC 520 } 521 522 func TestDeleteRunningContainer(t *testing.T) { 523 t.Parallel() 524 525 client, err := newClient(t, address) 526 if err != nil { 527 t.Fatal(err) 528 } 529 defer client.Close() 530 531 var ( 532 image Image 533 ctx, cancel = testContext(t) 534 id = t.Name() 535 ) 536 defer cancel() 537 538 image, err = client.GetImage(ctx, testImage) 539 if err != nil { 540 t.Fatal(err) 541 } 542 543 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 544 if err != nil { 545 t.Fatal(err) 546 } 547 defer container.Delete(ctx, WithSnapshotCleanup) 548 549 task, err := container.NewTask(ctx, empty()) 550 if err != nil { 551 t.Fatal(err) 552 } 553 defer task.Delete(ctx) 554 555 statusC, err := task.Wait(ctx) 556 if err != nil { 557 t.Fatal(err) 558 } 559 560 if err := task.Start(ctx); err != nil { 561 t.Fatal(err) 562 } 563 564 err = container.Delete(ctx, WithSnapshotCleanup) 565 if err == nil { 566 t.Error("delete did not error with running task") 567 } 568 if !errdefs.IsFailedPrecondition(err) { 569 t.Errorf("expected error %q but received %q", errdefs.ErrFailedPrecondition, err) 570 } 571 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 572 t.Fatal(err) 573 } 574 <-statusC 575 } 576 577 func TestContainerKill(t *testing.T) { 578 t.Parallel() 579 580 client, err := newClient(t, address) 581 if err != nil { 582 t.Fatal(err) 583 } 584 defer client.Close() 585 586 var ( 587 image Image 588 ctx, cancel = testContext(t) 589 id = t.Name() 590 ) 591 defer cancel() 592 593 image, err = client.GetImage(ctx, testImage) 594 if err != nil { 595 t.Fatal(err) 596 } 597 598 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "10"))) 599 if err != nil { 600 t.Fatal(err) 601 } 602 defer container.Delete(ctx) 603 604 task, err := container.NewTask(ctx, empty()) 605 if err != nil { 606 t.Fatal(err) 607 } 608 defer task.Delete(ctx) 609 610 statusC, err := task.Wait(ctx) 611 if err != nil { 612 t.Fatal(err) 613 } 614 615 if err := task.Start(ctx); err != nil { 616 t.Fatal(err) 617 } 618 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 619 t.Fatal(err) 620 } 621 <-statusC 622 623 err = task.Kill(ctx, syscall.SIGTERM) 624 if err == nil { 625 t.Fatal("second call to kill should return an error") 626 } 627 if !errdefs.IsNotFound(err) { 628 t.Errorf("expected error %q but received %q", errdefs.ErrNotFound, err) 629 } 630 } 631 632 func TestContainerNoBinaryExists(t *testing.T) { 633 t.Parallel() 634 635 client, err := newClient(t, address) 636 if err != nil { 637 t.Fatal(err) 638 } 639 defer client.Close() 640 641 var ( 642 image Image 643 ctx, cancel = testContext(t) 644 id = t.Name() 645 ) 646 defer cancel() 647 648 image, err = client.GetImage(ctx, testImage) 649 if err != nil { 650 t.Fatal(err) 651 } 652 653 container, err := client.NewContainer(ctx, id, 654 WithNewSnapshot(id, image), 655 WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("nothing"))) 656 if err != nil { 657 t.Fatal(err) 658 } 659 defer container.Delete(ctx, WithSnapshotCleanup) 660 661 task, err := container.NewTask(ctx, empty()) 662 switch runtime.GOOS { 663 case "windows": 664 if err != nil { 665 t.Fatalf("failed to create task %v", err) 666 } 667 defer task.Delete(ctx, WithProcessKill) 668 if err := task.Start(ctx); err == nil { 669 t.Error("task.Start() should return an error when binary does not exist") 670 } 671 default: 672 if err == nil { 673 t.Error("NewTask should return an error when binary does not exist") 674 task.Delete(ctx) 675 } 676 } 677 } 678 679 func TestContainerExecNoBinaryExists(t *testing.T) { 680 t.Parallel() 681 682 client, err := newClient(t, address) 683 if err != nil { 684 t.Fatal(err) 685 } 686 defer client.Close() 687 688 var ( 689 image Image 690 ctx, cancel = testContext(t) 691 id = t.Name() 692 ) 693 defer cancel() 694 695 image, err = client.GetImage(ctx, testImage) 696 if err != nil { 697 t.Fatal(err) 698 } 699 700 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 701 if err != nil { 702 t.Fatal(err) 703 } 704 defer container.Delete(ctx, WithSnapshotCleanup) 705 706 task, err := container.NewTask(ctx, empty()) 707 if err != nil { 708 t.Fatal(err) 709 } 710 defer task.Delete(ctx) 711 712 finishedC, err := task.Wait(ctx) 713 if err != nil { 714 t.Error(err) 715 } 716 if err := task.Start(ctx); err != nil { 717 t.Fatal(err) 718 } 719 spec, err := container.Spec(ctx) 720 if err != nil { 721 t.Fatal(err) 722 } 723 724 // start an exec process without running the original container process 725 processSpec := spec.Process 726 processSpec.Args = []string{ 727 "none", 728 } 729 execID := t.Name() + "_exec" 730 process, err := task.Exec(ctx, execID, processSpec, empty()) 731 if err != nil { 732 t.Fatal(err) 733 } 734 defer process.Delete(ctx) 735 if err := process.Start(ctx); err == nil { 736 t.Error("Process.Start should fail when process does not exist") 737 } 738 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 739 t.Error(err) 740 } 741 <-finishedC 742 } 743 744 func TestWaitStoppedTask(t *testing.T) { 745 t.Parallel() 746 747 client, err := newClient(t, address) 748 if err != nil { 749 t.Fatal(err) 750 } 751 defer client.Close() 752 753 var ( 754 image Image 755 ctx, cancel = testContext(t) 756 id = t.Name() 757 ) 758 defer cancel() 759 760 image, err = client.GetImage(ctx, testImage) 761 if err != nil { 762 t.Fatal(err) 763 } 764 765 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withExitStatus(7))) 766 if err != nil { 767 t.Fatal(err) 768 } 769 defer container.Delete(ctx, WithSnapshotCleanup) 770 771 task, err := container.NewTask(ctx, empty()) 772 if err != nil { 773 t.Fatal(err) 774 } 775 defer task.Delete(ctx) 776 777 statusC, err := task.Wait(ctx) 778 if err != nil { 779 t.Fatal(err) 780 } 781 782 if pid := task.Pid(); pid < 1 { 783 t.Errorf("invalid task pid %d", pid) 784 } 785 if err := task.Start(ctx); err != nil { 786 t.Error(err) 787 task.Delete(ctx) 788 return 789 } 790 791 // wait for the task to stop then call wait again 792 <-statusC 793 statusC, err = task.Wait(ctx) 794 if err != nil { 795 t.Fatal(err) 796 } 797 status := <-statusC 798 code, _, err := status.Result() 799 if err != nil { 800 t.Fatal(err) 801 } 802 if code != 7 { 803 t.Errorf("exit status from stopped task should be 7 but received %d", code) 804 } 805 } 806 807 func TestWaitStoppedProcess(t *testing.T) { 808 t.Parallel() 809 810 client, err := newClient(t, address) 811 if err != nil { 812 t.Fatal(err) 813 } 814 defer client.Close() 815 816 var ( 817 image Image 818 ctx, cancel = testContext(t) 819 id = t.Name() 820 ) 821 defer cancel() 822 823 image, err = client.GetImage(ctx, testImage) 824 if err != nil { 825 t.Fatal(err) 826 } 827 828 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 829 if err != nil { 830 t.Fatal(err) 831 } 832 defer container.Delete(ctx, WithSnapshotCleanup) 833 834 task, err := container.NewTask(ctx, empty()) 835 if err != nil { 836 t.Fatal(err) 837 } 838 defer task.Delete(ctx) 839 840 finishedC, err := task.Wait(ctx) 841 if err != nil { 842 t.Error(err) 843 } 844 845 if err := task.Start(ctx); err != nil { 846 t.Fatal(err) 847 } 848 spec, err := container.Spec(ctx) 849 if err != nil { 850 t.Fatal(err) 851 } 852 853 // start an exec process without running the original container process info 854 processSpec := spec.Process 855 withExecExitStatus(processSpec, 6) 856 execID := t.Name() + "_exec" 857 process, err := task.Exec(ctx, execID, processSpec, empty()) 858 if err != nil { 859 t.Fatal(err) 860 } 861 defer process.Delete(ctx) 862 863 statusC, err := process.Wait(ctx) 864 if err != nil { 865 t.Fatal(err) 866 } 867 868 if err := process.Start(ctx); err != nil { 869 t.Fatal(err) 870 } 871 872 // wait for the exec to return 873 <-statusC 874 875 // try to wait on the process after it has stopped 876 statusC, err = process.Wait(ctx) 877 if err != nil { 878 t.Fatal(err) 879 } 880 status := <-statusC 881 code, _, err := status.Result() 882 if err != nil { 883 t.Fatal(err) 884 } 885 if code != 6 { 886 t.Errorf("exit status from stopped process should be 6 but received %d", code) 887 } 888 889 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 890 t.Error(err) 891 } 892 <-finishedC 893 } 894 895 func TestTaskForceDelete(t *testing.T) { 896 t.Parallel() 897 898 client, err := newClient(t, address) 899 if err != nil { 900 t.Fatal(err) 901 } 902 defer client.Close() 903 904 var ( 905 image Image 906 ctx, cancel = testContext(t) 907 id = t.Name() 908 ) 909 defer cancel() 910 911 image, err = client.GetImage(ctx, testImage) 912 if err != nil { 913 t.Fatal(err) 914 } 915 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "30"))) 916 if err != nil { 917 t.Fatal(err) 918 } 919 defer container.Delete(ctx, WithSnapshotCleanup) 920 921 task, err := container.NewTask(ctx, empty()) 922 if err != nil { 923 t.Fatal(err) 924 } 925 if err := task.Start(ctx); err != nil { 926 t.Fatal(err) 927 } 928 if _, err := task.Delete(ctx); err == nil { 929 t.Error("task.Delete of a running task should create an error") 930 } 931 if _, err := task.Delete(ctx, WithProcessKill); err != nil { 932 t.Fatal(err) 933 } 934 } 935 936 func TestProcessForceDelete(t *testing.T) { 937 t.Parallel() 938 939 client, err := newClient(t, address) 940 if err != nil { 941 t.Fatal(err) 942 } 943 defer client.Close() 944 945 var ( 946 image Image 947 ctx, cancel = testContext(t) 948 id = t.Name() 949 ) 950 defer cancel() 951 952 image, err = client.GetImage(ctx, testImage) 953 if err != nil { 954 t.Fatal(err) 955 } 956 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "30"))) 957 if err != nil { 958 t.Fatal(err) 959 } 960 defer container.Delete(ctx, WithSnapshotCleanup) 961 962 task, err := container.NewTask(ctx, empty()) 963 if err != nil { 964 t.Fatal(err) 965 } 966 defer task.Delete(ctx) 967 968 statusC, err := task.Wait(ctx) 969 if err != nil { 970 t.Fatal(err) 971 } 972 973 // task must be started on windows 974 if err := task.Start(ctx); err != nil { 975 t.Fatal(err) 976 } 977 spec, err := container.Spec(ctx) 978 if err != nil { 979 t.Fatal(err) 980 } 981 982 processSpec := spec.Process 983 withExecArgs(processSpec, "sleep", "20") 984 execID := t.Name() + "_exec" 985 process, err := task.Exec(ctx, execID, processSpec, empty()) 986 if err != nil { 987 t.Fatal(err) 988 } 989 if err := process.Start(ctx); err != nil { 990 t.Fatal(err) 991 } 992 if _, err := process.Delete(ctx); err == nil { 993 t.Error("process.Delete should return an error when process is running") 994 } 995 if _, err := process.Delete(ctx, WithProcessKill); err != nil { 996 t.Error(err) 997 } 998 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 999 t.Fatal(err) 1000 } 1001 <-statusC 1002 } 1003 1004 func TestContainerHostname(t *testing.T) { 1005 t.Parallel() 1006 1007 client, err := newClient(t, address) 1008 if err != nil { 1009 t.Fatal(err) 1010 } 1011 defer client.Close() 1012 1013 var ( 1014 image Image 1015 ctx, cancel = testContext(t) 1016 id = t.Name() 1017 expected = "myhostname" 1018 ) 1019 defer cancel() 1020 1021 image, err = client.GetImage(ctx, testImage) 1022 if err != nil { 1023 t.Fatal(err) 1024 } 1025 1026 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), 1027 withProcessArgs("hostname"), 1028 oci.WithHostname(expected), 1029 )) 1030 if err != nil { 1031 t.Fatal(err) 1032 } 1033 defer container.Delete(ctx, WithSnapshotCleanup) 1034 1035 stdout := bytes.NewBuffer(nil) 1036 task, err := container.NewTask(ctx, cio.NewCreator(withByteBuffers(stdout))) 1037 if err != nil { 1038 t.Fatal(err) 1039 } 1040 defer task.Delete(ctx) 1041 1042 statusC, err := task.Wait(ctx) 1043 if err != nil { 1044 t.Fatal(err) 1045 } 1046 1047 if err := task.Start(ctx); err != nil { 1048 t.Fatal(err) 1049 } 1050 1051 status := <-statusC 1052 code, _, err := status.Result() 1053 if err != nil { 1054 t.Fatal(err) 1055 } 1056 if code != 0 { 1057 t.Errorf("expected status 0 but received %d", code) 1058 } 1059 if _, err := task.Delete(ctx); err != nil { 1060 t.Fatal(err) 1061 } 1062 cutset := "\n" 1063 if runtime.GOOS == "windows" { 1064 cutset = "\r\n" 1065 } 1066 1067 actual := strings.TrimSuffix(stdout.String(), cutset) 1068 if actual != expected { 1069 t.Errorf("expected output %q but received %q", expected, actual) 1070 } 1071 } 1072 1073 func TestContainerExitedAtSet(t *testing.T) { 1074 t.Parallel() 1075 1076 client, err := newClient(t, address) 1077 if err != nil { 1078 t.Fatal(err) 1079 } 1080 defer client.Close() 1081 1082 var ( 1083 image Image 1084 ctx, cancel = testContext(t) 1085 id = t.Name() 1086 ) 1087 defer cancel() 1088 1089 image, err = client.GetImage(ctx, testImage) 1090 if err != nil { 1091 t.Fatal(err) 1092 } 1093 1094 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withTrue())) 1095 if err != nil { 1096 t.Fatal(err) 1097 } 1098 defer container.Delete(ctx, WithSnapshotCleanup) 1099 1100 task, err := container.NewTask(ctx, empty()) 1101 if err != nil { 1102 t.Fatal(err) 1103 } 1104 defer task.Delete(ctx) 1105 1106 statusC, err := task.Wait(ctx) 1107 if err != nil { 1108 t.Error(err) 1109 } 1110 1111 startTime := time.Now() 1112 if err := task.Start(ctx); err != nil { 1113 t.Fatal(err) 1114 } 1115 1116 status := <-statusC 1117 code, _, err := status.Result() 1118 if code != 0 { 1119 t.Errorf("expected status 0 but received %d (err: %v)", code, err) 1120 } 1121 1122 if s, err := task.Status(ctx); err != nil { 1123 t.Errorf("failed to retrieve status: %v", err) 1124 } else if s.ExitTime.After(startTime) == false { 1125 t.Errorf("exit time is not after start time: %v <= %v", startTime, s.ExitTime) 1126 } 1127 1128 if _, err := task.Delete(ctx); err != nil { 1129 t.Fatal(err) 1130 } 1131 } 1132 1133 func TestDeleteContainerExecCreated(t *testing.T) { 1134 t.Parallel() 1135 1136 client, err := newClient(t, address) 1137 if err != nil { 1138 t.Fatal(err) 1139 } 1140 defer client.Close() 1141 1142 var ( 1143 image Image 1144 ctx, cancel = testContext(t) 1145 id = t.Name() 1146 ) 1147 defer cancel() 1148 1149 image, err = client.GetImage(ctx, testImage) 1150 if err != nil { 1151 t.Fatal(err) 1152 } 1153 1154 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100"))) 1155 if err != nil { 1156 t.Fatal(err) 1157 } 1158 defer container.Delete(ctx, WithSnapshotCleanup) 1159 1160 task, err := container.NewTask(ctx, empty()) 1161 if err != nil { 1162 t.Fatal(err) 1163 } 1164 defer task.Delete(ctx) 1165 1166 finished, err := task.Wait(ctx) 1167 if err != nil { 1168 t.Error(err) 1169 } 1170 1171 if err := task.Start(ctx); err != nil { 1172 t.Fatal(err) 1173 } 1174 spec, err := container.Spec(ctx) 1175 if err != nil { 1176 t.Fatal(err) 1177 } 1178 1179 // start an exec process without running the original container process info 1180 processSpec := spec.Process 1181 withExecExitStatus(processSpec, 6) 1182 execID := t.Name() + "_exec" 1183 process, err := task.Exec(ctx, execID, processSpec, empty()) 1184 if err != nil { 1185 t.Fatal(err) 1186 } 1187 deleteStatus, err := process.Delete(ctx) 1188 if err != nil { 1189 t.Fatal(err) 1190 } 1191 if ec := deleteStatus.ExitCode(); ec != 0 { 1192 t.Errorf("expected delete exit code 0 but received %d", ec) 1193 } 1194 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 1195 t.Error(err) 1196 } 1197 <-finished 1198 } 1199 1200 func TestContainerMetrics(t *testing.T) { 1201 if runtime.GOOS == "windows" { 1202 t.Skip("metrics are currently not supported on windows") 1203 } 1204 t.Parallel() 1205 1206 client, err := newClient(t, address) 1207 if err != nil { 1208 t.Fatal(err) 1209 } 1210 defer client.Close() 1211 1212 var ( 1213 image Image 1214 ctx, cancel = testContext(t) 1215 id = t.Name() 1216 ) 1217 defer cancel() 1218 1219 image, err = client.GetImage(ctx, testImage) 1220 if err != nil { 1221 t.Fatal(err) 1222 } 1223 container, err := client.NewContainer(ctx, id, 1224 WithNewSnapshot(id, image), 1225 WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "30"))) 1226 if err != nil { 1227 t.Fatal(err) 1228 } 1229 defer container.Delete(ctx, WithSnapshotCleanup) 1230 1231 task, err := container.NewTask(ctx, empty()) 1232 if err != nil { 1233 t.Fatal(err) 1234 } 1235 defer task.Delete(ctx, WithProcessKill) 1236 1237 statusC, err := task.Wait(ctx) 1238 if err != nil { 1239 t.Fatal(err) 1240 } 1241 1242 metric, err := task.Metrics(ctx) 1243 if err != nil { 1244 t.Error(err) 1245 return 1246 } 1247 if metric.ID != id { 1248 t.Errorf("expected metric id %q but received %q", id, metric.ID) 1249 } 1250 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 1251 t.Fatal(err) 1252 } 1253 1254 <-statusC 1255 } 1256 1257 func TestDeletedContainerMetrics(t *testing.T) { 1258 if runtime.GOOS == "windows" { 1259 t.Skip("metrics are currently not supported on windows") 1260 } 1261 t.Parallel() 1262 1263 client, err := newClient(t, address) 1264 if err != nil { 1265 t.Fatal(err) 1266 } 1267 defer client.Close() 1268 1269 var ( 1270 image Image 1271 ctx, cancel = testContext(t) 1272 id = t.Name() 1273 ) 1274 defer cancel() 1275 1276 image, err = client.GetImage(ctx, testImage) 1277 if err != nil { 1278 t.Fatal(err) 1279 } 1280 container, err := client.NewContainer(ctx, id, 1281 WithNewSnapshot(id, image), 1282 WithNewSpec(oci.WithImageConfig(image), withExitStatus(0))) 1283 if err != nil { 1284 t.Fatal(err) 1285 } 1286 defer container.Delete(ctx, WithSnapshotCleanup) 1287 1288 task, err := container.NewTask(ctx, empty()) 1289 if err != nil { 1290 t.Fatal(err) 1291 } 1292 defer task.Delete(ctx) 1293 1294 if err := task.Start(ctx); err != nil { 1295 t.Fatal(err) 1296 } 1297 1298 statusC, err := task.Wait(ctx) 1299 if err != nil { 1300 t.Fatal(err) 1301 } 1302 <-statusC 1303 1304 if _, err := task.Delete(ctx); err != nil { 1305 t.Fatal(err) 1306 } 1307 1308 if _, err := task.Metrics(ctx); err == nil { 1309 t.Errorf("Getting metrics of deleted task should have failed") 1310 } 1311 } 1312 1313 func TestContainerExtensions(t *testing.T) { 1314 t.Parallel() 1315 1316 ctx, cancel := testContext(t) 1317 defer cancel() 1318 id := t.Name() 1319 1320 client, err := newClient(t, address) 1321 if err != nil { 1322 t.Fatal(err) 1323 } 1324 defer client.Close() 1325 1326 ext := gogotypes.Any{TypeUrl: "test.ext.url", Value: []byte("hello")} 1327 container, err := client.NewContainer(ctx, id, WithNewSpec(), WithContainerExtension("hello", &ext)) 1328 if err != nil { 1329 t.Fatal(err) 1330 } 1331 defer container.Delete(ctx) 1332 1333 checkExt := func(container Container) { 1334 cExts, err := container.Extensions(ctx) 1335 if err != nil { 1336 t.Fatal(err) 1337 } 1338 if len(cExts) != 1 { 1339 t.Errorf("expected 1 container extension") 1340 } 1341 if cExts["hello"].TypeUrl != ext.TypeUrl { 1342 t.Errorf("got unexpected type url for extension: %s", cExts["hello"].TypeUrl) 1343 } 1344 if !bytes.Equal(cExts["hello"].Value, ext.Value) { 1345 t.Errorf("expected extension value %q, got: %q", ext.Value, cExts["hello"].Value) 1346 } 1347 } 1348 1349 checkExt(container) 1350 1351 container, err = client.LoadContainer(ctx, container.ID()) 1352 if err != nil { 1353 t.Fatal(err) 1354 } 1355 checkExt(container) 1356 } 1357 1358 func TestContainerUpdate(t *testing.T) { 1359 t.Parallel() 1360 1361 ctx, cancel := testContext(t) 1362 defer cancel() 1363 id := t.Name() 1364 1365 client, err := newClient(t, address) 1366 if err != nil { 1367 t.Fatal(err) 1368 } 1369 defer client.Close() 1370 1371 container, err := client.NewContainer(ctx, id, WithNewSpec()) 1372 if err != nil { 1373 t.Fatal(err) 1374 } 1375 defer container.Delete(ctx) 1376 1377 spec, err := container.Spec(ctx) 1378 if err != nil { 1379 t.Fatal(err) 1380 } 1381 1382 const hostname = "updated-hostname" 1383 spec.Hostname = hostname 1384 1385 if err := container.Update(ctx, func(ctx context.Context, client *Client, c *containers.Container) error { 1386 a, err := typeurl.MarshalAny(spec) 1387 if err != nil { 1388 return err 1389 } 1390 c.Spec = a 1391 return nil 1392 }); err != nil { 1393 t.Fatal(err) 1394 } 1395 if spec, err = container.Spec(ctx); err != nil { 1396 t.Fatal(err) 1397 } 1398 if spec.Hostname != hostname { 1399 t.Errorf("hostname %q != %q", spec.Hostname, hostname) 1400 } 1401 } 1402 1403 func TestContainerInfo(t *testing.T) { 1404 t.Parallel() 1405 1406 ctx, cancel := testContext(t) 1407 defer cancel() 1408 id := t.Name() 1409 1410 client, err := newClient(t, address) 1411 if err != nil { 1412 t.Fatal(err) 1413 } 1414 defer client.Close() 1415 1416 container, err := client.NewContainer(ctx, id, WithNewSpec()) 1417 if err != nil { 1418 t.Fatal(err) 1419 } 1420 defer container.Delete(ctx) 1421 1422 info, err := container.Info(ctx) 1423 if err != nil { 1424 t.Fatal(err) 1425 } 1426 if info.ID != container.ID() { 1427 t.Fatalf("info.ID=%s != container.ID()=%s", info.ID, container.ID()) 1428 } 1429 } 1430 1431 func TestContainerLabels(t *testing.T) { 1432 t.Parallel() 1433 1434 ctx, cancel := testContext(t) 1435 defer cancel() 1436 id := t.Name() 1437 1438 client, err := newClient(t, address) 1439 if err != nil { 1440 t.Fatal(err) 1441 } 1442 defer client.Close() 1443 1444 container, err := client.NewContainer(ctx, id, WithNewSpec(), WithContainerLabels(map[string]string{ 1445 "test": "yes", 1446 })) 1447 if err != nil { 1448 t.Fatal(err) 1449 } 1450 defer container.Delete(ctx) 1451 1452 labels, err := container.Labels(ctx) 1453 if err != nil { 1454 t.Fatal(err) 1455 } 1456 if labels["test"] != "yes" { 1457 t.Fatalf("expected label \"test\" to be \"yes\"") 1458 } 1459 labels["test"] = "no" 1460 if labels, err = container.SetLabels(ctx, labels); err != nil { 1461 t.Fatal(err) 1462 } 1463 if labels["test"] != "no" { 1464 t.Fatalf("expected label \"test\" to be \"no\"") 1465 } 1466 } 1467 1468 func TestContainerHook(t *testing.T) { 1469 t.Parallel() 1470 1471 client, err := newClient(t, address) 1472 if err != nil { 1473 t.Fatal(err) 1474 } 1475 defer client.Close() 1476 1477 var ( 1478 image Image 1479 ctx, cancel = testContext(t) 1480 id = t.Name() 1481 ) 1482 defer cancel() 1483 1484 image, err = client.GetImage(ctx, testImage) 1485 if err != nil { 1486 t.Fatal(err) 1487 } 1488 hook := func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { 1489 if s.Hooks == nil { 1490 s.Hooks = &specs.Hooks{} 1491 } 1492 path, err := exec.LookPath("containerd") 1493 if err != nil { 1494 return err 1495 } 1496 psPath, err := exec.LookPath("ps") 1497 if err != nil { 1498 return err 1499 } 1500 s.Hooks.Prestart = []specs.Hook{ 1501 { 1502 Path: path, 1503 Args: []string{ 1504 "containerd", 1505 "oci-hook", "--", 1506 psPath, "--pid", "{{pid}}", 1507 }, 1508 Env: os.Environ(), 1509 }, 1510 } 1511 return nil 1512 } 1513 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), hook)) 1514 if err != nil { 1515 t.Fatal(err) 1516 } 1517 defer container.Delete(ctx, WithSnapshotCleanup) 1518 1519 task, err := container.NewTask(ctx, empty()) 1520 if err != nil { 1521 t.Fatal(err) 1522 } 1523 defer task.Delete(ctx, WithProcessKill) 1524 } 1525 1526 func TestShimSockLength(t *testing.T) { 1527 t.Parallel() 1528 1529 // Max length of namespace should be 76 1530 namespace := strings.Repeat("n", 76) 1531 1532 ctx, cancel := context.WithCancel(context.Background()) 1533 defer cancel() 1534 1535 ctx = namespaces.WithNamespace(ctx, namespace) 1536 1537 client, err := newClient(t, address) 1538 if err != nil { 1539 t.Fatal(err) 1540 } 1541 defer client.Close() 1542 1543 image, err := client.Pull(ctx, testImage, 1544 WithPlatformMatcher(platforms.Default()), 1545 WithPullUnpack, 1546 ) 1547 if err != nil { 1548 t.Fatal(err) 1549 } 1550 1551 id := strings.Repeat("c", 64) 1552 1553 // We don't have limitation with length of container name, 1554 // but 64 bytes of sha256 is the common case 1555 container, err := client.NewContainer(ctx, id, 1556 WithNewSnapshot(id, image), 1557 WithNewSpec(oci.WithImageConfig(image), withExitStatus(0)), 1558 ) 1559 if err != nil { 1560 t.Fatal(err) 1561 } 1562 defer container.Delete(ctx, WithSnapshotCleanup) 1563 1564 task, err := container.NewTask(ctx, empty()) 1565 if err != nil { 1566 t.Fatal(err) 1567 } 1568 defer task.Delete(ctx) 1569 1570 statusC, err := task.Wait(ctx) 1571 if err != nil { 1572 t.Fatal(err) 1573 } 1574 1575 if err := task.Start(ctx); err != nil { 1576 t.Fatal(err) 1577 } 1578 1579 <-statusC 1580 } 1581 1582 func TestContainerExecLargeOutputWithTTY(t *testing.T) { 1583 t.Parallel() 1584 1585 client, err := newClient(t, address) 1586 if err != nil { 1587 t.Fatal(err) 1588 } 1589 defer client.Close() 1590 1591 var ( 1592 image Image 1593 ctx, cancel = testContext(t) 1594 id = t.Name() 1595 ) 1596 defer cancel() 1597 1598 image, err = client.GetImage(ctx, testImage) 1599 if err != nil { 1600 t.Fatal(err) 1601 } 1602 1603 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "999"))) 1604 if err != nil { 1605 t.Fatal(err) 1606 } 1607 defer container.Delete(ctx, WithSnapshotCleanup) 1608 1609 task, err := container.NewTask(ctx, empty()) 1610 if err != nil { 1611 t.Fatal(err) 1612 } 1613 defer task.Delete(ctx) 1614 1615 finishedC, err := task.Wait(ctx) 1616 if err != nil { 1617 t.Fatal(err) 1618 } 1619 1620 if err := task.Start(ctx); err != nil { 1621 t.Fatal(err) 1622 } 1623 1624 for i := 0; i < 100; i++ { 1625 spec, err := container.Spec(ctx) 1626 if err != nil { 1627 t.Fatal(err) 1628 } 1629 1630 // start an exec process without running the original container process info 1631 processSpec := spec.Process 1632 withExecArgs(processSpec, "sh", "-c", `seq -s " " 1000000`) 1633 1634 stdout := bytes.NewBuffer(nil) 1635 1636 execID := t.Name() + "_exec" 1637 process, err := task.Exec(ctx, execID, processSpec, cio.NewCreator(withByteBuffers(stdout), withProcessTTY())) 1638 if err != nil { 1639 t.Fatal(err) 1640 } 1641 processStatusC, err := process.Wait(ctx) 1642 if err != nil { 1643 t.Fatal(err) 1644 } 1645 1646 if err := process.Start(ctx); err != nil { 1647 t.Fatal(err) 1648 } 1649 1650 // wait for the exec to return 1651 status := <-processStatusC 1652 code, _, err := status.Result() 1653 if err != nil { 1654 t.Fatal(err) 1655 } 1656 1657 if code != 0 { 1658 t.Errorf("expected exec exit code 0 but received %d", code) 1659 } 1660 if _, err := process.Delete(ctx); err != nil { 1661 t.Fatal(err) 1662 } 1663 1664 const expectedSuffix = "999999 1000000" 1665 stdoutString := stdout.String() 1666 if !strings.Contains(stdoutString, expectedSuffix) { 1667 t.Fatalf("process output does not end with %q at iteration %d, here are the last 20 characters of the output:\n\n %q", expectedSuffix, i, stdoutString[len(stdoutString)-20:]) 1668 } 1669 1670 } 1671 1672 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 1673 t.Error(err) 1674 } 1675 <-finishedC 1676 } 1677 1678 func TestShortRunningTaskPid(t *testing.T) { 1679 t.Parallel() 1680 1681 client, err := newClient(t, address) 1682 if err != nil { 1683 t.Fatal(err) 1684 } 1685 defer client.Close() 1686 1687 var ( 1688 image Image 1689 ctx, cancel = testContext(t) 1690 id = t.Name() 1691 ) 1692 defer cancel() 1693 1694 image, err = client.GetImage(ctx, testImage) 1695 if err != nil { 1696 t.Fatal(err) 1697 } 1698 1699 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("true"))) 1700 if err != nil { 1701 t.Fatal(err) 1702 } 1703 defer container.Delete(ctx, WithSnapshotCleanup) 1704 1705 task, err := container.NewTask(ctx, empty()) 1706 if err != nil { 1707 t.Fatal(err) 1708 } 1709 defer task.Delete(ctx) 1710 1711 finishedC, err := task.Wait(ctx) 1712 if err != nil { 1713 t.Fatal(err) 1714 } 1715 1716 if err := task.Start(ctx); err != nil { 1717 t.Fatal(err) 1718 } 1719 1720 int32PID := int32(task.Pid()) 1721 if int32PID <= 0 { 1722 t.Errorf("Unexpected task pid %d", int32PID) 1723 } 1724 <-finishedC 1725 } 1726 1727 func withProcessTTY() cio.Opt { 1728 return func(opt *cio.Streams) { 1729 cio.WithTerminal(opt) 1730 } 1731 }