mirror of
https://github.com/containers/podman.git
synced 2025-05-19 16:18:51 +08:00
support container to container copy
Implement container to container copy. Previously data could only be copied from/to the host. Fixes: #7370 Co-authored-by: Mehul Arora <aroram18@mcmaster.ca> Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:

committed by
Valentin Rothberg

parent
b6c279be22
commit
6fe03b25ab
@ -82,7 +82,9 @@ func cp(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sourceContainerStr) > 0 {
|
||||
if len(sourceContainerStr) > 0 && len(destContainerStr) > 0 {
|
||||
return copyContainerToContainer(sourceContainerStr, sourcePath, destContainerStr, destPath)
|
||||
} else if len(sourceContainerStr) > 0 {
|
||||
return copyFromContainer(sourceContainerStr, sourcePath, destPath)
|
||||
}
|
||||
|
||||
@ -115,6 +117,110 @@ func doCopy(funcA func() error, funcB func() error) error {
|
||||
return errorhandling.JoinErrors(copyErrors)
|
||||
}
|
||||
|
||||
func copyContainerToContainer(sourceContainer string, sourcePath string, destContainer string, destPath string) error {
|
||||
if err := containerMustExist(sourceContainer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := containerMustExist(destContainer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sourceContainerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), sourceContainer, sourcePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%q could not be found on container %s", sourcePath, sourceContainer)
|
||||
}
|
||||
|
||||
var destContainerBaseName string
|
||||
destContainerInfo, destContainerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), destContainer, destPath)
|
||||
if destContainerInfoErr != nil {
|
||||
if strings.HasSuffix(destPath, "/") {
|
||||
return errors.Wrapf(destContainerInfoErr, "%q could not be found on container %s", destPath, destContainer)
|
||||
}
|
||||
// NOTE: containerInfo may actually be set. That happens when
|
||||
// the container path is a symlink into nirvana. In that case,
|
||||
// we must use the symlinked path instead.
|
||||
path := destPath
|
||||
if destContainerInfo != nil {
|
||||
destContainerBaseName = filepath.Base(destContainerInfo.LinkTarget)
|
||||
path = destContainerInfo.LinkTarget
|
||||
} else {
|
||||
destContainerBaseName = filepath.Base(destPath)
|
||||
}
|
||||
|
||||
parentDir, err := containerParentDir(destContainer, path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, destContainer)
|
||||
}
|
||||
destContainerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), destContainer, parentDir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "%q could not be found on container %s", destPath, destContainer)
|
||||
}
|
||||
} else {
|
||||
// If the specified path exists on the container, we must use
|
||||
// its base path as it may have changed due to symlink
|
||||
// evaluations.
|
||||
destContainerBaseName = filepath.Base(destContainerInfo.LinkTarget)
|
||||
}
|
||||
|
||||
if sourceContainerInfo.IsDir && !destContainerInfo.IsDir {
|
||||
return errors.New("destination must be a directory when copying a directory")
|
||||
}
|
||||
|
||||
sourceContainerTarget, destContainerTarget := sourceContainerInfo.LinkTarget, destContainerInfo.LinkTarget
|
||||
if !destContainerInfo.IsDir {
|
||||
destContainerTarget = filepath.Dir(destPath)
|
||||
}
|
||||
|
||||
// If we copy a directory via the "." notation and the container path
|
||||
// does not exist, we need to make sure that the destination on the
|
||||
// container gets created; otherwise the contents of the source
|
||||
// directory will be written to the destination's parent directory.
|
||||
//
|
||||
// Hence, whenever "." is the source and the destination does not
|
||||
// exist, we copy the source's parent and let the copier package create
|
||||
// the destination via the Rename option.
|
||||
if destContainerInfoErr != nil && sourceContainerInfo.IsDir && strings.HasSuffix(sourcePath, ".") {
|
||||
sourceContainerTarget = filepath.Dir(sourceContainerTarget)
|
||||
}
|
||||
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
sourceContainerCopy := func() error {
|
||||
defer writer.Close()
|
||||
copyFunc, err := registry.ContainerEngine().ContainerCopyToArchive(registry.GetContext(), sourceContainer, sourceContainerTarget, writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyFunc(); err != nil {
|
||||
return errors.Wrap(err, "error copying from container")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
destContainerCopy := func() error {
|
||||
defer reader.Close()
|
||||
|
||||
copyOptions := entities.CopyOptions{Chown: chown}
|
||||
if (!sourceContainerInfo.IsDir && !destContainerInfo.IsDir) || destContainerInfoErr != nil {
|
||||
// If we're having a file-to-file copy, make sure to
|
||||
// rename accordingly.
|
||||
copyOptions.Rename = map[string]string{filepath.Base(sourceContainerTarget): destContainerBaseName}
|
||||
}
|
||||
|
||||
copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), destContainer, destContainerTarget, reader, copyOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyFunc(); err != nil {
|
||||
return errors.Wrap(err, "error copying to container")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return doCopy(sourceContainerCopy, destContainerCopy)
|
||||
}
|
||||
|
||||
// copyFromContainer copies from the containerPath on the container to hostPath.
|
||||
func copyFromContainer(container string, containerPath string, hostPath string) error {
|
||||
if err := containerMustExist(container); err != nil {
|
||||
|
@ -9,10 +9,10 @@ podman\-cp - Copy files/folders between a container and the local filesystem
|
||||
**podman container cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path*
|
||||
|
||||
## DESCRIPTION
|
||||
Copy the contents of **src_path** to the **dest_path**. You can copy from the container's filesystem to the local machine or the reverse, from the local filesystem to the container.
|
||||
Copy the contents of **src_path** to the **dest_path**. You can copy from the container's filesystem to the local machine or the reverse, from the local filesystem to the container or between two containers.
|
||||
If `-` is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT.
|
||||
|
||||
The CONTAINER can be a running or stopped container. The **src_path** or **dest_path** can be a file or directory.
|
||||
The containers can be a running or stopped. The **src_path** or **dest_path** can be a file or directory.
|
||||
|
||||
The **podman cp** command assumes container paths are relative to the container's root directory (i.e., `/`).
|
||||
|
||||
@ -70,10 +70,9 @@ The default is *true*.
|
||||
|
||||
## ALTERNATIVES
|
||||
|
||||
Podman has much stronger capabilities than just `podman cp` to achieve copy files between host and container.
|
||||
Podman has much stronger capabilities than just `podman cp` to achieve copying files between the host and containers.
|
||||
|
||||
Using standard podman-mount and podman-umount takes advantage of the entire linux tool chain, rather
|
||||
then just cp.
|
||||
Using standard podman-mount and podman-umount takes advantage of the entire linux tool chain, rather than just cp.
|
||||
|
||||
If a user wants to copy contents out of a container or into a container, they can execute a few simple commands.
|
||||
|
||||
@ -113,6 +112,8 @@ podman cp containerID:/myapp/ /myapp/
|
||||
|
||||
podman cp containerID:/home/myuser/. /home/myuser/
|
||||
|
||||
podman cp containerA:/myapp containerB:/yourapp
|
||||
|
||||
podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz
|
||||
|
||||
## SEE ALSO
|
||||
|
@ -840,7 +840,7 @@ func (c *Container) ShouldRestart(ctx context.Context) bool {
|
||||
|
||||
// CopyFromArchive copies the contents from the specified tarStream to path
|
||||
// *inside* the container.
|
||||
func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, chown bool, tarStream io.Reader) (func() error, error) {
|
||||
func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, chown bool, rename map[string]string, tarStream io.Reader) (func() error, error) {
|
||||
if !c.batched {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
@ -850,7 +850,7 @@ func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, c
|
||||
}
|
||||
}
|
||||
|
||||
return c.copyFromArchive(ctx, containerPath, chown, tarStream)
|
||||
return c.copyFromArchive(ctx, containerPath, chown, rename, tarStream)
|
||||
}
|
||||
|
||||
// CopyToArchive copies the contents from the specified path *inside* the
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (c *Container) copyFromArchive(ctx context.Context, path string, chown bool, reader io.Reader) (func() error, error) {
|
||||
func (c *Container) copyFromArchive(ctx context.Context, path string, chown bool, rename map[string]string, reader io.Reader) (func() error, error) {
|
||||
var (
|
||||
mountPoint string
|
||||
resolvedRoot string
|
||||
@ -89,6 +89,7 @@ func (c *Container) copyFromArchive(ctx context.Context, path string, chown bool
|
||||
GIDMap: c.config.IDMappings.GIDMap,
|
||||
ChownDirs: idPair,
|
||||
ChownFiles: idPair,
|
||||
Rename: rename,
|
||||
}
|
||||
|
||||
return c.joinMountAndExec(ctx,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package compat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -93,8 +94,9 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De
|
||||
|
||||
func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) {
|
||||
query := struct {
|
||||
Path string `schema:"path"`
|
||||
Chown bool `schema:"copyUIDGID"`
|
||||
Path string `schema:"path"`
|
||||
Chown bool `schema:"copyUIDGID"`
|
||||
Rename string `schema:"rename"`
|
||||
// TODO handle params below
|
||||
NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"`
|
||||
}{
|
||||
@ -107,10 +109,19 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder,
|
||||
return
|
||||
}
|
||||
|
||||
var rename map[string]string
|
||||
if query.Rename != "" {
|
||||
if err := json.Unmarshal([]byte(query.Rename), &rename); err != nil {
|
||||
utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
containerName := utils.GetName(r)
|
||||
containerEngine := abi.ContainerEngine{Libpod: runtime}
|
||||
|
||||
copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body, entities.CopyOptions{Chown: query.Chown})
|
||||
copyOptions := entities.CopyOptions{Chown: query.Chown, Rename: rename}
|
||||
copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body, copyOptions)
|
||||
if errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err) {
|
||||
// 404 is returned for an absent container and path. The
|
||||
// clients must deal with it accordingly.
|
||||
|
@ -151,6 +151,10 @@ func (s *APIServer) registerArchiveHandlers(r *mux.Router) error {
|
||||
// type: string
|
||||
// description: Path to a directory in the container to extract
|
||||
// required: true
|
||||
// - in: query
|
||||
// name: rename
|
||||
// type: string
|
||||
// description: JSON encoded map[string]string to translate paths
|
||||
// responses:
|
||||
// 200:
|
||||
// description: no error
|
||||
|
@ -263,4 +263,6 @@ type CopyOptions struct {
|
||||
// If used with CopyFromArchive and set to true it will change ownership of files from the source tar archive
|
||||
// to the primary uid/gid of the target container.
|
||||
Chown *bool `schema:"copyUIDGID"`
|
||||
// Map to translate path names.
|
||||
Rename map[string]string
|
||||
}
|
||||
|
@ -35,3 +35,19 @@ func (o *CopyOptions) GetChown() bool {
|
||||
}
|
||||
return *o.Chown
|
||||
}
|
||||
|
||||
// WithRename
|
||||
func (o *CopyOptions) WithRename(value map[string]string) *CopyOptions {
|
||||
v := value
|
||||
o.Rename = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetRename
|
||||
func (o *CopyOptions) GetRename() map[string]string {
|
||||
var rename map[string]string
|
||||
if o.Rename == nil {
|
||||
return rename
|
||||
}
|
||||
return o.Rename
|
||||
}
|
||||
|
@ -18,18 +18,6 @@ func ParseSourceAndDestination(source, destination string) (string, string, stri
|
||||
sourceContainer, sourcePath := parseUserInput(source)
|
||||
destContainer, destPath := parseUserInput(destination)
|
||||
|
||||
numContainers := 0
|
||||
if len(sourceContainer) > 0 {
|
||||
numContainers++
|
||||
}
|
||||
if len(destContainer) > 0 {
|
||||
numContainers++
|
||||
}
|
||||
|
||||
if numContainers != 1 {
|
||||
return "", "", "", "", errors.Errorf("invalid arguments %q, %q: exactly 1 container expected but %d specified", source, destination, numContainers)
|
||||
}
|
||||
|
||||
if len(sourcePath) == 0 || len(destPath) == 0 {
|
||||
return "", "", "", "", errors.Errorf("invalid arguments %q, %q: you must specify paths", source, destination)
|
||||
}
|
||||
|
@ -165,6 +165,8 @@ type CopyOptions struct {
|
||||
// it will change ownership of files from the source tar archive
|
||||
// to the primary uid/gid of the destination container.
|
||||
Chown bool
|
||||
// Map to translate path names.
|
||||
Rename map[string]string
|
||||
}
|
||||
|
||||
type CommitReport struct {
|
||||
|
@ -12,7 +12,7 @@ func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrI
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return container.CopyFromArchive(ctx, containerPath, options.Chown, reader)
|
||||
return container.CopyFromArchive(ctx, containerPath, options.Chown, options.Rename, reader)
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) {
|
||||
|
@ -853,7 +853,8 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID, path string, reader io.Reader, options entities.CopyOptions) (entities.ContainerCopyFunc, error) {
|
||||
return containers.CopyFromArchiveWithOptions(ic.ClientCtx, nameOrID, path, reader, new(containers.CopyOptions).WithChown(options.Chown))
|
||||
copyOptions := new(containers.CopyOptions).WithChown(options.Chown).WithRename(options.Rename)
|
||||
return containers.CopyFromArchiveWithOptions(ic.ClientCtx, nameOrID, path, reader, copyOptions)
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) {
|
||||
|
@ -226,6 +226,96 @@ load helpers
|
||||
}
|
||||
|
||||
|
||||
@test "podman cp file from container to container" {
|
||||
# Create 3 files with random content in the container.
|
||||
local -a randomcontent=(
|
||||
random-0-$(random_string 10)
|
||||
random-1-$(random_string 15)
|
||||
random-2-$(random_string 20)
|
||||
)
|
||||
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
|
||||
run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile"
|
||||
run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/dotfile."
|
||||
run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1"
|
||||
run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2"
|
||||
|
||||
# Commit the image for testing non-running containers
|
||||
run_podman commit -q cpcontainer
|
||||
cpimage="$output"
|
||||
|
||||
# format is: <id> | <source arg to cp> | <destination arg (appended to $srcdir) to cp> | <full dest path (appended to $srcdir)> | <test name>
|
||||
tests="
|
||||
0 | /tmp/containerfile | | /containerfile | /
|
||||
0 | /tmp/dotfile. | | /dotfile. | /
|
||||
0 | /tmp/containerfile | / | /containerfile | /
|
||||
0 | /tmp/containerfile | /. | /containerfile | /.
|
||||
0 | /tmp/containerfile | /newfile | /newfile | /newfile
|
||||
1 | containerfile1 | / | /containerfile1 | copy from workdir (rel path) to /
|
||||
2 | subdir/containerfile2 | / | /containerfile2 | copy from workdir/subdir (rel path) to /
|
||||
"
|
||||
|
||||
# From RUNNING container
|
||||
while read id src dest dest_fullname description; do
|
||||
# dest may be "''" for empty table cells
|
||||
if [[ $dest == "''" ]];then
|
||||
unset dest
|
||||
fi
|
||||
|
||||
# To RUNNING container
|
||||
run_podman run -d $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman exec $destcontainer cat "/$dest_fullname"
|
||||
is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
|
||||
# To CREATED container
|
||||
run_podman create $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman start $destcontainer
|
||||
run_podman exec $destcontainer cat "/$dest_fullname"
|
||||
is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
done < <(parse_table "$tests")
|
||||
run_podman kill cpcontainer
|
||||
run_podman rm -f cpcontainer
|
||||
|
||||
# From CREATED container
|
||||
run_podman create --name cpcontainer --workdir=/srv $cpimage
|
||||
while read id src dest dest_fullname description; do
|
||||
# dest may be "''" for empty table cells
|
||||
if [[ $dest == "''" ]];then
|
||||
unset dest
|
||||
fi
|
||||
|
||||
# To RUNNING container
|
||||
run_podman run -d $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman exec $destcontainer cat "/$dest_fullname"
|
||||
is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
|
||||
# To CREATED container
|
||||
run_podman create $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman start $destcontainer
|
||||
run_podman exec $destcontainer cat "/$dest_fullname"
|
||||
is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
done < <(parse_table "$tests")
|
||||
run_podman rm -f cpcontainer
|
||||
|
||||
run_podman rmi -f $cpimage
|
||||
}
|
||||
|
||||
|
||||
@test "podman cp dir from host to container" {
|
||||
srcdir=$PODMAN_TMPDIR
|
||||
mkdir -p $srcdir/dir/sub
|
||||
@ -377,6 +467,110 @@ load helpers
|
||||
}
|
||||
|
||||
|
||||
@test "podman cp dir from container to container" {
|
||||
# Create 2 files with random content in the container.
|
||||
local -a randomcontent=(
|
||||
random-0-$(random_string 10)
|
||||
random-1-$(random_string 15)
|
||||
)
|
||||
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
|
||||
run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[0]} > /srv/subdir/containerfile0"
|
||||
run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/subdir/containerfile1"
|
||||
# "." and "dir/." will copy the contents, so make sure that a dir ending
|
||||
# with dot is treated correctly.
|
||||
run_podman exec cpcontainer sh -c 'mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./'
|
||||
|
||||
# Commit the image for testing non-running containers
|
||||
run_podman commit -q cpcontainer
|
||||
cpimage="$output"
|
||||
|
||||
# format is: <source arg to cp (appended to /srv)> | <dest> | <full dest path> | <test name>
|
||||
tests="
|
||||
/srv | | /srv/subdir | copy /srv
|
||||
/srv | /newdir | /newdir/subdir | copy /srv to /newdir
|
||||
/srv/ | | /srv/subdir | copy /srv/
|
||||
/srv/. | | /subdir | copy /srv/.
|
||||
/srv/. | /newdir | /newdir/subdir | copy /srv/. to /newdir
|
||||
/srv/subdir/. | | | copy /srv/subdir/.
|
||||
/tmp/subdir. | | /subdir. | copy /tmp/subdir.
|
||||
"
|
||||
|
||||
# From RUNNING container
|
||||
while read src dest dest_fullname description; do
|
||||
if [[ $src == "''" ]];then
|
||||
unset src
|
||||
fi
|
||||
if [[ $dest == "''" ]];then
|
||||
unset dest
|
||||
fi
|
||||
if [[ $dest_fullname == "''" ]];then
|
||||
unset dest_fullname
|
||||
fi
|
||||
|
||||
# To RUNNING container
|
||||
run_podman run -d $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1"
|
||||
is "$output" "${randomcontent[0]}
|
||||
${randomcontent[1]}" "$description"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
|
||||
# To CREATED container
|
||||
run_podman create $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman start $destcontainer
|
||||
run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1"
|
||||
is "$output" "${randomcontent[0]}
|
||||
${randomcontent[1]}" "$description"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
done < <(parse_table "$tests")
|
||||
run_podman kill cpcontainer
|
||||
run_podman rm -f cpcontainer
|
||||
|
||||
# From CREATED container
|
||||
run_podman create --name cpcontainer --workdir=/srv $cpimage
|
||||
while read src dest dest_fullname description; do
|
||||
if [[ $src == "''" ]];then
|
||||
unset src
|
||||
fi
|
||||
if [[ $dest == "''" ]];then
|
||||
unset dest
|
||||
fi
|
||||
if [[ $dest_fullname == "''" ]];then
|
||||
unset dest_fullname
|
||||
fi
|
||||
|
||||
# To RUNNING container
|
||||
run_podman run -d $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1"
|
||||
is "$output" "${randomcontent[0]}
|
||||
${randomcontent[1]}" "$description"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
|
||||
# To CREATED container
|
||||
run_podman create $IMAGE sleep infinity
|
||||
destcontainer="$output"
|
||||
run_podman start $destcontainer
|
||||
run_podman cp cpcontainer:$src $destcontainer:"/$dest"
|
||||
run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1"
|
||||
is "$output" "${randomcontent[0]}
|
||||
${randomcontent[1]}" "$description"
|
||||
run_podman kill $destcontainer
|
||||
run_podman rm -f $destcontainer
|
||||
done < <(parse_table "$tests")
|
||||
|
||||
run_podman rm -f cpcontainer
|
||||
run_podman rmi -f $cpimage
|
||||
}
|
||||
|
||||
|
||||
@test "podman cp symlinked directory from container" {
|
||||
destdir=$PODMAN_TMPDIR/cp-weird-symlink
|
||||
mkdir -p $destdir
|
||||
|
Reference in New Issue
Block a user