github.com/hauerwu/docker@v1.8.0-rc1/pkg/archive/copy_test.go (about) 1 package archive 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/hex" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strings" 13 "testing" 14 ) 15 16 func removeAllPaths(paths ...string) { 17 for _, path := range paths { 18 os.RemoveAll(path) 19 } 20 } 21 22 func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) { 23 var err error 24 25 if tmpDirA, err = ioutil.TempDir("", "archive-copy-test"); err != nil { 26 t.Fatal(err) 27 } 28 29 if tmpDirB, err = ioutil.TempDir("", "archive-copy-test"); err != nil { 30 t.Fatal(err) 31 } 32 33 return 34 } 35 36 func isNotDir(err error) bool { 37 return strings.Contains(err.Error(), "not a directory") 38 } 39 40 func joinTrailingSep(pathElements ...string) string { 41 joined := filepath.Join(pathElements...) 42 43 return fmt.Sprintf("%s%c", joined, filepath.Separator) 44 } 45 46 func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) { 47 t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB) 48 49 fileA, err := os.Open(filenameA) 50 if err != nil { 51 return 52 } 53 defer fileA.Close() 54 55 fileB, err := os.Open(filenameB) 56 if err != nil { 57 return 58 } 59 defer fileB.Close() 60 61 hasher := sha256.New() 62 63 if _, err = io.Copy(hasher, fileA); err != nil { 64 return 65 } 66 67 hashA := hasher.Sum(nil) 68 hasher.Reset() 69 70 if _, err = io.Copy(hasher, fileB); err != nil { 71 return 72 } 73 74 hashB := hasher.Sum(nil) 75 76 if !bytes.Equal(hashA, hashB) { 77 err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB)) 78 } 79 80 return 81 } 82 83 func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) { 84 t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir) 85 86 var changes []Change 87 88 if changes, err = ChangesDirs(newDir, oldDir); err != nil { 89 return 90 } 91 92 if len(changes) != 0 { 93 err = fmt.Errorf("expected no changes between directories, but got: %v", changes) 94 } 95 96 return 97 } 98 99 func logDirContents(t *testing.T, dirPath string) { 100 logWalkedPaths := filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { 101 if err != nil { 102 t.Errorf("stat error for path %q: %s", path, err) 103 return nil 104 } 105 106 if info.IsDir() { 107 path = joinTrailingSep(path) 108 } 109 110 t.Logf("\t%s", path) 111 112 return nil 113 }) 114 115 t.Logf("logging directory contents: %q", dirPath) 116 117 if err := filepath.Walk(dirPath, logWalkedPaths); err != nil { 118 t.Fatal(err) 119 } 120 } 121 122 func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) { 123 t.Logf("copying from %q to %q", srcPath, dstPath) 124 125 return CopyResource(srcPath, dstPath) 126 } 127 128 // Basic assumptions about SRC and DST: 129 // 1. SRC must exist. 130 // 2. If SRC ends with a trailing separator, it must be a directory. 131 // 3. DST parent directory must exist. 132 // 4. If DST exists as a file, it must not end with a trailing separator. 133 134 // First get these easy error cases out of the way. 135 136 // Test for error when SRC does not exist. 137 func TestCopyErrSrcNotExists(t *testing.T) { 138 tmpDirA, tmpDirB := getTestTempDirs(t) 139 defer removeAllPaths(tmpDirA, tmpDirB) 140 141 content, err := TarResource(filepath.Join(tmpDirA, "file1")) 142 if err == nil { 143 content.Close() 144 t.Fatal("expected IsNotExist error, but got nil instead") 145 } 146 147 if !os.IsNotExist(err) { 148 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 149 } 150 } 151 152 // Test for error when SRC ends in a trailing 153 // path separator but it exists as a file. 154 func TestCopyErrSrcNotDir(t *testing.T) { 155 tmpDirA, tmpDirB := getTestTempDirs(t) 156 defer removeAllPaths(tmpDirA, tmpDirB) 157 158 // Load A with some sample files and directories. 159 createSampleDir(t, tmpDirA) 160 161 content, err := TarResource(joinTrailingSep(tmpDirA, "file1")) 162 if err == nil { 163 content.Close() 164 t.Fatal("expected IsNotDir error, but got nil instead") 165 } 166 167 if !isNotDir(err) { 168 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 169 } 170 } 171 172 // Test for error when SRC is a valid file or directory, 173 // but the DST parent directory does not exist. 174 func TestCopyErrDstParentNotExists(t *testing.T) { 175 tmpDirA, tmpDirB := getTestTempDirs(t) 176 defer removeAllPaths(tmpDirA, tmpDirB) 177 178 // Load A with some sample files and directories. 179 createSampleDir(t, tmpDirA) 180 181 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 182 183 // Try with a file source. 184 content, err := TarResource(srcInfo.Path) 185 if err != nil { 186 t.Fatalf("unexpected error %T: %s", err, err) 187 } 188 defer content.Close() 189 190 // Copy to a file whose parent does not exist. 191 if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil { 192 t.Fatal("expected IsNotExist error, but got nil instead") 193 } 194 195 if !os.IsNotExist(err) { 196 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 197 } 198 199 // Try with a directory source. 200 srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} 201 202 content, err = TarResource(srcInfo.Path) 203 if err != nil { 204 t.Fatalf("unexpected error %T: %s", err, err) 205 } 206 defer content.Close() 207 208 // Copy to a directory whose parent does not exist. 209 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil { 210 t.Fatal("expected IsNotExist error, but got nil instead") 211 } 212 213 if !os.IsNotExist(err) { 214 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 215 } 216 } 217 218 // Test for error when DST ends in a trailing 219 // path separator but exists as a file. 220 func TestCopyErrDstNotDir(t *testing.T) { 221 tmpDirA, tmpDirB := getTestTempDirs(t) 222 defer removeAllPaths(tmpDirA, tmpDirB) 223 224 // Load A and B with some sample files and directories. 225 createSampleDir(t, tmpDirA) 226 createSampleDir(t, tmpDirB) 227 228 // Try with a file source. 229 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 230 231 content, err := TarResource(srcInfo.Path) 232 if err != nil { 233 t.Fatalf("unexpected error %T: %s", err, err) 234 } 235 defer content.Close() 236 237 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { 238 t.Fatal("expected IsNotDir error, but got nil instead") 239 } 240 241 if !isNotDir(err) { 242 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 243 } 244 245 // Try with a directory source. 246 srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} 247 248 content, err = TarResource(srcInfo.Path) 249 if err != nil { 250 t.Fatalf("unexpected error %T: %s", err, err) 251 } 252 defer content.Close() 253 254 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { 255 t.Fatal("expected IsNotDir error, but got nil instead") 256 } 257 258 if !isNotDir(err) { 259 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 260 } 261 } 262 263 // Possibilities are reduced to the remaining 10 cases: 264 // 265 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 266 // =================================================================================================== 267 // A | no | - | no | - | no | create file 268 // B | no | - | no | - | yes | error 269 // C | no | - | yes | no | - | overwrite file 270 // D | no | - | yes | yes | - | create file in dst dir 271 // E | yes | no | no | - | - | create dir, copy contents 272 // F | yes | no | yes | no | - | error 273 // G | yes | no | yes | yes | - | copy dir and contents 274 // H | yes | yes | no | - | - | create dir, copy contents 275 // I | yes | yes | yes | no | - | error 276 // J | yes | yes | yes | yes | - | copy dir contents 277 // 278 279 // A. SRC specifies a file and DST (no trailing path separator) doesn't 280 // exist. This should create a file with the name DST and copy the 281 // contents of the source file into it. 282 func TestCopyCaseA(t *testing.T) { 283 tmpDirA, tmpDirB := getTestTempDirs(t) 284 defer removeAllPaths(tmpDirA, tmpDirB) 285 286 // Load A with some sample files and directories. 287 createSampleDir(t, tmpDirA) 288 289 srcPath := filepath.Join(tmpDirA, "file1") 290 dstPath := filepath.Join(tmpDirB, "itWorks.txt") 291 292 var err error 293 294 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 295 t.Fatalf("unexpected error %T: %s", err, err) 296 } 297 298 if err = fileContentsEqual(t, srcPath, dstPath); err != nil { 299 t.Fatal(err) 300 } 301 } 302 303 // B. SRC specifies a file and DST (with trailing path separator) doesn't 304 // exist. This should cause an error because the copy operation cannot 305 // create a directory when copying a single file. 306 func TestCopyCaseB(t *testing.T) { 307 tmpDirA, tmpDirB := getTestTempDirs(t) 308 defer removeAllPaths(tmpDirA, tmpDirB) 309 310 // Load A with some sample files and directories. 311 createSampleDir(t, tmpDirA) 312 313 srcPath := filepath.Join(tmpDirA, "file1") 314 dstDir := joinTrailingSep(tmpDirB, "testDir") 315 316 var err error 317 318 if err = testCopyHelper(t, srcPath, dstDir); err == nil { 319 t.Fatal("expected ErrDirNotExists error, but got nil instead") 320 } 321 322 if err != ErrDirNotExists { 323 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 324 } 325 } 326 327 // C. SRC specifies a file and DST exists as a file. This should overwrite 328 // the file at DST with the contents of the source file. 329 func TestCopyCaseC(t *testing.T) { 330 tmpDirA, tmpDirB := getTestTempDirs(t) 331 defer removeAllPaths(tmpDirA, tmpDirB) 332 333 // Load A and B with some sample files and directories. 334 createSampleDir(t, tmpDirA) 335 createSampleDir(t, tmpDirB) 336 337 srcPath := filepath.Join(tmpDirA, "file1") 338 dstPath := filepath.Join(tmpDirB, "file2") 339 340 var err error 341 342 // Ensure they start out different. 343 if err = fileContentsEqual(t, srcPath, dstPath); err == nil { 344 t.Fatal("expected different file contents") 345 } 346 347 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 348 t.Fatalf("unexpected error %T: %s", err, err) 349 } 350 351 if err = fileContentsEqual(t, srcPath, dstPath); err != nil { 352 t.Fatal(err) 353 } 354 } 355 356 // D. SRC specifies a file and DST exists as a directory. This should place 357 // a copy of the source file inside it using the basename from SRC. Ensure 358 // this works whether DST has a trailing path separator or not. 359 func TestCopyCaseD(t *testing.T) { 360 tmpDirA, tmpDirB := getTestTempDirs(t) 361 defer removeAllPaths(tmpDirA, tmpDirB) 362 363 // Load A and B with some sample files and directories. 364 createSampleDir(t, tmpDirA) 365 createSampleDir(t, tmpDirB) 366 367 srcPath := filepath.Join(tmpDirA, "file1") 368 dstDir := filepath.Join(tmpDirB, "dir1") 369 dstPath := filepath.Join(dstDir, "file1") 370 371 var err error 372 373 // Ensure that dstPath doesn't exist. 374 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 375 t.Fatalf("did not expect dstPath %q to exist", dstPath) 376 } 377 378 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 379 t.Fatalf("unexpected error %T: %s", err, err) 380 } 381 382 if err = fileContentsEqual(t, srcPath, dstPath); err != nil { 383 t.Fatal(err) 384 } 385 386 // Now try again but using a trailing path separator for dstDir. 387 388 if err = os.RemoveAll(dstDir); err != nil { 389 t.Fatalf("unable to remove dstDir: %s", err) 390 } 391 392 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 393 t.Fatalf("unable to make dstDir: %s", err) 394 } 395 396 dstDir = joinTrailingSep(tmpDirB, "dir1") 397 398 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 399 t.Fatalf("unexpected error %T: %s", err, err) 400 } 401 402 if err = fileContentsEqual(t, srcPath, dstPath); err != nil { 403 t.Fatal(err) 404 } 405 } 406 407 // E. SRC specifies a directory and DST does not exist. This should create a 408 // directory at DST and copy the contents of the SRC directory into the DST 409 // directory. Ensure this works whether DST has a trailing path separator or 410 // not. 411 func TestCopyCaseE(t *testing.T) { 412 tmpDirA, tmpDirB := getTestTempDirs(t) 413 defer removeAllPaths(tmpDirA, tmpDirB) 414 415 // Load A with some sample files and directories. 416 createSampleDir(t, tmpDirA) 417 418 srcDir := filepath.Join(tmpDirA, "dir1") 419 dstDir := filepath.Join(tmpDirB, "testDir") 420 421 var err error 422 423 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 424 t.Fatalf("unexpected error %T: %s", err, err) 425 } 426 427 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 428 t.Log("dir contents not equal") 429 logDirContents(t, tmpDirA) 430 logDirContents(t, tmpDirB) 431 t.Fatal(err) 432 } 433 434 // Now try again but using a trailing path separator for dstDir. 435 436 if err = os.RemoveAll(dstDir); err != nil { 437 t.Fatalf("unable to remove dstDir: %s", err) 438 } 439 440 dstDir = joinTrailingSep(tmpDirB, "testDir") 441 442 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 443 t.Fatalf("unexpected error %T: %s", err, err) 444 } 445 446 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 447 t.Fatal(err) 448 } 449 } 450 451 // F. SRC specifies a directory and DST exists as a file. This should cause an 452 // error as it is not possible to overwrite a file with a directory. 453 func TestCopyCaseF(t *testing.T) { 454 tmpDirA, tmpDirB := getTestTempDirs(t) 455 defer removeAllPaths(tmpDirA, tmpDirB) 456 457 // Load A and B with some sample files and directories. 458 createSampleDir(t, tmpDirA) 459 createSampleDir(t, tmpDirB) 460 461 srcDir := filepath.Join(tmpDirA, "dir1") 462 dstFile := filepath.Join(tmpDirB, "file1") 463 464 var err error 465 466 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 467 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 468 } 469 470 if err != ErrCannotCopyDir { 471 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 472 } 473 } 474 475 // G. SRC specifies a directory and DST exists as a directory. This should copy 476 // the SRC directory and all its contents to the DST directory. Ensure this 477 // works whether DST has a trailing path separator or not. 478 func TestCopyCaseG(t *testing.T) { 479 tmpDirA, tmpDirB := getTestTempDirs(t) 480 defer removeAllPaths(tmpDirA, tmpDirB) 481 482 // Load A and B with some sample files and directories. 483 createSampleDir(t, tmpDirA) 484 createSampleDir(t, tmpDirB) 485 486 srcDir := filepath.Join(tmpDirA, "dir1") 487 dstDir := filepath.Join(tmpDirB, "dir2") 488 resultDir := filepath.Join(dstDir, "dir1") 489 490 var err error 491 492 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 493 t.Fatalf("unexpected error %T: %s", err, err) 494 } 495 496 if err = dirContentsEqual(t, resultDir, srcDir); err != nil { 497 t.Fatal(err) 498 } 499 500 // Now try again but using a trailing path separator for dstDir. 501 502 if err = os.RemoveAll(dstDir); err != nil { 503 t.Fatalf("unable to remove dstDir: %s", err) 504 } 505 506 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 507 t.Fatalf("unable to make dstDir: %s", err) 508 } 509 510 dstDir = joinTrailingSep(tmpDirB, "dir2") 511 512 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 513 t.Fatalf("unexpected error %T: %s", err, err) 514 } 515 516 if err = dirContentsEqual(t, resultDir, srcDir); err != nil { 517 t.Fatal(err) 518 } 519 } 520 521 // H. SRC specifies a directory's contents only and DST does not exist. This 522 // should create a directory at DST and copy the contents of the SRC 523 // directory (but not the directory itself) into the DST directory. Ensure 524 // this works whether DST has a trailing path separator or not. 525 func TestCopyCaseH(t *testing.T) { 526 tmpDirA, tmpDirB := getTestTempDirs(t) 527 defer removeAllPaths(tmpDirA, tmpDirB) 528 529 // Load A with some sample files and directories. 530 createSampleDir(t, tmpDirA) 531 532 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 533 dstDir := filepath.Join(tmpDirB, "testDir") 534 535 var err error 536 537 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 538 t.Fatalf("unexpected error %T: %s", err, err) 539 } 540 541 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 542 t.Log("dir contents not equal") 543 logDirContents(t, tmpDirA) 544 logDirContents(t, tmpDirB) 545 t.Fatal(err) 546 } 547 548 // Now try again but using a trailing path separator for dstDir. 549 550 if err = os.RemoveAll(dstDir); err != nil { 551 t.Fatalf("unable to remove dstDir: %s", err) 552 } 553 554 dstDir = joinTrailingSep(tmpDirB, "testDir") 555 556 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 557 t.Fatalf("unexpected error %T: %s", err, err) 558 } 559 560 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 561 t.Log("dir contents not equal") 562 logDirContents(t, tmpDirA) 563 logDirContents(t, tmpDirB) 564 t.Fatal(err) 565 } 566 } 567 568 // I. SRC specifies a direcotry's contents only and DST exists as a file. This 569 // should cause an error as it is not possible to overwrite a file with a 570 // directory. 571 func TestCopyCaseI(t *testing.T) { 572 tmpDirA, tmpDirB := getTestTempDirs(t) 573 defer removeAllPaths(tmpDirA, tmpDirB) 574 575 // Load A and B with some sample files and directories. 576 createSampleDir(t, tmpDirA) 577 createSampleDir(t, tmpDirB) 578 579 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 580 dstFile := filepath.Join(tmpDirB, "file1") 581 582 var err error 583 584 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 585 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 586 } 587 588 if err != ErrCannotCopyDir { 589 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 590 } 591 } 592 593 // J. SRC specifies a directory's contents only and DST exists as a directory. 594 // This should copy the contents of the SRC directory (but not the directory 595 // itself) into the DST directory. Ensure this works whether DST has a 596 // trailing path separator or not. 597 func TestCopyCaseJ(t *testing.T) { 598 tmpDirA, tmpDirB := getTestTempDirs(t) 599 defer removeAllPaths(tmpDirA, tmpDirB) 600 601 // Load A and B with some sample files and directories. 602 createSampleDir(t, tmpDirA) 603 createSampleDir(t, tmpDirB) 604 605 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 606 dstDir := filepath.Join(tmpDirB, "dir5") 607 608 var err error 609 610 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 611 t.Fatalf("unexpected error %T: %s", err, err) 612 } 613 614 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 615 t.Fatal(err) 616 } 617 618 // Now try again but using a trailing path separator for dstDir. 619 620 if err = os.RemoveAll(dstDir); err != nil { 621 t.Fatalf("unable to remove dstDir: %s", err) 622 } 623 624 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 625 t.Fatalf("unable to make dstDir: %s", err) 626 } 627 628 dstDir = joinTrailingSep(tmpDirB, "dir5") 629 630 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 631 t.Fatalf("unexpected error %T: %s", err, err) 632 } 633 634 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 635 t.Fatal(err) 636 } 637 }