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