github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/archive/archive_test.go (about) 1 package archive // import "github.com/demonoid81/moby/pkg/archive" 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "compress/gzip" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "reflect" 14 "runtime" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/demonoid81/moby/pkg/idtools" 20 "github.com/demonoid81/moby/pkg/ioutils" 21 rsystem "github.com/opencontainers/runc/libcontainer/system" 22 "gotest.tools/v3/assert" 23 is "gotest.tools/v3/assert/cmp" 24 "gotest.tools/v3/skip" 25 ) 26 27 var tmp string 28 29 func init() { 30 tmp = "/tmp/" 31 if runtime.GOOS == "windows" { 32 tmp = os.Getenv("TEMP") + `\` 33 } 34 } 35 36 var defaultArchiver = NewDefaultArchiver() 37 38 func defaultTarUntar(src, dst string) error { 39 return defaultArchiver.TarUntar(src, dst) 40 } 41 42 func defaultUntarPath(src, dst string) error { 43 return defaultArchiver.UntarPath(src, dst) 44 } 45 46 func defaultCopyFileWithTar(src, dst string) (err error) { 47 return defaultArchiver.CopyFileWithTar(src, dst) 48 } 49 50 func defaultCopyWithTar(src, dst string) error { 51 return defaultArchiver.CopyWithTar(src, dst) 52 } 53 54 func TestIsArchivePathDir(t *testing.T) { 55 cmd := exec.Command("sh", "-c", "mkdir -p /tmp/archivedir") 56 output, err := cmd.CombinedOutput() 57 if err != nil { 58 t.Fatalf("Fail to create an archive file for test : %s.", output) 59 } 60 if IsArchivePath(tmp + "archivedir") { 61 t.Fatalf("Incorrectly recognised directory as an archive") 62 } 63 } 64 65 func TestIsArchivePathInvalidFile(t *testing.T) { 66 cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1024 count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") 67 output, err := cmd.CombinedOutput() 68 if err != nil { 69 t.Fatalf("Fail to create an archive file for test : %s.", output) 70 } 71 if IsArchivePath(tmp + "archive") { 72 t.Fatalf("Incorrectly recognised invalid tar path as archive") 73 } 74 if IsArchivePath(tmp + "archive.gz") { 75 t.Fatalf("Incorrectly recognised invalid compressed tar path as archive") 76 } 77 } 78 79 func TestIsArchivePathTar(t *testing.T) { 80 whichTar := "tar" 81 cmdStr := fmt.Sprintf("touch /tmp/archivedata && %s -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz", whichTar) 82 cmd := exec.Command("sh", "-c", cmdStr) 83 output, err := cmd.CombinedOutput() 84 if err != nil { 85 t.Fatalf("Fail to create an archive file for test : %s.", output) 86 } 87 if !IsArchivePath(tmp + "/archive") { 88 t.Fatalf("Did not recognise valid tar path as archive") 89 } 90 if !IsArchivePath(tmp + "archive.gz") { 91 t.Fatalf("Did not recognise valid compressed tar path as archive") 92 } 93 } 94 95 func testDecompressStream(t *testing.T, ext, compressCommand string) io.Reader { 96 cmd := exec.Command("sh", "-c", 97 fmt.Sprintf("touch /tmp/archive && %s /tmp/archive", compressCommand)) 98 output, err := cmd.CombinedOutput() 99 if err != nil { 100 t.Fatalf("Failed to create an archive file for test : %s.", output) 101 } 102 filename := "archive." + ext 103 archive, err := os.Open(tmp + filename) 104 if err != nil { 105 t.Fatalf("Failed to open file %s: %v", filename, err) 106 } 107 defer archive.Close() 108 109 r, err := DecompressStream(archive) 110 if err != nil { 111 t.Fatalf("Failed to decompress %s: %v", filename, err) 112 } 113 if _, err = ioutil.ReadAll(r); err != nil { 114 t.Fatalf("Failed to read the decompressed stream: %v ", err) 115 } 116 if err = r.Close(); err != nil { 117 t.Fatalf("Failed to close the decompressed stream: %v ", err) 118 } 119 120 return r 121 } 122 123 func TestDecompressStreamGzip(t *testing.T) { 124 testDecompressStream(t, "gz", "gzip -f") 125 } 126 127 func TestDecompressStreamBzip2(t *testing.T) { 128 testDecompressStream(t, "bz2", "bzip2 -f") 129 } 130 131 func TestDecompressStreamXz(t *testing.T) { 132 if runtime.GOOS == "windows" { 133 t.Skip("Xz not present in msys2") 134 } 135 testDecompressStream(t, "xz", "xz -f") 136 } 137 138 func TestCompressStreamXzUnsupported(t *testing.T) { 139 dest, err := os.Create(tmp + "dest") 140 if err != nil { 141 t.Fatalf("Fail to create the destination file") 142 } 143 defer dest.Close() 144 145 _, err = CompressStream(dest, Xz) 146 if err == nil { 147 t.Fatalf("Should fail as xz is unsupported for compression format.") 148 } 149 } 150 151 func TestCompressStreamBzip2Unsupported(t *testing.T) { 152 dest, err := os.Create(tmp + "dest") 153 if err != nil { 154 t.Fatalf("Fail to create the destination file") 155 } 156 defer dest.Close() 157 158 _, err = CompressStream(dest, Bzip2) 159 if err == nil { 160 t.Fatalf("Should fail as bzip2 is unsupported for compression format.") 161 } 162 } 163 164 func TestCompressStreamInvalid(t *testing.T) { 165 dest, err := os.Create(tmp + "dest") 166 if err != nil { 167 t.Fatalf("Fail to create the destination file") 168 } 169 defer dest.Close() 170 171 _, err = CompressStream(dest, -1) 172 if err == nil { 173 t.Fatalf("Should fail as xz is unsupported for compression format.") 174 } 175 } 176 177 func TestExtensionInvalid(t *testing.T) { 178 compression := Compression(-1) 179 output := compression.Extension() 180 if output != "" { 181 t.Fatalf("The extension of an invalid compression should be an empty string.") 182 } 183 } 184 185 func TestExtensionUncompressed(t *testing.T) { 186 compression := Uncompressed 187 output := compression.Extension() 188 if output != "tar" { 189 t.Fatalf("The extension of an uncompressed archive should be 'tar'.") 190 } 191 } 192 func TestExtensionBzip2(t *testing.T) { 193 compression := Bzip2 194 output := compression.Extension() 195 if output != "tar.bz2" { 196 t.Fatalf("The extension of a bzip2 archive should be 'tar.bz2'") 197 } 198 } 199 func TestExtensionGzip(t *testing.T) { 200 compression := Gzip 201 output := compression.Extension() 202 if output != "tar.gz" { 203 t.Fatalf("The extension of a gzip archive should be 'tar.gz'") 204 } 205 } 206 func TestExtensionXz(t *testing.T) { 207 compression := Xz 208 output := compression.Extension() 209 if output != "tar.xz" { 210 t.Fatalf("The extension of a xz archive should be 'tar.xz'") 211 } 212 } 213 214 func TestCmdStreamLargeStderr(t *testing.T) { 215 cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello") 216 out, err := cmdStream(cmd, nil) 217 if err != nil { 218 t.Fatalf("Failed to start command: %s", err) 219 } 220 errCh := make(chan error, 1) 221 go func() { 222 _, err := io.Copy(ioutil.Discard, out) 223 errCh <- err 224 }() 225 select { 226 case err := <-errCh: 227 if err != nil { 228 t.Fatalf("Command should not have failed (err=%.100s...)", err) 229 } 230 case <-time.After(5 * time.Second): 231 t.Fatalf("Command did not complete in 5 seconds; probable deadlock") 232 } 233 } 234 235 func TestCmdStreamBad(t *testing.T) { 236 // TODO Windows: Figure out why this is failing in CI but not locally 237 if runtime.GOOS == "windows" { 238 t.Skip("Failing on Windows CI machines") 239 } 240 badCmd := exec.Command("sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1") 241 out, err := cmdStream(badCmd, nil) 242 if err != nil { 243 t.Fatalf("Failed to start command: %s", err) 244 } 245 if output, err := ioutil.ReadAll(out); err == nil { 246 t.Fatalf("Command should have failed") 247 } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" { 248 t.Fatalf("Wrong error value (%s)", err) 249 } else if s := string(output); s != "hello\n" { 250 t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) 251 } 252 } 253 254 func TestCmdStreamGood(t *testing.T) { 255 cmd := exec.Command("sh", "-c", "echo hello; exit 0") 256 out, err := cmdStream(cmd, nil) 257 if err != nil { 258 t.Fatal(err) 259 } 260 if output, err := ioutil.ReadAll(out); err != nil { 261 t.Fatalf("Command should not have failed (err=%s)", err) 262 } else if s := string(output); s != "hello\n" { 263 t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) 264 } 265 } 266 267 func TestUntarPathWithInvalidDest(t *testing.T) { 268 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 269 assert.NilError(t, err) 270 defer os.RemoveAll(tempFolder) 271 invalidDestFolder := filepath.Join(tempFolder, "invalidDest") 272 // Create a src file 273 srcFile := filepath.Join(tempFolder, "src") 274 tarFile := filepath.Join(tempFolder, "src.tar") 275 os.Create(srcFile) 276 os.Create(invalidDestFolder) // being a file (not dir) should cause an error 277 278 // Translate back to Unix semantics as next exec.Command is run under sh 279 srcFileU := srcFile 280 tarFileU := tarFile 281 if runtime.GOOS == "windows" { 282 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 283 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 284 } 285 286 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 287 _, err = cmd.CombinedOutput() 288 assert.NilError(t, err) 289 290 err = defaultUntarPath(tarFile, invalidDestFolder) 291 if err == nil { 292 t.Fatalf("UntarPath with invalid destination path should throw an error.") 293 } 294 } 295 296 func TestUntarPathWithInvalidSrc(t *testing.T) { 297 dest, err := ioutil.TempDir("", "docker-archive-test") 298 if err != nil { 299 t.Fatalf("Fail to create the destination file") 300 } 301 defer os.RemoveAll(dest) 302 err = defaultUntarPath("/invalid/path", dest) 303 if err == nil { 304 t.Fatalf("UntarPath with invalid src path should throw an error.") 305 } 306 } 307 308 func TestUntarPath(t *testing.T) { 309 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 310 tmpFolder, err := ioutil.TempDir("", "docker-archive-test") 311 assert.NilError(t, err) 312 defer os.RemoveAll(tmpFolder) 313 srcFile := filepath.Join(tmpFolder, "src") 314 tarFile := filepath.Join(tmpFolder, "src.tar") 315 os.Create(filepath.Join(tmpFolder, "src")) 316 317 destFolder := filepath.Join(tmpFolder, "dest") 318 err = os.MkdirAll(destFolder, 0740) 319 if err != nil { 320 t.Fatalf("Fail to create the destination file") 321 } 322 323 // Translate back to Unix semantics as next exec.Command is run under sh 324 srcFileU := srcFile 325 tarFileU := tarFile 326 if runtime.GOOS == "windows" { 327 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 328 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 329 } 330 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 331 _, err = cmd.CombinedOutput() 332 assert.NilError(t, err) 333 334 err = defaultUntarPath(tarFile, destFolder) 335 if err != nil { 336 t.Fatalf("UntarPath shouldn't throw an error, %s.", err) 337 } 338 expectedFile := filepath.Join(destFolder, srcFileU) 339 _, err = os.Stat(expectedFile) 340 if err != nil { 341 t.Fatalf("Destination folder should contain the source file but did not.") 342 } 343 } 344 345 // Do the same test as above but with the destination as file, it should fail 346 func TestUntarPathWithDestinationFile(t *testing.T) { 347 tmpFolder, err := ioutil.TempDir("", "docker-archive-test") 348 if err != nil { 349 t.Fatal(err) 350 } 351 defer os.RemoveAll(tmpFolder) 352 srcFile := filepath.Join(tmpFolder, "src") 353 tarFile := filepath.Join(tmpFolder, "src.tar") 354 os.Create(filepath.Join(tmpFolder, "src")) 355 356 // Translate back to Unix semantics as next exec.Command is run under sh 357 srcFileU := srcFile 358 tarFileU := tarFile 359 if runtime.GOOS == "windows" { 360 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 361 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 362 } 363 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 364 _, err = cmd.CombinedOutput() 365 if err != nil { 366 t.Fatal(err) 367 } 368 destFile := filepath.Join(tmpFolder, "dest") 369 _, err = os.Create(destFile) 370 if err != nil { 371 t.Fatalf("Fail to create the destination file") 372 } 373 err = defaultUntarPath(tarFile, destFile) 374 if err == nil { 375 t.Fatalf("UntarPath should throw an error if the destination if a file") 376 } 377 } 378 379 // Do the same test as above but with the destination folder already exists 380 // and the destination file is a directory 381 // It's working, see https://github.com/demonoid81/moby/issues/10040 382 func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) { 383 tmpFolder, err := ioutil.TempDir("", "docker-archive-test") 384 if err != nil { 385 t.Fatal(err) 386 } 387 defer os.RemoveAll(tmpFolder) 388 srcFile := filepath.Join(tmpFolder, "src") 389 tarFile := filepath.Join(tmpFolder, "src.tar") 390 os.Create(srcFile) 391 392 // Translate back to Unix semantics as next exec.Command is run under sh 393 srcFileU := srcFile 394 tarFileU := tarFile 395 if runtime.GOOS == "windows" { 396 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 397 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 398 } 399 400 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 401 _, err = cmd.CombinedOutput() 402 if err != nil { 403 t.Fatal(err) 404 } 405 destFolder := filepath.Join(tmpFolder, "dest") 406 err = os.MkdirAll(destFolder, 0740) 407 if err != nil { 408 t.Fatalf("Fail to create the destination folder") 409 } 410 // Let's create a folder that will has the same path as the extracted file (from tar) 411 destSrcFileAsFolder := filepath.Join(destFolder, srcFileU) 412 err = os.MkdirAll(destSrcFileAsFolder, 0740) 413 if err != nil { 414 t.Fatal(err) 415 } 416 err = defaultUntarPath(tarFile, destFolder) 417 if err != nil { 418 t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder") 419 } 420 } 421 422 func TestCopyWithTarInvalidSrc(t *testing.T) { 423 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 424 if err != nil { 425 t.Fatal(nil) 426 } 427 destFolder := filepath.Join(tempFolder, "dest") 428 invalidSrc := filepath.Join(tempFolder, "doesnotexists") 429 err = os.MkdirAll(destFolder, 0740) 430 if err != nil { 431 t.Fatal(err) 432 } 433 err = defaultCopyWithTar(invalidSrc, destFolder) 434 if err == nil { 435 t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") 436 } 437 } 438 439 func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) { 440 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 441 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 442 if err != nil { 443 t.Fatal(nil) 444 } 445 srcFolder := filepath.Join(tempFolder, "src") 446 inexistentDestFolder := filepath.Join(tempFolder, "doesnotexists") 447 err = os.MkdirAll(srcFolder, 0740) 448 if err != nil { 449 t.Fatal(err) 450 } 451 err = defaultCopyWithTar(srcFolder, inexistentDestFolder) 452 if err != nil { 453 t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") 454 } 455 _, err = os.Stat(inexistentDestFolder) 456 if err != nil { 457 t.Fatalf("CopyWithTar with an inexistent folder should create it.") 458 } 459 } 460 461 // Test CopyWithTar with a file as src 462 func TestCopyWithTarSrcFile(t *testing.T) { 463 folder, err := ioutil.TempDir("", "docker-archive-test") 464 if err != nil { 465 t.Fatal(err) 466 } 467 defer os.RemoveAll(folder) 468 dest := filepath.Join(folder, "dest") 469 srcFolder := filepath.Join(folder, "src") 470 src := filepath.Join(folder, filepath.Join("src", "src")) 471 err = os.MkdirAll(srcFolder, 0740) 472 if err != nil { 473 t.Fatal(err) 474 } 475 err = os.MkdirAll(dest, 0740) 476 if err != nil { 477 t.Fatal(err) 478 } 479 ioutil.WriteFile(src, []byte("content"), 0777) 480 err = defaultCopyWithTar(src, dest) 481 if err != nil { 482 t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) 483 } 484 _, err = os.Stat(dest) 485 // FIXME Check the content 486 if err != nil { 487 t.Fatalf("Destination file should be the same as the source.") 488 } 489 } 490 491 // Test CopyWithTar with a folder as src 492 func TestCopyWithTarSrcFolder(t *testing.T) { 493 folder, err := ioutil.TempDir("", "docker-archive-test") 494 if err != nil { 495 t.Fatal(err) 496 } 497 defer os.RemoveAll(folder) 498 dest := filepath.Join(folder, "dest") 499 src := filepath.Join(folder, filepath.Join("src", "folder")) 500 err = os.MkdirAll(src, 0740) 501 if err != nil { 502 t.Fatal(err) 503 } 504 err = os.MkdirAll(dest, 0740) 505 if err != nil { 506 t.Fatal(err) 507 } 508 ioutil.WriteFile(filepath.Join(src, "file"), []byte("content"), 0777) 509 err = defaultCopyWithTar(src, dest) 510 if err != nil { 511 t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) 512 } 513 _, err = os.Stat(dest) 514 // FIXME Check the content (the file inside) 515 if err != nil { 516 t.Fatalf("Destination folder should contain the source file but did not.") 517 } 518 } 519 520 func TestCopyFileWithTarInvalidSrc(t *testing.T) { 521 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 522 if err != nil { 523 t.Fatal(err) 524 } 525 defer os.RemoveAll(tempFolder) 526 destFolder := filepath.Join(tempFolder, "dest") 527 err = os.MkdirAll(destFolder, 0740) 528 if err != nil { 529 t.Fatal(err) 530 } 531 invalidFile := filepath.Join(tempFolder, "doesnotexists") 532 err = defaultCopyFileWithTar(invalidFile, destFolder) 533 if err == nil { 534 t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") 535 } 536 } 537 538 func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) { 539 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 540 if err != nil { 541 t.Fatal(nil) 542 } 543 defer os.RemoveAll(tempFolder) 544 srcFile := filepath.Join(tempFolder, "src") 545 inexistentDestFolder := filepath.Join(tempFolder, "doesnotexists") 546 _, err = os.Create(srcFile) 547 if err != nil { 548 t.Fatal(err) 549 } 550 err = defaultCopyFileWithTar(srcFile, inexistentDestFolder) 551 if err != nil { 552 t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") 553 } 554 _, err = os.Stat(inexistentDestFolder) 555 if err != nil { 556 t.Fatalf("CopyWithTar with an inexistent folder should create it.") 557 } 558 // FIXME Test the src file and content 559 } 560 561 func TestCopyFileWithTarSrcFolder(t *testing.T) { 562 folder, err := ioutil.TempDir("", "docker-archive-copyfilewithtar-test") 563 if err != nil { 564 t.Fatal(err) 565 } 566 defer os.RemoveAll(folder) 567 dest := filepath.Join(folder, "dest") 568 src := filepath.Join(folder, "srcfolder") 569 err = os.MkdirAll(src, 0740) 570 if err != nil { 571 t.Fatal(err) 572 } 573 err = os.MkdirAll(dest, 0740) 574 if err != nil { 575 t.Fatal(err) 576 } 577 err = defaultCopyFileWithTar(src, dest) 578 if err == nil { 579 t.Fatalf("CopyFileWithTar should throw an error with a folder.") 580 } 581 } 582 583 func TestCopyFileWithTarSrcFile(t *testing.T) { 584 folder, err := ioutil.TempDir("", "docker-archive-test") 585 if err != nil { 586 t.Fatal(err) 587 } 588 defer os.RemoveAll(folder) 589 dest := filepath.Join(folder, "dest") 590 srcFolder := filepath.Join(folder, "src") 591 src := filepath.Join(folder, filepath.Join("src", "src")) 592 err = os.MkdirAll(srcFolder, 0740) 593 if err != nil { 594 t.Fatal(err) 595 } 596 err = os.MkdirAll(dest, 0740) 597 if err != nil { 598 t.Fatal(err) 599 } 600 ioutil.WriteFile(src, []byte("content"), 0777) 601 err = defaultCopyWithTar(src, dest+"/") 602 if err != nil { 603 t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err) 604 } 605 _, err = os.Stat(dest) 606 if err != nil { 607 t.Fatalf("Destination folder should contain the source file but did not.") 608 } 609 } 610 611 func TestTarFiles(t *testing.T) { 612 // try without hardlinks 613 if err := checkNoChanges(1000, false); err != nil { 614 t.Fatal(err) 615 } 616 // try with hardlinks 617 if err := checkNoChanges(1000, true); err != nil { 618 t.Fatal(err) 619 } 620 } 621 622 func checkNoChanges(fileNum int, hardlinks bool) error { 623 srcDir, err := ioutil.TempDir("", "docker-test-srcDir") 624 if err != nil { 625 return err 626 } 627 defer os.RemoveAll(srcDir) 628 629 destDir, err := ioutil.TempDir("", "docker-test-destDir") 630 if err != nil { 631 return err 632 } 633 defer os.RemoveAll(destDir) 634 635 _, err = prepareUntarSourceDirectory(fileNum, srcDir, hardlinks) 636 if err != nil { 637 return err 638 } 639 640 err = defaultTarUntar(srcDir, destDir) 641 if err != nil { 642 return err 643 } 644 645 changes, err := ChangesDirs(destDir, srcDir) 646 if err != nil { 647 return err 648 } 649 if len(changes) > 0 { 650 return fmt.Errorf("with %d files and %v hardlinks: expected 0 changes, got %d", fileNum, hardlinks, len(changes)) 651 } 652 return nil 653 } 654 655 func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) { 656 archive, err := TarWithOptions(origin, options) 657 if err != nil { 658 t.Fatal(err) 659 } 660 defer archive.Close() 661 662 buf := make([]byte, 10) 663 if _, err := archive.Read(buf); err != nil { 664 return nil, err 665 } 666 wrap := io.MultiReader(bytes.NewReader(buf), archive) 667 668 detectedCompression := DetectCompression(buf) 669 compression := options.Compression 670 if detectedCompression.Extension() != compression.Extension() { 671 return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension()) 672 } 673 674 tmp, err := ioutil.TempDir("", "docker-test-untar") 675 if err != nil { 676 return nil, err 677 } 678 defer os.RemoveAll(tmp) 679 if err := Untar(wrap, tmp, nil); err != nil { 680 return nil, err 681 } 682 if _, err := os.Stat(tmp); err != nil { 683 return nil, err 684 } 685 686 return ChangesDirs(origin, tmp) 687 } 688 689 func TestTarUntar(t *testing.T) { 690 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 691 if err != nil { 692 t.Fatal(err) 693 } 694 defer os.RemoveAll(origin) 695 if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { 696 t.Fatal(err) 697 } 698 if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { 699 t.Fatal(err) 700 } 701 if err := ioutil.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil { 702 t.Fatal(err) 703 } 704 705 for _, c := range []Compression{ 706 Uncompressed, 707 Gzip, 708 } { 709 changes, err := tarUntar(t, origin, &TarOptions{ 710 Compression: c, 711 ExcludePatterns: []string{"3"}, 712 }) 713 714 if err != nil { 715 t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err) 716 } 717 718 if len(changes) != 1 || changes[0].Path != string(filepath.Separator)+"3" { 719 t.Fatalf("Unexpected differences after tarUntar: %v", changes) 720 } 721 } 722 } 723 724 func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) { 725 origin, err := ioutil.TempDir("", "docker-test-tar-chown-opt") 726 assert.NilError(t, err) 727 728 defer os.RemoveAll(origin) 729 filePath := filepath.Join(origin, "1") 730 err = ioutil.WriteFile(filePath, []byte("hello world"), 0700) 731 assert.NilError(t, err) 732 733 idMaps := []idtools.IDMap{ 734 0: { 735 ContainerID: 0, 736 HostID: 0, 737 Size: 65536, 738 }, 739 1: { 740 ContainerID: 0, 741 HostID: 100000, 742 Size: 65536, 743 }, 744 } 745 746 cases := []struct { 747 opts *TarOptions 748 expectedUID int 749 expectedGID int 750 }{ 751 {&TarOptions{ChownOpts: &idtools.Identity{UID: 1337, GID: 42}}, 1337, 42}, 752 {&TarOptions{ChownOpts: &idtools.Identity{UID: 100001, GID: 100001}, UIDMaps: idMaps, GIDMaps: idMaps}, 100001, 100001}, 753 {&TarOptions{ChownOpts: &idtools.Identity{UID: 0, GID: 0}, NoLchown: false}, 0, 0}, 754 {&TarOptions{ChownOpts: &idtools.Identity{UID: 1, GID: 1}, NoLchown: true}, 1, 1}, 755 {&TarOptions{ChownOpts: &idtools.Identity{UID: 1000, GID: 1000}, NoLchown: true}, 1000, 1000}, 756 } 757 for _, testCase := range cases { 758 reader, err := TarWithOptions(filePath, testCase.opts) 759 assert.NilError(t, err) 760 tr := tar.NewReader(reader) 761 defer reader.Close() 762 for { 763 hdr, err := tr.Next() 764 if err == io.EOF { 765 // end of tar archive 766 break 767 } 768 assert.NilError(t, err) 769 assert.Check(t, is.Equal(hdr.Uid, testCase.expectedUID), "Uid equals expected value") 770 assert.Check(t, is.Equal(hdr.Gid, testCase.expectedGID), "Gid equals expected value") 771 } 772 } 773 } 774 775 func TestTarWithOptions(t *testing.T) { 776 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 777 if err != nil { 778 t.Fatal(err) 779 } 780 if _, err := ioutil.TempDir(origin, "folder"); err != nil { 781 t.Fatal(err) 782 } 783 defer os.RemoveAll(origin) 784 if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { 785 t.Fatal(err) 786 } 787 if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { 788 t.Fatal(err) 789 } 790 791 cases := []struct { 792 opts *TarOptions 793 numChanges int 794 }{ 795 {&TarOptions{IncludeFiles: []string{"1"}}, 2}, 796 {&TarOptions{ExcludePatterns: []string{"2"}}, 1}, 797 {&TarOptions{ExcludePatterns: []string{"1", "folder*"}}, 2}, 798 {&TarOptions{IncludeFiles: []string{"1", "1"}}, 2}, 799 {&TarOptions{IncludeFiles: []string{"1"}, RebaseNames: map[string]string{"1": "test"}}, 4}, 800 } 801 for _, testCase := range cases { 802 changes, err := tarUntar(t, origin, testCase.opts) 803 if err != nil { 804 t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err) 805 } 806 if len(changes) != testCase.numChanges { 807 t.Errorf("Expected %d changes, got %d for %+v:", 808 testCase.numChanges, len(changes), testCase.opts) 809 } 810 } 811 } 812 813 // Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz 814 // use PAX Global Extended Headers. 815 // Failing prevents the archives from being uncompressed during ADD 816 func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) { 817 hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader} 818 tmpDir, err := ioutil.TempDir("", "docker-test-archive-pax-test") 819 if err != nil { 820 t.Fatal(err) 821 } 822 defer os.RemoveAll(tmpDir) 823 err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil, false) 824 if err != nil { 825 t.Fatal(err) 826 } 827 } 828 829 // Some tar have both GNU specific (huge uid) and Ustar specific (long name) things. 830 // Not supposed to happen (should use PAX instead of Ustar for long name) but it does and it should still work. 831 func TestUntarUstarGnuConflict(t *testing.T) { 832 f, err := os.Open("testdata/broken.tar") 833 if err != nil { 834 t.Fatal(err) 835 } 836 defer f.Close() 837 838 found := false 839 tr := tar.NewReader(f) 840 // Iterate through the files in the archive. 841 for { 842 hdr, err := tr.Next() 843 if err == io.EOF { 844 // end of tar archive 845 break 846 } 847 if err != nil { 848 t.Fatal(err) 849 } 850 if hdr.Name == "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm" { 851 found = true 852 break 853 } 854 } 855 if !found { 856 t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm") 857 } 858 } 859 860 func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) { 861 fileData := []byte("fooo") 862 for n := 0; n < numberOfFiles; n++ { 863 fileName := fmt.Sprintf("file-%d", n) 864 if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { 865 return 0, err 866 } 867 if makeLinks { 868 if err := os.Link(filepath.Join(targetPath, fileName), filepath.Join(targetPath, fileName+"-link")); err != nil { 869 return 0, err 870 } 871 } 872 } 873 totalSize := numberOfFiles * len(fileData) 874 return totalSize, nil 875 } 876 877 func BenchmarkTarUntar(b *testing.B) { 878 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 879 if err != nil { 880 b.Fatal(err) 881 } 882 tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") 883 if err != nil { 884 b.Fatal(err) 885 } 886 target := filepath.Join(tempDir, "dest") 887 n, err := prepareUntarSourceDirectory(100, origin, false) 888 if err != nil { 889 b.Fatal(err) 890 } 891 defer os.RemoveAll(origin) 892 defer os.RemoveAll(tempDir) 893 894 b.ResetTimer() 895 b.SetBytes(int64(n)) 896 for n := 0; n < b.N; n++ { 897 err := defaultTarUntar(origin, target) 898 if err != nil { 899 b.Fatal(err) 900 } 901 os.RemoveAll(target) 902 } 903 } 904 905 func BenchmarkTarUntarWithLinks(b *testing.B) { 906 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 907 if err != nil { 908 b.Fatal(err) 909 } 910 tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") 911 if err != nil { 912 b.Fatal(err) 913 } 914 target := filepath.Join(tempDir, "dest") 915 n, err := prepareUntarSourceDirectory(100, origin, true) 916 if err != nil { 917 b.Fatal(err) 918 } 919 defer os.RemoveAll(origin) 920 defer os.RemoveAll(tempDir) 921 922 b.ResetTimer() 923 b.SetBytes(int64(n)) 924 for n := 0; n < b.N; n++ { 925 err := defaultTarUntar(origin, target) 926 if err != nil { 927 b.Fatal(err) 928 } 929 os.RemoveAll(target) 930 } 931 } 932 933 func TestUntarInvalidFilenames(t *testing.T) { 934 for i, headers := range [][]*tar.Header{ 935 { 936 { 937 Name: "../victim/dotdot", 938 Typeflag: tar.TypeReg, 939 Mode: 0644, 940 }, 941 }, 942 { 943 { 944 // Note the leading slash 945 Name: "/../victim/slash-dotdot", 946 Typeflag: tar.TypeReg, 947 Mode: 0644, 948 }, 949 }, 950 } { 951 if err := testBreakout("untar", "docker-TestUntarInvalidFilenames", headers); err != nil { 952 t.Fatalf("i=%d. %v", i, err) 953 } 954 } 955 } 956 957 func TestUntarHardlinkToSymlink(t *testing.T) { 958 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 959 for i, headers := range [][]*tar.Header{ 960 { 961 { 962 Name: "symlink1", 963 Typeflag: tar.TypeSymlink, 964 Linkname: "regfile", 965 Mode: 0644, 966 }, 967 { 968 Name: "symlink2", 969 Typeflag: tar.TypeLink, 970 Linkname: "symlink1", 971 Mode: 0644, 972 }, 973 { 974 Name: "regfile", 975 Typeflag: tar.TypeReg, 976 Mode: 0644, 977 }, 978 }, 979 } { 980 if err := testBreakout("untar", "docker-TestUntarHardlinkToSymlink", headers); err != nil { 981 t.Fatalf("i=%d. %v", i, err) 982 } 983 } 984 } 985 986 func TestUntarInvalidHardlink(t *testing.T) { 987 for i, headers := range [][]*tar.Header{ 988 { // try reading victim/hello (../) 989 { 990 Name: "dotdot", 991 Typeflag: tar.TypeLink, 992 Linkname: "../victim/hello", 993 Mode: 0644, 994 }, 995 }, 996 { // try reading victim/hello (/../) 997 { 998 Name: "slash-dotdot", 999 Typeflag: tar.TypeLink, 1000 // Note the leading slash 1001 Linkname: "/../victim/hello", 1002 Mode: 0644, 1003 }, 1004 }, 1005 { // try writing victim/file 1006 { 1007 Name: "loophole-victim", 1008 Typeflag: tar.TypeLink, 1009 Linkname: "../victim", 1010 Mode: 0755, 1011 }, 1012 { 1013 Name: "loophole-victim/file", 1014 Typeflag: tar.TypeReg, 1015 Mode: 0644, 1016 }, 1017 }, 1018 { // try reading victim/hello (hardlink, symlink) 1019 { 1020 Name: "loophole-victim", 1021 Typeflag: tar.TypeLink, 1022 Linkname: "../victim", 1023 Mode: 0755, 1024 }, 1025 { 1026 Name: "symlink", 1027 Typeflag: tar.TypeSymlink, 1028 Linkname: "loophole-victim/hello", 1029 Mode: 0644, 1030 }, 1031 }, 1032 { // Try reading victim/hello (hardlink, hardlink) 1033 { 1034 Name: "loophole-victim", 1035 Typeflag: tar.TypeLink, 1036 Linkname: "../victim", 1037 Mode: 0755, 1038 }, 1039 { 1040 Name: "hardlink", 1041 Typeflag: tar.TypeLink, 1042 Linkname: "loophole-victim/hello", 1043 Mode: 0644, 1044 }, 1045 }, 1046 { // Try removing victim directory (hardlink) 1047 { 1048 Name: "loophole-victim", 1049 Typeflag: tar.TypeLink, 1050 Linkname: "../victim", 1051 Mode: 0755, 1052 }, 1053 { 1054 Name: "loophole-victim", 1055 Typeflag: tar.TypeReg, 1056 Mode: 0644, 1057 }, 1058 }, 1059 } { 1060 if err := testBreakout("untar", "docker-TestUntarInvalidHardlink", headers); err != nil { 1061 t.Fatalf("i=%d. %v", i, err) 1062 } 1063 } 1064 } 1065 1066 func TestUntarInvalidSymlink(t *testing.T) { 1067 for i, headers := range [][]*tar.Header{ 1068 { // try reading victim/hello (../) 1069 { 1070 Name: "dotdot", 1071 Typeflag: tar.TypeSymlink, 1072 Linkname: "../victim/hello", 1073 Mode: 0644, 1074 }, 1075 }, 1076 { // try reading victim/hello (/../) 1077 { 1078 Name: "slash-dotdot", 1079 Typeflag: tar.TypeSymlink, 1080 // Note the leading slash 1081 Linkname: "/../victim/hello", 1082 Mode: 0644, 1083 }, 1084 }, 1085 { // try writing victim/file 1086 { 1087 Name: "loophole-victim", 1088 Typeflag: tar.TypeSymlink, 1089 Linkname: "../victim", 1090 Mode: 0755, 1091 }, 1092 { 1093 Name: "loophole-victim/file", 1094 Typeflag: tar.TypeReg, 1095 Mode: 0644, 1096 }, 1097 }, 1098 { // try reading victim/hello (symlink, symlink) 1099 { 1100 Name: "loophole-victim", 1101 Typeflag: tar.TypeSymlink, 1102 Linkname: "../victim", 1103 Mode: 0755, 1104 }, 1105 { 1106 Name: "symlink", 1107 Typeflag: tar.TypeSymlink, 1108 Linkname: "loophole-victim/hello", 1109 Mode: 0644, 1110 }, 1111 }, 1112 { // try reading victim/hello (symlink, hardlink) 1113 { 1114 Name: "loophole-victim", 1115 Typeflag: tar.TypeSymlink, 1116 Linkname: "../victim", 1117 Mode: 0755, 1118 }, 1119 { 1120 Name: "hardlink", 1121 Typeflag: tar.TypeLink, 1122 Linkname: "loophole-victim/hello", 1123 Mode: 0644, 1124 }, 1125 }, 1126 { // try removing victim directory (symlink) 1127 { 1128 Name: "loophole-victim", 1129 Typeflag: tar.TypeSymlink, 1130 Linkname: "../victim", 1131 Mode: 0755, 1132 }, 1133 { 1134 Name: "loophole-victim", 1135 Typeflag: tar.TypeReg, 1136 Mode: 0644, 1137 }, 1138 }, 1139 { // try writing to victim/newdir/newfile with a symlink in the path 1140 { 1141 // this header needs to be before the next one, or else there is an error 1142 Name: "dir/loophole", 1143 Typeflag: tar.TypeSymlink, 1144 Linkname: "../../victim", 1145 Mode: 0755, 1146 }, 1147 { 1148 Name: "dir/loophole/newdir/newfile", 1149 Typeflag: tar.TypeReg, 1150 Mode: 0644, 1151 }, 1152 }, 1153 } { 1154 if err := testBreakout("untar", "docker-TestUntarInvalidSymlink", headers); err != nil { 1155 t.Fatalf("i=%d. %v", i, err) 1156 } 1157 } 1158 } 1159 1160 func TestTempArchiveCloseMultipleTimes(t *testing.T) { 1161 reader := ioutil.NopCloser(strings.NewReader("hello")) 1162 tempArchive, err := NewTempArchive(reader, "") 1163 assert.NilError(t, err) 1164 buf := make([]byte, 10) 1165 n, err := tempArchive.Read(buf) 1166 assert.NilError(t, err) 1167 if n != 5 { 1168 t.Fatalf("Expected to read 5 bytes. Read %d instead", n) 1169 } 1170 for i := 0; i < 3; i++ { 1171 if err = tempArchive.Close(); err != nil { 1172 t.Fatalf("i=%d. Unexpected error closing temp archive: %v", i, err) 1173 } 1174 } 1175 } 1176 1177 func TestReplaceFileTarWrapper(t *testing.T) { 1178 filesInArchive := 20 1179 testcases := []struct { 1180 doc string 1181 filename string 1182 modifier TarModifierFunc 1183 expected string 1184 fileCount int 1185 }{ 1186 { 1187 doc: "Modifier creates a new file", 1188 filename: "newfile", 1189 modifier: createModifier(t), 1190 expected: "the new content", 1191 fileCount: filesInArchive + 1, 1192 }, 1193 { 1194 doc: "Modifier replaces a file", 1195 filename: "file-2", 1196 modifier: createOrReplaceModifier, 1197 expected: "the new content", 1198 fileCount: filesInArchive, 1199 }, 1200 { 1201 doc: "Modifier replaces the last file", 1202 filename: fmt.Sprintf("file-%d", filesInArchive-1), 1203 modifier: createOrReplaceModifier, 1204 expected: "the new content", 1205 fileCount: filesInArchive, 1206 }, 1207 { 1208 doc: "Modifier appends to a file", 1209 filename: "file-3", 1210 modifier: appendModifier, 1211 expected: "fooo\nnext line", 1212 fileCount: filesInArchive, 1213 }, 1214 } 1215 1216 for _, testcase := range testcases { 1217 sourceArchive, cleanup := buildSourceArchive(t, filesInArchive) 1218 defer cleanup() 1219 1220 resultArchive := ReplaceFileTarWrapper( 1221 sourceArchive, 1222 map[string]TarModifierFunc{testcase.filename: testcase.modifier}) 1223 1224 actual := readFileFromArchive(t, resultArchive, testcase.filename, testcase.fileCount, testcase.doc) 1225 assert.Check(t, is.Equal(testcase.expected, actual), testcase.doc) 1226 } 1227 } 1228 1229 // TestPrefixHeaderReadable tests that files that could be created with the 1230 // version of this package that was built with <=go17 are still readable. 1231 func TestPrefixHeaderReadable(t *testing.T) { 1232 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 1233 skip.If(t, rsystem.RunningInUserNS(), "skipping test that requires more than 010000000 UIDs, which is unlikely to be satisfied when running in userns") 1234 // https://gist.github.com/stevvooe/e2a790ad4e97425896206c0816e1a882#file-out-go 1235 var testFile = []byte("\x1f\x8b\x08\x08\x44\x21\x68\x59\x00\x03\x74\x2e\x74\x61\x72\x00\x4b\xcb\xcf\x67\xa0\x35\x30\x80\x00\x86\x06\x10\x47\x01\xc1\x37\x40\x00\x54\xb6\xb1\xa1\xa9\x99\x09\x48\x25\x1d\x40\x69\x71\x49\x62\x91\x02\xe5\x76\xa1\x79\x84\x21\x91\xd6\x80\x72\xaf\x8f\x82\x51\x30\x0a\x46\x36\x00\x00\xf0\x1c\x1e\x95\x00\x06\x00\x00") 1236 1237 tmpDir, err := ioutil.TempDir("", "prefix-test") 1238 assert.NilError(t, err) 1239 defer os.RemoveAll(tmpDir) 1240 err = Untar(bytes.NewReader(testFile), tmpDir, nil) 1241 assert.NilError(t, err) 1242 1243 baseName := "foo" 1244 pth := strings.Repeat("a", 100-len(baseName)) + "/" + baseName 1245 1246 _, err = os.Lstat(filepath.Join(tmpDir, pth)) 1247 assert.NilError(t, err) 1248 } 1249 1250 func buildSourceArchive(t *testing.T, numberOfFiles int) (io.ReadCloser, func()) { 1251 srcDir, err := ioutil.TempDir("", "docker-test-srcDir") 1252 assert.NilError(t, err) 1253 1254 _, err = prepareUntarSourceDirectory(numberOfFiles, srcDir, false) 1255 assert.NilError(t, err) 1256 1257 sourceArchive, err := TarWithOptions(srcDir, &TarOptions{}) 1258 assert.NilError(t, err) 1259 return sourceArchive, func() { 1260 os.RemoveAll(srcDir) 1261 sourceArchive.Close() 1262 } 1263 } 1264 1265 func createOrReplaceModifier(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { 1266 return &tar.Header{ 1267 Mode: 0600, 1268 Typeflag: tar.TypeReg, 1269 }, []byte("the new content"), nil 1270 } 1271 1272 func createModifier(t *testing.T) TarModifierFunc { 1273 return func(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { 1274 assert.Check(t, is.Nil(content)) 1275 return createOrReplaceModifier(path, header, content) 1276 } 1277 } 1278 1279 func appendModifier(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { 1280 buffer := bytes.Buffer{} 1281 if content != nil { 1282 if _, err := buffer.ReadFrom(content); err != nil { 1283 return nil, nil, err 1284 } 1285 } 1286 buffer.WriteString("\nnext line") 1287 return &tar.Header{Mode: 0600, Typeflag: tar.TypeReg}, buffer.Bytes(), nil 1288 } 1289 1290 func readFileFromArchive(t *testing.T, archive io.ReadCloser, name string, expectedCount int, doc string) string { 1291 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 1292 destDir, err := ioutil.TempDir("", "docker-test-destDir") 1293 assert.NilError(t, err) 1294 defer os.RemoveAll(destDir) 1295 1296 err = Untar(archive, destDir, nil) 1297 assert.NilError(t, err) 1298 1299 files, _ := ioutil.ReadDir(destDir) 1300 assert.Check(t, is.Len(files, expectedCount), doc) 1301 1302 content, err := ioutil.ReadFile(filepath.Join(destDir, name)) 1303 assert.Check(t, err) 1304 return string(content) 1305 } 1306 1307 func TestDisablePigz(t *testing.T) { 1308 _, err := exec.LookPath("unpigz") 1309 if err != nil { 1310 t.Log("Test will not check full path when Pigz not installed") 1311 } 1312 1313 os.Setenv("MOBY_DISABLE_PIGZ", "true") 1314 defer os.Unsetenv("MOBY_DISABLE_PIGZ") 1315 1316 r := testDecompressStream(t, "gz", "gzip -f") 1317 // For the bufio pool 1318 outsideReaderCloserWrapper := r.(*ioutils.ReadCloserWrapper) 1319 // For the context canceller 1320 contextReaderCloserWrapper := outsideReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) 1321 1322 assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&gzip.Reader{})) 1323 } 1324 1325 func TestPigz(t *testing.T) { 1326 r := testDecompressStream(t, "gz", "gzip -f") 1327 // For the bufio pool 1328 outsideReaderCloserWrapper := r.(*ioutils.ReadCloserWrapper) 1329 // For the context canceller 1330 contextReaderCloserWrapper := outsideReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) 1331 1332 _, err := exec.LookPath("unpigz") 1333 if err == nil { 1334 t.Log("Tested whether Pigz is used, as it installed") 1335 // For the command wait wrapper 1336 cmdWaitCloserWrapper := contextReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) 1337 assert.Equal(t, reflect.TypeOf(cmdWaitCloserWrapper.Reader), reflect.TypeOf(&io.PipeReader{})) 1338 } else { 1339 t.Log("Tested whether Pigz is not used, as it not installed") 1340 assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&gzip.Reader{})) 1341 } 1342 }