attach: Use ContainerWait instead of Inspect

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
Kenfe-Mickael Laventure 2017-11-15 19:18:23 -08:00
parent 026c6db6f6
commit 66661761d5
No known key found for this signature in database
GPG Key ID: 40CF16616B361216
3 changed files with 71 additions and 37 deletions

View File

@ -1,12 +1,14 @@
package container package container
import ( import (
"fmt"
"io" "io"
"net/http/httputil" "net/http/httputil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/signal"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -66,6 +68,9 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
ctx := context.Background() ctx := context.Background()
client := dockerCli.Client() client := dockerCli.Client()
// request channel to wait for client
resultC, errC := client.ContainerWait(ctx, opts.container, "")
c, err := inspectContainerAndCheckState(ctx, client, opts.container) c, err := inspectContainerAndCheckState(ctx, client, opts.container)
if err != nil { if err != nil {
return err return err
@ -140,7 +145,24 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
if errAttach != nil { if errAttach != nil {
return errAttach return errAttach
} }
return getExitStatus(ctx, dockerCli.Client(), opts.container)
return getExitStatus(errC, resultC)
}
func getExitStatus(errC <-chan error, resultC <-chan container.ContainerWaitOKBody) error {
select {
case result := <-resultC:
if result.Error != nil {
return fmt.Errorf(result.Error.Message)
}
if result.StatusCode != 0 {
return cli.StatusError{StatusCode: int(result.StatusCode)}
}
case err := <-errC:
return err
}
return nil
} }
func resizeTTY(ctx context.Context, dockerCli command.Cli, containerID string) { func resizeTTY(ctx context.Context, dockerCli command.Cli, containerID string) {
@ -157,19 +179,3 @@ func resizeTTY(ctx context.Context, dockerCli command.Cli, containerID string) {
logrus.Debugf("Error monitoring TTY size: %s", err) logrus.Debugf("Error monitoring TTY size: %s", err)
} }
} }
func getExitStatus(ctx context.Context, apiclient client.ContainerAPIClient, containerID string) error {
container, err := apiclient.ContainerInspect(ctx, containerID)
if err != nil {
// If we can't connect, then the daemon probably died.
if !client.IsErrConnectionFailed(err) {
return err
}
return cli.StatusError{StatusCode: -1}
}
status := container.State.ExitCode
if status != 0 {
return cli.StatusError{StatusCode: status}
}
return nil
}

View File

@ -1,6 +1,7 @@
package container package container
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"testing" "testing"
@ -8,9 +9,9 @@ import (
"github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/testutil" "github.com/docker/cli/internal/test/testutil"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/net/context"
) )
func TestNewAttachCommandErrors(t *testing.T) { func TestNewAttachCommandErrors(t *testing.T) {
@ -78,40 +79,50 @@ func TestNewAttachCommandErrors(t *testing.T) {
} }
func TestGetExitStatus(t *testing.T) { func TestGetExitStatus(t *testing.T) {
containerID := "the exec id" var (
expecatedErr := errors.New("unexpected error") expectedErr = fmt.Errorf("unexpected error")
errC = make(chan error, 1)
resultC = make(chan container.ContainerWaitOKBody, 1)
)
testcases := []struct { testcases := []struct {
inspectError error result *container.ContainerWaitOKBody
exitCode int err error
expectedError error expectedError error
}{ }{
{ {
inspectError: nil, result: &container.ContainerWaitOKBody{
exitCode: 0, StatusCode: 0,
},
}, },
{ {
inspectError: expecatedErr, err: expectedErr,
expectedError: expecatedErr, expectedError: expectedErr,
}, },
{ {
exitCode: 15, result: &container.ContainerWaitOKBody{
Error: &container.ContainerWaitOKBodyError{
expectedErr.Error(),
},
},
expectedError: expectedErr,
},
{
result: &container.ContainerWaitOKBody{
StatusCode: 15,
},
expectedError: cli.StatusError{StatusCode: 15}, expectedError: cli.StatusError{StatusCode: 15},
}, },
} }
for _, testcase := range testcases { for _, testcase := range testcases {
client := &fakeClient{ if testcase.err != nil {
inspectFunc: func(id string) (types.ContainerJSON, error) { errC <- testcase.err
assert.Equal(t, containerID, id)
return types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
State: &types.ContainerState{ExitCode: testcase.exitCode},
},
}, testcase.inspectError
},
} }
err := getExitStatus(context.Background(), client, containerID) if testcase.result != nil {
resultC <- *testcase.result
}
err := getExitStatus(errC, resultC)
assert.Equal(t, testcase.expectedError, err) assert.Equal(t, testcase.expectedError, err)
} }
} }

View File

@ -0,0 +1,17 @@
package container
import (
"testing"
"github.com/gotestyourself/gotestyourself/icmd"
)
func TestAttachExitCode(t *testing.T) {
cName := "test-attach-exit-code"
icmd.RunCommand("docker", "run", "-d", "--rm", "--name", cName,
alpineImage, "sh", "-c", "sleep 5 ; exit 21").Assert(t, icmd.Success)
cmd := icmd.Command("docker", "wait", cName)
res := icmd.StartCmd(cmd)
icmd.RunCommand("docker", "attach", cName).Assert(t, icmd.Expected{ExitCode: 21})
icmd.WaitOnCmd(8, res).Assert(t, icmd.Expected{ExitCode: 0, Out: "21"})
}