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 return err
} }
status := <-statusC status := <-statusC
if status.Err != nil { _, _, err = status.Result()
if status.Err == context.DeadlineExceeded || status.Err == context.Canceled { if err != nil {
if err == context.DeadlineExceeded || err == context.Canceled {
return nil return nil
} }
w.failures++ w.failures++

View File

@ -60,11 +60,12 @@ var taskAttachCommand = cli.Command{
} }
ec := <-statusC ec := <-statusC
if ec.Err != nil { code, _, err := ec.Result()
if err != nil {
return err return err
} }
if ec.Code != 0 { if code != 0 {
return cli.NewExitError("", int(ec.Code)) return cli.NewExitError("", int(code))
} }
return nil return nil
}, },

View File

@ -95,11 +95,12 @@ var taskExecCommand = cli.Command{
defer stopCatch(sigc) defer stopCatch(sigc)
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
return status.Err if err != nil {
return err
} }
if status.Code != 0 { if code != 0 {
return cli.NewExitError("", int(status.Code)) return cli.NewExitError("", int(code))
} }
return nil return nil
}, },

View File

@ -155,15 +155,16 @@ var runCommand = cli.Command{
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
return status.Err if err != nil {
return err
} }
if _, err := task.Delete(ctx); err != nil { if _, err := task.Delete(ctx); err != nil {
return err return err
} }
if status.Code != 0 { if code != 0 {
return cli.NewExitError("", int(status.Code)) return cli.NewExitError("", int(code))
} }
return nil return nil
}, },

View File

@ -73,14 +73,15 @@ var taskStartCommand = cli.Command{
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
if err != nil {
return err return err
} }
if _, err := task.Delete(ctx); err != nil { if _, err := task.Delete(ctx); err != nil {
return err return err
} }
if status.Code != 0 { if code != 0 {
return cli.NewExitError("", int(status.Code)) return cli.NewExitError("", int(code))
} }
return nil return nil
}, },

View File

@ -232,7 +232,8 @@ func TestDaemonRestart(t *testing.T) {
} }
status := <-statusC status := <-statusC
if status.Err == nil { _, _, err = status.Result()
if err == nil {
t.Errorf(`first task.Wait() should have failed with "transport is closing"`) t.Errorf(`first task.Wait() should have failed with "transport is closing"`)
} }

View File

