Docker-DCO-1.1-Signed-off-by: Travis Cline <travis.cline@gmail.com> (github: tmc)
| ... | ... |
@@ -27,6 +27,7 @@ type ( |
| 27 | 27 |
Compression int |
| 28 | 28 |
TarOptions struct {
|
| 29 | 29 |
Includes []string |
| 30 |
+ Excludes []string |
|
| 30 | 31 |
Compression Compression |
| 31 | 32 |
NoLchown bool |
| 32 | 33 |
} |
| ... | ... |
@@ -286,7 +287,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L |
| 286 | 286 |
// Tar creates an archive from the directory at `path`, and returns it as a |
| 287 | 287 |
// stream of bytes. |
| 288 | 288 |
func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
| 289 |
- return TarFilter(path, &TarOptions{Compression: compression})
|
|
| 289 |
+ return TarWithOptions(path, &TarOptions{Compression: compression})
|
|
| 290 | 290 |
} |
| 291 | 291 |
|
| 292 | 292 |
func escapeName(name string) string {
|
| ... | ... |
@@ -305,12 +306,9 @@ func escapeName(name string) string {
|
| 305 | 305 |
return string(escaped) |
| 306 | 306 |
} |
| 307 | 307 |
|
| 308 |
-// TarFilter creates an archive from the directory at `srcPath` with `options`, and returns it as a |
|
| 309 |
-// stream of bytes. |
|
| 310 |
-// |
|
| 311 |
-// Files are included according to `options.Includes`, default to including all files. |
|
| 312 |
-// Stream is compressed according to `options.Compression', default to Uncompressed. |
|
| 313 |
-func TarFilter(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
|
| 308 |
+// TarWithOptions creates an archive from the directory at `path`, only including files whose relative |
|
| 309 |
+// paths are included in `options.Includes` (if non-nil) or not in `options.Excludes`. |
|
| 310 |
+func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
|
| 314 | 311 |
pipeReader, pipeWriter := io.Pipe() |
| 315 | 312 |
|
| 316 | 313 |
compressWriter, err := CompressStream(pipeWriter, options.Compression) |
| ... | ... |
@@ -342,6 +340,21 @@ func TarFilter(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
| 342 | 342 |
return nil |
| 343 | 343 |
} |
| 344 | 344 |
|
| 345 |
+ for _, exclude := range options.Excludes {
|
|
| 346 |
+ matched, err := filepath.Match(exclude, relFilePath) |
|
| 347 |
+ if err != nil {
|
|
| 348 |
+ utils.Errorf("Error matching: %s (pattern: %s)\n", relFilePath, exclude)
|
|
| 349 |
+ return err |
|
| 350 |
+ } |
|
| 351 |
+ if matched {
|
|
| 352 |
+ utils.Debugf("Skipping excluded path: %s\n", relFilePath)
|
|
| 353 |
+ if f.IsDir() {
|
|
| 354 |
+ return filepath.SkipDir |
|
| 355 |
+ } |
|
| 356 |
+ return nil |
|
| 357 |
+ } |
|
| 358 |
+ } |
|
| 359 |
+ |
|
| 345 | 360 |
if err := addTarFile(filePath, relFilePath, tw); err != nil {
|
| 346 | 361 |
utils.Debugf("Can't add file %s to tar: %s\n", srcPath, err)
|
| 347 | 362 |
} |
| ... | ... |
@@ -482,7 +495,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
| 482 | 482 |
// TarUntar aborts and returns the error. |
| 483 | 483 |
func TarUntar(src string, dst string) error {
|
| 484 | 484 |
utils.Debugf("TarUntar(%s %s)", src, dst)
|
| 485 |
- archive, err := TarFilter(src, &TarOptions{Compression: Uncompressed})
|
|
| 485 |
+ archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
|
|
| 486 | 486 |
if err != nil {
|
| 487 | 487 |
return err |
| 488 | 488 |
} |
| ... | ... |
@@ -63,8 +63,8 @@ func TestCmdStreamGood(t *testing.T) {
|
| 63 | 63 |
} |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
-func tarUntar(t *testing.T, origin string, compression Compression) error {
|
|
| 67 |
- archive, err := Tar(origin, compression) |
|
| 66 |
+func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) {
|
|
| 67 |
+ archive, err := TarWithOptions(origin, options) |
|
| 68 | 68 |
if err != nil {
|
| 69 | 69 |
t.Fatal(err) |
| 70 | 70 |
} |
| ... | ... |
@@ -72,37 +72,29 @@ func tarUntar(t *testing.T, origin string, compression Compression) error {
|
| 72 | 72 |
|
| 73 | 73 |
buf := make([]byte, 10) |
| 74 | 74 |
if _, err := archive.Read(buf); err != nil {
|
| 75 |
- return err |
|
| 75 |
+ return nil, err |
|
| 76 | 76 |
} |
| 77 | 77 |
wrap := io.MultiReader(bytes.NewReader(buf), archive) |
| 78 | 78 |
|
| 79 | 79 |
detectedCompression := DetectCompression(buf) |
| 80 |
+ compression := options.Compression |
|
| 80 | 81 |
if detectedCompression.Extension() != compression.Extension() {
|
| 81 |
- return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
|
| 82 |
+ return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
|
| 82 | 83 |
} |
| 83 | 84 |
|
| 84 | 85 |
tmp, err := ioutil.TempDir("", "docker-test-untar")
|
| 85 | 86 |
if err != nil {
|
| 86 |
- return err |
|
| 87 |
+ return nil, err |
|
| 87 | 88 |
} |
| 88 | 89 |
defer os.RemoveAll(tmp) |
| 89 | 90 |
if err := Untar(wrap, tmp, nil); err != nil {
|
| 90 |
- return err |
|
| 91 |
+ return nil, err |
|
| 91 | 92 |
} |
| 92 | 93 |
if _, err := os.Stat(tmp); err != nil {
|
| 93 |
- return err |
|
| 94 |
+ return nil, err |
|
| 94 | 95 |
} |
| 95 | 96 |
|
| 96 |
- changes, err := ChangesDirs(origin, tmp) |
|
| 97 |
- if err != nil {
|
|
| 98 |
- return err |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- if len(changes) != 0 {
|
|
| 102 |
- t.Fatalf("Unexpected differences after tarUntar: %v", changes)
|
|
| 103 |
- } |
|
| 104 |
- |
|
| 105 |
- return nil |
|
| 97 |
+ return ChangesDirs(origin, tmp) |
|
| 106 | 98 |
} |
| 107 | 99 |
|
| 108 | 100 |
func TestTarUntar(t *testing.T) {
|
| ... | ... |
@@ -122,9 +114,49 @@ func TestTarUntar(t *testing.T) {
|
| 122 | 122 |
Uncompressed, |
| 123 | 123 |
Gzip, |
| 124 | 124 |
} {
|
| 125 |
- if err := tarUntar(t, origin, c); err != nil {
|
|
| 125 |
+ changes, err := tarUntar(t, origin, &TarOptions{
|
|
| 126 |
+ Compression: c, |
|
| 127 |
+ }) |
|
| 128 |
+ |
|
| 129 |
+ if err != nil {
|
|
| 126 | 130 |
t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
|
| 127 | 131 |
} |
| 132 |
+ |
|
| 133 |
+ if len(changes) != 0 {
|
|
| 134 |
+ t.Fatalf("Unexpected differences after tarUntar: %v", changes)
|
|
| 135 |
+ } |
|
| 136 |
+ } |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+func TestTarWithOptions(t *testing.T) {
|
|
| 140 |
+ origin, err := ioutil.TempDir("", "docker-test-untar-origin")
|
|
| 141 |
+ if err != nil {
|
|
| 142 |
+ t.Fatal(err) |
|
| 143 |
+ } |
|
| 144 |
+ defer os.RemoveAll(origin) |
|
| 145 |
+ if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
|
|
| 146 |
+ t.Fatal(err) |
|
| 147 |
+ } |
|
| 148 |
+ if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
|
|
| 149 |
+ t.Fatal(err) |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ cases := []struct {
|
|
| 153 |
+ opts *TarOptions |
|
| 154 |
+ numChanges int |
|
| 155 |
+ }{
|
|
| 156 |
+ {&TarOptions{Includes: []string{"1"}}, 1},
|
|
| 157 |
+ {&TarOptions{Excludes: []string{"2"}}, 1},
|
|
| 158 |
+ } |
|
| 159 |
+ for _, testCase := range cases {
|
|
| 160 |
+ changes, err := tarUntar(t, origin, testCase.opts) |
|
| 161 |
+ if err != nil {
|
|
| 162 |
+ t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err)
|
|
| 163 |
+ } |
|
| 164 |
+ if len(changes) != testCase.numChanges {
|
|
| 165 |
+ t.Errorf("Expected %d changes, got %d for %+v:",
|
|
| 166 |
+ testCase.numChanges, len(changes), testCase.opts) |
|
| 167 |
+ } |
|
| 128 | 168 |
} |
| 129 | 169 |
} |
| 130 | 170 |
|
| ... | ... |
@@ -813,7 +813,7 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
| 813 | 813 |
basePath = path.Dir(basePath) |
| 814 | 814 |
} |
| 815 | 815 |
|
| 816 |
- archive, err := archive.TarFilter(basePath, &archive.TarOptions{
|
|
| 816 |
+ archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{
|
|
| 817 | 817 |
Compression: archive.Uncompressed, |
| 818 | 818 |
Includes: filter, |
| 819 | 819 |
}) |
| ... | ... |
@@ -295,7 +295,7 @@ func (a *Driver) Put(id string) {
|
| 295 | 295 |
|
| 296 | 296 |
// Returns an archive of the contents for the id |
| 297 | 297 |
func (a *Driver) Diff(id string) (archive.Archive, error) {
|
| 298 |
- return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
| 298 |
+ return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
|
| 299 | 299 |
Compression: archive.Uncompressed, |
| 300 | 300 |
}) |
| 301 | 301 |
} |