github.com/damirazo/docker@v1.9.0/integration-cli/docker_cli_cp_from_container_test.go (about) 1 package main 2 3 import ( 4 "os" 5 "path/filepath" 6 7 "github.com/go-check/check" 8 ) 9 10 // docker cp CONTAINER:PATH LOCALPATH 11 12 // Try all of the test cases from the archive package which implements the 13 // internals of `docker cp` and ensure that the behavior matches when actually 14 // copying to and from containers. 15 16 // Basic assumptions about SRC and DST: 17 // 1. SRC must exist. 18 // 2. If SRC ends with a trailing separator, it must be a directory. 19 // 3. DST parent directory must exist. 20 // 4. If DST exists as a file, it must not end with a trailing separator. 21 22 // First get these easy error cases out of the way. 23 24 // Test for error when SRC does not exist. 25 func (s *DockerSuite) TestCpFromErrSrcNotExists(c *check.C) { 26 testRequires(c, DaemonIsLinux) 27 cID := makeTestContainer(c, testContainerOptions{}) 28 defer deleteContainer(cID) 29 30 tmpDir := getTestDir(c, "test-cp-from-err-src-not-exists") 31 defer os.RemoveAll(tmpDir) 32 33 err := runDockerCp(c, containerCpPath(cID, "file1"), tmpDir) 34 if err == nil { 35 c.Fatal("expected IsNotExist error, but got nil instead") 36 } 37 38 if !isCpNotExist(err) { 39 c.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 40 } 41 } 42 43 // Test for error when SRC ends in a trailing 44 // path separator but it exists as a file. 45 func (s *DockerSuite) TestCpFromErrSrcNotDir(c *check.C) { 46 testRequires(c, DaemonIsLinux) 47 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 48 defer deleteContainer(cID) 49 50 tmpDir := getTestDir(c, "test-cp-from-err-src-not-dir") 51 defer os.RemoveAll(tmpDir) 52 53 err := runDockerCp(c, containerCpPathTrailingSep(cID, "file1"), tmpDir) 54 if err == nil { 55 c.Fatal("expected IsNotDir error, but got nil instead") 56 } 57 58 if !isCpNotDir(err) { 59 c.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 60 } 61 } 62 63 // Test for error when SRC is a valid file or directory, 64 // bu the DST parent directory does not exist. 65 func (s *DockerSuite) TestCpFromErrDstParentNotExists(c *check.C) { 66 testRequires(c, DaemonIsLinux) 67 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 68 defer deleteContainer(cID) 69 70 tmpDir := getTestDir(c, "test-cp-from-err-dst-parent-not-exists") 71 defer os.RemoveAll(tmpDir) 72 73 makeTestContentInDir(c, tmpDir) 74 75 // Try with a file source. 76 srcPath := containerCpPath(cID, "/file1") 77 dstPath := cpPath(tmpDir, "notExists", "file1") 78 79 err := runDockerCp(c, srcPath, dstPath) 80 if err == nil { 81 c.Fatal("expected IsNotExist error, but got nil instead") 82 } 83 84 if !isCpNotExist(err) { 85 c.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 86 } 87 88 // Try with a directory source. 89 srcPath = containerCpPath(cID, "/dir1") 90 91 if err := runDockerCp(c, srcPath, dstPath); err == nil { 92 c.Fatal("expected IsNotExist error, but got nil instead") 93 } 94 95 if !isCpNotExist(err) { 96 c.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 97 } 98 } 99 100 // Test for error when DST ends in a trailing 101 // path separator but exists as a file. 102 func (s *DockerSuite) TestCpFromErrDstNotDir(c *check.C) { 103 testRequires(c, DaemonIsLinux) 104 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 105 defer deleteContainer(cID) 106 107 tmpDir := getTestDir(c, "test-cp-from-err-dst-not-dir") 108 defer os.RemoveAll(tmpDir) 109 110 makeTestContentInDir(c, tmpDir) 111 112 // Try with a file source. 113 srcPath := containerCpPath(cID, "/file1") 114 dstPath := cpPathTrailingSep(tmpDir, "file1") 115 116 err := runDockerCp(c, srcPath, dstPath) 117 if err == nil { 118 c.Fatal("expected IsNotDir error, but got nil instead") 119 } 120 121 if !isCpNotDir(err) { 122 c.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 123 } 124 125 // Try with a directory source. 126 srcPath = containerCpPath(cID, "/dir1") 127 128 if err := runDockerCp(c, srcPath, dstPath); err == nil { 129 c.Fatal("expected IsNotDir error, but got nil instead") 130 } 131 132 if !isCpNotDir(err) { 133 c.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 134 } 135 } 136 137 // Check that copying from a container to a local symlink copies to the symlink 138 // target and does not overwrite the local symlink itself. 139 func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) { 140 testRequires(c, DaemonIsLinux) 141 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 142 defer deleteContainer(cID) 143 144 tmpDir := getTestDir(c, "test-cp-from-err-dst-not-dir") 145 defer os.RemoveAll(tmpDir) 146 147 makeTestContentInDir(c, tmpDir) 148 149 // First, copy a file from the container to a symlink to a file. This 150 // should overwrite the symlink target contents with the source contents. 151 srcPath := containerCpPath(cID, "/file2") 152 dstPath := cpPath(tmpDir, "symlinkToFile1") 153 154 if err := runDockerCp(c, srcPath, dstPath); err != nil { 155 c.Fatalf("unexpected error %T: %s", err, err) 156 } 157 158 // The symlink should not have been modified. 159 if err := symlinkTargetEquals(c, dstPath, "file1"); err != nil { 160 c.Fatal(err) 161 } 162 163 // The file should have the contents of "file2" now. 164 if err := fileContentEquals(c, cpPath(tmpDir, "file1"), "file2\n"); err != nil { 165 c.Fatal(err) 166 } 167 168 // Next, copy a file from the container to a symlink to a directory. This 169 // should copy the file into the symlink target directory. 170 dstPath = cpPath(tmpDir, "symlinkToDir1") 171 172 if err := runDockerCp(c, srcPath, dstPath); err != nil { 173 c.Fatalf("unexpected error %T: %s", err, err) 174 } 175 176 // The symlink should not have been modified. 177 if err := symlinkTargetEquals(c, dstPath, "dir1"); err != nil { 178 c.Fatal(err) 179 } 180 181 // The file should have the contents of "file2" now. 182 if err := fileContentEquals(c, cpPath(tmpDir, "file2"), "file2\n"); err != nil { 183 c.Fatal(err) 184 } 185 186 // Next, copy a file from the container to a symlink to a file that does 187 // not exist (a broken symlink). This should create the target file with 188 // the contents of the source file. 189 dstPath = cpPath(tmpDir, "brokenSymlinkToFileX") 190 191 if err := runDockerCp(c, srcPath, dstPath); err != nil { 192 c.Fatalf("unexpected error %T: %s", err, err) 193 } 194 195 // The symlink should not have been modified. 196 if err := symlinkTargetEquals(c, dstPath, "fileX"); err != nil { 197 c.Fatal(err) 198 } 199 200 // The file should have the contents of "file2" now. 201 if err := fileContentEquals(c, cpPath(tmpDir, "fileX"), "file2\n"); err != nil { 202 c.Fatal(err) 203 } 204 205 // Next, copy a directory from the container to a symlink to a local 206 // directory. This should copy the directory into the symlink target 207 // directory and not modify the symlink. 208 srcPath = containerCpPath(cID, "/dir2") 209 dstPath = cpPath(tmpDir, "symlinkToDir1") 210 211 if err := runDockerCp(c, srcPath, dstPath); err != nil { 212 c.Fatalf("unexpected error %T: %s", err, err) 213 } 214 215 // The symlink should not have been modified. 216 if err := symlinkTargetEquals(c, dstPath, "dir1"); err != nil { 217 c.Fatal(err) 218 } 219 220 // The directory should now contain a copy of "dir2". 221 if err := fileContentEquals(c, cpPath(tmpDir, "dir1/dir2/file2-1"), "file2-1\n"); err != nil { 222 c.Fatal(err) 223 } 224 225 // Next, copy a directory from the container to a symlink to a local 226 // directory that does not exist (a broken symlink). This should create 227 // the target as a directory with the contents of the source directory. It 228 // should not modify the symlink. 229 dstPath = cpPath(tmpDir, "brokenSymlinkToDirX") 230 231 if err := runDockerCp(c, srcPath, dstPath); err != nil { 232 c.Fatalf("unexpected error %T: %s", err, err) 233 } 234 235 // The symlink should not have been modified. 236 if err := symlinkTargetEquals(c, dstPath, "dirX"); err != nil { 237 c.Fatal(err) 238 } 239 240 // The "dirX" directory should now be a copy of "dir2". 241 if err := fileContentEquals(c, cpPath(tmpDir, "dirX/file2-1"), "file2-1\n"); err != nil { 242 c.Fatal(err) 243 } 244 } 245 246 // Possibilities are reduced to the remaining 10 cases: 247 // 248 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 249 // =================================================================================================== 250 // A | no | - | no | - | no | create file 251 // B | no | - | no | - | yes | error 252 // C | no | - | yes | no | - | overwrite file 253 // D | no | - | yes | yes | - | create file in dst dir 254 // E | yes | no | no | - | - | create dir, copy contents 255 // F | yes | no | yes | no | - | error 256 // G | yes | no | yes | yes | - | copy dir and contents 257 // H | yes | yes | no | - | - | create dir, copy contents 258 // I | yes | yes | yes | no | - | error 259 // J | yes | yes | yes | yes | - | copy dir contents 260 // 261 262 // A. SRC specifies a file and DST (no trailing path separator) doesn't 263 // exist. This should create a file with the name DST and copy the 264 // contents of the source file into it. 265 func (s *DockerSuite) TestCpFromCaseA(c *check.C) { 266 testRequires(c, DaemonIsLinux) 267 cID := makeTestContainer(c, testContainerOptions{ 268 addContent: true, workDir: "/root", 269 }) 270 defer deleteContainer(cID) 271 272 tmpDir := getTestDir(c, "test-cp-from-case-a") 273 defer os.RemoveAll(tmpDir) 274 275 srcPath := containerCpPath(cID, "/root/file1") 276 dstPath := cpPath(tmpDir, "itWorks.txt") 277 278 if err := runDockerCp(c, srcPath, dstPath); err != nil { 279 c.Fatalf("unexpected error %T: %s", err, err) 280 } 281 282 if err := fileContentEquals(c, dstPath, "file1\n"); err != nil { 283 c.Fatal(err) 284 } 285 } 286 287 // B. SRC specifies a file and DST (with trailing path separator) doesn't 288 // exist. This should cause an error because the copy operation cannot 289 // create a directory when copying a single file. 290 func (s *DockerSuite) TestCpFromCaseB(c *check.C) { 291 testRequires(c, DaemonIsLinux) 292 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 293 defer deleteContainer(cID) 294 295 tmpDir := getTestDir(c, "test-cp-from-case-b") 296 defer os.RemoveAll(tmpDir) 297 298 srcPath := containerCpPath(cID, "/file1") 299 dstDir := cpPathTrailingSep(tmpDir, "testDir") 300 301 err := runDockerCp(c, srcPath, dstDir) 302 if err == nil { 303 c.Fatal("expected DirNotExists error, but got nil instead") 304 } 305 306 if !isCpDirNotExist(err) { 307 c.Fatalf("expected DirNotExists error, but got %T: %s", err, err) 308 } 309 } 310 311 // C. SRC specifies a file and DST exists as a file. This should overwrite 312 // the file at DST with the contents of the source file. 313 func (s *DockerSuite) TestCpFromCaseC(c *check.C) { 314 testRequires(c, DaemonIsLinux) 315 cID := makeTestContainer(c, testContainerOptions{ 316 addContent: true, workDir: "/root", 317 }) 318 defer deleteContainer(cID) 319 320 tmpDir := getTestDir(c, "test-cp-from-case-c") 321 defer os.RemoveAll(tmpDir) 322 323 makeTestContentInDir(c, tmpDir) 324 325 srcPath := containerCpPath(cID, "/root/file1") 326 dstPath := cpPath(tmpDir, "file2") 327 328 // Ensure the local file starts with different content. 329 if err := fileContentEquals(c, dstPath, "file2\n"); err != nil { 330 c.Fatal(err) 331 } 332 333 if err := runDockerCp(c, srcPath, dstPath); err != nil { 334 c.Fatalf("unexpected error %T: %s", err, err) 335 } 336 337 if err := fileContentEquals(c, dstPath, "file1\n"); err != nil { 338 c.Fatal(err) 339 } 340 } 341 342 // D. SRC specifies a file and DST exists as a directory. This should place 343 // a copy of the source file inside it using the basename from SRC. Ensure 344 // this works whether DST has a trailing path separator or not. 345 func (s *DockerSuite) TestCpFromCaseD(c *check.C) { 346 testRequires(c, DaemonIsLinux) 347 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 348 defer deleteContainer(cID) 349 350 tmpDir := getTestDir(c, "test-cp-from-case-d") 351 defer os.RemoveAll(tmpDir) 352 353 makeTestContentInDir(c, tmpDir) 354 355 srcPath := containerCpPath(cID, "/file1") 356 dstDir := cpPath(tmpDir, "dir1") 357 dstPath := filepath.Join(dstDir, "file1") 358 359 // Ensure that dstPath doesn't exist. 360 if _, err := os.Stat(dstPath); !os.IsNotExist(err) { 361 c.Fatalf("did not expect dstPath %q to exist", dstPath) 362 } 363 364 if err := runDockerCp(c, srcPath, dstDir); err != nil { 365 c.Fatalf("unexpected error %T: %s", err, err) 366 } 367 368 if err := fileContentEquals(c, dstPath, "file1\n"); err != nil { 369 c.Fatal(err) 370 } 371 372 // Now try again but using a trailing path separator for dstDir. 373 374 if err := os.RemoveAll(dstDir); err != nil { 375 c.Fatalf("unable to remove dstDir: %s", err) 376 } 377 378 if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 379 c.Fatalf("unable to make dstDir: %s", err) 380 } 381 382 dstDir = cpPathTrailingSep(tmpDir, "dir1") 383 384 if err := runDockerCp(c, srcPath, dstDir); err != nil { 385 c.Fatalf("unexpected error %T: %s", err, err) 386 } 387 388 if err := fileContentEquals(c, dstPath, "file1\n"); err != nil { 389 c.Fatal(err) 390 } 391 } 392 393 // E. SRC specifies a directory and DST does not exist. This should create a 394 // directory at DST and copy the contents of the SRC directory into the DST 395 // directory. Ensure this works whether DST has a trailing path separator or 396 // not. 397 func (s *DockerSuite) TestCpFromCaseE(c *check.C) { 398 testRequires(c, DaemonIsLinux) 399 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 400 defer deleteContainer(cID) 401 402 tmpDir := getTestDir(c, "test-cp-from-case-e") 403 defer os.RemoveAll(tmpDir) 404 405 srcDir := containerCpPath(cID, "dir1") 406 dstDir := cpPath(tmpDir, "testDir") 407 dstPath := filepath.Join(dstDir, "file1-1") 408 409 if err := runDockerCp(c, srcDir, dstDir); err != nil { 410 c.Fatalf("unexpected error %T: %s", err, err) 411 } 412 413 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 414 c.Fatal(err) 415 } 416 417 // Now try again but using a trailing path separator for dstDir. 418 419 if err := os.RemoveAll(dstDir); err != nil { 420 c.Fatalf("unable to remove dstDir: %s", err) 421 } 422 423 dstDir = cpPathTrailingSep(tmpDir, "testDir") 424 425 if err := runDockerCp(c, srcDir, dstDir); err != nil { 426 c.Fatalf("unexpected error %T: %s", err, err) 427 } 428 429 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 430 c.Fatal(err) 431 } 432 } 433 434 // F. SRC specifies a directory and DST exists as a file. This should cause an 435 // error as it is not possible to overwrite a file with a directory. 436 func (s *DockerSuite) TestCpFromCaseF(c *check.C) { 437 testRequires(c, DaemonIsLinux) 438 cID := makeTestContainer(c, testContainerOptions{ 439 addContent: true, workDir: "/root", 440 }) 441 defer deleteContainer(cID) 442 443 tmpDir := getTestDir(c, "test-cp-from-case-f") 444 defer os.RemoveAll(tmpDir) 445 446 makeTestContentInDir(c, tmpDir) 447 448 srcDir := containerCpPath(cID, "/root/dir1") 449 dstFile := cpPath(tmpDir, "file1") 450 451 err := runDockerCp(c, srcDir, dstFile) 452 if err == nil { 453 c.Fatal("expected ErrCannotCopyDir error, but got nil instead") 454 } 455 456 if !isCpCannotCopyDir(err) { 457 c.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 458 } 459 } 460 461 // G. SRC specifies a directory and DST exists as a directory. This should copy 462 // the SRC directory and all its contents to the DST directory. Ensure this 463 // works whether DST has a trailing path separator or not. 464 func (s *DockerSuite) TestCpFromCaseG(c *check.C) { 465 testRequires(c, DaemonIsLinux) 466 cID := makeTestContainer(c, testContainerOptions{ 467 addContent: true, workDir: "/root", 468 }) 469 defer deleteContainer(cID) 470 471 tmpDir := getTestDir(c, "test-cp-from-case-g") 472 defer os.RemoveAll(tmpDir) 473 474 makeTestContentInDir(c, tmpDir) 475 476 srcDir := containerCpPath(cID, "/root/dir1") 477 dstDir := cpPath(tmpDir, "dir2") 478 resultDir := filepath.Join(dstDir, "dir1") 479 dstPath := filepath.Join(resultDir, "file1-1") 480 481 if err := runDockerCp(c, srcDir, dstDir); err != nil { 482 c.Fatalf("unexpected error %T: %s", err, err) 483 } 484 485 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 486 c.Fatal(err) 487 } 488 489 // Now try again but using a trailing path separator for dstDir. 490 491 if err := os.RemoveAll(dstDir); err != nil { 492 c.Fatalf("unable to remove dstDir: %s", err) 493 } 494 495 if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 496 c.Fatalf("unable to make dstDir: %s", err) 497 } 498 499 dstDir = cpPathTrailingSep(tmpDir, "dir2") 500 501 if err := runDockerCp(c, srcDir, dstDir); err != nil { 502 c.Fatalf("unexpected error %T: %s", err, err) 503 } 504 505 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 506 c.Fatal(err) 507 } 508 } 509 510 // H. SRC specifies a directory's contents only and DST does not exist. This 511 // should create a directory at DST and copy the contents of the SRC 512 // directory (but not the directory itself) into the DST directory. Ensure 513 // this works whether DST has a trailing path separator or not. 514 func (s *DockerSuite) TestCpFromCaseH(c *check.C) { 515 testRequires(c, DaemonIsLinux) 516 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 517 defer deleteContainer(cID) 518 519 tmpDir := getTestDir(c, "test-cp-from-case-h") 520 defer os.RemoveAll(tmpDir) 521 522 srcDir := containerCpPathTrailingSep(cID, "dir1") + "." 523 dstDir := cpPath(tmpDir, "testDir") 524 dstPath := filepath.Join(dstDir, "file1-1") 525 526 if err := runDockerCp(c, srcDir, dstDir); err != nil { 527 c.Fatalf("unexpected error %T: %s", err, err) 528 } 529 530 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 531 c.Fatal(err) 532 } 533 534 // Now try again but using a trailing path separator for dstDir. 535 536 if err := os.RemoveAll(dstDir); err != nil { 537 c.Fatalf("unable to remove resultDir: %s", err) 538 } 539 540 dstDir = cpPathTrailingSep(tmpDir, "testDir") 541 542 if err := runDockerCp(c, srcDir, dstDir); err != nil { 543 c.Fatalf("unexpected error %T: %s", err, err) 544 } 545 546 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 547 c.Fatal(err) 548 } 549 } 550 551 // I. SRC specifies a directory's contents only and DST exists as a file. This 552 // should cause an error as it is not possible to overwrite a file with a 553 // directory. 554 func (s *DockerSuite) TestCpFromCaseI(c *check.C) { 555 testRequires(c, DaemonIsLinux) 556 cID := makeTestContainer(c, testContainerOptions{ 557 addContent: true, workDir: "/root", 558 }) 559 defer deleteContainer(cID) 560 561 tmpDir := getTestDir(c, "test-cp-from-case-i") 562 defer os.RemoveAll(tmpDir) 563 564 makeTestContentInDir(c, tmpDir) 565 566 srcDir := containerCpPathTrailingSep(cID, "/root/dir1") + "." 567 dstFile := cpPath(tmpDir, "file1") 568 569 err := runDockerCp(c, srcDir, dstFile) 570 if err == nil { 571 c.Fatal("expected ErrCannotCopyDir error, but got nil instead") 572 } 573 574 if !isCpCannotCopyDir(err) { 575 c.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 576 } 577 } 578 579 // J. SRC specifies a directory's contents only and DST exists as a directory. 580 // This should copy the contents of the SRC directory (but not the directory 581 // itself) into the DST directory. Ensure this works whether DST has a 582 // trailing path separator or not. 583 func (s *DockerSuite) TestCpFromCaseJ(c *check.C) { 584 testRequires(c, DaemonIsLinux) 585 cID := makeTestContainer(c, testContainerOptions{ 586 addContent: true, workDir: "/root", 587 }) 588 defer deleteContainer(cID) 589 590 tmpDir := getTestDir(c, "test-cp-from-case-j") 591 defer os.RemoveAll(tmpDir) 592 593 makeTestContentInDir(c, tmpDir) 594 595 srcDir := containerCpPathTrailingSep(cID, "/root/dir1") + "." 596 dstDir := cpPath(tmpDir, "dir2") 597 dstPath := filepath.Join(dstDir, "file1-1") 598 599 if err := runDockerCp(c, srcDir, dstDir); err != nil { 600 c.Fatalf("unexpected error %T: %s", err, err) 601 } 602 603 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 604 c.Fatal(err) 605 } 606 607 // Now try again but using a trailing path separator for dstDir. 608 609 if err := os.RemoveAll(dstDir); err != nil { 610 c.Fatalf("unable to remove dstDir: %s", err) 611 } 612 613 if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 614 c.Fatalf("unable to make dstDir: %s", err) 615 } 616 617 dstDir = cpPathTrailingSep(tmpDir, "dir2") 618 619 if err := runDockerCp(c, srcDir, dstDir); err != nil { 620 c.Fatalf("unexpected error %T: %s", err, err) 621 } 622 623 if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil { 624 c.Fatal(err) 625 } 626 }