integration-cli/docker_cli_cp_utils_test.go
418135e7
 package main
 
 import (
 	"bytes"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
8a7ff5ff
 	"runtime"
418135e7
 	"strings"
e25352a4
 	"testing"
418135e7
 
 	"github.com/docker/docker/pkg/archive"
6345208b
 	"gotest.tools/assert"
418135e7
 )
 
6b3c9281
 type fileType uint32
418135e7
 
 const (
6b3c9281
 	ftRegular fileType = iota
 	ftDir
 	ftSymlink
418135e7
 )
 
6b3c9281
 type fileData struct {
 	filetype fileType
418135e7
 	path     string
 	contents string
8a7ff5ff
 	uid      int
 	gid      int
 	mode     int
418135e7
 }
 
6b3c9281
 func (fd fileData) creationCommand() string {
418135e7
 	var command string
 
 	switch fd.filetype {
6b3c9281
 	case ftRegular:
418135e7
 		// Don't overwrite the file if it already exists!
 		command = fmt.Sprintf("if [ ! -f %s ]; then echo %q > %s; fi", fd.path, fd.contents, fd.path)
6b3c9281
 	case ftDir:
418135e7
 		command = fmt.Sprintf("mkdir -p %s", fd.path)
6b3c9281
 	case ftSymlink:
418135e7
 		command = fmt.Sprintf("ln -fs %s %s", fd.contents, fd.path)
 	}
 
 	return command
 }
 
6b3c9281
 func mkFilesCommand(fds []fileData) string {
418135e7
 	commands := make([]string, len(fds))
 
 	for i, fd := range fds {
 		commands[i] = fd.creationCommand()
 	}
 
 	return strings.Join(commands, " && ")
 }
 
6b3c9281
 var defaultFileData = []fileData{
8a7ff5ff
 	{ftRegular, "file1", "file1", 0, 0, 0666},
 	{ftRegular, "file2", "file2", 0, 0, 0666},
 	{ftRegular, "file3", "file3", 0, 0, 0666},
 	{ftRegular, "file4", "file4", 0, 0, 0666},
 	{ftRegular, "file5", "file5", 0, 0, 0666},
 	{ftRegular, "file6", "file6", 0, 0, 0666},
 	{ftRegular, "file7", "file7", 0, 0, 0666},
 	{ftDir, "dir1", "", 0, 0, 0777},
 	{ftRegular, "dir1/file1-1", "file1-1", 0, 0, 0666},
 	{ftRegular, "dir1/file1-2", "file1-2", 0, 0, 0666},
 	{ftDir, "dir2", "", 0, 0, 0666},
 	{ftRegular, "dir2/file2-1", "file2-1", 0, 0, 0666},
 	{ftRegular, "dir2/file2-2", "file2-2", 0, 0, 0666},
 	{ftDir, "dir3", "", 0, 0, 0666},
 	{ftRegular, "dir3/file3-1", "file3-1", 0, 0, 0666},
 	{ftRegular, "dir3/file3-2", "file3-2", 0, 0, 0666},
 	{ftDir, "dir4", "", 0, 0, 0666},
 	{ftRegular, "dir4/file3-1", "file4-1", 0, 0, 0666},
 	{ftRegular, "dir4/file3-2", "file4-2", 0, 0, 0666},
 	{ftDir, "dir5", "", 0, 0, 0666},
 	{ftSymlink, "symlinkToFile1", "file1", 0, 0, 0666},
 	{ftSymlink, "symlinkToDir1", "dir1", 0, 0, 0666},
 	{ftSymlink, "brokenSymlinkToFileX", "fileX", 0, 0, 0666},
 	{ftSymlink, "brokenSymlinkToDirX", "dirX", 0, 0, 0666},
 	{ftSymlink, "symlinkToAbsDir", "/root", 0, 0, 0666},
 	{ftDir, "permdirtest", "", 2, 2, 0700},
 	{ftRegular, "permdirtest/permtest", "perm_test", 65534, 65534, 0400},
418135e7
 }
 
 func defaultMkContentCommand() string {
 	return mkFilesCommand(defaultFileData)
 }
 
