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