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