diff --git a/cmd/containerd-stress/main.go b/cmd/containerd-stress/main.go index ede505d96..fa2c167e4 100644 --- a/cmd/containerd-stress/main.go +++ b/cmd/containerd-stress/main.go @@ -218,8 +218,9 @@ func (w *worker) runContainer(ctx context.Context, id string) error { return err } status := <-statusC - if status.Err != nil { - if status.Err == context.DeadlineExceeded || status.Err == context.Canceled { + _, _, err = status.Result() + if err != nil { + if err == context.DeadlineExceeded || err == context.Canceled { return nil } w.failures++ diff --git a/cmd/ctr/attach.go b/cmd/ctr/attach.go index 390835c3e..25a676053 100644 --- a/cmd/ctr/attach.go +++ b/cmd/ctr/attach.go @@ -60,11 +60,12 @@ var taskAttachCommand = cli.Command{ } ec := <-statusC - if ec.Err != nil { + code, _, err := ec.Result() + if err != nil { return err } - if ec.Code != 0 { - return cli.NewExitError("", int(ec.Code)) + if code != 0 { + return cli.NewExitError("", int(code)) } return nil }, diff --git a/cmd/ctr/exec.go b/cmd/ctr/exec.go index a9fd7530e..849903b6b 100644 --- a/cmd/ctr/exec.go +++ b/cmd/ctr/exec.go @@ -95,11 +95,12 @@ var taskExecCommand = cli.Command{ defer stopCatch(sigc) } status := <-statusC - if status.Err != nil { - return status.Err + code, _, err := status.Result() + if err != nil { + return err } - if status.Code != 0 { - return cli.NewExitError("", int(status.Code)) + if code != 0 { + return cli.NewExitError("", int(code)) } return nil }, diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index 12611345c..da7a4412c 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -155,15 +155,16 @@ var runCommand = cli.Command{ } status := <-statusC - if status.Err != nil { - return status.Err + code, _, err := status.Result() + if err != nil { + return err } if _, err := task.Delete(ctx); err != nil { return err } - if status.Code != 0 { - return cli.NewExitError("", int(status.Code)) + if code != 0 { + return cli.NewExitError("", int(code)) } return nil }, diff --git a/cmd/ctr/start.go b/cmd/ctr/start.go index ff7195886..40d32060a 100644 --- a/cmd/ctr/start.go +++ b/cmd/ctr/start.go @@ -73,14 +73,15 @@ var taskStartCommand = cli.Command{ } status := <-statusC - if status.Err != nil { + code, _, err := status.Result() + if err != nil { return err } if _, err := task.Delete(ctx); err != nil { return err } - if status.Code != 0 { - return cli.NewExitError("", int(status.Code)) + if code != 0 { + return cli.NewExitError("", int(code)) } return nil }, diff --git a/container_linux_test.go b/container_linux_test.go index 4675ac17b..18e66d4a9 100644 --- a/container_linux_test.go +++ b/container_linux_test.go @@ -232,7 +232,8 @@ func TestDaemonRestart(t *testing.T) { } status := <-statusC - if status.Err == nil { + _, _, err = status.Result() + if err == nil { t.Errorf(`first task.Wait() should have failed with "transport is closing"`) } diff --git a/container_test.go b/container_test.go index 514c5409e..e73b8c8b3 100644 --- a/container_test.go +++ b/container_test.go @@ -139,12 +139,13 @@ func TestContainerStart(t *testing.T) { return } status := <-statusC - if status.Err != nil { + code, _, err := status.Result() + if err != nil { t.Error(err) return } - if status.Code != 7 { - t.Errorf("expected status 7 from wait but received %d", status.Code) + if code != 7 { + t.Errorf("expected status 7 from wait but received %d", code) } deleteStatus, err := task.Delete(ctx) @@ -214,8 +215,9 @@ func TestContainerOutput(t *testing.T) { } status := <-statusC - if status.Code != 0 { - t.Errorf("expected status 0 but received %d", status) + code, _, _ := status.Result() + if code != 0 { + t.Errorf("expected status 0 but received %d", code) } if _, err := task.Delete(ctx); err != nil { t.Error(err) @@ -306,13 +308,14 @@ func TestContainerExec(t *testing.T) { // wait for the exec to return status := <-processStatusC - if status.Err != nil { + code, _, err := status.Result() + if err != nil { t.Error(err) return } - if status.Code != 6 { - t.Errorf("expected exec exit code 6 but received %d", status.Code) + if code != 6 { + t.Errorf("expected exec exit code 6 but received %d", code) } deleteStatus, err := process.Delete(ctx) if err != nil { @@ -610,7 +613,8 @@ func TestContainerAttach(t *testing.T) { } status := <-statusC - if status.Err != nil { + _, _, err = status.Result() + if err != nil { t.Error(err) return } @@ -960,12 +964,13 @@ func TestUserNamespaces(t *testing.T) { return } status := <-statusC - if status.Err != nil { + code, _, err := status.Result() + if err != nil { t.Error(err) return } - if status.Code != 7 { - t.Errorf("expected status 7 from wait but received %d", status.Code) + if code != 7 { + t.Errorf("expected status 7 from wait but received %d", code) } deleteStatus, err := task.Delete(ctx) if err != nil { @@ -1043,12 +1048,13 @@ func TestWaitStoppedTask(t *testing.T) { return } status := <-statusC - if status.Err != nil { - t.Error(status.Err) + code, _, err := status.Result() + if err != nil { + t.Error(err) return } - if status.Code != 7 { - t.Errorf("exit status from stopped task should be 7 but received %d", status.Code) + if code != 7 { + t.Errorf("exit status from stopped task should be 7 but received %d", code) } } @@ -1137,12 +1143,13 @@ func TestWaitStoppedProcess(t *testing.T) { return } status := <-statusC - if status.Err != nil { + code, _, err := status.Result() + if err != nil { t.Error(err) return } - if status.Code != 6 { - t.Errorf("exit status from stopped process should be 6 but received %d", status.Code) + if code != 6 { + t.Errorf("exit status from stopped process should be 6 but received %d", code) } if err := task.Kill(ctx, syscall.SIGKILL); err != nil { @@ -1343,12 +1350,13 @@ func TestContainerHostname(t *testing.T) { } status := <-statusC - if status.Err != nil { - t.Error(status.Err) + code, _, err := status.Result() + if err != nil { + t.Error(err) return } - if status.Code != 0 { - t.Errorf("expected status 0 but received %d", status) + if code != 0 { + t.Errorf("expected status 0 but received %d", code) } if _, err := task.Delete(ctx); err != nil { t.Error(err) @@ -1420,8 +1428,9 @@ func TestContainerExitedAtSet(t *testing.T) { } status := <-statusC - if status.Code != 0 { - t.Errorf("expected status 0 but received %d", status) + code, _, _ := status.Result() + if code != 0 { + t.Errorf("expected status 0 but received %d", code) } if s, err := task.Status(ctx); err != nil { diff --git a/docs/getting-started.md b/docs/getting-started.md index 5e0e33aaa..ab96f76db 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -188,10 +188,11 @@ To do this we will simply call `Kill` on the task after waiting a couple of seco } status := <-exitStatusC - if status.Err != nil { - return status.Err + code, exitedAt, err := status.Result() + if err != nil { + return err } - fmt.Printf("redis-server exited with status: %d\n", status.Code) + fmt.Printf("redis-server exited with status: %d\n", code) ``` We wait on our exit status channel that we setup to ensure the task has fully exited and we get the exit status. @@ -291,10 +292,11 @@ func redisExample() error { // wait for the process to fully exit and print out the exit status status := <-exitStatusC - if status.Err != nil { + code, _, err := status.Result() + if err != nil { return err } - fmt.Printf("redis-server exited with status: %d\n", status.Code) + fmt.Printf("redis-server exited with status: %d\n", code) return nil } diff --git a/process.go b/process.go index 0facb5f41..8d6edf4d8 100644 --- a/process.go +++ b/process.go @@ -37,14 +37,21 @@ type Process interface { Status(context.Context) (Status, error) } -// ExitStatus encapsulates a process' exit code. +// ExitStatus encapsulates a process' exit status. // It is used by `Wait()` to return either a process exit code or an error -// The `Err` field is provided to return an error that may occur while waiting -// `Err` is not used to convey an error with the process itself. type ExitStatus struct { - Code uint32 - ExitedAt time.Time - Err error + code uint32 + exitedAt time.Time + err error +} + +// Result returns the exit code and time of the exit status. +// An error may be returned here to which indicates there was an error +// at some point while waiting for the exit status. It does not signify +// an error with the process itself. +// If an error is returned, the process may still be running. +func (s ExitStatus) Result() (uint32, time.Time, error) { + return s.code, s.exitedAt, s.err } type process struct { @@ -109,7 +116,7 @@ func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) { chStatus := make(chan ExitStatus, 1) if status.Status == Stopped { cancel() - chStatus <- ExitStatus{Code: status.ExitStatus, ExitedAt: status.ExitTime} + chStatus <- ExitStatus{code: status.ExitStatus, exitedAt: status.ExitTime} return chStatus, nil } @@ -119,18 +126,18 @@ func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) { for { evt, err := eventstream.Recv() if err != nil { - chStatus <- ExitStatus{Code: UnknownExitStatus, Err: err} + chStatus <- ExitStatus{code: UnknownExitStatus, err: err} return } if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) { v, err := typeurl.UnmarshalAny(evt.Event) if err != nil { - chStatus <- ExitStatus{Code: UnknownExitStatus, Err: err} + chStatus <- ExitStatus{code: UnknownExitStatus, err: err} return } e := v.(*eventsapi.TaskExit) if e.ID == p.id && e.ContainerID == p.task.id { - chStatus <- ExitStatus{Code: e.ExitStatus, ExitedAt: e.ExitedAt} + chStatus <- ExitStatus{code: e.ExitStatus, exitedAt: e.ExitedAt} return } } diff --git a/task.go b/task.go index c0c6182f8..e55aab6ed 100644 --- a/task.go +++ b/task.go @@ -225,7 +225,7 @@ func (t *task) Wait(ctx context.Context) (<-chan ExitStatus, error) { } if status.Status == Stopped { cancel() - chStatus <- ExitStatus{Code: status.ExitStatus, ExitedAt: status.ExitTime} + chStatus <- ExitStatus{code: status.ExitStatus, exitedAt: status.ExitTime} return chStatus, nil } } @@ -236,18 +236,18 @@ func (t *task) Wait(ctx context.Context) (<-chan ExitStatus, error) { for { evt, err := eventstream.Recv() if err != nil { - chStatus <- ExitStatus{Code: UnknownExitStatus, Err: errdefs.FromGRPC(err)} + chStatus <- ExitStatus{code: UnknownExitStatus, err: errdefs.FromGRPC(err)} return } if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) { v, err := typeurl.UnmarshalAny(evt.Event) if err != nil { - chStatus <- ExitStatus{Code: UnknownExitStatus, Err: err} + chStatus <- ExitStatus{code: UnknownExitStatus, err: err} return } e := v.(*eventsapi.TaskExit) if e.ContainerID == t.id && e.Pid == t.pid { - chStatus <- ExitStatus{Code: e.ExitStatus, ExitedAt: e.ExitedAt} + chStatus <- ExitStatus{code: e.ExitStatus, exitedAt: e.ExitedAt} return } }