github.com/stuarqi/moby@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  }