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)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ func runExec(dockerCli *command.DockerCli, options *execOptions, container strin
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
errCh = promise.Go(func() error {
|
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() {
|
if execConfig.Tty && dockerCli.In().IsTerminal() {
|
||||||
|
|
|
@ -8,14 +8,19 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"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/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
"golang.org/x/net/context"
|
"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
|
// holdHijackedConnection handles copying input to and output from streams to the
|
||||||
// connection
|
// connection
|
||||||
// nolint: gocyclo
|
// 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 (
|
var (
|
||||||
err error
|
err error
|
||||||
restoreOnce sync.Once
|
restoreOnce sync.Once
|
||||||
|
@ -29,6 +34,15 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
|
||||||
restoreTerminal(streams, inputStream)
|
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)
|
receiveStdout := make(chan error, 1)
|
||||||
|
@ -54,9 +68,10 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
|
||||||
}
|
}
|
||||||
|
|
||||||
stdinDone := make(chan struct{})
|
stdinDone := make(chan struct{})
|
||||||
|
detachedC := make(chan term.EscapeError)
|
||||||
go func() {
|
go func() {
|
||||||
if inputStream != nil {
|
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
|
// we should restore the terminal as soon as possible once connection end
|
||||||
// so any following print messages will be in normal type.
|
// so any following print messages will be in normal type.
|
||||||
if tty {
|
if tty {
|
||||||
|
@ -65,6 +80,11 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
logrus.Debug("[hijack] End of stdin")
|
logrus.Debug("[hijack] End of stdin")
|
||||||
|
|
||||||
|
if detached, ok := inputErr.(term.EscapeError); ok {
|
||||||
|
detachedC <- detached
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := resp.CloseWrite(); err != nil {
|
if err := resp.CloseWrite(); err != nil {
|
||||||
|
@ -90,6 +110,9 @@ func holdHijackedConnection(ctx context.Context, streams command.Streams, tty bo
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case err := <-detachedC:
|
||||||
|
// Got a detach key sequence.
|
||||||
|
return err
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/docker/libnetwork/resolvconf/dns"
|
"github.com/docker/libnetwork/resolvconf/dns"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -199,6 +200,11 @@ func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *contain
|
||||||
|
|
||||||
if errCh != nil {
|
if errCh != nil {
|
||||||
if err := <-errCh; err != 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)
|
logrus.Debugf("Error hijack: %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -261,7 +267,7 @@ func attachContainer(
|
||||||
}
|
}
|
||||||
|
|
||||||
*errCh = promise.Go(func() error {
|
*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 errHijack
|
||||||
}
|
}
|
||||||
return errAttach
|
return errAttach
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/promise"
|
"github.com/docker/docker/pkg/promise"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -103,7 +104,7 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
cErr := promise.Go(func() error {
|
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 {
|
if errHijack == nil {
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,10 @@ func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if attchErr := <-cErr; attchErr != nil {
|
if attchErr := <-cErr; attchErr != nil {
|
||||||
|
if _, ok := err.(term.EscapeError); ok {
|
||||||
|
// The user entered the detach escape sequence.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return attchErr
|
return attchErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"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/api/types/events"
|
"github.com/docker/docker/api/types/events"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
@ -13,12 +14,41 @@ import (
|
||||||
"golang.org/x/net/context"
|
"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 {
|
if len(containerID) == 0 {
|
||||||
// containerID can never be empty
|
// containerID can never be empty
|
||||||
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
|
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
|
var removeErr error
|
||||||
statusChan := make(chan int)
|
statusChan := make(chan int)
|
||||||
exitCode := 125
|
exitCode := 125
|
||||||
|
|
|
@ -28,6 +28,7 @@ func NewWaitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
return runWait(dockerCli, &opts)
|
return runWait(dockerCli, &opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,12 +37,14 @@ func runWait(dockerCli *command.DockerCli, opts *waitOptions) error {
|
||||||
|
|
||||||
var errs []string
|
var errs []string
|
||||||
for _, container := range opts.containers {
|
for _, container := range opts.containers {
|
||||||
status, err := dockerCli.Client().ContainerWait(ctx, container)
|
resultC, errC := dockerCli.Client().ContainerWait(ctx, container, "")
|
||||||
if err != nil {
|
|
||||||
|
select {
|
||||||
|
case result := <-resultC:
|
||||||
|
fmt.Fprintf(dockerCli.Out(), "%d\n", result.StatusCode)
|
||||||
|
case err := <-errC:
|
||||||
errs = append(errs, err.Error())
|
errs = append(errs, err.Error())
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
fmt.Fprintf(dockerCli.Out(), "%d\n", status)
|
|
||||||
}
|
}
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return errors.New(strings.Join(errs, "\n"))
|
return errors.New(strings.Join(errs, "\n"))
|
||||||
|
|
Loading…
Reference in New Issue