mirror of https://github.com/docker/compose.git
add support for detach keys on compose run|exec
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
d20c3b0e22
commit
b6552cd935
|
@ -15,7 +15,6 @@ linters:
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- interfacer
|
|
||||||
- lll
|
- lll
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/docker/compose-cli/pkg/api"
|
"github.com/docker/compose-cli/pkg/api"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
"github.com/moby/term"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/pkg/utils"
|
"github.com/docker/compose-cli/pkg/utils"
|
||||||
)
|
)
|
||||||
|
@ -77,23 +78,23 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
|
||||||
Line: line,
|
Line: line,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
_, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w)
|
_, _, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, r io.ReadCloser, w io.Writer) (func(), error) {
|
func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, r io.ReadCloser, w io.Writer) (func(), chan bool, error) {
|
||||||
|
detached := make(chan bool)
|
||||||
var (
|
var (
|
||||||
in *streams.In
|
in *streams.In
|
||||||
restore = func() { /* noop */ }
|
restore = func() { /* noop */ }
|
||||||
)
|
)
|
||||||
if r != nil {
|
if r != nil {
|
||||||
in = streams.NewIn(r)
|
in = streams.NewIn(r)
|
||||||
restore = in.RestoreTerminal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stdin, stdout, err := s.getContainerStreams(ctx, container)
|
stdin, stdout, err := s.getContainerStreams(ctx, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return restore, err
|
return restore, detached, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -105,12 +106,20 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if in != nil && stdin != nil {
|
if in != nil && stdin != nil {
|
||||||
err := in.SetRawTerminal()
|
if in.IsTerminal() {
|
||||||
|
state, err := term.SetRawTerminal(in.FD())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return restore, err
|
return restore, detached, err
|
||||||
|
}
|
||||||
|
restore = func() {
|
||||||
|
term.RestoreTerminal(in.FD(), state) //nolint:errcheck
|
||||||
|
}
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(stdin, in) //nolint:errcheck
|
_, err := io.Copy(stdin, r)
|
||||||
|
if _, ok := err.(term.EscapeError); ok {
|
||||||
|
close(detached)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +132,7 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return restore, nil
|
return restore, detached, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) getContainerStreams(ctx context.Context, container string) (io.WriteCloser, io.ReadCloser, error) {
|
func (s *composeService) getContainerStreams(ctx context.Context, container string) (io.WriteCloser, io.ReadCloser, error) {
|
||||||
|
|
|
@ -22,9 +22,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
"github.com/moby/term"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/pkg/api"
|
"github.com/docker/compose-cli/pkg/api"
|
||||||
)
|
)
|
||||||
|
@ -92,18 +94,34 @@ func (s *composeService) interactiveExec(ctx context.Context, opts api.RunOption
|
||||||
outputDone := make(chan error)
|
outputDone := make(chan error)
|
||||||
inputDone := make(chan error)
|
inputDone := make(chan error)
|
||||||
|
|
||||||
|
stdout := ContainerStdout{HijackedResponse: resp}
|
||||||
|
stdin := ContainerStdin{HijackedResponse: resp}
|
||||||
|
r, err := s.getEscapeKeyProxy(opts.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
in := streams.NewIn(opts.Reader)
|
||||||
|
if in.IsTerminal() {
|
||||||
|
state, err := term.SetRawTerminal(in.FD())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer term.RestoreTerminal(in.FD(), state) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if opts.Tty {
|
if opts.Tty {
|
||||||
_, err := io.Copy(opts.Writer, resp.Reader)
|
_, err := io.Copy(opts.Writer, stdout)
|
||||||
outputDone <- err
|
outputDone <- err
|
||||||
} else {
|
} else {
|
||||||
_, err := stdcopy.StdCopy(opts.Writer, opts.Writer, resp.Reader)
|
_, err := stdcopy.StdCopy(opts.Writer, opts.Writer, stdout)
|
||||||
outputDone <- err
|
outputDone <- err
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(resp.Conn, opts.Reader)
|
_, err := io.Copy(stdin, r)
|
||||||
inputDone <- err
|
inputDone <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -112,6 +130,9 @@ func (s *composeService) interactiveExec(ctx context.Context, opts api.RunOption
|
||||||
case err := <-outputDone:
|
case err := <-outputDone:
|
||||||
return err
|
return err
|
||||||
case err := <-inputDone:
|
case err := <-inputDone:
|
||||||
|
if _, ok := err.(term.EscapeError); ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,16 @@ package compose
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/docker/compose-cli/pkg/api"
|
"github.com/docker/compose-cli/pkg/api"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/moby/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) {
|
func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) {
|
||||||
|
@ -75,7 +78,11 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
restore, err := s.attachContainerStreams(ctx, containerID, service.Tty, opts.Reader, opts.Writer)
|
r, err := s.getEscapeKeyProxy(opts.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
restore, detachC, err := s.attachContainerStreams(ctx, containerID, service.Tty, r, opts.Writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -93,12 +100,26 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
|
||||||
select {
|
select {
|
||||||
case status := <-statusC:
|
case status := <-statusC:
|
||||||
return int(status.StatusCode), nil
|
return int(status.StatusCode), nil
|
||||||
|
case <-detachC:
|
||||||
|
return 0, nil
|
||||||
case err := <-errC:
|
case err := <-errC:
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *composeService) getEscapeKeyProxy(r io.ReadCloser) (io.ReadCloser, error) {
|
||||||
|
var escapeKeys = []byte{16, 17}
|
||||||
|
if s.configFile.DetachKeys != "" {
|
||||||
|
customEscapeKeys, err := term.ToBytes(s.configFile.DetachKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
escapeKeys = customEscapeKeys
|
||||||
|
}
|
||||||
|
return ioutils.NewReadCloserWrapper(term.NewEscapeProxy(r, escapeKeys), r.Close), nil
|
||||||
|
}
|
||||||
|
|
||||||
func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts api.RunOptions) {
|
func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts api.RunOptions) {
|
||||||
service.Tty = opts.Tty
|
service.Tty = opts.Tty
|
||||||
service.ContainerName = opts.Name
|
service.ContainerName = opts.Name
|
||||||
|
|
Loading…
Reference in New Issue