github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/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  	"gotest.tools/assert"
    10  )
    11  
    12  // Try all of the test cases from the archive package which implements the
    13  // internals of `docker cp` and ensure that the behavior matches when actually
    14  // copying to and from containers.
    15  
    16  // Basic assumptions about SRC and DST:
    17  // 1. SRC must exist.
    18  // 2. If SRC ends with a trailing separator, it must be a directory.
    19  // 3. DST parent directory must exist.
    20  // 4. If DST exists as a file, it must not end with a trailing separator.
    21  
    22  // Check that copying from a container to a local symlink copies to the symlink
    23  // target and does not overwrite the local symlink itself.
    24  // TODO: move to docker/cli and/or integration/container/copy_test.go
    25  func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
    26  	testRequires(c, DaemonIsLinux)
    27  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
    28  
    29  	tmpDir := getTestDir(c, "test-cp-from-err-dst-not-dir")
    30  	defer os.RemoveAll(tmpDir)
    31  
    32  	makeTestContentInDir(c, tmpDir)
    33  
    34  	// First, copy a file from the container to a symlink to a file. This
    35  	// should overwrite the symlink target contents with the source contents.
    36  	srcPath := containerCpPath(containerID, "/file2")
    37  	dstPath := cpPath(tmpDir, "symlinkToFile1")
    38  
    39  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
    40  
    41  	// The symlink should not have been modified.
    42  	c.Assert(symlinkTargetEquals(c, dstPath, "file1"), checker.IsNil)
    43  
    44  	// The file should have the contents of "file2" now.
    45  	c.Assert(fileContentEquals(c, cpPath(tmpDir, "file1"), "file2\n"), checker.IsNil)
    46  
    47  	// Next, copy a file from the container to a symlink to a directory. This
    48  	// should copy the file into the symlink target directory.
    49  	dstPath = cpPath(tmpDir, "symlinkToDir1")
    50  
    51  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
    52  
    53  	// The symlink should not have been modified.
    54  	c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
    55  
    56  	// The file should have the contents of "file2" now.
    57  	c.Assert(fileContentEquals(c, cpPath(tmpDir, "file2"), "file2\n"), checker.IsNil)
    58  
    59  	// Next, copy a file from the container to a symlink to a file that does
    60  	// not exist (a broken symlink). This should create the target file with
    61  	// the contents of the source file.
    62  	dstPath = cpPath(tmpDir, "brokenSymlinkToFileX")
    63  
    64  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
    65  
    66  	// The symlink should not have been modified.
    67  	c.Assert(symlinkTargetEquals(c, dstPath, "fileX"), checker.IsNil)
    68  
    69  	// The file should have the contents of "file2" now.
    70  	c.Assert(fileContentEquals(c, cpPath(tmpDir, "fileX"), "file2\n"), checker.IsNil)
    71  
    72  	// Next, copy a directory from the container to a symlink to a local
    73  	// directory. This should copy the directory into the symlink target
    74  	// directory and not modify the symlink.
    75  	srcPath = containerCpPath(containerID, "/dir2")
    76  	dstPath = cpPath(tmpDir, "symlinkToDir1")
    77  
    78  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
    79  
    80  	// The symlink should not have been modified.
    81  	c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
    82  
    83  	// The directory should now contain a copy of "dir2".
    84  	c.Assert(fileContentEquals(c, cpPath(tmpDir, "dir1/dir2/file2-1"), "file2-1\n"), checker.IsNil)
    85  
    86  	// Next, copy a directory from the container to a symlink to a local
    87  	// directory that does not exist (a broken symlink). This should create
    88  	// the target as a directory with the contents of the source directory. It
    89  	// should not modify the symlink.
    90  	dstPath = cpPath(tmpDir, "brokenSymlinkToDirX")
    91  
    92  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
    93  
    94  	// The symlink should not have been modified.
    95  	c.Assert(symlinkTargetEquals(c, dstPath, "dirX"), checker.IsNil)
    96  
    97  	// The "dirX" directory should now be a copy of "dir2".
    98  	c.Assert(fileContentEquals(c, cpPath(tmpDir, "dirX/file2-1"), "file2-1\n"), checker.IsNil)
    99  }
   100  
   101  // Possibilities are reduced to the remaining 10 cases:
   102  //
   103  //  case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
   104  // ===================================================================================================
   105  //   A   |  no      |  -              |  no       |  -       |  no      |  create file
   106  //   B   |  no      |  -              |  no       |  -       |  yes     |  error
   107  //   C   |  no      |  -              |  yes      |  no      |  -       |  overwrite file
   108  //   D   |  no      |  -              |  yes      |  yes     |  -       |  create file in dst dir
   109  //   E   |  yes     |  no             |  no       |  -       |  -       |  create dir, copy contents
   110  //   F   |  yes     |  no             |  yes      |  no      |  -       |  error
   111  //   G   |  yes     |  no             |  yes      |  yes     |  -       |  copy dir and contents
   112  //   H   |  yes     |  yes            |  no       |  -       |  -       |  create dir, copy contents
   113  //   I   |  yes     |  yes            |  yes      |  no      |  -       |  error
   114  //   J   |  yes     |  yes            |  yes      |  yes     |  -       |  copy dir contents
   115  //
   116  
   117  // A. SRC specifies a file and DST (no trailing path separator) doesn't
   118  //    exist. This should create a file with the name DST and copy the
   119  //    contents of the source file into it.
   120  func (s *DockerSuite) TestCpFromCaseA(c *check.C) {
   121  	testRequires(c, DaemonIsLinux)
   122  	containerID := makeTestContainer(c, testContainerOptions{
   123  		addContent: true, workDir: "/root",
   124  	})
   125  
   126  	tmpDir := getTestDir(c, "test-cp-from-case-a")
   127  	defer os.RemoveAll(tmpDir)
   128  
   129  	srcPath := containerCpPath(containerID, "/root/file1")
   130  	dstPath := cpPath(tmpDir, "itWorks.txt")
   131  
   132  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   133  
   134  	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
   135  }
   136  
   137  // B. SRC specifies a file and DST (with trailing path separator) doesn't
   138  //    exist. This should cause an error because the copy operation cannot
   139  //    create a directory when copying a single file.
   140  func (s *DockerSuite) TestCpFromCaseB(c *check.C) {
   141  	testRequires(c, DaemonIsLinux)
   142  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
   143  
   144  	tmpDir := getTestDir(c, "test-cp-from-case-b")
   145  	defer os.RemoveAll(tmpDir)
   146  
   147  	srcPath := containerCpPath(containerID, "/file1")
   148  	dstDir := cpPathTrailingSep(tmpDir, "testDir")
   149  
   150  	err := runDockerCp(c, srcPath, dstDir, nil)
   151  	assert.ErrorContains(c, err, "")
   152  
   153  	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err))
   154  }
   155  
   156  // C. SRC specifies a file and DST exists as a file. This should overwrite
   157  //    the file at DST with the contents of the source file.
   158  func (s *DockerSuite) TestCpFromCaseC(c *check.C) {
   159  	testRequires(c, DaemonIsLinux)
   160  	containerID := makeTestContainer(c, testContainerOptions{
   161  		addContent: true, workDir: "/root",
   162  	})
   163  
   164  	tmpDir := getTestDir(c, "test-cp-from-case-c")
   165  	defer os.RemoveAll(tmpDir)
   166  
   167  	makeTestContentInDir(c, tmpDir)
   168  
   169  	srcPath := containerCpPath(containerID, "/root/file1")
   170  	dstPath := cpPath(tmpDir, "file2")
   171  
   172  	// Ensure the local file starts with different content.
   173  	c.Assert(fileContentEquals(c, dstPath, "file2\n"), checker.IsNil)
   174  
   175  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   176  
   177  	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
   178  }
   179  
   180  // D. SRC specifies a file and DST exists as a directory. This should place
   181  //    a copy of the source file inside it using the basename from SRC. Ensure
   182  //    this works whether DST has a trailing path separator or not.
   183  func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
   184  	testRequires(c, DaemonIsLinux)
   185  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
   186  
   187  	tmpDir := getTestDir(c, "test-cp-from-case-d")
   188  	defer os.RemoveAll(tmpDir)
   189  
   190  	makeTestContentInDir(c, tmpDir)
   191  
   192  	srcPath := containerCpPath(containerID, "/file1")
   193  	dstDir := cpPath(tmpDir, "dir1")
   194  	dstPath := filepath.Join(dstDir, "file1")
   195  
   196  	// Ensure that dstPath doesn't exist.
   197  	_, err := os.Stat(dstPath)
   198  	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("did not expect dstPath %q to exist", dstPath))
   199  
   200  	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
   201  
   202  	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
   203  
   204  	// Now try again but using a trailing path separator for dstDir.
   205  
   206  	// unable to remove dstDir
   207  	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
   208  
   209  	// unable to make dstDir
   210  	c.Assert(os.MkdirAll(dstDir, os.FileMode(0755)), checker.IsNil)
   211  
   212  	dstDir = cpPathTrailingSep(tmpDir, "dir1")
   213  
   214  	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
   215  
   216  	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
   217  }
   218  
   219  // E. SRC specifies a directory and DST does not exist. This should create a
   220  //    directory at DST and copy the contents of the SRC directory into the DST
   221  //    directory. Ensure this works whether DST has a trailing path separator or
   222  //    not.
   223  func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
   224  	testRequires(c, DaemonIsLinux)
   225  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
   226  
   227  	tmpDir := getTestDir(c, "test-cp-from-case-e")
   228  	defer os.RemoveAll(tmpDir)
   229  
   230  	srcDir := containerCpPath(containerID, "dir1")
   231  	dstDir := cpPath(tmpDir, "testDir")
   232  	dstPath := filepath.Join(dstDir, "file1-1")
   233  
   234  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   235  
   236  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   237  
   238  	// Now try again but using a trailing path separator for dstDir.
   239  
   240  	// unable to remove dstDir
   241  	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
   242  
   243  	dstDir = cpPathTrailingSep(tmpDir, "testDir")
   244  
   245  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   246  
   247  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   248  }
   249  
   250  // F. SRC specifies a directory and DST exists as a file. This should cause an
   251  //    error as it is not possible to overwrite a file with a directory.
   252  func (s *DockerSuite) TestCpFromCaseF(c *check.C) {
   253  	testRequires(c, DaemonIsLinux)
   254  	containerID := makeTestContainer(c, testContainerOptions{
   255  		addContent: true, workDir: "/root",
   256  	})
   257  
   258  	tmpDir := getTestDir(c, "test-cp-from-case-f")
   259  	defer os.RemoveAll(tmpDir)
   260  
   261  	makeTestContentInDir(c, tmpDir)
   262  
   263  	srcDir := containerCpPath(containerID, "/root/dir1")
   264  	dstFile := cpPath(tmpDir, "file1")
   265  
   266  	err := runDockerCp(c, srcDir, dstFile, nil)
   267  	assert.ErrorContains(c, err, "")
   268  
   269  	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
   270  }
   271  
   272  // G. SRC specifies a directory and DST exists as a directory. This should copy
   273  //    the SRC directory and all its contents to the DST directory. Ensure this
   274  //    works whether DST has a trailing path separator or not.
   275  func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
   276  	testRequires(c, DaemonIsLinux)
   277  	containerID := makeTestContainer(c, testContainerOptions{
   278  		addContent: true, workDir: "/root",
   279  	})
   280  
   281  	tmpDir := getTestDir(c, "test-cp-from-case-g")
   282  	defer os.RemoveAll(tmpDir)
   283  
   284  	makeTestContentInDir(c, tmpDir)
   285  
   286  	srcDir := containerCpPath(containerID, "/root/dir1")
   287  	dstDir := cpPath(tmpDir, "dir2")
   288  	resultDir := filepath.Join(dstDir, "dir1")
   289  	dstPath := filepath.Join(resultDir, "file1-1")
   290  
   291  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   292  
   293  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   294  
   295  	// Now try again but using a trailing path separator for dstDir.
   296  
   297  	// unable to remove dstDir
   298  	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
   299  
   300  	// unable to make dstDir
   301  	c.Assert(os.MkdirAll(dstDir, os.FileMode(0755)), checker.IsNil)
   302  
   303  	dstDir = cpPathTrailingSep(tmpDir, "dir2")
   304  
   305  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   306  
   307  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   308  }
   309  
   310  // H. SRC specifies a directory's contents only and DST does not exist. This
   311  //    should create a directory at DST and copy the contents of the SRC
   312  //    directory (but not the directory itself) into the DST directory. Ensure
   313  //    this works whether DST has a trailing path separator or not.
   314  func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
   315  	testRequires(c, DaemonIsLinux)
   316  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
   317  
   318  	tmpDir := getTestDir(c, "test-cp-from-case-h")
   319  	defer os.RemoveAll(tmpDir)
   320  
   321  	srcDir := containerCpPathTrailingSep(containerID, "dir1") + "."
   322  	dstDir := cpPath(tmpDir, "testDir")
   323  	dstPath := filepath.Join(dstDir, "file1-1")
   324  
   325  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   326  
   327  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   328  
   329  	// Now try again but using a trailing path separator for dstDir.
   330  
   331  	// unable to remove resultDir
   332  	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
   333  
   334  	dstDir = cpPathTrailingSep(tmpDir, "testDir")
   335  
   336  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   337  
   338  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   339  }
   340  
   341  // I. SRC specifies a directory's contents only and DST exists as a file. This
   342  //    should cause an error as it is not possible to overwrite a file with a
   343  //    directory.
   344  func (s *DockerSuite) TestCpFromCaseI(c *check.C) {
   345  	testRequires(c, DaemonIsLinux)
   346  	containerID := makeTestContainer(c, testContainerOptions{
   347  		addContent: true, workDir: "/root",
   348  	})
   349  
   350  	tmpDir := getTestDir(c, "test-cp-from-case-i")
   351  	defer os.RemoveAll(tmpDir)
   352  
   353  	makeTestContentInDir(c, tmpDir)
   354  
   355  	srcDir := containerCpPathTrailingSep(containerID, "/root/dir1") + "."
   356  	dstFile := cpPath(tmpDir, "file1")
   357  
   358  	err := runDockerCp(c, srcDir, dstFile, nil)
   359  	assert.ErrorContains(c, err, "")
   360  
   361  	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
   362  }
   363  
   364  // J. SRC specifies a directory's contents only and DST exists as a directory.
   365  //    This should copy the contents of the SRC directory (but not the directory
   366  //    itself) into the DST directory. Ensure this works whether DST has a
   367  //    trailing path separator or not.
   368  func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
   369  	testRequires(c, DaemonIsLinux)
   370  	containerID := makeTestContainer(c, testContainerOptions{
   371  		addContent: true, workDir: "/root",
   372  	})
   373  
   374  	tmpDir := getTestDir(c, "test-cp-from-case-j")
   375  	defer os.RemoveAll(tmpDir)
   376  
   377  	makeTestContentInDir(c, tmpDir)
   378  
   379  	srcDir := containerCpPathTrailingSep(containerID, "/root/dir1") + "."
   380  	dstDir := cpPath(tmpDir, "dir2")
   381  	dstPath := filepath.Join(dstDir, "file1-1")
   382  
   383  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   384  
   385  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   386  
   387  	// Now try again but using a trailing path separator for dstDir.
   388  
   389  	// unable to remove dstDir
   390  	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
   391  
   392  	// unable to make dstDir
   393  	c.Assert(os.MkdirAll(dstDir, os.FileMode(0755)), checker.IsNil)
   394  
   395  	dstDir = cpPathTrailingSep(tmpDir, "dir2")
   396  
   397  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   398  
   399  	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
   400  }