mirror of
https://github.com/docker/compose.git
synced 2025-07-24 22:24:41 +02:00
use docker/cli RunExec and RunStart to handle all the interactive/tty/* terminal logic
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
d999c230a5
commit
1d4b4e3c8e
@ -62,6 +62,9 @@ func (opts runOptions) apply(project *types.Project) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target.Tty = !opts.noTty
|
||||||
|
target.StdinOpen = opts.interactive
|
||||||
if !opts.servicePorts {
|
if !opts.servicePorts {
|
||||||
target.Ports = []types.ServicePortConfig{}
|
target.Ports = []types.ServicePortConfig{}
|
||||||
}
|
}
|
||||||
@ -207,6 +210,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
|||||||
Detach: opts.Detach,
|
Detach: opts.Detach,
|
||||||
AutoRemove: opts.Remove,
|
AutoRemove: opts.Remove,
|
||||||
Tty: !opts.noTty,
|
Tty: !opts.noTty,
|
||||||
|
Interactive: opts.interactive,
|
||||||
WorkingDir: opts.workdir,
|
WorkingDir: opts.workdir,
|
||||||
User: opts.user,
|
User: opts.user,
|
||||||
Environment: opts.environment,
|
Environment: opts.environment,
|
||||||
|
@ -68,7 +68,7 @@ func pluginMain() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if commands.RunningAsStandalone() {
|
if plugin.RunningStandalone() {
|
||||||
os.Args = append([]string{"docker"}, compatibility.Convert(os.Args[1:])...)
|
os.Args = append([]string{"docker"}, compatibility.Convert(os.Args[1:])...)
|
||||||
}
|
}
|
||||||
pluginMain()
|
pluginMain()
|
||||||
|
@ -216,6 +216,7 @@ type RunOptions struct {
|
|||||||
Detach bool
|
Detach bool
|
||||||
AutoRemove bool
|
AutoRemove bool
|
||||||
Tty bool
|
Tty bool
|
||||||
|
Interactive bool
|
||||||
WorkingDir string
|
WorkingDir string
|
||||||
User string
|
User string
|
||||||
Environment []string
|
Environment []string
|
||||||
|
@ -19,123 +19,41 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli"
|
||||||
|
"github.com/docker/cli/cli/command/container"
|
||||||
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
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/moby/term"
|
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Exec(ctx context.Context, project string, opts api.RunOptions) (int, error) {
|
func (s *composeService) Exec(ctx context.Context, project string, opts api.RunOptions) (int, error) {
|
||||||
container, err := s.getExecTarget(ctx, project, opts)
|
target, err := s.getExecTarget(ctx, project, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
exec, err := s.apiClient().ContainerExecCreate(ctx, container.ID, moby.ExecConfig{
|
exec := container.NewExecOptions()
|
||||||
Cmd: opts.Command,
|
exec.Interactive = opts.Interactive
|
||||||
Env: opts.Environment,
|
exec.TTY = opts.Tty
|
||||||
User: opts.User,
|
exec.Detach = opts.Detach
|
||||||
Privileged: opts.Privileged,
|
exec.User = opts.User
|
||||||
Tty: opts.Tty,
|
exec.Privileged = opts.Privileged
|
||||||
Detach: opts.Detach,
|
exec.Workdir = opts.WorkingDir
|
||||||
WorkingDir: opts.WorkingDir,
|
exec.Container = target.ID
|
||||||
|
exec.Command = opts.Command
|
||||||
AttachStdin: true,
|
for _, v := range opts.Environment {
|
||||||
AttachStdout: true,
|
err := exec.Env.Set(v)
|
||||||
AttachStderr: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Detach {
|
|
||||||
return 0, s.apiClient().ContainerExecStart(ctx, exec.ID, moby.ExecStartCheck{
|
|
||||||
Detach: true,
|
|
||||||
Tty: opts.Tty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := s.apiClient().ContainerExecAttach(ctx, exec.ID, moby.ExecStartCheck{
|
|
||||||
Tty: opts.Tty,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer resp.Close() //nolint:errcheck
|
|
||||||
|
|
||||||
if opts.Tty {
|
|
||||||
s.monitorTTySize(ctx, exec.ID, s.apiClient().ContainerExecResize)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.interactiveExec(ctx, opts, resp)
|
err = container.RunExec(s.dockerCli, exec)
|
||||||
if err != nil {
|
if sterr, ok := err.(cli.StatusError); ok {
|
||||||
return 0, err
|
return sterr.StatusCode, nil
|
||||||
}
|
|
||||||
|
|
||||||
return s.getExecExitStatus(ctx, exec.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inspired by https://github.com/docker/cli/blob/master/cli/command/container/exec.go#L116
|
|
||||||
func (s *composeService) interactiveExec(ctx context.Context, opts api.RunOptions, resp moby.HijackedResponse) error {
|
|
||||||
outputDone := make(chan error)
|
|
||||||
inputDone := make(chan error)
|
|
||||||
|
|
||||||
stdout := ContainerStdout{HijackedResponse: resp}
|
|
||||||
stdin := ContainerStdin{HijackedResponse: resp}
|
|
||||||
r, err := s.getEscapeKeyProxy(s.stdin(), opts.Tty)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
in := s.stdin()
|
|
||||||
if in.IsTerminal() && opts.Tty {
|
|
||||||
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(s.stdout(), stdout)
|
|
||||||
outputDone <- err
|
|
||||||
} else {
|
|
||||||
_, err := stdcopy.StdCopy(s.stdout(), s.stderr(), stdout)
|
|
||||||
outputDone <- err
|
|
||||||
}
|
|
||||||
stdout.Close() //nolint:errcheck
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
_, err := io.Copy(stdin, r)
|
|
||||||
inputDone <- err
|
|
||||||
stdin.Close() //nolint:errcheck
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case err := <-outputDone:
|
|
||||||
return err
|
|
||||||
case err := <-inputDone:
|
|
||||||
if _, ok := err.(term.EscapeError); ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Wait for output to complete streaming
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (moby.Container, error) {
|
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (moby.Container, error) {
|
||||||
@ -155,11 +73,3 @@ func (s *composeService) getExecTarget(ctx context.Context, projectName string,
|
|||||||
container := containers[0]
|
container := containers[0]
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) getExecExitStatus(ctx context.Context, execID string) (int, error) {
|
|
||||||
resp, err := s.apiClient().ContainerExecInspect(ctx, execID)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return resp.ExitCode, nil
|
|
||||||
}
|
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2020 Docker Compose CLI authors
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package compose
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
gosignal "os/signal"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/buger/goterm"
|
|
||||||
moby "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/pkg/signal"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *composeService) monitorTTySize(ctx context.Context, container string, resize func(context.Context, string, moby.ResizeOptions) error) {
|
|
||||||
err := resize(ctx, container, moby.ResizeOptions{ // nolint:errcheck
|
|
||||||
Height: uint(goterm.Height()),
|
|
||||||
Width: uint(goterm.Width()),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sigchan := make(chan os.Signal, 1)
|
|
||||||
gosignal.Notify(sigchan, signal.SIGWINCH)
|
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// Windows has no SIGWINCH support, so we have to poll tty size ¯\_(ツ)_/¯
|
|
||||||
go func() {
|
|
||||||
prevH := goterm.Height()
|
|
||||||
prevW := goterm.Width()
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Millisecond * 250)
|
|
||||||
h := goterm.Height()
|
|
||||||
w := goterm.Width()
|
|
||||||
if prevW != w || prevH != h {
|
|
||||||
sigchan <- signal.SIGWINCH
|
|
||||||
}
|
|
||||||
prevH = h
|
|
||||||
prevW = w
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-sigchan:
|
|
||||||
resize(ctx, container, moby.ResizeOptions{ // nolint:errcheck
|
|
||||||
Height: uint(goterm.Height()),
|
|
||||||
Width: uint(goterm.Width()),
|
|
||||||
})
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
@ -19,16 +19,11 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/docker/cli/cli"
|
||||||
|
cmd "github.com/docker/cli/cli/command/container"
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
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/stdcopy"
|
|
||||||
"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) {
|
||||||
@ -37,98 +32,16 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Detach {
|
start := cmd.NewStartOptions()
|
||||||
err := s.apiClient().ContainerStart(ctx, containerID, moby.ContainerStartOptions{})
|
start.OpenStdin = !opts.Detach && opts.Interactive
|
||||||
if err != nil {
|
start.Attach = !opts.Detach
|
||||||
return 0, err
|
start.Containers = []string{containerID}
|
||||||
}
|
|
||||||
fmt.Fprintln(s.stdout(), containerID)
|
err = cmd.RunStart(s.dockerCli, &start)
|
||||||
return 0, nil
|
if sterr, ok := err.(cli.StatusError); ok {
|
||||||
|
return sterr.StatusCode, nil
|
||||||
}
|
}
|
||||||
|
return 0, err
|
||||||
return s.runInteractive(ctx, containerID, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *composeService) runInteractive(ctx context.Context, containerID string, opts api.RunOptions) (int, error) {
|
|
||||||
in := s.stdin()
|
|
||||||
r, err := s.getEscapeKeyProxy(in, opts.Tty)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stdin, stdout, err := s.getContainerStreams(ctx, containerID)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.IsTerminal() && opts.Tty {
|
|
||||||
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(s.stdout(), stdout) //nolint:errcheck
|
|
||||||
outputDone <- err
|
|
||||||
} else {
|
|
||||||
_, err := stdcopy.StdCopy(s.stdout(), s.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
|
|
||||||
}
|
|
||||||
return s.terminateRun(ctx, containerID, opts)
|
|
||||||
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) terminateRun(ctx context.Context, containerID string, opts api.RunOptions) (exitCode int, err error) {
|
|
||||||
exitCh, errCh := s.apiClient().ContainerWait(ctx, containerID, container.WaitConditionNotRunning)
|
|
||||||
select {
|
|
||||||
case exit := <-exitCh:
|
|
||||||
exitCode = int(exit.StatusCode)
|
|
||||||
case err = <-errCh:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if opts.AutoRemove {
|
|
||||||
err = s.apiClient().ContainerRemove(ctx, containerID, moby.ContainerRemoveOptions{})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) {
|
func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) {
|
||||||
@ -147,7 +60,6 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
|
|||||||
service.ContainerName = fmt.Sprintf("%s_%s_run_%s", project.Name, service.Name, stringid.TruncateID(slug))
|
service.ContainerName = fmt.Sprintf("%s_%s_run_%s", project.Name, service.Name, stringid.TruncateID(slug))
|
||||||
}
|
}
|
||||||
service.Scale = 1
|
service.Scale = 1
|
||||||
service.StdinOpen = true
|
|
||||||
service.Restart = ""
|
service.Restart = ""
|
||||||
if service.Deploy != nil {
|
if service.Deploy != nil {
|
||||||
service.Deploy.RestartPolicy = nil
|
service.Deploy.RestartPolicy = nil
|
||||||
@ -171,32 +83,17 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
|
|||||||
}
|
}
|
||||||
updateServices(&service, observedState)
|
updateServices(&service, observedState)
|
||||||
|
|
||||||
created, err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.Detach && opts.AutoRemove, opts.UseNetworkAliases, true)
|
created, err := s.createContainer(ctx, project, service, service.ContainerName, 1,
|
||||||
|
opts.Detach && opts.AutoRemove, opts.UseNetworkAliases, opts.Interactive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
containerID := created.ID
|
return created.ID, nil
|
||||||
return containerID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *composeService) getEscapeKeyProxy(r io.ReadCloser, isTty bool) (io.ReadCloser, error) {
|
|
||||||
if !isTty {
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
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.StdinOpen = true
|
service.StdinOpen = opts.Interactive
|
||||||
service.ContainerName = opts.Name
|
service.ContainerName = opts.Name
|
||||||
|
|
||||||
if len(opts.Command) > 0 {
|
if len(opts.Command) > 0 {
|
||||||
|
@ -29,11 +29,11 @@ func TestLocalComposeRun(t *testing.T) {
|
|||||||
c := NewParallelE2eCLI(t, binDir)
|
c := NewParallelE2eCLI(t, binDir)
|
||||||
|
|
||||||
t.Run("compose run", func(t *testing.T) {
|
t.Run("compose run", func(t *testing.T) {
|
||||||
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "back")
|
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "-T", "back")
|
||||||
lines := Lines(res.Stdout())
|
lines := Lines(res.Stdout())
|
||||||
assert.Equal(t, lines[len(lines)-1], "Hello there!!", res.Stdout())
|
assert.Equal(t, lines[len(lines)-1], "Hello there!!", res.Stdout())
|
||||||
assert.Assert(t, !strings.Contains(res.Combined(), "orphan"))
|
assert.Assert(t, !strings.Contains(res.Combined(), "orphan"))
|
||||||
res = c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello one more time")
|
res = c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "-T", "back", "echo", "Hello one more time")
|
||||||
lines = Lines(res.Stdout())
|
lines = Lines(res.Stdout())
|
||||||
assert.Equal(t, lines[len(lines)-1], "Hello one more time", res.Stdout())
|
assert.Equal(t, lines[len(lines)-1], "Hello one more time", res.Stdout())
|
||||||
assert.Assert(t, !strings.Contains(res.Combined(), "orphan"))
|
assert.Assert(t, !strings.Contains(res.Combined(), "orphan"))
|
||||||
@ -68,7 +68,7 @@ func TestLocalComposeRun(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("compose run --rm", func(t *testing.T) {
|
t.Run("compose run --rm", func(t *testing.T) {
|
||||||
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "--rm", "back", "echo", "Hello again")
|
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "-T", "--rm", "back", "echo", "Hello again")
|
||||||
lines := Lines(res.Stdout())
|
lines := Lines(res.Stdout())
|
||||||
assert.Equal(t, lines[len(lines)-1], "Hello again", res.Stdout())
|
assert.Equal(t, lines[len(lines)-1], "Hello again", res.Stdout())
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ func TestLocalComposeRun(t *testing.T) {
|
|||||||
t.Run("compose run --volumes", func(t *testing.T) {
|
t.Run("compose run --volumes", func(t *testing.T) {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "--volumes", wd+":/foo", "back", "/bin/sh", "-c", "ls /foo")
|
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "-T", "--volumes", wd+":/foo", "back", "/bin/sh", "-c", "ls /foo")
|
||||||
res.Assert(t, icmd.Expected{Out: "compose_run_test.go"})
|
res.Assert(t, icmd.Expected{Out: "compose_run_test.go"})
|
||||||
|
|
||||||
res = c.RunDockerCmd("ps", "--all")
|
res = c.RunDockerCmd("ps", "--all")
|
||||||
@ -93,18 +93,18 @@ func TestLocalComposeRun(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("compose run --publish", func(t *testing.T) {
|
t.Run("compose run --publish", func(t *testing.T) {
|
||||||
c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "--publish", "8081:80", "-d", "back", "/bin/sh", "-c", "sleep 1")
|
c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "-T", "--publish", "8081:80", "-d", "back", "/bin/sh", "-c", "sleep 1")
|
||||||
res := c.RunDockerCmd("ps")
|
res := c.RunDockerCmd("ps")
|
||||||
assert.Assert(t, strings.Contains(res.Stdout(), "8081->80/tcp"), res.Stdout())
|
assert.Assert(t, strings.Contains(res.Stdout(), "8081->80/tcp"), res.Stdout())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("compose run orphan", func(t *testing.T) {
|
t.Run("compose run orphan", func(t *testing.T) {
|
||||||
// Use different compose files to get an orphan container
|
// Use different compose files to get an orphan container
|
||||||
c.RunDockerComposeCmd("-f", "./fixtures/run-test/orphan.yaml", "run", "simple")
|
c.RunDockerComposeCmd("-f", "./fixtures/run-test/orphan.yaml", "run", "-T", "simple")
|
||||||
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello")
|
res := c.RunDockerComposeCmd("-f", "./fixtures/run-test/compose.yaml", "run", "-T", "back", "echo", "Hello")
|
||||||
assert.Assert(t, strings.Contains(res.Combined(), "orphan"))
|
assert.Assert(t, strings.Contains(res.Combined(), "orphan"))
|
||||||
|
|
||||||
cmd := c.NewDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello")
|
cmd := c.NewDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "-T", "back", "echo", "Hello")
|
||||||
res = icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
|
res = icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
|
||||||
cmd.Env = append(cmd.Env, "COMPOSE_IGNORE_ORPHANS=True")
|
cmd.Env = append(cmd.Env, "COMPOSE_IGNORE_ORPHANS=True")
|
||||||
})
|
})
|
||||||
|
1
pkg/e2e/toto.sh
Executable file
1
pkg/e2e/toto.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
../../bin/docker-compose -f ./fixtures/run-test/compose.yaml run --volumes $(pwd):/foo back ls /foo
|
Loading…
x
Reference in New Issue
Block a user