diff --git a/container.go b/container.go index c23dfef228..70146ab9a2 100644 --- a/container.go +++ b/container.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "path/filepath" "time" "github.com/docker/docker/api/types" @@ -234,6 +235,13 @@ func (c *ContainerRequest) GetContext() (io.Reader, error) { return c.ContextArchive, nil } + // always pass context as absolute path + abs, err := filepath.Abs(c.Context) + if err != nil { + return nil, fmt.Errorf("error getting absolute path: %w", err) + } + c.Context = abs + buildContext, err := archive.TarWithOptions(c.Context, &archive.TarOptions{}) if err != nil { return nil, err diff --git a/docker_test.go b/docker_test.go index 08894b9b2e..faa934d031 100644 --- a/docker_test.go +++ b/docker_test.go @@ -2022,18 +2022,20 @@ func TestDockerContainerCopyDirToContainer(t *testing.T) { Started: true, }) + p := filepath.Join(".", "testresources", "Dokerfile") require.NoError(t, err) terminateContainerOnEnd(t, ctx, nginxC) - err = nginxC.CopyDirToContainer(ctx, "./testresources/Dockerfile", "/tmp/testresources/Dockerfile", 700) + err = nginxC.CopyDirToContainer(ctx, p, "/tmp/testresources/Dockerfile", 700) require.Error(t, err) // copying a file using the directory method will raise an error - err = nginxC.CopyDirToContainer(ctx, "./testresources", "/tmp/testresources", 700) + p = filepath.Join(".", "testresources") + err = nginxC.CopyDirToContainer(ctx, p, "/tmp/testresources", 700) if err != nil { t.Fatal(err) } - assertExtractedFiles(t, ctx, nginxC, "./testresources", "/tmp/testresources/") + assertExtractedFiles(t, ctx, nginxC, p, "/tmp/testresources/") } func TestDockerCreateContainerWithFiles(t *testing.T) { @@ -2109,15 +2111,27 @@ func TestDockerCreateContainerWithDirs(t *testing.T) { ctx := context.Background() hostDirName := "testresources" + abs, err := filepath.Abs(filepath.Join(".", hostDirName)) + assert.Nil(t, err) + tests := []struct { name string dir ContainerFile hasError bool }{ + { + name: "success copy directory with full path", + dir: ContainerFile{ + HostFilePath: abs, + ContainerFilePath: "/tmp/" + hostDirName, // the parent dir must exist + FileMode: 700, + }, + hasError: false, + }, { name: "success copy directory", dir: ContainerFile{ - HostFilePath: "./" + hostDirName, + HostFilePath: filepath.Join("./", hostDirName), ContainerFilePath: "/tmp/" + hostDirName, // the parent dir must exist FileMode: 700, }, @@ -2561,10 +2575,11 @@ func assertExtractedFiles(t *testing.T, ctx context.Context, container Container require.NoError(t, err) } + fp := filepath.Join(containerFilePath, srcFile.Name()) // copy file by file, as there is a limitation in the Docker client to copy an entiry directory from the container // paths for the container files are using Linux path separators - fd, err := container.CopyFileFromContainer(ctx, containerFilePath+"/"+srcFile.Name()) - require.NoError(t, err, "Path not found in container: %s", containerFilePath+"/"+srcFile.Name()) + fd, err := container.CopyFileFromContainer(ctx, fp) + require.NoError(t, err, "Path not found in container: %s", fp) defer fd.Close() targetPath := filepath.Join(tmpDir, srcFile.Name()) diff --git a/file.go b/file.go index 1f402f3842..509da643d4 100644 --- a/file.go +++ b/file.go @@ -8,6 +8,7 @@ import ( "io" "os" "path/filepath" + "strings" ) func isDir(path string) (bool, error) { @@ -31,6 +32,13 @@ func isDir(path string) (bool, error) { // tarDir compress a directory using tar + gzip algorithms func tarDir(src string, fileMode int64) (*bytes.Buffer, error) { + // always pass src as absolute path + abs, err := filepath.Abs(src) + if err != nil { + return &bytes.Buffer{}, fmt.Errorf("error getting absolute path: %w", err) + } + src = abs + buffer := &bytes.Buffer{} fmt.Printf(">> creating TAR file from directory: %s\n", src) @@ -39,8 +47,12 @@ func tarDir(src string, fileMode int64) (*bytes.Buffer, error) { zr := gzip.NewWriter(buffer) tw := tar.NewWriter(zr) + _, baseDir := filepath.Split(src) + // keep the path relative to the parent directory + index := strings.LastIndex(src, baseDir) + // walk through every file in the folder - err := filepath.Walk(src, func(file string, fi os.FileInfo, errFn error) error { + err = filepath.Walk(src, func(file string, fi os.FileInfo, errFn error) error { if errFn != nil { return fmt.Errorf("error traversing the file system: %w", errFn) } @@ -57,9 +69,10 @@ func tarDir(src string, fileMode int64) (*bytes.Buffer, error) { return fmt.Errorf("error getting file info header: %w", err) } - // must provide real name - // (see https://golang.org/src/archive/tar/common.go?#L626) - header.Name = filepath.ToSlash(file) + // see https://pkg.go.dev/archive/tar#FileInfoHeader: + // Since fs.FileInfo's Name method only returns the base name of the file it describes, + // it may be necessary to modify Header.Name to provide the full path name of the file. + header.Name = filepath.ToSlash(file[index:]) header.Mode = fileMode // write header diff --git a/file_test.go b/file_test.go index c34acf7060..74a020378b 100644 --- a/file_test.go +++ b/file_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_IsDir(t *testing.T) { @@ -53,38 +54,60 @@ func Test_IsDir(t *testing.T) { } func Test_TarDir(t *testing.T) { - src := filepath.Join(".", "testresources") - - buff, err := tarDir(src, 0755) - if err != nil { - t.Fatal(err) + originalSrc := filepath.Join(".", "testresources") + tests := []struct { + abs bool + }{ + { + abs: false, + }, + { + abs: true, + }, } - tmpDir := filepath.Join(t.TempDir(), "subfolder") - err = untar(tmpDir, bytes.NewReader(buff.Bytes())) - if err != nil { - t.Fatal(err) - } + for _, test := range tests { + t.Run(fmt.Sprintf("TarDir with abs=%t", test.abs), func(t *testing.T) { + src := originalSrc + if test.abs { + absSrc, err := filepath.Abs(src) + require.Nil(t, err) - srcFiles, err := os.ReadDir(src) - if err != nil { - log.Fatal(err) - } + src = absSrc + } - for _, srcFile := range srcFiles { - if srcFile.IsDir() { - continue - } - srcBytes, err := os.ReadFile(filepath.Join(src, srcFile.Name())) - if err != nil { - t.Fatal(err) - } + buff, err := tarDir(src, 0755) + if err != nil { + t.Fatal(err) + } - untarBytes, err := os.ReadFile(filepath.Join(tmpDir, "testresources", srcFile.Name())) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, srcBytes, untarBytes) + tmpDir := filepath.Join(t.TempDir(), "subfolder") + err = untar(tmpDir, bytes.NewReader(buff.Bytes())) + if err != nil { + t.Fatal(err) + } + + srcFiles, err := os.ReadDir(src) + if err != nil { + log.Fatal(err) + } + + for _, srcFile := range srcFiles { + if srcFile.IsDir() { + continue + } + srcBytes, err := os.ReadFile(filepath.Join(src, srcFile.Name())) + if err != nil { + t.Fatal(err) + } + + untarBytes, err := os.ReadFile(filepath.Join(tmpDir, "testresources", srcFile.Name())) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, srcBytes, untarBytes) + } + }) } }