feat: abra app cp enhancements
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
cb49cf06d1
commit
2de6934322
372
cli/app/cp.go
372
cli/app/cp.go
@ -2,19 +2,24 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/autocomplete"
|
||||
"coopcloud.tech/abra/pkg/client"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/container"
|
||||
containerPkg "coopcloud.tech/abra/pkg/container"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"coopcloud.tech/abra/pkg/upstream/container"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
@ -49,46 +54,14 @@ And if you want to copy that file back to your current working directory locally
|
||||
dst := c.Args().Get(2)
|
||||
if src == "" {
|
||||
logrus.Fatal("missing <src> argument")
|
||||
} else if dst == "" {
|
||||
}
|
||||
if dst == "" {
|
||||
logrus.Fatal("missing <dest> argument")
|
||||
}
|
||||
|
||||
parsedSrc := strings.SplitN(src, ":", 2)
|
||||
parsedDst := strings.SplitN(dst, ":", 2)
|
||||
errorMsg := "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
|
||||
if len(parsedSrc) == 2 && len(parsedDst) == 2 {
|
||||
logrus.Fatal(errorMsg)
|
||||
} else if len(parsedSrc) != 2 {
|
||||
if len(parsedDst) != 2 {
|
||||
logrus.Fatal(errorMsg)
|
||||
}
|
||||
} else if len(parsedDst) != 2 {
|
||||
if len(parsedSrc) != 2 {
|
||||
logrus.Fatal(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
var service string
|
||||
var srcPath string
|
||||
var dstPath string
|
||||
isToContainer := false // <container:src> <dst>
|
||||
if len(parsedSrc) == 2 {
|
||||
service = parsedSrc[0]
|
||||
srcPath = parsedSrc[1]
|
||||
dstPath = dst
|
||||
logrus.Debugf("assuming transfer is coming FROM the container")
|
||||
} else if len(parsedDst) == 2 {
|
||||
service = parsedDst[0]
|
||||
dstPath = parsedDst[1]
|
||||
srcPath = src
|
||||
isToContainer = true // <src> <container:dst>
|
||||
logrus.Debugf("assuming transfer is going TO the container")
|
||||
}
|
||||
|
||||
if isToContainer {
|
||||
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
|
||||
logrus.Fatalf("%s does not exist locally?", srcPath)
|
||||
}
|
||||
srcPath, dstPath, service, toContainer, err := parseSrcAndDst(src, dst)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
cl, err := client.New(app.Server)
|
||||
@ -96,7 +69,18 @@ And if you want to copy that file back to your current working directory locally
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
if err := configureAndCp(c, cl, app, srcPath, dstPath, service, isToContainer); err != nil {
|
||||
container, err := containerPkg.GetContainerFromStackAndService(cl, app.StackName(), service)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
|
||||
|
||||
if toContainer {
|
||||
err = copyToContainer(cl, container.ID, srcPath, dstPath)
|
||||
} else {
|
||||
err = copyFromContainer(cl, container.ID, srcPath, dstPath)
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
@ -104,46 +88,292 @@ And if you want to copy that file back to your current working directory locally
|
||||
},
|
||||
}
|
||||
|
||||
func configureAndCp(
|
||||
c *cli.Context,
|
||||
cl *dockerClient.Client,
|
||||
app config.App,
|
||||
srcPath string,
|
||||
dstPath string,
|
||||
service string,
|
||||
isToContainer bool) error {
|
||||
filters := filters.NewArgs()
|
||||
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), service))
|
||||
var errServiceMissing = errors.New("one of <src>/<dest> arguments must take $SERVICE:$PATH form")
|
||||
|
||||
container, err := container.GetContainer(context.Background(), cl, filters, internal.NoInput)
|
||||
// parseSrcAndDst parses src and dest string. One of src or dst must be of the form $SERVICE:$PATH
|
||||
func parseSrcAndDst(src, dst string) (srcPath string, dstPath string, service string, toContainer bool, err error) {
|
||||
parsedSrc := strings.SplitN(src, ":", 2)
|
||||
parsedDst := strings.SplitN(dst, ":", 2)
|
||||
if len(parsedSrc)+len(parsedDst) != 3 {
|
||||
return "", "", "", false, errServiceMissing
|
||||
}
|
||||
if len(parsedSrc) == 2 {
|
||||
return parsedSrc[1], dst, parsedSrc[0], false, nil
|
||||
}
|
||||
if len(parsedDst) == 2 {
|
||||
return src, parsedDst[1], parsedDst[0], true, nil
|
||||
}
|
||||
return "", "", "", false, errServiceMissing
|
||||
}
|
||||
|
||||
// copyToContainer copies a file or directory from the local file system to the container.
|
||||
// See the possible copy modes and their documentation.
|
||||
func copyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath string) error {
|
||||
srcStat, err := os.Stat(srcPath)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
return fmt.Errorf("local %s ", err)
|
||||
}
|
||||
|
||||
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
|
||||
dstStat, err := cl.ContainerStatPath(context.Background(), containerID, dstPath)
|
||||
dstExists := true
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
dstExists = false
|
||||
} else {
|
||||
return fmt.Errorf("remote path: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if isToContainer {
|
||||
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
||||
content, err := archive.TarWithOptions(srcPath, toTarOpts)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
mode, err := copyMode(srcPath, dstPath, srcStat.Mode(), dstStat.Mode, dstExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
movePath := ""
|
||||
switch mode {
|
||||
case CopyModeDirToDir:
|
||||
// Add the src directory to the destination path
|
||||
_, srcDir := path.Split(srcPath)
|
||||
dstPath = path.Join(dstPath, srcDir)
|
||||
|
||||
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
||||
if err := cl.CopyToContainer(context.Background(), container.ID, dstPath, content, copyOpts); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
content, _, err := cl.CopyFromContainer(context.Background(), container.ID, srcPath)
|
||||
// Make sure the dst directory exits.
|
||||
dcli, err := command.NewDockerCli()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
return err
|
||||
}
|
||||
defer content.Close()
|
||||
fromTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
||||
if err := archive.Untar(content, dstPath, fromTarOpts); err != nil {
|
||||
logrus.Fatal(err)
|
||||
if err := container.RunExec(dcli, cl, containerID, &types.ExecConfig{
|
||||
AttachStderr: true,
|
||||
AttachStdin: true,
|
||||
AttachStdout: true,
|
||||
Cmd: []string{"mkdir", "-p", dstPath},
|
||||
Detach: false,
|
||||
Tty: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("create remote directory: %s", err)
|
||||
}
|
||||
case CopyModeFileToFile:
|
||||
// Remove the file component from the path, since docker can only copy
|
||||
// to a directory.
|
||||
dstPath, _ = path.Split(dstPath)
|
||||
case CopyModeFileToFileRename:
|
||||
// Copy the file to the temp directory and move it to its dstPath
|
||||
// afterwards.
|
||||
movePath = dstPath
|
||||
dstPath = "/tmp"
|
||||
}
|
||||
|
||||
toTarOpts := &archive.TarOptions{IncludeSourceDir: true, NoOverwriteDirNonDir: true, Compression: archive.Gzip}
|
||||
content, err := archive.TarWithOptions(srcPath, toTarOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("copy %s from local to %s on container", srcPath, dstPath)
|
||||
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
|
||||
if err := cl.CopyToContainer(context.Background(), containerID, dstPath, content, copyOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if movePath != "" {
|
||||
_, srcFile := path.Split(srcPath)
|
||||
dcli, err := command.NewDockerCli()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.RunExec(dcli, cl, containerID, &types.ExecConfig{
|
||||
AttachStderr: true,
|
||||
AttachStdin: true,
|
||||
AttachStdout: true,
|
||||
Cmd: []string{"mv", path.Join("/tmp", srcFile), movePath},
|
||||
Detach: false,
|
||||
Tty: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("create remote directory: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFromContainer copies a file or directory from the given container to the local file system.
|
||||
// See the possible copy modes and their documentation.
|
||||
func copyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath string) error {
|
||||
srcStat, err := cl.ContainerStatPath(context.Background(), containerID, srcPath)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
return fmt.Errorf("remote: %s does not exist", srcPath)
|
||||
} else {
|
||||
return fmt.Errorf("remote path: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
dstStat, err := os.Stat(dstPath)
|
||||
dstExists := true
|
||||
var dstMode os.FileMode
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
dstExists = false
|
||||
} else {
|
||||
return fmt.Errorf("remote path: %s", err)
|
||||
}
|
||||
} else {
|
||||
dstMode = dstStat.Mode()
|
||||
}
|
||||
|
||||
mode, err := copyMode(srcPath, dstPath, srcStat.Mode, dstMode, dstExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
moveDstDir := ""
|
||||
moveDstFile := ""
|
||||
switch mode {
|
||||
case CopyModeFileToFile:
|
||||
// Remove the file component from the path, since docker can only copy
|
||||
// to a directory.
|
||||
dstPath, _ = path.Split(dstPath)
|
||||
case CopyModeFileToFileRename:
|
||||
// Copy the file to the temp directory and move it to its dstPath
|
||||
// afterwards.
|
||||
moveDstFile = dstPath
|
||||
dstPath = "/tmp"
|
||||
case CopyModeFilesToDir:
|
||||
// Copy the directory to the temp directory and move it to its
|
||||
// dstPath afterwards.
|
||||
moveDstDir = path.Join(dstPath, "/")
|
||||
dstPath = "/tmp"
|
||||
|
||||
// Make sure the temp directory always gets removed
|
||||
defer os.Remove(path.Join("/tmp"))
|
||||
}
|
||||
|
||||
content, _, err := cl.CopyFromContainer(context.Background(), containerID, srcPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy: %s", err)
|
||||
}
|
||||
defer content.Close()
|
||||
if err := archive.Untar(content, dstPath, &archive.TarOptions{
|
||||
NoOverwriteDirNonDir: true,
|
||||
Compression: archive.Gzip,
|
||||
NoLchown: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("untar: %s", err)
|
||||
}
|
||||
|
||||
if moveDstFile != "" {
|
||||
_, srcFile := path.Split(strings.TrimSuffix(srcPath, "/"))
|
||||
if err := moveFile(path.Join("/tmp", srcFile), moveDstFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if moveDstDir != "" {
|
||||
_, srcDir := path.Split(strings.TrimSuffix(srcPath, "/"))
|
||||
if err := moveDir(path.Join("/tmp", srcDir), moveDstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrCopyDirToFile = fmt.Errorf("can't copy dir to file")
|
||||
ErrDstDirNotExist = fmt.Errorf("destination directory does not exist")
|
||||
)
|
||||
|
||||
type CopyMode int
|
||||
|
||||
const (
|
||||
// Copy a src file to a dest file. The src and dest file names are the same.
|
||||
// <dir_src>/<file> + <dir_dst>/<file> -> <dir_dst>/<file>
|
||||
CopyModeFileToFile = CopyMode(iota)
|
||||
// Copy a src file to a dest file. The src and dest file names are not the same.
|
||||
// <dir_src>/<file_src> + <dir_dst>/<file_dst> -> <dir_dst>/<file_dst>
|
||||
CopyModeFileToFileRename
|
||||
// Copy a src file to dest directory. The dest file gets created in the dest
|
||||
// folder with the src filename.
|
||||
// <dir_src>/<file> + <dir_dst> -> <dir_dst>/<file>
|
||||
CopyModeFileToDir
|
||||
// Copy a src directory to dest directory.
|
||||
// <dir_src> + <dir_dst> -> <dir_dst>/<dir_src>
|
||||
CopyModeDirToDir
|
||||
// Copy all files in the src directory to the dest directory. This works recursively.
|
||||
// <dir_src>/ + <dir_dst> -> <dir_dst>/<files_from_dir_src>
|
||||
CopyModeFilesToDir
|
||||
)
|
||||
|
||||
// copyMode takes a src and dest path and file mode to determine the copy mode.
|
||||
// See the possible copy modes and their documentation.
|
||||
func copyMode(srcPath, dstPath string, srcMode os.FileMode, dstMode os.FileMode, dstExists bool) (CopyMode, error) {
|
||||
_, srcFile := path.Split(srcPath)
|
||||
_, dstFile := path.Split(dstPath)
|
||||
if srcMode.IsDir() {
|
||||
if !dstExists {
|
||||
return -1, ErrDstDirNotExist
|
||||
}
|
||||
if dstMode.IsDir() {
|
||||
if strings.HasSuffix(srcPath, "/") {
|
||||
return CopyModeFilesToDir, nil
|
||||
}
|
||||
return CopyModeDirToDir, nil
|
||||
}
|
||||
return -1, ErrCopyDirToFile
|
||||
}
|
||||
|
||||
if dstMode.IsDir() {
|
||||
return CopyModeFileToDir, nil
|
||||
}
|
||||
|
||||
if srcFile != dstFile {
|
||||
return CopyModeFileToFileRename, nil
|
||||
}
|
||||
|
||||
return CopyModeFileToFile, nil
|
||||
}
|
||||
|
||||
// moveDir moves all files from a source path to the destination path recursively.
|
||||
func moveDir(sourcePath, destPath string) error {
|
||||
return filepath.Walk(sourcePath, func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newPath := path.Join(destPath, strings.TrimPrefix(p, sourcePath))
|
||||
if info.IsDir() {
|
||||
err := os.Mkdir(newPath, info.Mode())
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
if info.Mode().IsRegular() {
|
||||
return moveFile(p, newPath)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// moveFile moves a file from a source path to a destination path.
|
||||
func moveFile(sourcePath, destPath string) error {
|
||||
inputFile, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
inputFile.Close()
|
||||
return err
|
||||
}
|
||||
defer outputFile.Close()
|
||||
_, err = io.Copy(outputFile, inputFile)
|
||||
inputFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove file after succesfull copy.
|
||||
err = os.Remove(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
113
cli/app/cp_test.go
Normal file
113
cli/app/cp_test.go
Normal file
@ -0,0 +1,113 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
src string
|
||||
dst string
|
||||
srcPath string
|
||||
dstPath string
|
||||
service string
|
||||
toContainer bool
|
||||
err error
|
||||
}{
|
||||
{src: "foo", dst: "bar", err: errServiceMissing},
|
||||
{src: "app:foo", dst: "app:bar", err: errServiceMissing},
|
||||
{src: "app:foo", dst: "bar", srcPath: "foo", dstPath: "bar", service: "app", toContainer: false},
|
||||
{src: "foo", dst: "app:bar", srcPath: "foo", dstPath: "bar", service: "app", toContainer: true},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
srcPath, dstPath, service, toContainer, err := parseSrcAndDst(tc.src, tc.dst)
|
||||
if srcPath != tc.srcPath {
|
||||
t.Errorf("[%d] srcPath: want (%s), got(%s)", i, tc.srcPath, srcPath)
|
||||
}
|
||||
if dstPath != tc.dstPath {
|
||||
t.Errorf("[%d] dstPath: want (%s), got(%s)", i, tc.dstPath, dstPath)
|
||||
}
|
||||
if service != tc.service {
|
||||
t.Errorf("[%d] service: want (%s), got(%s)", i, tc.service, service)
|
||||
}
|
||||
if toContainer != tc.toContainer {
|
||||
t.Errorf("[%d] toConainer: want (%t), got(%t)", i, tc.toContainer, toContainer)
|
||||
}
|
||||
if err == nil && tc.err != nil && err.Error() != tc.err.Error() {
|
||||
t.Errorf("[%d] err: want (%s), got(%s)", i, tc.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyMode(t *testing.T) {
|
||||
tests := []struct {
|
||||
srcPath string
|
||||
dstPath string
|
||||
srcMode os.FileMode
|
||||
dstMode os.FileMode
|
||||
dstExists bool
|
||||
mode CopyMode
|
||||
err error
|
||||
}{
|
||||
{
|
||||
srcPath: "foo.txt",
|
||||
dstPath: "foo.txt",
|
||||
srcMode: os.ModePerm,
|
||||
dstMode: os.ModePerm,
|
||||
dstExists: true,
|
||||
mode: CopyModeFileToFile,
|
||||
},
|
||||
{
|
||||
srcPath: "foo.txt",
|
||||
dstPath: "bar.txt",
|
||||
srcMode: os.ModePerm,
|
||||
dstExists: true,
|
||||
mode: CopyModeFileToFileRename,
|
||||
},
|
||||
{
|
||||
srcPath: "foo",
|
||||
dstPath: "foo",
|
||||
srcMode: os.ModeDir,
|
||||
dstMode: os.ModeDir,
|
||||
dstExists: true,
|
||||
mode: CopyModeDirToDir,
|
||||
},
|
||||
{
|
||||
srcPath: "foo/",
|
||||
dstPath: "foo",
|
||||
srcMode: os.ModeDir,
|
||||
dstMode: os.ModeDir,
|
||||
dstExists: true,
|
||||
mode: CopyModeFilesToDir,
|
||||
},
|
||||
{
|
||||
srcPath: "foo",
|
||||
dstPath: "foo",
|
||||
srcMode: os.ModeDir,
|
||||
dstExists: false,
|
||||
mode: -1,
|
||||
err: ErrDstDirNotExist,
|
||||
},
|
||||
{
|
||||
srcPath: "foo",
|
||||
dstPath: "foo",
|
||||
srcMode: os.ModeDir,
|
||||
dstMode: os.ModePerm,
|
||||
dstExists: true,
|
||||
mode: -1,
|
||||
err: ErrCopyDirToFile,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
mode, err := copyMode(tc.srcPath, tc.dstPath, tc.srcMode, tc.dstMode, tc.dstExists)
|
||||
if mode != tc.mode {
|
||||
t.Errorf("[%d] mode: want (%d), got(%d)", i, tc.mode, mode)
|
||||
}
|
||||
if err != tc.err {
|
||||
t.Errorf("[%d] err: want (%s), got(%s)", i, tc.err, err)
|
||||
}
|
||||
}
|
||||
}
|
1
go.sum
1
go.sum
@ -1315,6 +1315,7 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
|
@ -68,3 +68,15 @@ func GetContainer(c context.Context, cl *client.Client, filters filters.Args, no
|
||||
|
||||
return containers[0], nil
|
||||
}
|
||||
|
||||
// GetContainerFromStackAndService retrieves the container for the given stack and service.
|
||||
func GetContainerFromStackAndService(cl *client.Client, stack, service string) (types.Container, error) {
|
||||
filters := filters.NewArgs()
|
||||
filters.Add("name", fmt.Sprintf("^%s_%s", stack, service))
|
||||
|
||||
container, err := GetContainer(context.Background(), cl, filters, true)
|
||||
if err != nil {
|
||||
return types.Container{}, err
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
@ -5,9 +5,11 @@ setup_file(){
|
||||
_common_setup
|
||||
_add_server
|
||||
_new_app
|
||||
_deploy_app
|
||||
}
|
||||
|
||||
teardown_file(){
|
||||
_undeploy_app
|
||||
_rm_app
|
||||
_rm_server
|
||||
}
|
||||
@ -17,11 +19,29 @@ setup(){
|
||||
_common_setup
|
||||
}
|
||||
|
||||
teardown(){
|
||||
# https://github.com/bats-core/bats-core/issues/383#issuecomment-738628888
|
||||
if [[ -z "${BATS_TEST_COMPLETED}" ]]; then
|
||||
_undeploy_app
|
||||
fi
|
||||
_mkfile() {
|
||||
run bash -c "echo $2 > $1"
|
||||
assert_success
|
||||
}
|
||||
|
||||
_mkfile_remote() {
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app "bash -c \"echo $2 > $1\""
|
||||
assert_success
|
||||
}
|
||||
|
||||
_mkdir() {
|
||||
run bash -c "mkdir -p $1"
|
||||
assert_success
|
||||
}
|
||||
|
||||
_rm() {
|
||||
run rm -rf "$1"
|
||||
assert_success
|
||||
}
|
||||
|
||||
_rm_remote() {
|
||||
run "$ABRA" app run "$TEST_APP_DOMAIN" app rm -rf "$1"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "validate app argument" {
|
||||
@ -54,68 +74,120 @@ teardown(){
|
||||
assert_output --partial 'arguments must take $SERVICE:$PATH form'
|
||||
}
|
||||
|
||||
@test "detect 'coming FROM' syntax" {
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" app:/myfile.txt . --debug
|
||||
assert_failure
|
||||
assert_output --partial 'coming FROM the container'
|
||||
}
|
||||
|
||||
@test "detect 'going TO' syntax" {
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt app:/somewhere --debug
|
||||
assert_failure
|
||||
assert_output --partial 'going TO the container'
|
||||
}
|
||||
|
||||
@test "error if local file missing" {
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" myfile.txt app:/somewhere
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" thisfileshouldnotexist.txt app:/somewhere
|
||||
assert_failure
|
||||
assert_output --partial 'myfile.txt does not exist locally?'
|
||||
assert_output --partial 'local stat thisfileshouldnotexist.txt: no such file or directory'
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "error if service doesn't exist" {
|
||||
_deploy_app
|
||||
_mkfile "$BATS_TMPDIR/myfile.txt" "foo"
|
||||
|
||||
run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"
|
||||
assert_success
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" doesnt_exist:/
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" doesnt_exist:/ --debug
|
||||
assert_failure
|
||||
assert_output --partial 'no containers matching'
|
||||
|
||||
run rm -rf "$BATS_TMPDIR/myfile.txt"
|
||||
assert_success
|
||||
|
||||
_undeploy_app
|
||||
_rm "$BATS_TMPDIR/myfile.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy to container" {
|
||||
_deploy_app
|
||||
|
||||
run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"
|
||||
assert_success
|
||||
@test "copy local file to container directory" {
|
||||
_mkfile "$BATS_TMPDIR/myfile.txt" "foo"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc
|
||||
assert_success
|
||||
|
||||
run rm -rf "$BATS_TMPDIR/myfile.txt"
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile.txt
|
||||
assert_success
|
||||
assert_output --partial "foo"
|
||||
|
||||
_undeploy_app
|
||||
_rm "$BATS_TMPDIR/myfile.txt"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy from container" {
|
||||
_deploy_app
|
||||
@test "copy local file to container file (and override on remote)" {
|
||||
_mkfile "$BATS_TMPDIR/myfile.txt" "foo"
|
||||
|
||||
run bash -c "echo foo >> $BATS_TMPDIR/myfile.txt"
|
||||
# create
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc/myfile.txt
|
||||
assert_success
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile.txt
|
||||
assert_success
|
||||
assert_output --partial "foo"
|
||||
|
||||
_mkfile "$BATS_TMPDIR/myfile.txt" "bar"
|
||||
|
||||
# override
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc/myfile.txt
|
||||
assert_success
|
||||
|
||||
run rm -rf "$BATS_TMPDIR/myfile.txt"
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile.txt
|
||||
assert_success
|
||||
assert_output --partial "bar"
|
||||
|
||||
_rm "$BATS_TMPDIR/myfile.txt"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy local file to container file (and rename)" {
|
||||
_mkfile "$BATS_TMPDIR/myfile.txt" "foo"
|
||||
|
||||
# rename
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/myfile.txt" app:/etc/myfile2.txt
|
||||
assert_success
|
||||
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app cat /etc/myfile2.txt
|
||||
assert_success
|
||||
assert_output --partial "foo"
|
||||
|
||||
_rm "$BATS_TMPDIR/myfile.txt"
|
||||
_rm_remote "/etc/myfile2.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy local directory to container directory (and creates missing directory)" {
|
||||
_mkdir "$BATS_TMPDIR/mydir"
|
||||
_mkfile "$BATS_TMPDIR/mydir/myfile.txt" "foo"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/mydir" app:/etc
|
||||
assert_success
|
||||
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app ls /etc/mydir
|
||||
assert_success
|
||||
assert_output --partial "myfile.txt"
|
||||
|
||||
_rm "$BATS_TMPDIR/mydir"
|
||||
_rm_remote "/etc/mydir"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy local files to container directory" {
|
||||
_mkdir "$BATS_TMPDIR/mydir"
|
||||
_mkfile "$BATS_TMPDIR/mydir/myfile.txt" "foo"
|
||||
_mkfile "$BATS_TMPDIR/mydir/myfile2.txt" "foo"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" "$BATS_TMPDIR/mydir/" app:/etc
|
||||
assert_success
|
||||
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app ls /etc/myfile.txt
|
||||
assert_success
|
||||
assert_output --partial "myfile.txt"
|
||||
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app ls /etc/myfile2.txt
|
||||
assert_success
|
||||
assert_output --partial "myfile2.txt"
|
||||
|
||||
_rm "$BATS_TMPDIR/mydir"
|
||||
_rm_remote "/etc/myfile*"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy container file to local directory" {
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo foo > /etc/myfile.txt"
|
||||
assert_success
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" app:/etc/myfile.txt "$BATS_TMPDIR"
|
||||
@ -123,8 +195,76 @@ teardown(){
|
||||
assert_exists "$BATS_TMPDIR/myfile.txt"
|
||||
assert bash -c "cat $BATS_TMPDIR/myfile.txt | grep -q foo"
|
||||
|
||||
run rm -rf "$BATS_TMPDIR/myfile.txt"
|
||||
_rm "$BATS_TMPDIR/myfile.txt"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy container file to local file" {
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo foo > /etc/myfile.txt"
|
||||
assert_success
|
||||
|
||||
_undeploy_app
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" app:/etc/myfile.txt "$BATS_TMPDIR/myfile.txt"
|
||||
assert_success
|
||||
assert_exists "$BATS_TMPDIR/myfile.txt"
|
||||
assert bash -c "cat $BATS_TMPDIR/myfile.txt | grep -q foo"
|
||||
|
||||
_rm "$BATS_TMPDIR/myfile.txt"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy container file to local file and rename" {
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo foo > /etc/myfile.txt"
|
||||
assert_success
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" app:/etc/myfile.txt "$BATS_TMPDIR/myfile2.txt"
|
||||
assert_success
|
||||
assert_exists "$BATS_TMPDIR/myfile2.txt"
|
||||
assert bash -c "cat $BATS_TMPDIR/myfile2.txt | grep -q foo"
|
||||
|
||||
_rm "$BATS_TMPDIR/myfile2.txt"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy container directory to local directory" {
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo foo > /etc/myfile.txt"
|
||||
assert_success
|
||||
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo bar > /etc/myfile2.txt"
|
||||
assert_success
|
||||
|
||||
mkdir "$BATS_TMPDIR/mydir"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" app:/etc "$BATS_TMPDIR/mydir"
|
||||
assert_success
|
||||
assert_exists "$BATS_TMPDIR/mydir/etc/myfile.txt"
|
||||
assert_success
|
||||
assert_exists "$BATS_TMPDIR/mydir/etc/myfile2.txt"
|
||||
|
||||
_rm "$BATS_TMPDIR/mydir"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
_rm_remote "/etc/myfile2.txt"
|
||||
}
|
||||
|
||||
# bats test_tags=slow
|
||||
@test "copy container files to local directory" {
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo foo > /etc/myfile.txt"
|
||||
assert_success
|
||||
|
||||
run $ABRA app run "$TEST_APP_DOMAIN" app bash -c "echo bar > /etc/myfile2.txt"
|
||||
assert_success
|
||||
|
||||
mkdir "$BATS_TMPDIR/mydir"
|
||||
|
||||
run $ABRA app cp "$TEST_APP_DOMAIN" app:/etc/ "$BATS_TMPDIR/mydir"
|
||||
assert_success
|
||||
assert_exists "$BATS_TMPDIR/mydir/myfile.txt"
|
||||
assert_success
|
||||
assert_exists "$BATS_TMPDIR/mydir/myfile2.txt"
|
||||
|
||||
_rm "$BATS_TMPDIR/mydir"
|
||||
_rm_remote "/etc/myfile.txt"
|
||||
_rm_remote "/etc/myfile2.txt"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user