github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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  }