@ -139,12 +139,13 @@ func TestContainerStart(t *testing.T) {
return return
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
if err != nil {
t.Error(err) t.Error(err)
return return
} }
if status.Code != 7 { if code != 7 {
t.Errorf("expected status 7 from wait but received %d", status.Code) t.Errorf("expected status 7 from wait but received %d", code)
} }
deleteStatus, err := task.Delete(ctx) deleteStatus, err := task.Delete(ctx)
@ -214,8 +215,9 @@ func TestContainerOutput(t *testing.T) {
} }
status := <-statusC status := <-statusC
if status.Code != 0 { code, _, _ := status.Result()
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 { if _, err := task.Delete(ctx); err != nil {
t.Error(err) t.Error(err)
@ -306,13 +308,14 @@ func TestContainerExec(t *testing.T) {
// wait for the exec to return // wait for the exec to return
status := <-processStatusC status := <-processStatusC
if status.Err != nil { code, _, err := status.Result()
if err != nil {
t.Error(err) t.Error(err)
return return
} }
if status.Code != 6 { if code != 6 {
t.Errorf("expected exec exit code 6 but received %d", status.Code) t.Errorf("expected exec exit code 6 but received %d", code)
} }
deleteStatus, err := process.Delete(ctx) deleteStatus, err := process.Delete(ctx)
if err != nil { if err != nil {
@ -610,7 +613,8 @@ func TestContainerAttach(t *testing.T) {
} }
status := <-statusC status := <-statusC
if status.Err != nil { _, _, err = status.Result()
if err != nil {
t.Error(err) t.Error(err)
return return
} }
@ -960,12 +964,13 @@ func TestUserNamespaces(t *testing.T) {
return return
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
if err != nil {
t.Error(err) t.Error(err)
return return
} }
if status.Code != 7 { if code != 7 {
t.Errorf("expected status 7 from wait but received %d", status.Code) t.Errorf("expected status 7 from wait but received %d", code)
} }
deleteStatus, err := task.Delete(ctx) deleteStatus, err := task.Delete(ctx)
if err != nil { if err != nil {
@ -1043,12 +1048,13 @@ func TestWaitStoppedTask(t *testing.T) {
return return
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
t.Error(status.Err) if err != nil {
t.Error(err)
return return
} }
if status.Code != 7 { if code != 7 {
t.Errorf("exit status from stopped task should be 7 but received %d", status.Code) t.Errorf("exit status from stopped task should be 7 but received %d", code)
} }
} }
@ -1137,12 +1143,13 @@ func TestWaitStoppedProcess(t *testing.T) {
return return
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
if err != nil {
t.Error(err) t.Error(err)
return return
} }
if status.Code != 6 { if code != 6 {
t.Errorf("exit status from stopped process should be 6 but received %d", status.Code) t.Errorf("exit status from stopped process should be 6 but received %d", code)
} }
if err := task.Kill(ctx, syscall.SIGKILL); err != nil { if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
@ -1343,12 +1350,13 @@ func TestContainerHostname(t *testing.T) {
} }
status := <-statusC status := <-statusC
if status.Err != nil { code, _, err := status.Result()
t.Error(status.Err) if err != nil {
t.Error(err)
return return
} }
if status.Code != 0 { if code != 0 {
t.Errorf("expected status 0 but received %d", status) t.Errorf("expected status 0 but received %d", code)
} }
if _, err := task.Delete(ctx); err != nil { if _, err := task.Delete(ctx); err != nil {
t.Error(err) t.Error(err)
@ -1420,8 +1428,9 @@ func TestContainerExitedAtSet(t *testing.T) {
} }
status := <-statusC status := <-statusC
if status.Code != 0 { code, _, _ := status.Result()
t.Errorf("expected status 0 but received %d", status) if code != 0 {
t.Errorf("expected status 0 but received %d", code)
} }
if s, err := task.Status(ctx); err != nil { 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 status := <-exitStatusC
if status.Err != nil { code, exitedAt, err := status.Result()
return status.Err 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. 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 // wait for the process to fully exit and print out the exit status
status := <-exitStatusC status := <-exitStatusC
if status.Err != nil { code, _, err := status.Result()
if err != nil {
return err 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 return nil
} }

View File

@ -37,14 +37,21 @@ type Process interface {
Status(context.Context) (Status, error) 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 // 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 { type ExitStatus struct {
Code uint32 code uint32
ExitedAt time.Time exitedAt time.Time
Err error 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 { type process struct {
@ -109,7 +116,7 @@ func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) {
chStatus := make(chan ExitStatus, 1) chStatus := make(chan ExitStatus, 1)
if status.Status == Stopped { if status.Status == Stopped {
cancel() cancel()
chStatus <- ExitStatus{Code: status.ExitStatus, ExitedAt: status.ExitTime} chStatus <- ExitStatus{code: status.ExitStatus, exitedAt: status.ExitTime}
return chStatus, nil return chStatus, nil
} }
@ -119,18 +126,18 @@ func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) {
for { for {
evt, err := eventstream.Recv() evt, err := eventstream.Recv()
if err != nil { if err != nil {
chStatus <- ExitStatus{Code: UnknownExitStatus, Err: err} chStatus <- ExitStatus{code: UnknownExitStatus, err: err}
return return
} }
if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) { if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) {
v, err := typeurl.UnmarshalAny(evt.Event) v, err := typeurl.UnmarshalAny(evt.Event)
if err != nil { if err != nil {
chStatus <- ExitStatus{Code: UnknownExitStatus, Err: err} chStatus <- ExitStatus{code: UnknownExitStatus, err: err}
return return
} }
e := v.(*eventsapi.TaskExit) e := v.(*eventsapi.TaskExit)
if e.ID == p.id && e.ContainerID == p.task.id { 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 return
} }
} }

View File

@ -225,7 +225,7 @@ func (t *task) Wait(ctx context.Context) (<-chan ExitStatus, error) {
} }
if status.Status == Stopped { if status.Status == Stopped {
cancel() cancel()
chStatus <- ExitStatus{Code: status.ExitStatus, ExitedAt: status.ExitTime} chStatus <- ExitStatus{code: status.ExitStatus, exitedAt: status.ExitTime}
return chStatus, nil return chStatus, nil
} }
} }
@ -236,18 +236,18 @@ func (t *task) Wait(ctx context.Context) (<-chan ExitStatus, error) {
for { for {
evt, err := eventstream.Recv() evt, err := eventstream.Recv()
if err != nil { if err != nil {
chStatus <- ExitStatus{Code: UnknownExitStatus, Err: errdefs.FromGRPC(err)} chStatus <- ExitStatus{code: UnknownExitStatus, err: errdefs.FromGRPC(err)}
return return
} }
if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) { if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) {
v, err := typeurl.UnmarshalAny(evt.Event) v, err := typeurl.UnmarshalAny(evt.Event)
if err != nil { if err != nil {
chStatus <- ExitStatus{Code: UnknownExitStatus, Err: err} chStatus <- ExitStatus{code: UnknownExitStatus, err: err}
return return
} }
e := v.(*eventsapi.TaskExit) e := v.(*eventsapi.TaskExit)
if e.ContainerID == t.id && e.Pid == t.pid { 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 return
} }
} }