64a928a3
 func makeTestContentInDir(c *testing.T, dir string) {
418135e7
 	for _, fd := range defaultFileData {
 		path := filepath.Join(dir, filepath.FromSlash(fd.path))
 		switch fd.filetype {
6b3c9281
 		case ftRegular:
6345208b
 			assert.NilError(c, ioutil.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(fd.mode)))
6b3c9281
 		case ftDir:
6345208b
 			assert.NilError(c, os.Mkdir(path, os.FileMode(fd.mode)))
6b3c9281
 		case ftSymlink:
6345208b
 			assert.NilError(c, os.Symlink(fd.contents, path))
418135e7
 		}
8a7ff5ff
 
 		if fd.filetype != ftSymlink && runtime.GOOS != "windows" {
6345208b
 			assert.NilError(c, os.Chown(path, fd.uid, fd.gid))
8a7ff5ff
 		}
418135e7
 	}
 }
 
 type testContainerOptions struct {
 	addContent bool
 	readOnly   bool
 	volumes    []string
 	workDir    string
 	command    string
 }
 
64a928a3
 func makeTestContainer(c *testing.T, options testContainerOptions) (containerID string) {
418135e7
 	if options.addContent {
 		mkContentCmd := defaultMkContentCommand()
 		if options.command == "" {
 			options.command = mkContentCmd
 		} else {
 			options.command = fmt.Sprintf("%s && %s", defaultMkContentCommand(), options.command)
 		}
 	}
 
 	if options.command == "" {
 		options.command = "#(nop)"
 	}
 
 	args := []string{"run", "-d"}
 
 	for _, volume := range options.volumes {
 		args = append(args, "-v", volume)
 	}
 
 	if options.workDir != "" {
 		args = append(args, "-w", options.workDir)
 	}
 
 	if options.readOnly {
 		args = append(args, "--read-only")
 	}
 
 	args = append(args, "busybox", "/bin/sh", "-c", options.command)
 
f26a31e8
 	out, _ := dockerCmd(c, args...)
418135e7
 
 	containerID = strings.TrimSpace(out)
 
f26a31e8
 	out, _ = dockerCmd(c, "wait", containerID)
418135e7
 
f26a31e8
 	exitCode := strings.TrimSpace(out)
 	if exitCode != "0" {
 		out, _ = dockerCmd(c, "logs", containerID)
418135e7
 	}
6345208b
 	assert.Equal(c, exitCode, "0", "failed to make test container: %s", out)
418135e7
 
 	return
 }
 
 func makeCatFileCommand(path string) string {
 	return fmt.Sprintf("if [ -f %s ]; then cat %s; fi", path, path)
 }
 
 func cpPath(pathElements ...string) string {
 	localizedPathElements := make([]string, len(pathElements))
 	for i, path := range pathElements {
 		localizedPathElements[i] = filepath.FromSlash(path)
 	}
 	return strings.Join(localizedPathElements, string(filepath.Separator))
 }
 
 func cpPathTrailingSep(pathElements ...string) string {
 	return fmt.Sprintf("%s%c", cpPath(pathElements...), filepath.Separator)
 }
 
 func containerCpPath(containerID string, pathElements ...string) string {
 	joined := strings.Join(pathElements, "/")
 	return fmt.Sprintf("%s:%s", containerID, joined)
 }
 
 func containerCpPathTrailingSep(containerID string, pathElements ...string) string {
 	return fmt.Sprintf("%s/", containerCpPath(containerID, pathElements...))
 }
 
64a928a3
 func runDockerCp(c *testing.T, src, dst string, params []string) (err error) {
8a7ff5ff
 	c.Logf("running `docker cp %s %s %s`", strings.Join(params, " "), src, dst)
 
 	args := []string{"cp"}
 
94cefa21
 	args = append(args, params...)
418135e7
 
8a7ff5ff
 	args = append(args, src, dst)
418135e7
 
 	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
 	if err != nil {
 		err = fmt.Errorf("error executing `docker cp` command: %s: %s", err, out)
 	}
 
 	return
 }
 
