forked from toolshed/abra
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			small-impr
			...
			cp-enhance
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 296b2e0312 | 
							
								
								
									
										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"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user