Convert ExitStatus to use fn to get details

Instead of requiring callers to read the struct fields to check for an
error, provide the exit results via a function instead which is more
natural.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2017-08-21 21:28:37 -04:00
parent 026896ac4c
commit 6ab99edb71
10 changed files with 85 additions and 61 deletions

View File

@ -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++

View File

@ -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
},

View File

@ -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
},

View File

@ -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
},

View File

@ -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
},

View File

@ -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"`)
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}