diff --git a/cmd/executor/cmd/root.go b/cmd/executor/cmd/root.go index b43a85b68..3172a88d0 100644 --- a/cmd/executor/cmd/root.go +++ b/cmd/executor/cmd/root.go @@ -25,6 +25,7 @@ import ( "github.com/GoogleContainerTools/kaniko/pkg/buildcontext" "github.com/GoogleContainerTools/kaniko/pkg/config" "github.com/GoogleContainerTools/kaniko/pkg/constants" + "github.com/GoogleContainerTools/kaniko/pkg/dockerfile" "github.com/GoogleContainerTools/kaniko/pkg/executor" "github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/genuinetools/amicontained/container" @@ -61,6 +62,9 @@ var RootCmd = &cobra.Command{ if err := resolveSourceContext(); err != nil { return errors.Wrap(err, "error resolving source context") } + if err := removeIgnoredFiles(); err != nil { + return errors.Wrap(err, "error removing .dockerignore files from build context") + } return resolveDockerfilePath() }, Run: func(cmd *cobra.Command, args []string) { @@ -184,6 +188,44 @@ func resolveSourceContext() error { return nil } +func removeIgnoredFiles() error { + if !dockerfile.DockerignoreExists(opts) { + return nil + } + ignore, err := dockerfile.ParseDockerignore(opts) + if err != nil { + return err + } + logrus.Infof("Removing ignored files from build context: %s", ignore) + for r, i := range ignore { + ignore[r] = filepath.Clean(filepath.Join(opts.SrcContext, i)) + } + // remove all files in .dockerignore + return filepath.Walk(opts.SrcContext, func(path string, fi os.FileInfo, _ error) error { + if ignoreFile(path, ignore) { + if err := os.RemoveAll(path); err != nil { + // don't return error, because this path could have been removed already + logrus.Debugf("error removing %s from buildcontext", path) + } + } + return nil + }) +} + +// ignoreFile returns true if the path matches any of the paths in ignore +func ignoreFile(path string, ignore []string) bool { + for _, i := range ignore { + matched, err := filepath.Match(i, path) + if err != nil { + return false + } + if matched { + return true + } + } + return false +} + func exit(err error) { fmt.Println(err) os.Exit(1) diff --git a/integration/dockerfiles/Dockerfile_test_ignore b/integration/dockerfiles/Dockerfile_test_ignore new file mode 100644 index 000000000..04fdc2701 --- /dev/null +++ b/integration/dockerfiles/Dockerfile_test_ignore @@ -0,0 +1,2 @@ +FROM scratch +COPY . . diff --git a/integration/images.go b/integration/images.go index 464db8cc8..1f392be57 100644 --- a/integration/images.go +++ b/integration/images.go @@ -37,6 +37,7 @@ const ( buildContextPath = "/workspace" cacheDir = "/workspace/cache" baseImageToCache = "gcr.io/google-appengine/debian9@sha256:1d6a9a6d106bd795098f60f4abb7083626354fa6735e81743c7f8cfca11259f0" + testDirPath = "context/test" ) // Arguments to build Dockerfiles with, used for both docker and kaniko builds @@ -54,6 +55,8 @@ var argsMap = map[string][]string{ "Dockerfile_test_multistage": {"file=/foo2"}, } +var filesToIgnore = []string{"context/test/*"} + // Arguments to build Dockerfiles with when building with docker var additionalDockerFlagsMap = map[string][]string{ "Dockerfile_test_target": {"--target=second"}, @@ -158,11 +161,25 @@ func (d *DockerFileBuilder) BuildImage(imageRepo, gcsBucket, dockerfilesPath, do "."}, additionalFlags...)..., ) + if err := setupTestDir(); err != nil { + return err + } + if err := generateDockerIgnore(); err != nil { + return err + } + _, err := RunCommandWithoutTest(dockerCmd) if err != nil { return fmt.Errorf("Failed to build image %s with docker command \"%s\": %s", dockerImage, dockerCmd.Args, err) } + if err := setupTestDir(); err != nil { + return err + } + if err := generateDockerIgnore(); err != nil { + return err + } + contextFlag := "-c" contextPath := buildContextPath for _, d := range bucketContextTests { @@ -252,3 +269,28 @@ func (d *DockerFileBuilder) buildCachedImages(imageRepo, cacheRepo, dockerfilesP } return nil } + +func setupTestDir() error { + if err := os.MkdirAll(testDirPath, 0750); err != nil { + return err + } + p := filepath.Join(testDirPath, "foo") + f, err := os.Create(p) + if err != nil { + return err + } + return f.Close() +} + +func generateDockerIgnore() error { + f, err := os.Create(".dockerignore") + if err != nil { + return err + } + defer f.Close() + contents := strings.Join(filesToIgnore, "\n") + if _, err := f.Write([]byte(contents)); err != nil { + return err + } + return nil +} diff --git a/pkg/dockerfile/dockerfile.go b/pkg/dockerfile/dockerfile.go index 8b06985b6..c2eb9bff7 100644 --- a/pkg/dockerfile/dockerfile.go +++ b/pkg/dockerfile/dockerfile.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "io/ioutil" + "path/filepath" "strconv" "strings" @@ -168,3 +169,23 @@ func saveStage(index int, stages []instructions.Stage) bool { } return false } + +// DockerignoreExists returns true if .dockerignore exists in the source context +func DockerignoreExists(opts *config.KanikoOptions) bool { + path := filepath.Join(opts.SrcContext, ".dockerignore") + return util.FilepathExists(path) +} + +// ParseDockerignore returns a list of all paths in .dockerignore +func ParseDockerignore(opts *config.KanikoOptions) ([]string, error) { + path := filepath.Join(opts.SrcContext, ".dockerignore") + contents, err := ioutil.ReadFile(path) + if err != nil { + return nil, errors.Wrap(err, "parsing .dockerignore") + } + return strings.FieldsFunc(string(contents), split), nil +} + +func split(r rune) bool { + return r == '\n' || r == ' ' +}