mirror of
https://github.com/docker/compose.git
synced 2025-07-23 13:45:00 +02:00
Merge pull request #1934 from ndeloof/control_char_run
This commit is contained in:
commit
1b76c746fe
@ -24,11 +24,11 @@ import (
|
|||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/cli/cli/streams"
|
"github.com/docker/cli/cli/streams"
|
||||||
"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/moby/term"
|
||||||
|
|
||||||
|
"github.com/docker/compose-cli/pkg/api"
|
||||||
"github.com/docker/compose-cli/pkg/utils"
|
"github.com/docker/compose-cli/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -85,27 +85,10 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
|
|||||||
func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, stdin io.ReadCloser, stdout, stderr io.Writer) (func(), chan bool, error) {
|
func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, stdin io.ReadCloser, stdout, stderr io.Writer) (func(), chan bool, error) {
|
||||||
detached := make(chan bool)
|
detached := make(chan bool)
|
||||||
var (
|
var (
|
||||||
in *streams.In
|
|
||||||
restore = func() { /* noop */ }
|
restore = func() { /* noop */ }
|
||||||
)
|
)
|
||||||
if stdin != nil {
|
if stdin != nil {
|
||||||
in = streams.NewIn(stdin)
|
in := streams.NewIn(stdin)
|
||||||
}
|
|
||||||
|
|
||||||
streamIn, streamOut, err := s.getContainerStreams(ctx, container)
|
|
||||||
if err != nil {
|
|
||||||
return restore, detached, err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
if in != nil {
|
|
||||||
in.Close() //nolint:errcheck
|
|
||||||
}
|
|
||||||
streamOut.Close() //nolint:errcheck
|
|
||||||
}()
|
|
||||||
|
|
||||||
if in != nil && streamIn != nil {
|
|
||||||
if in.IsTerminal() {
|
if in.IsTerminal() {
|
||||||
state, err := term.SetRawTerminal(in.FD())
|
state, err := term.SetRawTerminal(in.FD())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -115,6 +98,22 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
|
|||||||
term.RestoreTerminal(in.FD(), state) //nolint:errcheck
|
term.RestoreTerminal(in.FD(), state) //nolint:errcheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
streamIn, streamOut, err := s.getContainerStreams(ctx, container)
|
||||||
|
if err != nil {
|
||||||
|
return restore, detached, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
if stdin != nil {
|
||||||
|
stdin.Close() //nolint:errcheck
|
||||||
|
}
|
||||||
|
streamOut.Close() //nolint:errcheck
|
||||||
|
}()
|
||||||
|
|
||||||
|
if streamIn != nil && stdin != nil {
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(streamIn, stdin)
|
_, err := io.Copy(streamIn, stdin)
|
||||||
if _, ok := err.(term.EscapeError); ok {
|
if _, ok := err.(term.EscapeError); ok {
|
||||||
|
@ -118,13 +118,13 @@ func (s *composeService) interactiveExec(ctx context.Context, opts api.RunOption
|
|||||||
_, err := stdcopy.StdCopy(opts.Stdout, opts.Stderr, stdout)
|
_, err := stdcopy.StdCopy(opts.Stdout, opts.Stderr, stdout)
|
||||||
outputDone <- err
|
outputDone <- err
|
||||||
}
|
}
|
||||||
defer stdout.Close() //nolint:errcheck
|
stdout.Close() //nolint:errcheck
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_, err := io.Copy(stdin, r)
|
_, err := io.Copy(stdin, r)
|
||||||
inputDone <- err
|
inputDone <- err
|
||||||
defer stdin.Close() //nolint:errcheck
|
stdin.Close() //nolint:errcheck
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -24,9 +24,10 @@ import (
|
|||||||
"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"
|
||||||
|
"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/container"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
)
|
)
|
||||||
@ -37,11 +38,105 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
service, err := project.GetService(opts.Service)
|
containerID, err := s.prepareRun(ctx, project, observedState, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Detach {
|
||||||
|
err := s.apiClient.ContainerStart(ctx, containerID, moby.ContainerStartOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
fmt.Fprintln(opts.Stdout, containerID)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.runInteractive(ctx, containerID, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) runInteractive(ctx context.Context, containerID string, opts api.RunOptions) (int, error) {
|
||||||
|
r, err := s.getEscapeKeyProxy(opts.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdin, stdout, err := s.getContainerStreams(ctx, containerID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
in := streams.NewIn(opts.Stdin)
|
||||||
|
if in.IsTerminal() {
|
||||||
|
state, err := term.SetRawTerminal(in.FD())
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer term.RestoreTerminal(in.FD(), state) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
outputDone := make(chan error)
|
||||||
|
inputDone := make(chan error)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if opts.Tty {
|
||||||
|
_, err := io.Copy(opts.Stdout, stdout) //nolint:errcheck
|
||||||
|
outputDone <- err
|
||||||
|
} else {
|
||||||
|
_, err := stdcopy.StdCopy(opts.Stdout, opts.Stderr, stdout) //nolint:errcheck
|
||||||
|
outputDone <- err
|
||||||
|
}
|
||||||
|
stdout.Close() //nolint:errcheck
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := io.Copy(stdin, r)
|
||||||
|
inputDone <- err
|
||||||
|
stdin.Close() //nolint:errcheck
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = s.apiClient.ContainerStart(ctx, containerID, moby.ContainerStartOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.monitorTTySize(ctx, containerID, s.apiClient.ContainerResize)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case err := <-outputDone:
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
inspect, err := s.apiClient.ContainerInspect(ctx, containerID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
exitCode := 0
|
||||||
|
if inspect.State != nil {
|
||||||
|
exitCode = inspect.State.ExitCode
|
||||||
|
}
|
||||||
|
return exitCode, nil
|
||||||
|
case err := <-inputDone:
|
||||||
|
if _, ok := err.(term.EscapeError); ok {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Wait for output to complete streaming
|
||||||
|
case <-ctx.Done():
|
||||||
|
return 0, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *composeService) prepareRun(ctx context.Context, project *types.Project, observedState Containers, opts api.RunOptions) (string, error) {
|
||||||
|
service, err := project.GetService(opts.Service)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
applyRunOptions(project, &service, opts)
|
applyRunOptions(project, &service, opts)
|
||||||
|
|
||||||
slug := stringid.GenerateRandomID()
|
slug := stringid.GenerateRandomID()
|
||||||
@ -58,54 +153,17 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
|
|||||||
service.Labels = service.Labels.Add(api.OneoffLabel, "True")
|
service.Labels = service.Labels.Add(api.OneoffLabel, "True")
|
||||||
|
|
||||||
if err := s.ensureImagesExists(ctx, project, observedState, false); err != nil { // all dependencies already checked, but might miss service img
|
if err := s.ensureImagesExists(ctx, project, observedState, false); err != nil { // all dependencies already checked, but might miss service img
|
||||||
return 0, err
|
return "", err
|
||||||
}
|
}
|
||||||
if err := s.waitDependencies(ctx, project, service); err != nil {
|
if err := s.waitDependencies(ctx, project, service); err != nil {
|
||||||
return 0, err
|
return "", err
|
||||||
}
|
}
|
||||||
created, err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.AutoRemove, opts.UseNetworkAliases)
|
created, err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.AutoRemove, opts.UseNetworkAliases)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return "", err
|
||||||
}
|
}
|
||||||
containerID := created.ID
|
containerID := created.ID
|
||||||
|
return containerID, nil
|
||||||
if opts.Detach {
|
|
||||||
err := s.apiClient.ContainerStart(ctx, containerID, moby.ContainerStartOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(opts.Stdout, containerID)
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := s.getEscapeKeyProxy(opts.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
restore, detachC, err := s.attachContainerStreams(ctx, containerID, service.Tty, r, opts.Stdout, opts.Stderr)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer restore()
|
|
||||||
|
|
||||||
statusC, errC := s.apiClient.ContainerWait(context.Background(), containerID, container.WaitConditionNextExit)
|
|
||||||
|
|
||||||
err = s.apiClient.ContainerStart(ctx, containerID, moby.ContainerStartOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.monitorTTySize(ctx, containerID, s.apiClient.ContainerResize)
|
|
||||||
|
|
||||||
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) {
|
func (s *composeService) getEscapeKeyProxy(r io.ReadCloser) (io.ReadCloser, error) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user