64a928a3
 func startContainerGetOutput(c *testing.T, containerID string) (out string, err error) {
f26a31e8
 	c.Logf("running `docker start -a %s`", containerID)
418135e7
 
f26a31e8
 	args := []string{"start", "-a", containerID}
418135e7
 
 	out, _, err = runCommandWithOutput(exec.Command(dockerBinary, args...))
 	if err != nil {
 		err = fmt.Errorf("error executing `docker start` command: %s: %s", err, out)
 	}
 
 	return
 }
 
64a928a3
 func getTestDir(c *testing.T, label string) (tmpDir string) {
418135e7
 	var err error
 
f26a31e8
 	tmpDir, err = ioutil.TempDir("", label)
 	// unable to make temporary directory
6345208b
 	assert.NilError(c, err)
418135e7
 
 	return
 }
 
 func isCpDirNotExist(err error) bool {
 	return strings.Contains(err.Error(), archive.ErrDirNotExists.Error())
 }
 
 func isCpCannotCopyDir(err error) bool {
 	return strings.Contains(err.Error(), archive.ErrCannotCopyDir.Error())
 }
 
 func isCpCannotCopyReadOnly(err error) bool {
 	return strings.Contains(err.Error(), "marked read-only")
 }
 
64a928a3
 func fileContentEquals(c *testing.T, filename, contents string) (err error) {
418135e7
 	c.Logf("checking that file %q contains %q\n", filename, contents)
 
 	fileBytes, err := ioutil.ReadFile(filename)
 	if err != nil {
 		return
 	}
 
 	expectedBytes, err := ioutil.ReadAll(strings.NewReader(contents))
 	if err != nil {
 		return
 	}
 
 	if !bytes.Equal(fileBytes, expectedBytes) {
 		err = fmt.Errorf("file content not equal - expected %q, got %q", string(expectedBytes), string(fileBytes))
 	}
 
 	return
 }
 
64a928a3
 func symlinkTargetEquals(c *testing.T, symlink, expectedTarget string) (err error) {
75f6929b
 	c.Logf("checking that the symlink %q points to %q\n", symlink, expectedTarget)
 
 	actualTarget, err := os.Readlink(symlink)
 	if err != nil {
f26a31e8
 		return
75f6929b
 	}
 
 	if actualTarget != expectedTarget {
f26a31e8
 		err = fmt.Errorf("symlink target points to %q not %q", actualTarget, expectedTarget)
75f6929b
 	}
 
f26a31e8
 	return
75f6929b
 }
 
64a928a3
 func containerStartOutputEquals(c *testing.T, containerID, contents string) (err error) {
f26a31e8
 	c.Logf("checking that container %q start output contains %q\n", containerID, contents)
418135e7
 
f26a31e8
 	out, err := startContainerGetOutput(c, containerID)
418135e7
 	if err != nil {
f26a31e8
 		return
418135e7
 	}
 
 	if out != contents {
 		err = fmt.Errorf("output contents not equal - expected %q, got %q", contents, out)
 	}
 
 	return
 }
 
 func defaultVolumes(tmpDir string) []string {
43b15e92
 	if testEnv.IsLocalDaemon() {
418135e7
 		return []string{
 			"/vol1",
 			fmt.Sprintf("%s:/vol2", tmpDir),
 			fmt.Sprintf("%s:/vol3", filepath.Join(tmpDir, "vol3")),
 			fmt.Sprintf("%s:/vol_ro:ro", filepath.Join(tmpDir, "vol_ro")),
 		}
 	}
 
 	// Can't bind-mount volumes with separate host daemon.
 	return []string{"/vol1", "/vol2", "/vol3", "/vol_ro:/vol_ro:ro"}
 }