Skip to content

Commit 38591f2

Browse files
author
Josh Hawn
committed
Update CLI package with containerWait changes
The docker/client package was updated to support the updated Container Wait API functionality. The run and start commands have been updated to use the new wait features. Docker-DCO-1.1-Signed-off-by: Josh Hawn <[email protected]> (github: jlhawn)
1 parent 4f9ac48 commit 38591f2

File tree

7 files changed

+78
-11
lines changed

7 files changed

+78
-11
lines changed

cli/command/container/attach.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func runAttach(dockerCli *command.DockerCli, opts *attachOptions) error {
109109
logrus.Debugf("Error monitoring TTY size: %s", err)
110110
}
111111
}
112-
if err := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
112+
if err := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, options.DetachKeys, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
113113
return err
114114
}
115115

cli/command/container/exec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func runExec(dockerCli *command.DockerCli, options *execOptions, container strin
146146
}
147147
defer resp.Close()
148148
errCh = promise.Go(func() error {
149-
return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, in, out, stderr, resp)
149+
return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, execConfig.DetachKeys, in, out, stderr, resp)
150150
})
151151

152152
if execConfig.Tty && dockerCli.In().IsTerminal() {

cli/command/container/hijack.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ import (
88
"github.com/Sirupsen/logrus"
99
"github.com/docker/cli/cli/command"
1010
"github.com/docker/docker/api/types"
11+
"github.com/docker/docker/pkg/ioutils"
1112
"github.com/docker/docker/pkg/stdcopy"
13+
"github.com/docker/docker/pkg/term"
1214
"golang.org/x/net/context"
1315
)
1416

17+
// The default escape key sequence: ctrl-p, ctrl-q
18+
var defaultEscapeKeys = []byte{16, 17}
19+
1520
// holdHijackedConnection handles copying input to and output from streams to the
1621
// connection
1722
// nolint: gocyclo
18-
func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
23+
func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bool, detachKeys string, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
1924
var (
2025
err error
2126
restoreOnce sync.Once
@@ -29,6 +34,15 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
2934
restoreTerminal(streams, inputStream)
3035
})
3136
}()
37+
38+
// Wrap the input to detect detach control sequence.
39+
// Use default detach sequence if an invalid sequence is given.
40+
escapeKeys, err := term.ToBytes(detachKeys)
41+
if len(escapeKeys) == 0 || err != nil {
42+
escapeKeys = defaultEscapeKeys
43+
}
44+
45+
inputStream = ioutils.NewReadCloserWrapper(term.NewEscapeProxy(inputStream, escapeKeys), inputStream.Close)
3246
}
3347

3448
receiveStdout := make(chan error, 1)
@@ -54,9 +68,10 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
5468
}
5569

5670
stdinDone := make(chan struct{})
71+
detachedC := make(chan term.EscapeError)
5772
go func() {
5873
if inputStream != nil {
59-
io.Copy(resp.Conn, inputStream)
74+
_, inputErr := io.Copy(resp.Conn, inputStream)
6075
// we should restore the terminal as soon as possible once connection end
6176
// so any following print messages will be in normal type.
6277
if tty {
@@ -65,6 +80,11 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
6580
})
6681
}
6782
logrus.Debug("[hijack] End of stdin")
83+
84+
if detached, ok := inputErr.(term.EscapeError); ok {
85+
detachedC <- detached
86+
return
87+
}
6888
}
6989

7090
if err := resp.CloseWrite(); err != nil {
@@ -90,6 +110,9 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
90110
case <-ctx.Done():
91111
}
92112
}
113+
case err := <-detachedC:
114+
// Got a detach key sequence.
115+
return err
93116
case <-ctx.Done():
94117
}
95118

cli/command/container/run.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/docker/docker/api/types/container"
1717
"github.com/docker/docker/pkg/promise"
1818
"github.com/docker/docker/pkg/signal"
19+
"github.com/docker/docker/pkg/term"
1920
"github.com/docker/libnetwork/resolvconf/dns"
2021
"github.com/pkg/errors"
2122
"github.com/spf13/cobra"
@@ -199,6 +200,11 @@ func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *contain
199200

