commit
						08251159cd
					
				|  | @ -98,7 +98,7 @@ func execute() error { | |||
| 	// Currently only supports single stage builds
 | ||||
| 	for _, stage := range stages { | ||||
| 		for _, cmd := range stage.Commands { | ||||
| 			dockerCommand, err := commands.GetCommand(cmd) | ||||
| 			dockerCommand, err := commands.GetCommand(cmd, srcContext) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| hello | ||||
|  | @ -0,0 +1 @@ | |||
| bat | ||||
|  | @ -0,0 +1 @@ | |||
| bat | ||||
|  | @ -0,0 +1 @@ | |||
| baz | ||||
|  | @ -0,0 +1 @@ | |||
| foo | ||||
|  | @ -0,0 +1,14 @@ | |||
| FROM gcr.io/distroless/base | ||||
| COPY context/foo foo | ||||
| COPY context/foo /foodir/ | ||||
| COPY context/bar/b* bar/ | ||||
| COPY context/fo? /foo2 | ||||
| COPY context/bar/doesnotexist* context/foo hello | ||||
| COPY ./context/empty /empty | ||||
| COPY ./ dir/ | ||||
| COPY . newdir | ||||
| COPY context/bar /baz/ | ||||
| COPY ["context/foo", "/tmp/foo" ] | ||||
| COPY context/b* /baz/ | ||||
| COPY context/foo context/bar/ba? /test/ | ||||
| COPY context/arr[[]0].txt /mydir/ | ||||
|  | @ -0,0 +1,12 @@ | |||
| [ | ||||
|   { | ||||
|     "Image1": "gcr.io/kbuild-test/docker-test-copy:latest", | ||||
|     "Image2": "gcr.io/kbuild-test/kbuild-test-copy:latest", | ||||
|     "DiffType": "File", | ||||
|     "Diff": { | ||||
|       "Adds": null, | ||||
|       "Dels": null, | ||||
|       "Mods": null | ||||
|     } | ||||
|   } | ||||
| ] | ||||
|  | @ -49,6 +49,13 @@ var fileTests = []struct { | |||
| 		context:        "integration_tests/dockerfiles/", | ||||
| 		repo:           "test-run-2", | ||||
| 	}, | ||||
| 	{ | ||||
| 		description:    "test copy", | ||||
| 		dockerfilePath: "/workspace/integration_tests/dockerfiles/Dockerfile_test_copy", | ||||
| 		configPath:     "/workspace/integration_tests/dockerfiles/config_test_copy.json", | ||||
| 		context:        "/workspace/integration_tests/", | ||||
| 		repo:           "test-copy", | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| var structureTests = []struct { | ||||
|  | @ -126,7 +133,7 @@ func main() { | |||
| 		kbuildImage := testRepo + kbuildPrefix + test.repo | ||||
| 		kbuild := step{ | ||||
| 			Name: executorImage, | ||||
| 			Args: []string{executorCommand, "--destination", kbuildImage, "--dockerfile", test.dockerfilePath}, | ||||
| 			Args: []string{executorCommand, "--destination", kbuildImage, "--dockerfile", test.dockerfilePath, "--context", test.context}, | ||||
| 		} | ||||
| 
 | ||||
| 		// Pull the kbuild image
 | ||||
|  |  | |||
|  | @ -34,10 +34,12 @@ type DockerCommand interface { | |||
| 	FilesToSnapshot() []string | ||||
| } | ||||
| 
 | ||||
| func GetCommand(cmd instructions.Command) (DockerCommand, error) { | ||||
| func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, error) { | ||||
| 	switch c := cmd.(type) { | ||||
| 	case *instructions.RunCommand: | ||||
| 		return &RunCommand{cmd: c}, nil | ||||
| 	case *instructions.CopyCommand: | ||||
| 		return &CopyCommand{cmd: c, buildcontext: buildcontext}, nil | ||||
| 	case *instructions.ExposeCommand: | ||||
| 		return &ExposeCommand{cmd: c}, nil | ||||
| 	case *instructions.EnvCommand: | ||||
|  |  | |||
|  | @ -0,0 +1,91 @@ | |||
| /* | ||||
| Copyright 2018 Google LLC | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package commands | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/k8s-container-builder/pkg/util" | ||||
| 	"github.com/containers/image/manifest" | ||||
| 	"github.com/docker/docker/builder/dockerfile/instructions" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| type CopyCommand struct { | ||||
| 	cmd           *instructions.CopyCommand | ||||
| 	buildcontext  string | ||||
| 	snapshotFiles []string | ||||
| } | ||||
| 
 | ||||
| func (c *CopyCommand) ExecuteCommand(config *manifest.Schema2Config) error { | ||||
| 	srcs := c.cmd.SourcesAndDest[:len(c.cmd.SourcesAndDest)-1] | ||||
| 	dest := c.cmd.SourcesAndDest[len(c.cmd.SourcesAndDest)-1] | ||||
| 
 | ||||
| 	logrus.Infof("cmd: copy %s", srcs) | ||||
| 	logrus.Infof("dest: %s", dest) | ||||
| 
 | ||||
| 	// Get a map of [src]:[files rooted at src]
 | ||||
| 	srcMap, err := util.ResolveSources(c.cmd.SourcesAndDest, c.buildcontext) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// For each source, iterate through each file within and copy it over
 | ||||
| 	for src, files := range srcMap { | ||||
| 		for _, file := range files { | ||||
| 			fi, err := os.Stat(filepath.Join(c.buildcontext, file)) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			destPath, err := util.DestinationFilepath(file, src, dest, config.WorkingDir, c.buildcontext) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			// If source file is a directory, we want to create a directory ...
 | ||||
| 			if fi.IsDir() { | ||||
| 				logrus.Infof("Creating directory %s", destPath) | ||||
| 				if err := os.MkdirAll(destPath, fi.Mode()); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} else { | ||||
| 				// ... Else, we want to copy over a file
 | ||||
| 				logrus.Infof("Copying file %s to %s", file, destPath) | ||||
| 				srcFile, err := os.Open(filepath.Join(c.buildcontext, file)) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				defer srcFile.Close() | ||||
| 				if err := util.CreateFile(destPath, srcFile, fi.Mode()); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 			// Append the destination file to the list of files that should be snapshotted later
 | ||||
| 			c.snapshotFiles = append(c.snapshotFiles, destPath) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // FilesToSnapshot should return an empty array if still nil; no files were changed
 | ||||
| func (c *CopyCommand) FilesToSnapshot() []string { | ||||
| 	return c.snapshotFiles | ||||
| } | ||||
| 
 | ||||
| // CreatedBy returns some information about the command for the image config
 | ||||
| func (c *CopyCommand) CreatedBy() string { | ||||
| 	return strings.Join(c.cmd.SourcesAndDest, " ") | ||||
| } | ||||
|  | @ -0,0 +1,163 @@ | |||
| /* | ||||
| Copyright 2018 Google LLC | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/docker/docker/builder/dockerfile/instructions" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // ContainsWildcards returns true if any entry in paths contains wildcards
 | ||||
| func ContainsWildcards(paths []string) bool { | ||||
| 	for _, path := range paths { | ||||
| 		if strings.ContainsAny(path, "*?[") { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // ResolveSources resolves the given sources if the sources contains wildcards
 | ||||
| // It returns a map of [src]:[files rooted at src]
 | ||||
| func ResolveSources(srcsAndDest instructions.SourcesAndDest, root string) (map[string][]string, error) { | ||||
| 	srcs := srcsAndDest[:len(srcsAndDest)-1] | ||||
| 	// If sources contain wildcards, we first need to resolve them to actual paths
 | ||||
| 	if ContainsWildcards(srcs) { | ||||
| 		logrus.Debugf("Resolving srcs %v...", srcs) | ||||
| 		files, err := RelativeFiles("", root) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		srcs, err = matchSources(srcs, files) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		logrus.Debugf("Resolved sources to %v", srcs) | ||||
| 	} | ||||
| 	// Now, get a map of [src]:[files rooted at src]
 | ||||
| 	srcMap, err := SourcesToFilesMap(srcs, root) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	// Check to make sure the sources are valid
 | ||||
| 	return srcMap, IsSrcsValid(srcsAndDest, srcMap) | ||||
| } | ||||
| 
 | ||||
| // matchSources returns a list of sources that match wildcards
 | ||||
| func matchSources(srcs, files []string) ([]string, error) { | ||||
| 	var matchedSources []string | ||||
| 	for _, src := range srcs { | ||||
| 		src = filepath.Clean(src) | ||||
| 		for _, file := range files { | ||||
| 			matched, err := filepath.Match(src, file) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if matched { | ||||
| 				matchedSources = append(matchedSources, file) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return matchedSources, nil | ||||
| } | ||||
| 
 | ||||
| func IsDestDir(path string) bool { | ||||
| 	return strings.HasSuffix(path, "/") || path == "." | ||||
| } | ||||
| 
 | ||||
| // DestinationFilepath returns the destination filepath from the build context to the image filesystem
 | ||||
| // If source is a file:
 | ||||
| //	If dest is a dir, copy it to /dest/relpath
 | ||||
| // 	If dest is a file, copy directly to dest
 | ||||
| // If source is a dir:
 | ||||
| //	Assume dest is also a dir, and copy to dest/relpath
 | ||||
| // If dest is not an absolute filepath, add /cwd to the beginning
 | ||||
| func DestinationFilepath(filename, srcName, dest, cwd, buildcontext string) (string, error) { | ||||
| 	fi, err := os.Stat(filepath.Join(buildcontext, filename)) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	src, err := os.Stat(filepath.Join(buildcontext, srcName)) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if src.IsDir() || IsDestDir(dest) { | ||||
| 		relPath, err := filepath.Rel(srcName, filename) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		if relPath == "." && !fi.IsDir() { | ||||
| 			relPath = filepath.Base(filename) | ||||
| 		} | ||||
| 		destPath := filepath.Join(dest, relPath) | ||||
| 		if filepath.IsAbs(dest) { | ||||
| 			return destPath, nil | ||||
| 		} | ||||
| 		return filepath.Join(cwd, destPath), nil | ||||
| 	} | ||||
| 	if filepath.IsAbs(dest) { | ||||
| 		return dest, nil | ||||
| 	} | ||||
| 	return filepath.Join(cwd, dest), nil | ||||
| } | ||||
| 
 | ||||
| // SourcesToFilesMap returns a map of [src]:[files rooted at source]
 | ||||
| func SourcesToFilesMap(srcs []string, root string) (map[string][]string, error) { | ||||
| 	srcMap := make(map[string][]string) | ||||
| 	for _, src := range srcs { | ||||
| 		src = filepath.Clean(src) | ||||
| 		files, err := RelativeFiles(src, root) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		srcMap[src] = files | ||||
| 	} | ||||
| 	return srcMap, nil | ||||
| } | ||||
| 
 | ||||
| // IsSrcsValid returns an error if the sources provided are invalid, or nil otherwise
 | ||||
| func IsSrcsValid(srcsAndDest instructions.SourcesAndDest, srcMap map[string][]string) error { | ||||
| 	srcs := srcsAndDest[:len(srcsAndDest)-1] | ||||
| 	dest := srcsAndDest[len(srcsAndDest)-1] | ||||
| 
 | ||||
| 	totalFiles := 0 | ||||
| 	for _, files := range srcMap { | ||||
| 		totalFiles += len(files) | ||||
| 	} | ||||
| 	if totalFiles == 0 { | ||||
| 		return errors.New("copy failed: no source files specified") | ||||
| 	} | ||||
| 
 | ||||
| 	if !ContainsWildcards(srcs) { | ||||
| 		// If multiple sources and destination isn't a directory, return an error
 | ||||
| 		if len(srcs) > 1 && !IsDestDir(dest) { | ||||
| 			return errors.New("when specifying multiple sources in a COPY command, destination must be a directory and end in '/'") | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// If there are wildcards, and the destination is a file, there must be exactly one file to copy over,
 | ||||
| 	// Otherwise, return an error
 | ||||
| 	if !IsDestDir(dest) && totalFiles > 1 { | ||||
| 		return errors.New("when specifying multiple sources in a COPY command, destination must be a directory and end in '/'") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -0,0 +1,323 @@ | |||
| /* | ||||
| Copyright 2018 Google LLC | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/k8s-container-builder/testutil" | ||||
| 	"sort" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| var buildContextPath = "../../integration_tests/" | ||||
| 
 | ||||
| var destinationFilepathTests = []struct { | ||||
| 	srcName          string | ||||
| 	filename         string | ||||
| 	dest             string | ||||
| 	cwd              string | ||||
| 	buildcontext     string | ||||
| 	expectedFilepath string | ||||
| }{ | ||||
| 	{ | ||||
| 		srcName:          "context/foo", | ||||
| 		filename:         "context/foo", | ||||
| 		dest:             "/foo", | ||||
| 		cwd:              "/", | ||||
| 		expectedFilepath: "/foo", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "context/foo", | ||||
| 		filename:         "context/foo", | ||||
| 		dest:             "/foodir/", | ||||
| 		cwd:              "/", | ||||
| 		expectedFilepath: "/foodir/foo", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "context/foo", | ||||
| 		filename:         "./context/foo", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "foo", | ||||
| 		expectedFilepath: "/foo", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "context/bar/", | ||||
| 		filename:         "context/bar/bam/bat", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "pkg/", | ||||
| 		expectedFilepath: "/pkg/bam/bat", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "context/bar/", | ||||
| 		filename:         "context/bar/bam/bat", | ||||
| 		cwd:              "/newdir", | ||||
| 		dest:             "pkg/", | ||||
| 		expectedFilepath: "/newdir/pkg/bam/bat", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "./context/empty", | ||||
| 		filename:         "context/empty", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "/empty", | ||||
| 		expectedFilepath: "/empty", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "./context/empty", | ||||
| 		filename:         "context/empty", | ||||
| 		cwd:              "/dir", | ||||
| 		dest:             "/empty", | ||||
| 		expectedFilepath: "/empty", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "./", | ||||
| 		filename:         "./", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "/dir", | ||||
| 		expectedFilepath: "/dir", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "./", | ||||
| 		filename:         "context/foo", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "/dir", | ||||
| 		expectedFilepath: "/dir/context/foo", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          ".", | ||||
| 		filename:         "context/bar", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "/dir", | ||||
| 		expectedFilepath: "/dir/context/bar", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          ".", | ||||
| 		filename:         "context/bar", | ||||
| 		cwd:              "/", | ||||
| 		dest:             "/dir", | ||||
| 		expectedFilepath: "/dir/context/bar", | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcName:          "context/foo", | ||||
| 		filename:         "context/foo", | ||||
| 		cwd:              "/test", | ||||
| 		dest:             ".", | ||||
| 		expectedFilepath: "/test/foo", | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func Test_DestinationFilepath(t *testing.T) { | ||||
| 	for _, test := range destinationFilepathTests { | ||||
| 		actualFilepath, err := DestinationFilepath(test.filename, test.srcName, test.dest, test.cwd, buildContextPath) | ||||
| 		testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedFilepath, actualFilepath) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var matchSourcesTests = []struct { | ||||
| 	srcs          []string | ||||
| 	files         []string | ||||
| 	expectedFiles []string | ||||
| }{ | ||||
| 	{ | ||||
| 		srcs: []string{ | ||||
| 			"pkg/*", | ||||
| 		}, | ||||
| 		files: []string{ | ||||
| 			"pkg/a", | ||||
| 			"pkg/b", | ||||
| 			"/pkg/d", | ||||
| 			"pkg/b/d/", | ||||
| 			"dir/", | ||||
| 		}, | ||||
| 		expectedFiles: []string{ | ||||
| 			"pkg/a", | ||||
| 			"pkg/b", | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func Test_MatchSources(t *testing.T) { | ||||
| 	for _, test := range matchSourcesTests { | ||||
| 		actualFiles, err := matchSources(test.srcs, test.files) | ||||
| 		sort.Strings(actualFiles) | ||||
| 		sort.Strings(test.expectedFiles) | ||||
| 		testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedFiles, actualFiles) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var isSrcValidTests = []struct { | ||||
| 	srcsAndDest []string | ||||
| 	files       map[string][]string | ||||
| 	shouldErr   bool | ||||
| }{ | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src1", | ||||
| 			"src2", | ||||
| 			"dest", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src1": { | ||||
| 				"file1", | ||||
| 			}, | ||||
| 			"src2:": { | ||||
| 				"file2", | ||||
| 			}, | ||||
| 		}, | ||||
| 		shouldErr: true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src1", | ||||
| 			"src2", | ||||
| 			"dest/", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src1": { | ||||
| 				"file1", | ||||
| 			}, | ||||
| 			"src2:": { | ||||
| 				"file2", | ||||
| 			}, | ||||
| 		}, | ||||
| 		shouldErr: false, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src2/", | ||||
| 			"dest", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src1": { | ||||
| 				"file1", | ||||
| 			}, | ||||
| 			"src2:": { | ||||
| 				"file2", | ||||
| 			}, | ||||
| 		}, | ||||
| 		shouldErr: false, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src2", | ||||
| 			"dest", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src1": { | ||||
| 				"file1", | ||||
| 			}, | ||||
| 			"src2:": { | ||||
| 				"file2", | ||||
| 			}, | ||||
| 		}, | ||||
| 		shouldErr: false, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src2", | ||||
| 			"src*", | ||||
| 			"dest/", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src1": { | ||||
| 				"file1", | ||||
| 			}, | ||||
| 			"src2:": { | ||||
| 				"file2", | ||||
| 			}, | ||||
| 		}, | ||||
| 		shouldErr: false, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src2", | ||||
| 			"src*", | ||||
| 			"dest", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src2": { | ||||
| 				"src2/a", | ||||
| 				"src2/b", | ||||
| 			}, | ||||
| 			"src*": {}, | ||||
| 		}, | ||||
| 		shouldErr: true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src2", | ||||
| 			"src*", | ||||
| 			"dest", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src2": { | ||||
| 				"src2/a", | ||||
| 			}, | ||||
| 			"src*": {}, | ||||
| 		}, | ||||
| 		shouldErr: false, | ||||
| 	}, | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"src2", | ||||
| 			"src*", | ||||
| 			"dest", | ||||
| 		}, | ||||
| 		files: map[string][]string{ | ||||
| 			"src2": {}, | ||||
| 			"src*": {}, | ||||
| 		}, | ||||
| 		shouldErr: true, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func Test_IsSrcsValid(t *testing.T) { | ||||
| 	for _, test := range isSrcValidTests { | ||||
| 		err := IsSrcsValid(test.srcsAndDest, test.files) | ||||
| 		testutil.CheckError(t, test.shouldErr, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var testResolveSources = []struct { | ||||
| 	srcsAndDest []string | ||||
| 	expectedMap map[string][]string | ||||
| }{ | ||||
| 	{ | ||||
| 		srcsAndDest: []string{ | ||||
| 			"context/foo", | ||||
| 			"context/b*", | ||||
| 			"dest/", | ||||
| 		}, | ||||
| 		expectedMap: map[string][]string{ | ||||
| 			"context/foo": { | ||||
| 				"context/foo", | ||||
| 			}, | ||||
| 			"context/bar": { | ||||
| 				"context/bar", | ||||
| 				"context/bar/bam", | ||||
| 				"context/bar/bam/bat", | ||||
| 				"context/bar/bat", | ||||
| 				"context/bar/baz", | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func Test_ResolveSources(t *testing.T) { | ||||
| 	for _, test := range testResolveSources { | ||||
| 		actualMap, err := ResolveSources(test.srcsAndDest, buildContextPath) | ||||
| 		testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedMap, actualMap) | ||||
| 	} | ||||
| } | ||||
|  | @ -97,3 +97,48 @@ func fileSystemWhitelist(path string) ([]string, error) { | |||
| 	} | ||||
| 	return whitelist, nil | ||||
| } | ||||
| 
 | ||||
| // RelativeFiles returns a list of all files at the filepath relative to root
 | ||||
| func RelativeFiles(fp string, root string) ([]string, error) { | ||||
| 	var files []string | ||||
| 	fullPath := filepath.Join(root, fp) | ||||
| 	logrus.Debugf("Getting files and contents at root %s", fullPath) | ||||
| 	err := filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error { | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		relPath, err := filepath.Rel(root, path) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		files = append(files, relPath) | ||||
| 		return nil | ||||
| 	}) | ||||
| 	return files, err | ||||
| } | ||||
| 
 | ||||
| // FilepathExists returns true if the path exists
 | ||||
| func FilepathExists(path string) bool { | ||||
| 	_, err := os.Stat(path) | ||||
| 	return !os.IsNotExist(err) | ||||
| } | ||||
| 
 | ||||
| // CreateFile creates a file at path and copies over contents from the reader
 | ||||
| func CreateFile(path string, reader io.Reader, perm os.FileMode) error { | ||||
| 	// Create directory path if it doesn't exist
 | ||||
| 	baseDir := filepath.Dir(path) | ||||
| 	if _, err := os.Stat(baseDir); os.IsNotExist(err) { | ||||
| 		logrus.Debugf("baseDir %s for file %s does not exist. Creating.", baseDir, path) | ||||
| 		if err := os.MkdirAll(baseDir, 0755); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	dest, err := os.Create(path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := io.Copy(dest, reader); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return dest.Chmod(perm) | ||||
| } | ||||
|  |  | |||
|  | @ -51,3 +51,82 @@ func Test_fileSystemWhitelist(t *testing.T) { | |||
| 	sort.Strings(expectedWhitelist) | ||||
| 	testutil.CheckErrorAndDeepEqual(t, false, err, expectedWhitelist, actualWhitelist) | ||||
| } | ||||
| 
 | ||||
| var tests = []struct { | ||||
| 	files         map[string]string | ||||
| 	directory     string | ||||
| 	expectedFiles []string | ||||
| }{ | ||||
| 	{ | ||||
| 		files: map[string]string{ | ||||
| 			"/workspace/foo/a": "baz1", | ||||
| 			"/workspace/foo/b": "baz2", | ||||
| 			"/kbuild/file":     "file", | ||||
| 		}, | ||||
| 		directory: "/workspace/foo/", | ||||
| 		expectedFiles: []string{ | ||||
| 			"workspace/foo/a", | ||||
| 			"workspace/foo/b", | ||||
| 			"workspace/foo", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		files: map[string]string{ | ||||
| 			"/workspace/foo/a": "baz1", | ||||
| 		}, | ||||
| 		directory: "/workspace/foo/a", | ||||
| 		expectedFiles: []string{ | ||||
| 			"workspace/foo/a", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		files: map[string]string{ | ||||
| 			"/workspace/foo/a": "baz1", | ||||
| 			"/workspace/foo/b": "baz2", | ||||
| 			"/workspace/baz":   "hey", | ||||
| 			"/kbuild/file":     "file", | ||||
| 		}, | ||||
| 		directory: "/workspace", | ||||
| 		expectedFiles: []string{ | ||||
| 			"workspace/foo/a", | ||||
| 			"workspace/foo/b", | ||||
| 			"workspace/baz", | ||||
| 			"workspace", | ||||
| 			"workspace/foo", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		files: map[string]string{ | ||||
| 			"/workspace/foo/a": "baz1", | ||||
| 			"/workspace/foo/b": "baz2", | ||||
| 			"/kbuild/file":     "file", | ||||
| 		}, | ||||
| 		directory: "", | ||||
| 		expectedFiles: []string{ | ||||
| 			"workspace/foo/a", | ||||
| 			"workspace/foo/b", | ||||
| 			"kbuild/file", | ||||
| 			"workspace", | ||||
| 			"workspace/foo", | ||||
| 			"kbuild", | ||||
| 			".", | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func Test_RelativeFiles(t *testing.T) { | ||||
| 	for _, test := range tests { | ||||
| 		testDir, err := ioutil.TempDir("", "") | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("err setting up temp dir: %v", err) | ||||
| 		} | ||||
| 		defer os.RemoveAll(testDir) | ||||
| 		if err := testutil.SetupFiles(testDir, test.files); err != nil { | ||||
| 			t.Fatalf("err setting up files: %v", err) | ||||
| 		} | ||||
| 		actualFiles, err := RelativeFiles(test.directory, testDir) | ||||
| 		sort.Strings(actualFiles) | ||||
| 		sort.Strings(test.expectedFiles) | ||||
| 		testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedFiles, actualFiles) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -50,6 +50,12 @@ func CheckErrorAndDeepEqual(t *testing.T, shouldErr bool, err error, expected, a | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func CheckError(t *testing.T, shouldErr bool, err error) { | ||||
| 	if err := checkErr(shouldErr, err); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func checkErr(shouldErr bool, err error) error { | ||||
| 	if err == nil && shouldErr { | ||||
| 		return fmt.Errorf("Expected error, but returned none") | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue