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  }