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