github.com/slene/docker@v1.8.0-rc1/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/go-check/check"
     8  )
     9  
    10  // docker cp CONTAINER:PATH LOCALPATH
    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  // First get these easy error cases out of the way.
    23  
    24  // Test for error when SRC does not exist.
    25  func (s *DockerSuite) TestCpFromErrSrcNotExists(c *check.C) {
    26  	cID := makeTestContainer(c, testContainerOptions{})
    27  	defer deleteContainer(cID)
    28  
    29  	tmpDir := getTestDir(c, "test-cp-from-err-src-not-exists")
    30  	defer os.RemoveAll(tmpDir)
    31  
    32  	err := runDockerCp(c, containerCpPath(cID, "file1"), tmpDir)
    33  	if err == nil {
    34  		c.Fatal("expected IsNotExist error, but got nil instead")
    35  	}
    36  
    37  	if !isCpNotExist(err) {
    38  		c.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
    39  	}
    40  }
    41  
    42  // Test for error when SRC ends in a trailing
    43  // path separator but it exists as a file.
    44  func (s *DockerSuite) TestCpFromErrSrcNotDir(c *check.C) {
    45  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
    46  	defer deleteContainer(cID)
    47  
    48  	tmpDir := getTestDir(c, "test-cp-from-err-src-not-dir")
    49  	defer os.RemoveAll(tmpDir)
    50  
    51  	err := runDockerCp(c, containerCpPathTrailingSep(cID, "file1"), tmpDir)
    52  	if err == nil {
    53  		c.Fatal("expected IsNotDir error, but got nil instead")
    54  	}
    55  
    56  	if !isCpNotDir(err) {
    57  		c.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
    58  	}
    59  }
    60  
    61  // Test for error when SRC is a valid file or directory,
    62  // bu the DST parent directory does not exist.
    63  func (s *DockerSuite) TestCpFromErrDstParentNotExists(c *check.C) {
    64  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
    65  	defer deleteContainer(cID)
    66  
    67  	tmpDir := getTestDir(c, "test-cp-from-err-dst-parent-not-exists")
    68  	defer os.RemoveAll(tmpDir)
    69  
    70  	makeTestContentInDir(c, tmpDir)
    71  
    72  	// Try with a file source.
    73  	srcPath := containerCpPath(cID, "/file1")
    74  	dstPath := cpPath(tmpDir, "notExists", "file1")
    75  
    76  	err := runDockerCp(c, srcPath, dstPath)
    77  	if err == nil {
    78  		c.Fatal("expected IsNotExist error, but got nil instead")
    79  	}
    80  
    81  	if !isCpNotExist(err) {
    82  		c.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
    83  	}
    84  
    85  	// Try with a directory source.
    86  	srcPath = containerCpPath(cID, "/dir1")
    87  
    88  	if err := runDockerCp(c, srcPath, dstPath); err == nil {
    89  		c.Fatal("expected IsNotExist error, but got nil instead")
    90  	}
    91  
    92  	if !isCpNotExist(err) {
    93  		c.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
    94  	}
    95  }
    96  
    97  // Test for error when DST ends in a trailing
    98  // path separator but exists as a file.
    99  func (s *DockerSuite) TestCpFromErrDstNotDir(c *check.C) {
   100  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
   101  	defer deleteContainer(cID)
   102  
   103  	tmpDir := getTestDir(c, "test-cp-from-err-dst-not-dir")
   104  	defer os.RemoveAll(tmpDir)
   105  
   106  	makeTestContentInDir(c, tmpDir)
   107  
   108  	// Try with a file source.
   109  	srcPath := containerCpPath(cID, "/file1")
   110  	dstPath := cpPathTrailingSep(tmpDir, "file1")
   111  
   112  	err := runDockerCp(c, srcPath, dstPath)
   113  	if err == nil {
   114  		c.Fatal("expected IsNotDir error, but got nil instead")
   115  	}
   116  
   117  	if !isCpNotDir(err) {
   118  		c.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   119  	}
   120  
   121  	// Try with a directory source.
   122  	srcPath = containerCpPath(cID, "/dir1")
   123  
   124  	if err := runDockerCp(c, srcPath, dstPath); err == nil {
   125  		c.Fatal("expected IsNotDir error, but got nil instead")
   126  	}
   127  
   128  	if !isCpNotDir(err) {
   129  		c.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   130  	}
   131  }
   132  
   133  // Possibilities are reduced to the remaining 10 cases:
   134  //
   135  //  case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
   136  // ===================================================================================================
   137  //   A   |  no      |  -              |  no       |  -       |  no      |  create file
   138  //   B   |  no      |  -              |  no       |  -       |  yes     |  error
   139  //   C   |  no      |  -              |  yes      |  no      |  -       |  overwrite file
   140  //   D   |  no      |  -              |  yes      |  yes     |  -       |  create file in dst dir
   141  //   E   |  yes     |  no             |  no       |  -       |  -       |  create dir, copy contents
   142  //   F   |  yes     |  no             |  yes      |  no      |  -       |  error
   143  //   G   |  yes     |  no             |  yes      |  yes     |  -       |  copy dir and contents
   144  //   H   |  yes     |  yes            |  no       |  -       |  -       |  create dir, copy contents
   145  //   I   |  yes     |  yes            |  yes      |  no      |  -       |  error
   146  //   J   |  yes     |  yes            |  yes      |  yes     |  -       |  copy dir contents
   147  //
   148  
   149  // A. SRC specifies a file and DST (no trailing path separator) doesn't
   150  //    exist. This should create a file with the name DST and copy the
   151  //    contents of the source file into it.
   152  func (s *DockerSuite) TestCpFromCaseA(c *check.C) {
   153  	cID := makeTestContainer(c, testContainerOptions{
   154  		addContent: true, workDir: "/root",
   155  	})
   156  	defer deleteContainer(cID)
   157  
   158  	tmpDir := getTestDir(c, "test-cp-from-case-a")
   159  	defer os.RemoveAll(tmpDir)
   160  
   161  	srcPath := containerCpPath(cID, "/root/file1")
   162  	dstPath := cpPath(tmpDir, "itWorks.txt")
   163  
   164  	if err := runDockerCp(c, srcPath, dstPath); err != nil {
   165  		c.Fatalf("unexpected error %T: %s", err, err)
   166  	}
   167  
   168  	if err := fileContentEquals(c, dstPath, "file1\n"); err != nil {
   169  		c.Fatal(err)
   170  	}
   171  }
   172  
   173  // B. SRC specifies a file and DST (with trailing path separator) doesn't
   174  //    exist. This should cause an error because the copy operation cannot
   175  //    create a directory when copying a single file.
   176  func (s *DockerSuite) TestCpFromCaseB(c *check.C) {
   177  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
   178  	defer deleteContainer(cID)
   179  
   180  	tmpDir := getTestDir(c, "test-cp-from-case-b")
   181  	defer os.RemoveAll(tmpDir)
   182  
   183  	srcPath := containerCpPath(cID, "/file1")
   184  	dstDir := cpPathTrailingSep(tmpDir, "testDir")
   185  
   186  	err := runDockerCp(c, srcPath, dstDir)
   187  	if err == nil {
   188  		c.Fatal("expected DirNotExists error, but got nil instead")
   189  	}
   190  
   191  	if !isCpDirNotExist(err) {
   192  		c.Fatalf("expected DirNotExists error, but got %T: %s", err, err)
   193  	}
   194  }
   195  
   196  // C. SRC specifies a file and DST exists as a file. This should overwrite
   197  //    the file at DST with the contents of the source file.
   198  func (s *DockerSuite) TestCpFromCaseC(c *check.C) {
   199  	cID := makeTestContainer(c, testContainerOptions{
   200  		addContent: true, workDir: "/root",
   201  	})
   202  	defer deleteContainer(cID)
   203  
   204  	tmpDir := getTestDir(c, "test-cp-from-case-c")
   205  	defer os.RemoveAll(tmpDir)
   206  
   207  	makeTestContentInDir(c, tmpDir)
   208  
   209  	srcPath := containerCpPath(cID, "/root/file1")
   210  	dstPath := cpPath(tmpDir, "file2")
   211  
   212  	// Ensure the local file starts with different content.
   213  	if err := fileContentEquals(c, dstPath, "file2\n"); err != nil {
   214  		c.Fatal(err)
   215  	}
   216  
   217  	if err := runDockerCp(c, srcPath, dstPath); err != nil {
   218  		c.Fatalf("unexpected error %T: %s", err, err)
   219  	}
   220  
   221  	if err := fileContentEquals(c, dstPath, "file1\n"); err != nil {
   222  		c.Fatal(err)
   223  	}
   224  }
   225  
   226  // D. SRC specifies a file and DST exists as a directory. This should place
   227  //    a copy of the source file inside it using the basename from SRC. Ensure
   228  //    this works whether DST has a trailing path separator or not.
   229  func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
   230  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
   231  	defer deleteContainer(cID)
   232  
   233  	tmpDir := getTestDir(c, "test-cp-from-case-d")
   234  	defer os.RemoveAll(tmpDir)
   235  
   236  	makeTestContentInDir(c, tmpDir)
   237  
   238  	srcPath := containerCpPath(cID, "/file1")
   239  	dstDir := cpPath(tmpDir, "dir1")
   240  	dstPath := filepath.Join(dstDir, "file1")
   241  
   242  	// Ensure that dstPath doesn't exist.
   243  	if _, err := os.Stat(dstPath); !os.IsNotExist(err) {
   244  		c.Fatalf("did not expect dstPath %q to exist", dstPath)
   245  	}
   246  
   247  	if err := runDockerCp(c, srcPath, dstDir); err != nil {
   248  		c.Fatalf("unexpected error %T: %s", err, err)
   249  	}
   250  
   251  	if err := fileContentEquals(c, dstPath, "file1\n"); err != nil {
   252  		c.Fatal(err)
   253  	}
   254  
   255  	// Now try again but using a trailing path separator for dstDir.
   256  
   257  	if err := os.RemoveAll(dstDir); err != nil {
   258  		c.Fatalf("unable to remove dstDir: %s", err)
   259  	}
   260  
   261  	if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   262  		c.Fatalf("unable to make dstDir: %s", err)
   263  	}
   264  
   265  	dstDir = cpPathTrailingSep(tmpDir, "dir1")
   266  
   267  	if err := runDockerCp(c, srcPath, dstDir); err != nil {
   268  		c.Fatalf("unexpected error %T: %s", err, err)
   269  	}
   270  
   271  	if err := fileContentEquals(c, dstPath, "file1\n"); err != nil {
   272  		c.Fatal(err)
   273  	}
   274  }
   275  
   276  // E. SRC specifies a directory and DST does not exist. This should create a
   277  //    directory at DST and copy the contents of the SRC directory into the DST
   278  //    directory. Ensure this works whether DST has a trailing path separator or
   279  //    not.
   280  func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
   281  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
   282  	defer deleteContainer(cID)
   283  
   284  	tmpDir := getTestDir(c, "test-cp-from-case-e")
   285  	defer os.RemoveAll(tmpDir)
   286  
   287  	srcDir := containerCpPath(cID, "dir1")
   288  	dstDir := cpPath(tmpDir, "testDir")
   289  	dstPath := filepath.Join(dstDir, "file1-1")
   290  
   291  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   292  		c.Fatalf("unexpected error %T: %s", err, err)
   293  	}
   294  
   295  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   296  		c.Fatal(err)
   297  	}
   298  
   299  	// Now try again but using a trailing path separator for dstDir.
   300  
   301  	if err := os.RemoveAll(dstDir); err != nil {
   302  		c.Fatalf("unable to remove dstDir: %s", err)
   303  	}
   304  
   305  	dstDir = cpPathTrailingSep(tmpDir, "testDir")
   306  
   307  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   308  		c.Fatalf("unexpected error %T: %s", err, err)
   309  	}
   310  
   311  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   312  		c.Fatal(err)
   313  	}
   314  }
   315  
   316  // F. SRC specifies a directory and DST exists as a file. This should cause an
   317  //    error as it is not possible to overwrite a file with a directory.
   318  func (s *DockerSuite) TestCpFromCaseF(c *check.C) {
   319  	cID := makeTestContainer(c, testContainerOptions{
   320  		addContent: true, workDir: "/root",
   321  	})
   322  	defer deleteContainer(cID)
   323  
   324  	tmpDir := getTestDir(c, "test-cp-from-case-f")
   325  	defer os.RemoveAll(tmpDir)
   326  
   327  	makeTestContentInDir(c, tmpDir)
   328  
   329  	srcDir := containerCpPath(cID, "/root/dir1")
   330  	dstFile := cpPath(tmpDir, "file1")
   331  
   332  	err := runDockerCp(c, srcDir, dstFile)
   333  	if err == nil {
   334  		c.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   335  	}
   336  
   337  	if !isCpCannotCopyDir(err) {
   338  		c.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   339  	}
   340  }
   341  
   342  // G. SRC specifies a directory and DST exists as a directory. This should copy
   343  //    the SRC directory and all its contents to the DST directory. Ensure this
   344  //    works whether DST has a trailing path separator or not.
   345  func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
   346  	cID := makeTestContainer(c, testContainerOptions{
   347  		addContent: true, workDir: "/root",
   348  	})
   349  	defer deleteContainer(cID)
   350  
   351  	tmpDir := getTestDir(c, "test-cp-from-case-g")
   352  	defer os.RemoveAll(tmpDir)
   353  
   354  	makeTestContentInDir(c, tmpDir)
   355  
   356  	srcDir := containerCpPath(cID, "/root/dir1")
   357  	dstDir := cpPath(tmpDir, "dir2")
   358  	resultDir := filepath.Join(dstDir, "dir1")
   359  	dstPath := filepath.Join(resultDir, "file1-1")
   360  
   361  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   362  		c.Fatalf("unexpected error %T: %s", err, err)
   363  	}
   364  
   365  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   366  		c.Fatal(err)
   367  	}
   368  
   369  	// Now try again but using a trailing path separator for dstDir.
   370  
   371  	if err := os.RemoveAll(dstDir); err != nil {
   372  		c.Fatalf("unable to remove dstDir: %s", err)
   373  	}
   374  
   375  	if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   376  		c.Fatalf("unable to make dstDir: %s", err)
   377  	}
   378  
   379  	dstDir = cpPathTrailingSep(tmpDir, "dir2")
   380  
   381  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   382  		c.Fatalf("unexpected error %T: %s", err, err)
   383  	}
   384  
   385  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   386  		c.Fatal(err)
   387  	}
   388  }
   389  
   390  // H. SRC specifies a directory's contents only and DST does not exist. This
   391  //    should create a directory at DST and copy the contents of the SRC
   392  //    directory (but not the directory itself) into the DST directory. Ensure
   393  //    this works whether DST has a trailing path separator or not.
   394  func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
   395  	cID := makeTestContainer(c, testContainerOptions{addContent: true})
   396  	defer deleteContainer(cID)
   397  
   398  	tmpDir := getTestDir(c, "test-cp-from-case-h")
   399  	defer os.RemoveAll(tmpDir)
   400  
   401  	srcDir := containerCpPathTrailingSep(cID, "dir1") + "."
   402  	dstDir := cpPath(tmpDir, "testDir")
   403  	dstPath := filepath.Join(dstDir, "file1-1")
   404  
   405  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   406  		c.Fatalf("unexpected error %T: %s", err, err)
   407  	}
   408  
   409  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   410  		c.Fatal(err)
   411  	}
   412  
   413  	// Now try again but using a trailing path separator for dstDir.
   414  
   415  	if err := os.RemoveAll(dstDir); err != nil {
   416  		c.Fatalf("unable to remove resultDir: %s", err)
   417  	}
   418  
   419  	dstDir = cpPathTrailingSep(tmpDir, "testDir")
   420  
   421  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   422  		c.Fatalf("unexpected error %T: %s", err, err)
   423  	}
   424  
   425  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   426  		c.Fatal(err)
   427  	}
   428  }
   429  
   430  // I. SRC specifies a direcotry'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  	cID := makeTestContainer(c, testContainerOptions{
   435  		addContent: true, workDir: "/root",
   436  	})
   437  	defer deleteContainer(cID)
   438  
   439  	tmpDir := getTestDir(c, "test-cp-from-case-i")
   440  	defer os.RemoveAll(tmpDir)
   441  
   442  	makeTestContentInDir(c, tmpDir)
   443  
   444  	srcDir := containerCpPathTrailingSep(cID, "/root/dir1") + "."
   445  	dstFile := cpPath(tmpDir, "file1")
   446  
   447  	err := runDockerCp(c, srcDir, dstFile)
   448  	if err == nil {
   449  		c.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   450  	}
   451  
   452  	if !isCpCannotCopyDir(err) {
   453  		c.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   454  	}
   455  }
   456  
   457  // J. SRC specifies a directory's contents only and DST exists as a directory.
   458  //    This should copy the contents of the SRC directory (but not the directory
   459  //    itself) into the DST directory. Ensure this works whether DST has a
   460  //    trailing path separator or not.
   461  func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
   462  	cID := makeTestContainer(c, testContainerOptions{
   463  		addContent: true, workDir: "/root",
   464  	})
   465  	defer deleteContainer(cID)
   466  
   467  	tmpDir := getTestDir(c, "test-cp-from-case-j")
   468  	defer os.RemoveAll(tmpDir)
   469  
   470  	makeTestContentInDir(c, tmpDir)
   471  
   472  	srcDir := containerCpPathTrailingSep(cID, "/root/dir1") + "."
   473  	dstDir := cpPath(tmpDir, "dir2")
   474  	dstPath := filepath.Join(dstDir, "file1-1")
   475  
   476  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   477  		c.Fatalf("unexpected error %T: %s", err, err)
   478  	}
   479  
   480  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   481  		c.Fatal(err)
   482  	}
   483  
   484  	// Now try again but using a trailing path separator for dstDir.
   485  
   486  	if err := os.RemoveAll(dstDir); err != nil {
   487  		c.Fatalf("unable to remove dstDir: %s", err)
   488  	}
   489  
   490  	if err := os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   491  		c.Fatalf("unable to make dstDir: %s", err)
   492  	}
   493  
   494  	dstDir = cpPathTrailingSep(tmpDir, "dir2")
   495  
   496  	if err := runDockerCp(c, srcDir, dstDir); err != nil {
   497  		c.Fatalf("unexpected error %T: %s", err, err)
   498  	}
   499  
   500  	if err := fileContentEquals(c, dstPath, "file1-1\n"); err != nil {
   501  		c.Fatal(err)
   502  	}
   503  }