mirror of https://github.com/docker/cli.git
Merge pull request #696 from mlaventure/attach-use-containerwait
attach: Use ContainerWait instead of Inspect
This commit is contained in:
commit
44a1168ae5
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
Loading…
Reference in New Issue