Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug with DockerExecutor's CopyFile #1275

Merged
merged 1 commit into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions util/file/fileutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,27 @@ import (
"encoding/base64"
"io"
"io/ioutil"
"os"
"strings"

log "github.com/sirupsen/logrus"
)

// IsFileOrDirExistInGZip return true if file or directory exists in GZip file
func IsFileOrDirExistInGZip(sourcePath string, gzipFilePath string) bool {

fi, err := os.Open(gzipFilePath)

if os.IsNotExist(err) {
return false
}
defer close(fi)
type TarReader interface {
Next() (*tar.Header, error)
}

fz, err := gzip.NewReader(fi)
if err != nil {
return false
}
tr := tar.NewReader(fz)
// ExistsInTar return true if file or directory exists in tar
func ExistsInTar(sourcePath string, tarReader TarReader) bool {
sourcePath = strings.Trim(sourcePath, "/")
for {
hdr, err := tr.Next()
hdr, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {

return false
}
if hdr.FileInfo().IsDir() && strings.Contains(strings.Trim(hdr.Name, "/"), strings.Trim(sourcePath, "/")) {
if hdr.FileInfo().IsDir() && strings.Contains(sourcePath, strings.Trim(hdr.Name, "/")) {
return true
}
if strings.Contains(sourcePath, hdr.Name) && hdr.Size > 0 {
Expand Down
110 changes: 105 additions & 5 deletions util/file/fileutil_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package file
package file_test

import (
"testing"

"archive/tar"
"bytes"
"github.com/argoproj/argo/util/file"
"github.com/stretchr/testify/assert"
"os"
"testing"
)

// TestResubmitWorkflowWithOnExit ensures we do not carry over the onExit node even if successful
Expand All @@ -13,9 +16,106 @@ func TestCompressContentString(t *testing.T) {
"\"Succeeded\",\"boundaryID\":\"pod-limits-rrdm8\",\"startedAt\":\"2019-03-07T19:14:50Z\",\"finishedAt\":" +
"\"2019-03-07T19:14:55Z\"}}"

compString := CompressEncodeString(content)
compString := file.CompressEncodeString(content)

resultString, _ := DecodeDecompressString(compString)
resultString, _ := file.DecodeDecompressString(compString)

assert.Equal(t, content, resultString)
}

func TestExistsInTar(t *testing.T) {
type fakeFile struct {
name, body string
isDir bool
}

newTarReader := func(t *testing.T, files []fakeFile) *tar.Reader {
var buf bytes.Buffer
writer := tar.NewWriter(&buf)
for _, f := range files {
mode := os.FileMode(0600)
if f.isDir {
mode |= os.ModeDir
}
hdr := tar.Header{Name: f.name, Mode: int64(mode), Size: int64(len(f.body))}
err := writer.WriteHeader(&hdr)
assert.Nil(t, err)
_, err = writer.Write([]byte(f.body))
assert.Nil(t, err)
}
err := writer.Close()
assert.Nil(t, err)
return tar.NewReader(&buf)
}

type TestCase struct {
sourcePath string
expected bool
files []fakeFile
}

tests := []TestCase{
{
sourcePath: "/root.txt", expected: true,
files: []fakeFile{{name: "root.txt", body: "file in the root"}},
},
{
sourcePath: "/tmp/file/in/subfolder.txt", expected: true,
files: []fakeFile{{name: "subfolder.txt", body: "a file in a subfolder"}},
},
{
sourcePath: "/root", expected: true,
files: []fakeFile{
{name: "root/", isDir: true},
{name: "root/a.txt", body: "a"},
{name: "root/b.txt", body: "b"},
},
},
{
sourcePath: "/tmp/subfolder", expected: true,
files: []fakeFile{
{name: "subfolder/", isDir: true},
{name: "subfolder/a.txt", body: "a"},
{name: "subfolder/b.txt", body: "b"},
},
},
{
// should an empty tar return true??
sourcePath: "/tmp/empty", expected: true,
files: []fakeFile{
{name: "empty/", isDir: true},
},
},
{
sourcePath: "/tmp/folder/that", expected: false,
files: []fakeFile{
{name: "this/", isDir: true},
{name: "this/a.txt", body: "a"},
{name: "this/b.txt", body: "b"},
},
},
{
sourcePath: "/empty.txt", expected: false,
files: []fakeFile{
// fails because empty.txt is empty
{name: "empty.txt", body: ""},
},
},
{
sourcePath: "/tmp/empty.txt", expected: false,
files: []fakeFile{
// fails because empty.txt is empty
{name: "empty.txt", body: ""},
},
},
}
for _, tc := range tests {
tc := tc
t.Run("source path "+tc.sourcePath, func(t *testing.T) {
t.Parallel()
tarReader := newTarReader(t, tc.files)
actual := file.ExistsInTar(tc.sourcePath, tarReader)
assert.Equalf(t, tc.expected, actual, "sourcePath %s not found", tc.sourcePath)
})
}
}
17 changes: 13 additions & 4 deletions workflow/executor/docker/docker.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package docker

import (
"archive/tar"
"compress/gzip"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -53,10 +55,17 @@ func (d *DockerExecutor) CopyFile(containerID string, sourcePath string, destPat
if err != nil {
return err
}
if !file.IsFileOrDirExistInGZip(sourcePath, destPath) {
errMsg := fmt.Sprintf("File or Artifact does not exist. %s", sourcePath)
log.Warn(errMsg)
return errors.InternalError(errMsg)
copiedFile, err := os.Open(destPath)
if err != nil {
return err
}
defer util.Close(copiedFile)
gzipReader, err := gzip.NewReader(copiedFile)
if err != nil {
return err
}
if !file.ExistsInTar(sourcePath, tar.NewReader(gzipReader)) {
return errors.InternalErrorf("path %s does not exist (or %s is empty) in archive %s", sourcePath, sourcePath, destPath)
}
log.Infof("Archiving completed")
return nil
Expand Down