diff --git a/container.go b/container.go index 51b3d68927..64ee0f2c08 100644 --- a/container.go +++ b/container.go @@ -52,6 +52,7 @@ type Container interface { Exec(ctx context.Context, cmd []string) (int, error) ContainerIP(context.Context) (string, error) // get container ip CopyFileToContainer(ctx context.Context, hostFilePath string, containerFilePath string, fileMode int64) error + CopyFileFromContainer(ctx context.Context, filePath string) (io.ReadCloser, error) } // ImageBuildInfo defines what is needed to build an image diff --git a/docker.go b/docker.go index 668b9d5f72..efb324aa54 100644 --- a/docker.go +++ b/docker.go @@ -334,6 +334,41 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, error) { return exitCode, nil } +type FileFromContainer struct { + underlying *io.ReadCloser + tarreader *tar.Reader +} + +func (fc *FileFromContainer) Read(b []byte) (int, error) { + return (*fc.tarreader).Read(b) +} + +func (fc *FileFromContainer) Close() error { + return (*fc.underlying).Close() +} + +func (c *DockerContainer) CopyFileFromContainer(ctx context.Context, filePath string) (io.ReadCloser, error) { + r, _, err := c.provider.client.CopyFromContainer(ctx, c.ID, filePath) + if err != nil { + return nil, err + } + tarReader := tar.NewReader(r) + + //if we got here we have exactly one file in the TAR-stream + //so we advance the index by one so the next call to Read will start reading it + _, err = tarReader.Next() + if err != nil { + return nil, err + } + + ret := &FileFromContainer{ + underlying: &r, + tarreader: tarReader, + } + + return ret, nil +} + func (c *DockerContainer) CopyFileToContainer(ctx context.Context, hostFilePath string, containerFilePath string, fileMode int64) error { fileContent, err := ioutil.ReadFile(hostFilePath) if err != nil { diff --git a/docker_test.go b/docker_test.go index 011e16b85f..9349c4fedc 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1455,6 +1455,80 @@ func TestDockerContainerCopyFileToContainer(t *testing.T) { } } +func TestDockerContainerCopyFileFromContainer(t *testing.T) { + fileContent, err := ioutil.ReadFile("./testresources/hello.sh") + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + ContainerRequest: ContainerRequest{ + Image: "nginx:1.17.6", + ExposedPorts: []string{"80/tcp"}, + WaitingFor: wait.ForListeningPort("80/tcp"), + }, + Started: true, + }) + defer nginxC.Terminate(ctx) + + copiedFileName := "hello_copy.sh" + nginxC.CopyFileToContainer(ctx, "./testresources/hello.sh", "/"+copiedFileName, 700) + c, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) + if err != nil { + t.Fatal(err) + } + if c != 0 { + t.Fatalf("File %s should exist, expected return code 0, got %v", copiedFileName, c) + } + + reader, err := nginxC.CopyFileFromContainer(ctx, "/"+copiedFileName) + if err != nil { + t.Fatal(err) + } + + fileContentFromContainer, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, fileContent, fileContentFromContainer) +} + +func TestDockerContainerCopyEmptyFileFromContainer(t *testing.T) { + ctx := context.Background() + + nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + ContainerRequest: ContainerRequest{ + Image: "nginx:1.17.6", + ExposedPorts: []string{"80/tcp"}, + WaitingFor: wait.ForListeningPort("80/tcp"), + }, + Started: true, + }) + defer nginxC.Terminate(ctx) + + copiedFileName := "hello_copy.sh" + nginxC.CopyFileToContainer(ctx, "./testresources/empty.sh", "/"+copiedFileName, 700) + c, err := nginxC.Exec(ctx, []string{"bash", copiedFileName}) + if err != nil { + t.Fatal(err) + } + if c != 0 { + t.Fatalf("File %s should exist, expected return code 0, got %v", copiedFileName, c) + } + + reader, err := nginxC.CopyFileFromContainer(ctx, "/"+copiedFileName) + if err != nil { + t.Fatal(err) + } + + fileContentFromContainer, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + assert.Empty(t, fileContentFromContainer) +} + func TestContainerWithReaperNetwork(t *testing.T) { ctx := context.Background() networks := []string{ diff --git a/testresources/empty.sh b/testresources/empty.sh new file mode 100644 index 0000000000..e69de29bb2