Merge pull request #696 from mlaventure/attach-use-containerwait

attach: Use ContainerWait instead of Inspect
This commit is contained in:
Sebastiaan van Stijn 2018-01-20 23:55:16 +01:00 committed by GitHub
commit 44a1168ae5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 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,29 @@
package container
import (
"strings"
"testing"
"github.com/gotestyourself/gotestyourself/icmd"
)
func TestAttachExitCode(t *testing.T) {
containerID := runBackgroundContainsWithExitCode(t, 21)
result := icmd.RunCmd(
icmd.Command("docker", "attach", containerID),
withStdinNewline)
result.Assert(t, icmd.Expected{ExitCode: 21})
}
func runBackgroundContainsWithExitCode(t *testing.T, exitcode int) string {
result := icmd.RunCmd(shell(t,
"docker run -d -i --rm %s sh -c 'read; exit %d'", alpineImage, exitcode))
result.Assert(t, icmd.Success)
return strings.TrimSpace(result.Stdout())
}
func withStdinNewline(cmd *icmd.Cmd) {
cmd.Stdin = strings.NewReader("\n")
}