From bde2f2e43d74bcdaa5f51fa3542501535b0697d8 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 16 Dec 2024 03:39:24 -0500 Subject: [PATCH 1/3] refactor: write contents to stdout before writing to a file Rather than writing directly to the file - thus utilizing whatever user docker is being executed as - we copy the file to stdout and then write it to the temp file. This ensures permissions on the file are as expected from Dokku's end. Closes #7336 --- plugins/common/docker.go | 47 ++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/plugins/common/docker.go b/plugins/common/docker.go index 18c90b41e92..10d64b8f69d 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -104,14 +104,6 @@ func CopyFromImage(appName string, image string, source string, destination stri } } - tmpFile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("dokku-%s-%s", MustGetEnv("DOKKU_PID"), "CopyFromImage")) - if err != nil { - return fmt.Errorf("Cannot create temporary file: %v", err) - } - - defer tmpFile.Close() - defer os.Remove(tmpFile.Name()) - globalRunArgs := MustGetEnv("DOKKU_GLOBAL_RUN_ARGS") createLabelArgs := []string{"--label", fmt.Sprintf("com.dokku.app-name=%s", appName), globalRunArgs} containerID, err := DockerContainerCreate(image, createLabelArgs) @@ -125,7 +117,7 @@ func CopyFromImage(appName string, image string, source string, destination stri // ref: https://github.com/dotcloud/docker/issues/3986 result, err := CallExecCommand(ExecCommandInput{ Command: DockerBin(), - Args: []string{"container", "cp", fmt.Sprintf("%s:%s", containerID, source), tmpFile.Name()}, + Args: []string{"container", "cp", "--quiet", fmt.Sprintf("%s:%s", containerID, source), "-"}, }) if err != nil { return fmt.Errorf("Unable to copy file %s from image: %w", source, err) @@ -134,6 +126,43 @@ func CopyFromImage(appName string, image string, source string, destination stri return fmt.Errorf("Unable to copy file %s from image: %v", source, result.StderrContents()) } + tarContents := result.StdoutContents() + if tarContents == "" { + return fmt.Errorf("Unable to copy file %s from image", source) + } + + // extract the contents via tar + // tar -xOf - + result, err = CallExecCommand(ExecCommandInput{ + Command: "tar", + Args: []string{"-xOf", "-"}, + Stdin: strings.NewReader(tarContents), + }) + if err != nil { + return fmt.Errorf("Unable to extract contents from tar: %v", err) + } + + contents = result.StdoutContents() + + tmpFile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("dokku-%s-%s", MustGetEnv("DOKKU_PID"), "CopyFromImage")) + if err != nil { + return fmt.Errorf("Cannot create temporary file: %v", err) + } + + defer func() { + if err := tmpFile.Close(); err != nil { + LogWarn(fmt.Sprintf("Unable to close temporary file: %v", err)) + } + if err := os.Remove(tmpFile.Name()); err != nil { + LogWarn(fmt.Sprintf("Unable to remove temporary file: %v", err)) + } + }() + + // write contents to tmpFile + if _, err := tmpFile.Write([]byte(contents)); err != nil { + return fmt.Errorf("Unable to write to temporary file: %v", err) + } + fi, err := os.Stat(tmpFile.Name()) if err != nil { return err From 78cd787622caff2c2e2bf7f9c65ace71d7b5a9c9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 16 Dec 2024 23:32:16 -0500 Subject: [PATCH 2/3] refactor: avoid shelling out to run tar --- plugins/common/docker.go | 42 ++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/plugins/common/docker.go b/plugins/common/docker.go index 10d64b8f69d..b813a0cacd2 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -1,7 +1,10 @@ package common import ( + "archive/tar" + "bytes" "fmt" + "io" "os" "strconv" "strings" @@ -132,18 +135,11 @@ func CopyFromImage(appName string, image string, source string, destination stri } // extract the contents via tar - // tar -xOf - - result, err = CallExecCommand(ExecCommandInput{ - Command: "tar", - Args: []string{"-xOf", "-"}, - Stdin: strings.NewReader(tarContents), - }) + content, err := extractTarToString(tarContents) if err != nil { return fmt.Errorf("Unable to extract contents from tar: %v", err) } - contents = result.StdoutContents() - tmpFile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("dokku-%s-%s", MustGetEnv("DOKKU_PID"), "CopyFromImage")) if err != nil { return fmt.Errorf("Cannot create temporary file: %v", err) @@ -201,6 +197,36 @@ func CopyFromImage(appName string, image string, source string, destination stri return nil } +// Function to extract tar contents and return them as a string +func extractTarToString(in string) (string, error) { + // Initialize a buffer to accumulate the extracted content + var extractedContent bytes.Buffer + + // Create a tar reader from standard input + tarReader := tar.NewReader(strings.NewReader(in)) + + // Iterate through the files in the tar archive + for { + // Read the next header (file entry) + _, err := tarReader.Next() + if err == io.EOF { + break // End of archive + } + if err != nil { + return "", fmt.Errorf("error reading tar header: %v", err) + } + + // Write the content of the current file into the buffer + _, err = io.Copy(&extractedContent, tarReader) + if err != nil { + return "", fmt.Errorf("error copying file content: %v", err) + } + } + + // Return the accumulated content as a string + return strings.TrimSpace(extractedContent.String()), nil +} + // DockerBin returns a string which contains a path to the current docker binary func DockerBin() string { dockerBin := os.Getenv("DOCKER_BIN") From 3f49c701768b31491b12354f95367b75f9f7a81f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 16 Dec 2024 23:32:47 -0500 Subject: [PATCH 3/3] fix: use correct variableg --- plugins/common/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/common/docker.go b/plugins/common/docker.go index b813a0cacd2..25740a5d159 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -155,7 +155,7 @@ func CopyFromImage(appName string, image string, source string, destination stri }() // write contents to tmpFile - if _, err := tmpFile.Write([]byte(contents)); err != nil { + if _, err := tmpFile.Write([]byte(content)); err != nil { return fmt.Errorf("Unable to write to temporary file: %v", err) }