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