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 <josh.hawn@docker.com> (github: jlhawn)
This commit is contained in:
Josh Hawn 2017-05-16 18:31:04 -07:00
parent 4f9ac4899f
commit 38591f20d0
7 changed files with 78 additions and 11 deletions

View File

@ -109,7 +109,7 @@ func runAttach(dockerCli *command.DockerCli, opts *attachOptions) error {
logrus.Debugf("Error monitoring TTY size: %s", err)
}
}
if err := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
if err := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, options.DetachKeys, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
return err
}

View File

@ -146,7 +146,7 @@ func runExec(dockerCli *command.DockerCli, options *execOptions, container strin
}
defer resp.Close()
errCh = promise.Go(func() error {
return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, in, out, stderr, resp)
return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, execConfig.DetachKeys, in, out, stderr, resp)
})
if execConfig.Tty && dockerCli.In().IsTerminal() {

View File

@ -8,14 +8,19 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/term"
"golang.org/x/net/context"
)
// The default escape key sequence: ctrl-p, ctrl-q
var defaultEscapeKeys = []byte{16, 17}
// holdHijackedConnection handles copying input to and output from streams to the
// connection
// nolint: gocyclo
func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bool, detachKeys string, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
var (
err error
restoreOnce sync.Once
@ -29,6 +34,15 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
restoreTerminal(streams, inputStream)
})
}()
// Wrap the input to detect detach control sequence.
// Use default detach sequence if an invalid sequence is given.
escapeKeys, err := term.ToBytes(detachKeys)
if len(escapeKeys) == 0 || err != nil {
escapeKeys = defaultEscapeKeys
}
inputStream = ioutils.NewReadCloserWrapper(term.NewEscapeProxy(inputStream, escapeKeys), inputStream.Close)
}
receiveStdout := make(chan error, 1)
@ -54,9 +68,10 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
}
stdinDone := make(chan struct{})
detachedC := make(chan term.EscapeError)
go func() {
if inputStream != nil {
io.Copy(resp.Conn, inputStream)
_, inputErr := io.Copy(resp.Conn, inputStream)
// we should restore the terminal as soon as possible once connection end
// so any following print messages will be in normal type.
if tty {
@ -65,6 +80,11 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
})
}
logrus.Debug("[hijack] End of stdin")
if detached, ok := inputErr.(term.EscapeError); ok {
detachedC <- detached
return
}
}
if err := resp.CloseWrite(); err != nil {
@ -90,6 +110,9 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
case <-ctx.Done():
}
}
case err := <-detachedC:
// Got a detach key sequence.
return err
case <-ctx.Done():
}

View File

@ -16,6 +16,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/term"
"github.com/docker/libnetwork/resolvconf/dns"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -199,6 +200,11 @@ func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *contain
if errCh != nil {
if err := <-errCh; err != nil {
if _, ok := err.(term.EscapeError); ok {
// The user entered the detach escape sequence.
return nil
}
logrus.Debugf("Error hijack: %s", err)
return err
}
@ -261,7 +267,7 @@ func attachContainer(
}
*errCh = promise.Go(func() error {
if errHijack := holdHijackedConnection(ctx, dockerCli, config.Tty, in, out, cerr, resp); errHijack != nil {
if errHijack := holdHijackedConnection(ctx, dockerCli, config.Tty, options.DetachKeys, in, out, cerr, resp); errHijack != nil {
return errHijack
}
return errAttach

View File

@ -11,6 +11,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/term"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/net/context"
@ -103,7 +104,7 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
}
defer resp.Close()
cErr := promise.Go(func() error {
errHijack := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
errHijack := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, options.DetachKeys, in, dockerCli.Out(), dockerCli.Err(), resp)
if errHijack == nil {
return errAttach
}
@ -136,6 +137,10 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
}
}
if attchErr := <-cErr; attchErr != nil {
if _, ok := err.(term.EscapeError); ok {
// The user entered the detach escape sequence.
return nil
}
return attchErr
}

View File

@ -6,6 +6,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/versions"
@ -13,12 +14,41 @@ import (
"golang.org/x/net/context"
)
func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) chan int {
func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
if len(containerID) == 0 {
// containerID can never be empty
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
}
// Older versions used the Events API, and even older versions did not
// support server-side removal. This legacyWaitExitOrRemoved method
// preserves that old behavior and any issues it may have.
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") {
return legacyWaitExitOrRemoved(ctx, dockerCli, containerID, waitRemove)
}
condition := container.WaitConditionNextExit
if waitRemove {
condition = container.WaitConditionRemoved
}
resultC, errC := dockerCli.Client().ContainerWait(ctx, containerID, condition)
statusC := make(chan int)
go func() {
select {
case result := <-resultC:
statusC <- int(result.StatusCode)
case err := <-errC:
logrus.Errorf("error waiting for container: %v", err)
statusC <- 125
}
}()
return statusC
}
func legacyWaitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
var removeErr error
statusChan := make(chan int)
exitCode := 125

View File

@ -28,6 +28,7 @@ func NewWaitCommand(dockerCli *command.DockerCli) *cobra.Command {
return runWait(dockerCli, &opts)
},
}
return cmd
}
@ -36,12 +37,14 @@ func runWait(dockerCli *command.DockerCli, opts *waitOptions) error {
var errs []string
for _, container := range opts.containers {
status, err := dockerCli.Client().ContainerWait(ctx, container)
if err != nil {
resultC, errC := dockerCli.Client().ContainerWait(ctx, container, "")
select {
case result := <-resultC:
fmt.Fprintf(dockerCli.Out(), "%d\n", result.StatusCode)
case err := <-errC:
errs = append(errs, err.Error())
continue
}
fmt.Fprintf(dockerCli.Out(), "%d\n", status)
}
if len(errs) > 0 {
return errors.New(strings.Join(errs, "\n"))