200201
if errCh != nil {
201202
if err := <-errCh; err != nil {
203+
if _, ok := err.(term.EscapeError); ok {
204+
// The user entered the detach escape sequence.
205+
return nil
206+
}
207+
202208
logrus.Debugf("Error hijack: %s", err)
203209
return err
204210
}
@@ -261,7 +267,7 @@ func attachContainer(
261267
}
262268

263269
*errCh = promise.Go(func() error {
264-
if errHijack := holdHijackedConnection(ctx, dockerCli, config.Tty, in, out, cerr, resp); errHijack != nil {
270+
if errHijack := holdHijackedConnection(ctx, dockerCli, config.Tty, options.DetachKeys, in, out, cerr, resp); errHijack != nil {
265271
return errHijack
266272
}
267273
return errAttach

cli/command/container/start.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/docker/docker/api/types"
1212
"github.com/docker/docker/pkg/promise"
1313
"github.com/docker/docker/pkg/signal"
14+
"github.com/docker/docker/pkg/term"
1415
"github.com/pkg/errors"
1516
"github.com/spf13/cobra"
1617
"golang.org/x/net/context"
@@ -103,7 +104,7 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
103104
}
104105
defer resp.Close()
105106
cErr := promise.Go(func() error {
106-
errHijack := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
107+
errHijack := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, options.DetachKeys, in, dockerCli.Out(), dockerCli.Err(), resp)
107108
if errHijack == nil {
108109
return errAttach
109110
}
@@ -136,6 +137,10 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
136137
}
137138
}
138139
if attchErr := <-cErr; attchErr != nil {
140+
if _, ok := err.(term.EscapeError); ok {
141+
// The user entered the detach escape sequence.
142+
return nil
143+
}
139144
return attchErr
140145
}
141146

cli/command/container/utils.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,49 @@ import (
66
"github.com/Sirupsen/logrus"
77
"github.com/docker/cli/cli/command"
88
"github.com/docker/docker/api/types"
9+
"github.com/docker/docker/api/types/container"
910
"github.com/docker/docker/api/types/events"
1011
"github.com/docker/docker/api/types/filters"
1112
"github.com/docker/docker/api/types/versions"
1213
clientapi "github.com/docker/docker/client"
1314
"golang.org/x/net/context"
1415
)
1516

16-
func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) chan int {
17+
func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
1718
if len(containerID) == 0 {
1819
// containerID can never be empty
1920
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
2021
}
2122

23+
// Older versions used the Events API, and even older versions did not
24+
// support server-side removal. This legacyWaitExitOrRemoved method
25+
// preserves that old behavior and any issues it may have.
26+
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") {
27+
return legacyWaitExitOrRemoved(ctx, dockerCli, containerID, waitRemove)
28+
}
29+
30+
condition := container.WaitConditionNextExit
31+
if waitRemove {
32+
condition = container.WaitConditionRemoved
33+
}
34+
35+
resultC, errC := dockerCli.Client().ContainerWait(ctx, containerID, condition)
36+
37+
statusC := make(chan int)
38+
go func() {
39+
select {
40+
case result := <-resultC:
41+
statusC <- int(result.StatusCode)
42+
case err := <-errC:
43+
logrus.Errorf("error waiting for container: %v", err)
44+
statusC <- 125
45+
}
46+
}()
47+
48+
return statusC
49+
}
50+
51+
func legacyWaitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
2252
var removeErr error
2353
statusChan := make(chan int)
2454
exitCode := 125

cli/command/container/wait.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func NewWaitCommand(dockerCli *command.DockerCli) *cobra.Command {
2828
return runWait(dockerCli, &opts)
2929
},
3030
}
31+
3132
return cmd
3233
}
3334

@@ -36,12 +37,14 @@ func runWait(dockerCli *command.DockerCli, opts *waitOptions) error {
3637

3738
var errs []string
3839
for _, container := range opts.containers {
39-
status, err := dockerCli.Client().ContainerWait(ctx, container)
40-
if err != nil {
40+
resultC, errC := dockerCli.Client().ContainerWait(ctx, container, "")
41+
42+
select {
43+
case result := <-resultC:
44+
fmt.Fprintf(dockerCli.Out(), "%d\n", result.StatusCode)
45+
case err := <-errC:
4146
errs = append(errs, err.Error())
42-
continue
4347
}
44-
fmt.Fprintf(dockerCli.Out(), "%d\n", status)
4548
}
4649
if len(errs) > 0 {
4750
return errors.New(strings.Join(errs, "\n"))

0 commit comments

Comments
 (0)