Skip to content

Commit 44a1168

Browse files
authored
Merge pull request #696 from mlaventure/attach-use-containerwait
attach: Use ContainerWait instead of Inspect
2 parents da86425 + bace33d commit 44a1168

File tree

3 files changed

+83
-37
lines changed

3 files changed

+83
-37
lines changed

cli/command/container/attach.go

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package container
22

33
import (
4+
"fmt"
45
"io"
56
"net/http/httputil"
67

78
"github.com/docker/cli/cli"
89
"github.com/docker/cli/cli/command"
910
"github.com/docker/docker/api/types"
11+
"github.com/docker/docker/api/types/container"
1012
"github.com/docker/docker/client"
1113
"github.com/docker/docker/pkg/signal"
1214
"github.com/pkg/errors"
@@ -66,6 +68,9 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
6668
ctx := context.Background()
6769
client := dockerCli.Client()
6870

71+
// request channel to wait for client
72+
resultC, errC := client.ContainerWait(ctx, opts.container, "")
73+
6974
c, err := inspectContainerAndCheckState(ctx, client, opts.container)
7075
if err != nil {
7176
return err
@@ -140,7 +145,24 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
140145
if errAttach != nil {
141146
return errAttach
142147
}
143-
return getExitStatus(ctx, dockerCli.Client(), opts.container)
148+
149+
return getExitStatus(errC, resultC)
150+
}
151+
152+
func getExitStatus(errC <-chan error, resultC <-chan container.ContainerWaitOKBody) error {
153+
select {
154+
case result := <-resultC:
155+
if result.Error != nil {
156+
return fmt.Errorf(result.Error.Message)
157+
}
158+
if result.StatusCode != 0 {
159+
return cli.StatusError{StatusCode: int(result.StatusCode)}
160+
}
161+
case err := <-errC:
162+
return err
163+
}
164+
165+
return nil
144166
}
145167

146168
func resizeTTY(ctx context.Context, dockerCli command.Cli, containerID string) {
@@ -157,19 +179,3 @@ func resizeTTY(ctx context.Context, dockerCli command.Cli, containerID string) {
157179
logrus.Debugf("Error monitoring TTY size: %s", err)
158180
}
159181
}
160-
161-
func getExitStatus(ctx context.Context, apiclient client.ContainerAPIClient, containerID string) error {
162-
container, err := apiclient.ContainerInspect(ctx, containerID)
163-
if err != nil {
164-
// If we can't connect, then the daemon probably died.
165-
if !client.IsErrConnectionFailed(err) {
166-
return err
167-
}
168-
return cli.StatusError{StatusCode: -1}
169-
}
170-
status := container.State.ExitCode
171-
if status != 0 {
172-
return cli.StatusError{StatusCode: status}
173-
}
174-
return nil
175-
}

cli/command/container/attach_test.go

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package container
22

33
import (
4+
"fmt"
45
"io/ioutil"
56
"testing"
67

78
"github.com/docker/cli/cli"
89
"github.com/docker/cli/internal/test"
910
"github.com/docker/cli/internal/test/testutil"
1011
"github.com/docker/docker/api/types"
12+
"github.com/docker/docker/api/types/container"
1113
"github.com/pkg/errors"
1214
"github.com/stretchr/testify/assert"
13-
"golang.org/x/net/context"
1415
)
1516

1617
func TestNewAttachCommandErrors(t *testing.T) {
@@ -78,40 +79,50 @@ func TestNewAttachCommandErrors(t *testing.T) {
7879
}
7980

8081
func TestGetExitStatus(t *testing.T) {
81-
containerID := "the exec id"
82-
expecatedErr := errors.New("unexpected error")
82+
var (
83+
expectedErr = fmt.Errorf("unexpected error")
84+
errC = make(chan error, 1)
85+
resultC = make(chan container.ContainerWaitOKBody, 1)
86+
)
8387

8488
testcases := []struct {
85-
inspectError error
86-
exitCode int
89+
result *container.ContainerWaitOKBody
90+
err error
8791
expectedError error
8892
}{
8993
{
90-
inspectError: nil,
91-
exitCode: 0,
94+
result: &container.ContainerWaitOKBody{
95+
StatusCode: 0,
96+
},
97+
},
98+
{
99+
err: expectedErr,
100+
expectedError: expectedErr,
92101
},
93102
{
94-
inspectError: expecatedErr,
95-
expectedError: expecatedErr,
103+
result: &container.ContainerWaitOKBody{
104+
Error: &container.ContainerWaitOKBodyError{
105+
expectedErr.Error(),
106+
},
107+
},
108+
expectedError: expectedErr,
96109
},
97110
{
98-
exitCode: 15,
111+
result: &container.ContainerWaitOKBody{
112+
StatusCode: 15,
113+
},
99114
expectedError: cli.StatusError{StatusCode: 15},
100115
},
101116
}
102117

103118
for _, testcase := range testcases {
104-
client := &fakeClient{
105-
inspectFunc: func(id string) (types.ContainerJSON, error) {
106-
assert.Equal(t, containerID, id)
107-
return types.ContainerJSON{
108-
ContainerJSONBase: &types.ContainerJSONBase{
109-
State: &types.ContainerState{ExitCode: testcase.exitCode},
110-
},
111-
}, testcase.inspectError
112-
},
119+
if testcase.err != nil {
120+
errC <- testcase.err
121+
}
122+
if testcase.result != nil {
123+
resultC <- *testcase.result
113124
}
114-
err := getExitStatus(context.Background(), client, containerID)
125+
err := getExitStatus(errC, resultC)
115126
assert.Equal(t, testcase.expectedError, err)
116127
}
117128
}

e2e/container/attach_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package container
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/gotestyourself/gotestyourself/icmd"
8+
)
9+
10+
func TestAttachExitCode(t *testing.T) {
11+
containerID := runBackgroundContainsWithExitCode(t, 21)
12+
13+
result := icmd.RunCmd(
14+
icmd.Command("docker", "attach", containerID),
15+
withStdinNewline)
16+
17+
result.Assert(t, icmd.Expected{ExitCode: 21})
18+
}
19+
20+
func runBackgroundContainsWithExitCode(t *testing.T, exitcode int) string {
21+
result := icmd.RunCmd(shell(t,
22+
"docker run -d -i --rm %s sh -c 'read; exit %d'", alpineImage, exitcode))
23+
result.Assert(t, icmd.Success)
24+
return strings.TrimSpace(result.Stdout())
25+
}
26+
27+
func withStdinNewline(cmd *icmd.Cmd) {
28+
cmd.Stdin = strings.NewReader("\n")
29+
}

0 commit comments

Comments
 (0)