diff --git a/container.go b/container.go index 16fed83e36..efdd89b05e 100644 --- a/container.go +++ b/container.go @@ -51,7 +51,7 @@ type Container interface { State(context.Context) (*types.ContainerState, error) // returns container's running state Networks(context.Context) ([]string, error) // get container networks NetworkAliases(context.Context) (map[string][]string, error) // get container network aliases for a network - Exec(ctx context.Context, cmd []string) (int, error) + Exec(ctx context.Context, cmd []string) (int, io.Reader, error) ContainerIP(context.Context) (string, error) // get container ip CopyToContainer(ctx context.Context, fileContent []byte, containerFilePath string, fileMode int64) error CopyFileToContainer(ctx context.Context, hostFilePath string, containerFilePath string, fileMode int64) error diff --git a/docker.go b/docker.go index d67708051b..97407e7d89 100644 --- a/docker.go +++ b/docker.go @@ -342,28 +342,35 @@ func (c *DockerContainer) NetworkAliases(ctx context.Context) (map[string][]stri return a, nil } -func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, error) { +func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, io.Reader, error) { cli := c.provider.client response, err := cli.ContainerExecCreate(ctx, c.ID, types.ExecConfig{ - Cmd: cmd, - Detach: false, + Cmd: cmd, + Detach: false, + AttachStdout: true, + AttachStderr: true, }) if err != nil { - return 0, err + return 0, nil, err + } + + hijack, err := cli.ContainerExecAttach(ctx, response.ID, types.ExecStartCheck{}) + if err != nil { + return 0, nil, err } err = cli.ContainerExecStart(ctx, response.ID, types.ExecStartCheck{ Detach: false, }) if err != nil { - return 0, err + return 0, nil, err } var exitCode int for { execResp, err := cli.ContainerExecInspect(ctx, response.ID) if err != nil { - return 0, err + return 0, nil, err } if !execResp.Running { @@ -374,7 +381,7 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, error) { time.Sleep(100 * time.Millisecond) } - return exitCode, nil + return exitCode, hijack.Reader, nil } type FileFromContainer struct { diff --git a/docker_test.go b/docker_test.go index eb6f5bfd3d..08dc6f3787 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1561,7 +1561,7 @@ func TestContainerWithTmpFs(t *testing.T) { path := "/testtmpfs/test.file" - c, err := container.Exec(ctx, []string{"ls", path}) + c, _, err := container.Exec(ctx, []string{"ls", path}) if err != nil { t.Fatal(err) } @@ -1569,7 +1569,7 @@ func TestContainerWithTmpFs(t *testing.T) { t.Fatalf("File %s should not have existed, expected return code 1, got %v", path, c) } - c, err = container.Exec(ctx, []string{"touch", path}) + c, _, err = container.Exec(ctx, []string{"touch", path}) if err != nil { t.Fatal(err) } @@ -1577,7 +1577,7 @@ func TestContainerWithTmpFs(t *testing.T) { t.Fatalf("File %s should have been created successfully, expected return code 0, got %v", path, c) } - c, err = container.Exec(ctx, []string{"ls", path}) + c, _, err = container.Exec(ctx, []string{"ls", path}) if err != nil { t.Fatal(err) } @@ -1740,7 +1740,7 @@ func TestDockerContainerCopyFileToContainer(t *testing.T) { copiedFileName := "hello_copy.sh" _ = nginxC.CopyFileToContainer(ctx, "./testresources/hello.sh", "/"+copiedFileName, 700) - c, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) + c, _, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) if err != nil { t.Fatal(err) } @@ -1769,7 +1769,7 @@ func TestDockerContainerCopyToContainer(t *testing.T) { t.Fatal(err) } _ = nginxC.CopyToContainer(ctx, fileContent, "/"+copiedFileName, 700) - c, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) + c, _, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) if err != nil { t.Fatal(err) } @@ -1797,7 +1797,7 @@ func TestDockerContainerCopyFileFromContainer(t *testing.T) { copiedFileName := "hello_copy.sh" _ = nginxC.CopyFileToContainer(ctx, "./testresources/hello.sh", "/"+copiedFileName, 700) - c, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) + c, _, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) if err != nil { t.Fatal(err) } @@ -1832,7 +1832,7 @@ func TestDockerContainerCopyEmptyFileFromContainer(t *testing.T) { copiedFileName := "hello_copy.sh" _ = nginxC.CopyFileToContainer(ctx, "./testresources/empty.sh", "/"+copiedFileName, 700) - c, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) + c, _, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) if err != nil { t.Fatal(err) } diff --git a/logconsumer_test.go b/logconsumer_test.go index b9a06487f1..bfc837f084 100644 --- a/logconsumer_test.go +++ b/logconsumer_test.go @@ -246,7 +246,7 @@ func TestContainerLogWithErrClosed(t *testing.T) { existingLogs := len(consumer.Msgs) hitNginx := func() { - i, err := dind.Exec(ctx, []string{"wget", "--spider", "localhost:" + port.Port()}) + i, _, err := dind.Exec(ctx, []string{"wget", "--spider", "localhost:" + port.Port()}) if err != nil || i > 0 { t.Fatalf("Can't make request to nginx container from dind container") } @@ -264,11 +264,11 @@ func TestContainerLogWithErrClosed(t *testing.T) { "-j", "REJECT", "--reject-with", "tcp-reset", } // Simulate a transient closed connection to the docker daemon - i, err := dind.Exec(ctx, append([]string{"iptables", "-A"}, iptableArgs...)) + i, _, err := dind.Exec(ctx, append([]string{"iptables", "-A"}, iptableArgs...)) if err != nil || i > 0 { t.Fatalf("Failed to close connection to dind daemon") } - i, err = dind.Exec(ctx, append([]string{"iptables", "-D"}, iptableArgs...)) + i, _, err = dind.Exec(ctx, append([]string{"iptables", "-D"}, iptableArgs...)) if err != nil || i > 0 { t.Fatalf("Failed to re-open connection to dind daemon") } diff --git a/wait/exec.go b/wait/exec.go index 1255187808..c034b3c166 100644 --- a/wait/exec.go +++ b/wait/exec.go @@ -63,7 +63,7 @@ func (ws ExecStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget case <-ctx.Done(): return ctx.Err() case <-time.After(ws.PollInterval): - exitCode, err := target.Exec(ctx, ws.cmd) + exitCode, _, err := target.Exec(ctx, ws.cmd) if err != nil { return err } diff --git a/wait/exec_test.go b/wait/exec_test.go index db45548274..c5a932f40f 100644 --- a/wait/exec_test.go +++ b/wait/exec_test.go @@ -52,18 +52,18 @@ func (st mockExecTarget) Logs(_ context.Context) (io.ReadCloser, error) { return nil, errors.New("not implemented") } -func (st mockExecTarget) Exec(ctx context.Context, _ []string) (int, error) { +func (st mockExecTarget) Exec(ctx context.Context, _ []string) (int, io.Reader, error) { time.Sleep(st.waitDuration) if err := ctx.Err(); err != nil { - return st.exitCode, err + return st.exitCode, nil, err } if !st.successAfter.IsZero() && time.Now().After(st.successAfter) { - return 0, st.failure + return 0, nil, st.failure } - return st.exitCode, st.failure + return st.exitCode, nil, st.failure } func (st mockExecTarget) State(_ context.Context) (*types.ContainerState, error) { diff --git a/wait/exit_test.go b/wait/exit_test.go index cbfa4a6913..63563d640d 100644 --- a/wait/exit_test.go +++ b/wait/exit_test.go @@ -27,8 +27,8 @@ func (st exitStrategyTarget) Logs(ctx context.Context) (io.ReadCloser, error) { return nil, nil } -func (st exitStrategyTarget) Exec(ctx context.Context, cmd []string) (int, error) { - return 0, nil +func (st exitStrategyTarget) Exec(ctx context.Context, cmd []string) (int, io.Reader, error) { + return 0, nil, nil } func (st exitStrategyTarget) State(ctx context.Context) (*types.ContainerState, error) { diff --git a/wait/host_port.go b/wait/host_port.go index d53218941e..4c3a25fa41 100644 --- a/wait/host_port.go +++ b/wait/host_port.go @@ -106,7 +106,7 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT if ctx.Err() != nil { return ctx.Err() } - exitCode, err := target.Exec(ctx, []string{"/bin/sh", "-c", command}) + exitCode, _, err := target.Exec(ctx, []string{"/bin/sh", "-c", command}) if err != nil { return fmt.Errorf("%w, host port waiting failed", err) } diff --git a/wait/log_test.go b/wait/log_test.go index 85be13effa..add532eaca 100644 --- a/wait/log_test.go +++ b/wait/log_test.go @@ -28,8 +28,8 @@ func (st noopStrategyTarget) Logs(ctx context.Context) (io.ReadCloser, error) { return st.ioReaderCloser, nil } -func (st noopStrategyTarget) Exec(ctx context.Context, cmd []string) (int, error) { - return 0, nil +func (st noopStrategyTarget) Exec(ctx context.Context, cmd []string) (int, io.Reader, error) { + return 0, nil, nil } func (st noopStrategyTarget) State(ctx context.Context) (*types.ContainerState, error) { return nil, nil diff --git a/wait/wait.go b/wait/wait.go index 5fcc284ccc..914483b9ca 100644 --- a/wait/wait.go +++ b/wait/wait.go @@ -17,7 +17,7 @@ type StrategyTarget interface { Host(context.Context) (string, error) MappedPort(context.Context, nat.Port) (nat.Port, error) Logs(context.Context) (io.ReadCloser, error) - Exec(ctx context.Context, cmd []string) (int, error) + Exec(ctx context.Context, cmd []string) (int, io.Reader, error) State(context.Context) (*types.ContainerState, error) }