From f8ec68ef678db023c05c42a1e0ce787d38549d5f Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Thu, 23 Oct 2014 14:30:11 -0700 Subject: [PATCH] Have .dockerignore support Dockerfile/.dockerignore If .dockerignore mentions either then the client will send them to the daemon but the daemon will erase them after the Dockerfile has been parsed to simulate them never being sent in the first place. an events test kept failing for me so I tried to fix that too Closes #8330 Signed-off-by: Doug Davis --- archive/archive.go | 48 +++++++++++++++++++++++------------ archive/archive_test.go | 8 +++--- chrootarchive/archive.go | 4 +-- chrootarchive/archive_test.go | 2 +- tarsum/builder_context.go | 20 +++++++++++++++ 5 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 tarsum/builder_context.go diff --git a/archive/archive.go b/archive/archive.go index ec45d85..3556652 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -30,11 +30,11 @@ type ( ArchiveReader io.Reader Compression int TarOptions struct { - Includes []string - Excludes []string - Compression Compression - NoLchown bool - Name string + IncludeFiles []string + ExcludePatterns []string + Compression Compression + NoLchown bool + Name string } // Archiver allows the reuse of most utility functions of this package @@ -378,7 +378,7 @@ func escapeName(name string) string { } // TarWithOptions creates an archive from the directory at `path`, only including files whose relative -// paths are included in `options.Includes` (if non-nil) or not in `options.Excludes`. +// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`. func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) { pipeReader, pipeWriter := io.Pipe() @@ -401,12 +401,14 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) // mutating the filesystem and we can see transient errors // from this - if options.Includes == nil { - options.Includes = []string{"."} + if options.IncludeFiles == nil { + options.IncludeFiles = []string{"."} } + seen := make(map[string]bool) + var renamedRelFilePath string // For when tar.Options.Name is set - for _, include := range options.Includes { + for _, include := range options.IncludeFiles { filepath.Walk(filepath.Join(srcPath, include), func(filePath string, f os.FileInfo, err error) error { if err != nil { log.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err) @@ -420,10 +422,19 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) return nil } - skip, err := fileutils.Matches(relFilePath, options.Excludes) - if err != nil { - log.Debugf("Error matching %s", relFilePath, err) - return err + skip := false + + // If "include" is an exact match for the current file + // then even if there's an "excludePatterns" pattern that + // matches it, don't skip it. IOW, assume an explicit 'include' + // is asking for that file no matter what - which is true + // for some files, like .dockerignore and Dockerfile (sometimes) + if include != relFilePath { + skip, err = fileutils.Matches(relFilePath, options.ExcludePatterns) + if err != nil { + log.Debugf("Error matching %s", relFilePath, err) + return err + } } if skip { @@ -433,6 +444,11 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) return nil } + if seen[relFilePath] { + return nil + } + seen[relFilePath] = true + // Rename the base resource if options.Name != "" && filePath == srcPath+"/"+filepath.Base(relFilePath) { renamedRelFilePath = relFilePath @@ -487,7 +503,7 @@ loop: // This keeps "../" as-is, but normalizes "/../" to "/" hdr.Name = filepath.Clean(hdr.Name) - for _, exclude := range options.Excludes { + for _, exclude := range options.ExcludePatterns { if strings.HasPrefix(hdr.Name, exclude) { continue loop } @@ -563,8 +579,8 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error { if options == nil { options = &TarOptions{} } - if options.Excludes == nil { - options.Excludes = []string{} + if options.ExcludePatterns == nil { + options.ExcludePatterns = []string{} } decompressedArchive, err := DecompressStream(archive) if err != nil { diff --git a/archive/archive_test.go b/archive/archive_test.go index fdba6fb..6cd95d5 100644 --- a/archive/archive_test.go +++ b/archive/archive_test.go @@ -165,8 +165,8 @@ func TestTarUntar(t *testing.T) { Gzip, } { changes, err := tarUntar(t, origin, &TarOptions{ - Compression: c, - Excludes: []string{"3"}, + Compression: c, + ExcludePatterns: []string{"3"}, }) if err != nil { @@ -196,8 +196,8 @@ func TestTarWithOptions(t *testing.T) { opts *TarOptions numChanges int }{ - {&TarOptions{Includes: []string{"1"}}, 1}, - {&TarOptions{Excludes: []string{"2"}}, 1}, + {&TarOptions{IncludeFiles: []string{"1"}}, 1}, + {&TarOptions{ExcludePatterns: []string{"2"}}, 1}, } for _, testCase := range cases { changes, err := tarUntar(t, origin, testCase.opts) diff --git a/chrootarchive/archive.go b/chrootarchive/archive.go index 66f8373..ae15a2a 100644 --- a/chrootarchive/archive.go +++ b/chrootarchive/archive.go @@ -50,8 +50,8 @@ func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error if options == nil { options = &archive.TarOptions{} } - if options.Excludes == nil { - options.Excludes = []string{} + if options.ExcludePatterns == nil { + options.ExcludePatterns = []string{} } var ( diff --git a/chrootarchive/archive_test.go b/chrootarchive/archive_test.go index bb8a22d..b3f7d57 100644 --- a/chrootarchive/archive_test.go +++ b/chrootarchive/archive_test.go @@ -40,7 +40,7 @@ func TestChrootTarUntar(t *testing.T) { if err := os.MkdirAll(dest, 0700); err != nil { t.Fatal(err) } - if err := Untar(stream, dest, &archive.TarOptions{Excludes: []string{"lolo"}}); err != nil { + if err := Untar(stream, dest, &archive.TarOptions{ExcludePatterns: []string{"lolo"}}); err != nil { t.Fatal(err) } } diff --git a/tarsum/builder_context.go b/tarsum/builder_context.go new file mode 100644 index 0000000..06a4282 --- /dev/null +++ b/tarsum/builder_context.go @@ -0,0 +1,20 @@ +package tarsum + +// This interface extends TarSum by adding the Remove method. In general +// there was concern about adding this method to TarSum itself so instead +// it is being added just to "BuilderContext" which will then only be used +// during the .dockerignore file processing - see builder/evaluator.go +type BuilderContext interface { + TarSum + Remove(string) +} + +func (bc *tarSum) Remove(filename string) { + for i, fis := range bc.sums { + if fis.Name() == filename { + bc.sums = append(bc.sums[:i], bc.sums[i+1:]...) + // Note, we don't just return because there could be + // more than one with this name + } + } +}