2017-10-30 17:52:08 -04:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2018-02-27 16:38:02 -05:00
|
|
|
"os"
|
2017-10-30 17:52:08 -04:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/docker/cli/internal/test"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/pkg/archive"
|
2020-02-22 12:12:14 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
|
|
|
"gotest.tools/v3/fs"
|
|
|
|
"gotest.tools/v3/skip"
|
2017-10-30 17:52:08 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestRunCopyWithInvalidArguments(t *testing.T) {
|
|
|
|
var testcases = []struct {
|
|
|
|
doc string
|
|
|
|
options copyOptions
|
|
|
|
expectedErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "copy between container",
|
|
|
|
options: copyOptions{
|
|
|
|
source: "first:/path",
|
|
|
|
destination: "second:/path",
|
|
|
|
},
|
|
|
|
expectedErr: "copying between containers is not supported",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "copy without a container",
|
|
|
|
options: copyOptions{
|
|
|
|
source: "./source",
|
|
|
|
destination: "./dest",
|
|
|
|
},
|
|
|
|
expectedErr: "must specify at least one container source",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.doc, func(t *testing.T) {
|
|
|
|
err := runCopy(test.NewFakeCli(nil), testcase.options)
|
2018-03-06 15:54:24 -05:00
|
|
|
assert.Error(t, err, testcase.expectedErr)
|
2017-10-30 17:52:08 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRunCopyFromContainerToStdout(t *testing.T) {
|
|
|
|
tarContent := "the tar content"
|
|
|
|
|
|
|
|
fakeClient := &fakeClient{
|
|
|
|
containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal("container", container))
|
2017-10-30 17:52:08 -04:00
|
|
|
return ioutil.NopCloser(strings.NewReader(tarContent)), types.ContainerPathStat{}, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
options := copyOptions{source: "container:/path", destination: "-"}
|
|
|
|
cli := test.NewFakeCli(fakeClient)
|
|
|
|
err := runCopy(cli, options)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(tarContent, cli.OutBuffer().String()))
|
|
|
|
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
|
2017-10-30 17:52:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRunCopyFromContainerToFilesystem(t *testing.T) {
|
|
|
|
destDir := fs.NewDir(t, "cp-test",
|
|
|
|
fs.WithFile("file1", "content\n"))
|
|
|
|
defer destDir.Remove()
|
|
|
|
|
|
|
|
fakeClient := &fakeClient{
|
|
|
|
containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal("container", container))
|
2017-10-30 17:52:08 -04:00
|
|
|
readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{})
|
|
|
|
return readCloser, types.ContainerPathStat{}, err
|
|
|
|
},
|
|
|
|
}
|
2020-09-04 16:23:13 -04:00
|
|
|
options := copyOptions{source: "container:/path", destination: destDir.Path(), quiet: true}
|
2017-10-30 17:52:08 -04:00
|
|
|
cli := test.NewFakeCli(fakeClient)
|
|
|
|
err := runCopy(cli, options)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal("", cli.OutBuffer().String()))
|
|
|
|
assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
|
2017-10-30 17:52:08 -04:00
|
|
|
|
|
|
|
content, err := ioutil.ReadFile(destDir.Join("file1"))
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal("content\n", string(content)))
|
2017-10-30 17:52:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing.T) {
|
|
|
|
destDir := fs.NewDir(t, "cp-test",
|
|
|
|
fs.WithFile("file1", "content\n"))
|
|
|
|
defer destDir.Remove()
|
|
|
|
|
|
|
|
fakeClient := &fakeClient{
|
|
|
|
containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal("container", container))
|
2017-10-30 17:52:08 -04:00
|
|
|
readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{})
|
|
|
|
return readCloser, types.ContainerPathStat{}, err
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
options := copyOptions{
|
|
|
|
source: "container:/path",
|
|
|
|
destination: destDir.Join("missing", "foo"),
|
|
|
|
}
|
|
|
|
cli := test.NewFakeCli(fakeClient)
|
|
|
|
err := runCopy(cli, options)
|
2018-03-06 14:03:47 -05:00
|
|
|
assert.ErrorContains(t, err, destDir.Join("missing"))
|
2017-10-30 17:52:08 -04:00
|
|
|
}
|
|
|
|
|
2018-02-27 16:38:02 -05:00
|
|
|
func TestRunCopyToContainerFromFileWithTrailingSlash(t *testing.T) {
|
|
|
|
srcFile := fs.NewFile(t, t.Name())
|
|
|
|
defer srcFile.Remove()
|
|
|
|
|
|
|
|
options := copyOptions{
|
|
|
|
source: srcFile.Path() + string(os.PathSeparator),
|
|
|
|
destination: "container:/path",
|
|
|
|
}
|
|
|
|
cli := test.NewFakeCli(&fakeClient{})
|
|
|
|
err := runCopy(cli, options)
|
2018-02-27 10:54:36 -05:00
|
|
|
|
|
|
|
expectedError := "not a directory"
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
expectedError = "The filename, directory name, or volume label syntax is incorrect"
|
|
|
|
}
|
|
|
|
assert.ErrorContains(t, err, expectedError)
|
2018-02-27 16:38:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRunCopyToContainerSourceDoesNotExist(t *testing.T) {
|
|
|
|
options := copyOptions{
|
|
|
|
source: "/does/not/exist",
|
|
|
|
destination: "container:/path",
|
|
|
|
}
|
|
|
|
cli := test.NewFakeCli(&fakeClient{})
|
|
|
|
err := runCopy(cli, options)
|
|
|
|
expected := "no such file or directory"
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
expected = "cannot find the file specified"
|
|
|
|
}
|
2018-03-06 14:03:47 -05:00
|
|
|
assert.ErrorContains(t, err, expected)
|
2018-02-27 16:38:02 -05:00
|
|
|
}
|
|
|
|
|
2017-10-30 17:52:08 -04:00
|
|
|
func TestSplitCpArg(t *testing.T) {
|
|
|
|
var testcases = []struct {
|
|
|
|
doc string
|
|
|
|
path string
|
|
|
|
os string
|
|
|
|
expectedContainer string
|
|
|
|
expectedPath string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "absolute path with colon",
|
|
|
|
os: "linux",
|
|
|
|
path: "/abs/path:withcolon",
|
|
|
|
expectedPath: "/abs/path:withcolon",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "relative path with colon",
|
|
|
|
path: "./relative:path",
|
|
|
|
expectedPath: "./relative:path",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "absolute path with drive",
|
|
|
|
os: "windows",
|
|
|
|
path: `d:\abs\path`,
|
|
|
|
expectedPath: `d:\abs\path`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "no separator",
|
|
|
|
path: "relative/path",
|
|
|
|
expectedPath: "relative/path",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "with separator",
|
|
|
|
path: "container:/opt/foo",
|
|
|
|
expectedPath: "/opt/foo",
|
|
|
|
expectedContainer: "container",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.doc, func(t *testing.T) {
|
2018-06-08 12:24:26 -04:00
|
|
|
skip.If(t, testcase.os != "" && testcase.os != runtime.GOOS)
|
2017-10-30 17:52:08 -04:00
|
|
|
|
|
|
|
container, path := splitCpArg(testcase.path)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal(testcase.expectedContainer, container))
|
|
|
|
assert.Check(t, is.Equal(testcase.expectedPath, path))
|
2017-10-30 17:52:08 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-02-07 03:17:35 -05:00
|
|
|
|
|
|
|
func TestRunCopyFromContainerToFilesystemIrregularDestination(t *testing.T) {
|
|
|
|
options := copyOptions{source: "container:/dev/null", destination: "/dev/random"}
|
|
|
|
cli := test.NewFakeCli(nil)
|
|
|
|
err := runCopy(cli, options)
|
|
|
|
assert.Assert(t, err != nil)
|
|
|
|
expected := `"/dev/random" must be a directory or a regular file`
|
|
|
|
assert.ErrorContains(t, err, expected)
|
|
|
|
}
|