mirror of https://github.com/docker/cli.git
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:
parent
4f9ac4899f
commit
38591f20d0
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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():
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
|
|
Loading…
Reference in New Issue