From 8bc13326904d5337183fe4f8f3893c249cb87fe5 Mon Sep 17 00:00:00 2001 From: codepitbull Date: Tue, 17 Aug 2021 13:38:29 +0200 Subject: [PATCH 1/3] Added CopyFileFromContainer to DcokerContainer to allow retrieving files from a container --- container.go | 1 + docker.go | 24 ++++++++++++++++++++++++ docker_test.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/container.go b/container.go index 51b3d68927..b032151a23 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) ([]byte, error) } // ImageBuildInfo defines what is needed to build an image diff --git a/docker.go b/docker.go index 12dc8fe8cd..c1a531e99a 100644 --- a/docker.go +++ b/docker.go @@ -334,6 +334,30 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, error) { return exitCode, nil } +func (c *DockerContainer) CopyFileFromContainer(ctx context.Context, filePath string) ([]byte, error) { + r, _, err := c.provider.client.CopyFromContainer(ctx, c.ID, filePath) + if err != nil { + return nil, err + } + defer r.Close() + + tr := tar.NewReader(r) + for { + _, err := tr.Next() + if err == io.EOF { + // end of tar archive + return []byte{}, nil + } + if err != nil { + return nil, err + } + buf := new(bytes.Buffer) + buf.ReadFrom(tr) + + return buf.Bytes(), 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 3de0994687..04bebf34c8 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1455,6 +1455,40 @@ 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) + } + + fileContentFromContainer, err := nginxC.CopyFileFromContainer(ctx, "/"+copiedFileName) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, fileContent, fileContentFromContainer) +} + func TestContainerWithReaperNetwork(t *testing.T) { ctx := context.Background() networks := []string{ From 3792295fdefdc8b545312e644db84d54ef7541d7 Mon Sep 17 00:00:00 2001 From: Jochen Mader Date: Tue, 24 Aug 2021 21:35:10 +0200 Subject: [PATCH 2/3] Changed siganture to return a ReadCloser --- container.go | 2 +- docker.go | 41 ++++++++++++++++++++++++++--------------- docker_test.go | 42 +++++++++++++++++++++++++++++++++++++++++- testresources/empty.sh | 0 4 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 testresources/empty.sh diff --git a/container.go b/container.go index b032151a23..64ee0f2c08 100644 --- a/container.go +++ b/container.go @@ -52,7 +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) ([]byte, 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 c1a531e99a..d931d4ca79 100644 --- a/docker.go +++ b/docker.go @@ -334,28 +334,39 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, error) { return exitCode, nil } -func (c *DockerContainer) CopyFileFromContainer(ctx context.Context, filePath string) ([]byte, error) { +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 } - defer r.Close() + tarReader := tar.NewReader(r) - tr := tar.NewReader(r) - for { - _, err := tr.Next() - if err == io.EOF { - // end of tar archive - return []byte{}, nil - } - if err != nil { - return nil, err - } - buf := new(bytes.Buffer) - buf.ReadFrom(tr) + //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 + } - return buf.Bytes(), nil + ret := &FileFromContainer{ + underlying: &r, + tarreader: tarReader, } + + return ret, nil } func (c *DockerContainer) CopyFileToContainer(ctx context.Context, hostFilePath string, containerFilePath string, fileMode int64) error { diff --git a/docker_test.go b/docker_test.go index 04bebf34c8..ac521a0587 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1482,13 +1482,53 @@ func TestDockerContainerCopyFileFromContainer(t *testing.T) { t.Fatalf("File %s should exist, expected return code 0, got %v", copiedFileName, c) } - fileContentFromContainer, err := nginxC.CopyFileFromContainer(ctx, "/"+copiedFileName) + 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 From 28d20caa48630a79ce08e597f0fd73ccb1695238 Mon Sep 17 00:00:00 2001 From: Jochen Mader Date: Thu, 9 Sep 2021 22:33:02 +0200 Subject: [PATCH 3/3] go fmt --- docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker.go b/docker.go index d931d4ca79..083a3ecc3a 100644 --- a/docker.go +++ b/docker.go @@ -336,7 +336,7 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string) (int, error) { type FileFromContainer struct { underlying *io.ReadCloser - tarreader *tar.Reader + tarreader *tar.Reader } func (fc *FileFromContainer) Read(b []byte) (int, error) {