1
0
Эх сурвалжийг харах

add support for detach keys on compose run|exec

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 4 жил өмнө
parent
commit
b6552cd935

+ 0 - 1
.golangci.yml

@@ -15,7 +15,6 @@ linters:
     - gosimple
     - govet
     - ineffassign
-    - interfacer
     - lll
     - misspell
     - nakedret

+ 18 - 9
pkg/compose/attach.go

@@ -27,6 +27,7 @@ import (
 	"github.com/docker/compose-cli/pkg/api"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/moby/term"
 
 	"github.com/docker/compose-cli/pkg/utils"
 )
@@ -77,23 +78,23 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
 			Line:      line,
 		})
 	})
-	_, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w)
+	_, _, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w)
 	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 (
 		in      *streams.In
 		restore = func() { /* noop */ }
 	)
 	if r != nil {
 		in = streams.NewIn(r)
-		restore = in.RestoreTerminal
 	}
 
 	stdin, stdout, err := s.getContainerStreams(ctx, container)
 	if err != nil {
-		return restore, err
+		return restore, detached, err
 	}
 
 	go func() {
@@ -105,12 +106,20 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
 	}()
 
 	if in != nil && stdin != nil {
-		err := in.SetRawTerminal()
-		if err != nil {
-			return restore, err
+		if in.IsTerminal() {
+			state, err := term.SetRawTerminal(in.FD())
+			if err != nil {
+				return restore, detached, err
+			}
+			restore = func() {
+				term.RestoreTerminal(in.FD(), state) //nolint:errcheck
+			}
 		}
 		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) {

+ 24 - 3
pkg/compose/exec.go

@@ -22,9 +22,11 @@ import (
 	"io"
 
 	"github.com/compose-spec/compose-go/types"
+	"github.com/docker/cli/cli/streams"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/moby/term"
 
 	"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)
 	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() {
 		if opts.Tty {
-			_, err := io.Copy(opts.Writer, resp.Reader)
+			_, err := io.Copy(opts.Writer, stdout)
 			outputDone <- err
 		} else {
-			_, err := stdcopy.StdCopy(opts.Writer, opts.Writer, resp.Reader)
+			_, err := stdcopy.StdCopy(opts.Writer, opts.Writer, stdout)
 			outputDone <- err
 		}
 	}()
 
 	go func() {
-		_, err := io.Copy(resp.Conn, opts.Reader)
+		_, err := io.Copy(stdin, r)
 		inputDone <- err
 	}()
 
@@ -112,6 +130,9 @@ func (s *composeService) interactiveExec(ctx context.Context, opts api.RunOption
 		case err := <-outputDone:
 			return err
 		case err := <-inputDone:
+			if _, ok := err.(term.EscapeError); ok {
+				return nil
+			}
 			if err != nil {
 				return err
 			}

+ 22 - 1
pkg/compose/run.go

@@ -19,13 +19,16 @@ package compose
 import (
 	"context"
 	"fmt"
+	"io"
 
 	"github.com/docker/compose-cli/pkg/api"
 
 	"github.com/compose-spec/compose-go/types"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/pkg/ioutils"
 	"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) {
@@ -75,7 +78,11 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
 		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 {
 		return 0, err
 	}
@@ -93,12 +100,26 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
 	select {
 	case status := <-statusC:
 		return int(status.StatusCode), nil
+	case <-detachC:
+		return 0, nil
 	case err := <-errC:
 		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) {
 	service.Tty = opts.Tty
 	service.ContainerName = opts.Name