Merge pull request #1462 from docker/rawTerminal

set terminal in character mode when attached to a container
This commit is contained in:
Guillaume Tardif 2021-03-25 18:17:27 +01:00 committed by GitHub
commit ee4e85ae1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 14 deletions

View File

@ -186,8 +186,8 @@ type RunOptions struct {
Entrypoint []string Entrypoint []string
Detach bool Detach bool
AutoRemove bool AutoRemove bool
Writer io.Writer Writer io.WriteCloser
Reader io.Reader Reader io.ReadCloser
Tty bool Tty bool
WorkingDir string WorkingDir string
User string User string

View File

@ -27,6 +27,7 @@ import (
"github.com/docker/compose-cli/utils" "github.com/docker/compose-cli/utils"
"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/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
) )
@ -108,26 +109,40 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
Service: container.Labels[serviceLabel], Service: container.Labels[serviceLabel],
}) })
return 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.Reader, w io.Writer) error { func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, r io.ReadCloser, w io.Writer) (func(), error) {
var (
in *streams.In
restore = func() { /* noop */ }
)
if r != nil {
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 err return restore, err
} }
go func() { go func() {
<-ctx.Done() <-ctx.Done()
stdout.Close() //nolint:errcheck if in != nil {
if stdin != nil { in.Close() //nolint:errcheck
stdin.Close() //nolint:errcheck
} }
stdout.Close() //nolint:errcheck
}() }()
if r != nil && stdin != nil { if in != nil && stdin != nil {
err := in.SetRawTerminal()
if err != nil {
return restore, err
}
go func() { go func() {
io.Copy(stdin, r) //nolint:errcheck io.Copy(stdin, in) //nolint:errcheck
}() }()
} }
@ -140,7 +155,7 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
} }
}() }()
} }
return nil return restore, 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) {

View File

@ -249,7 +249,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
ExposedPorts: buildContainerPorts(service), ExposedPorts: buildContainerPorts(service),
Tty: tty, Tty: tty,
OpenStdin: stdinOpen, OpenStdin: stdinOpen,
StdinOnce: true, StdinOnce: attachStdin && stdinOpen,
AttachStdin: attachStdin, AttachStdin: attachStdin,
AttachStderr: true, AttachStderr: true,
AttachStdout: true, AttachStdout: true,

View File

@ -86,10 +86,11 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
return 0, err return 0, err
} }
oneoffContainer := containers[0] oneoffContainer := containers[0]
err = s.attachContainerStreams(ctx, oneoffContainer.ID, service.Tty, opts.Reader, opts.Writer) restore, err := s.attachContainerStreams(ctx, oneoffContainer.ID, service.Tty, opts.Reader, opts.Writer)
if err != nil { if err != nil {
return 0, err return 0, err
} }
defer restore()
err = s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{}) err = s.apiClient.ContainerStart(ctx, containerID, apitypes.ContainerStartOptions{})
if err != nil { if err != nil {

View File

@ -62,7 +62,7 @@ func (a *allowListLogConsumer) Register(name string) {
} }
// GetWriter creates a io.Writer that will actually split by line and format by LogConsumer // GetWriter creates a io.Writer that will actually split by line and format by LogConsumer
func GetWriter(container, service string, events compose.ContainerEventListener) io.Writer { func GetWriter(container, service string, events compose.ContainerEventListener) io.WriteCloser {
return &splitBuffer{ return &splitBuffer{
buffer: bytes.Buffer{}, buffer: bytes.Buffer{},
service: service, service: service,
@ -100,3 +100,17 @@ func (s *splitBuffer) Write(b []byte) (int, error) {
} }
return n, nil return n, nil
} }
func (s *splitBuffer) Close() error {
b := s.buffer.Bytes()
if len(b) == 0 {
return nil
}
s.consumer(compose.ContainerEvent{
Type: compose.ContainerEventLog,
Service: s.service,
Container: s.container,
Line: string(b),
})
return nil
}