mirror of
https://github.com/containers/podman.git
synced 2025-08-03 01:37:51 +08:00
implement cp reads tar file from stdin/stdout
enables podman cp uses - to stream a tar archive from STDIN or to STDOUT. Signed-off-by: Qi Wang <qiwan@redhat.com>
This commit is contained in:
@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -142,7 +144,11 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
|
||||
|
||||
var lastError error
|
||||
for _, src := range glob {
|
||||
err := copy(src, destPath, dest, idMappingOpts, &containerOwner, extract)
|
||||
if src == "-" {
|
||||
src = os.Stdin.Name()
|
||||
extract = true
|
||||
}
|
||||
err := copy(src, destPath, dest, idMappingOpts, &containerOwner, extract, isFromHostToCtr)
|
||||
if lastError != nil {
|
||||
logrus.Error(lastError)
|
||||
}
|
||||
@ -195,7 +201,7 @@ func getPathInfo(path string) (string, os.FileInfo, error) {
|
||||
return path, srcfi, nil
|
||||
}
|
||||
|
||||
func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract bool) error {
|
||||
func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) error {
|
||||
srcPath, err := filepath.EvalSymlinks(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error evaluating symlinks %q", srcPath)
|
||||
@ -205,6 +211,16 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := filepath.Base(destPath)
|
||||
if filename == "-" && !isFromHostToCtr {
|
||||
err := streamFileToStdout(srcPath, srcfi)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error streaming source file %s to Stdout", srcPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
destdir := destPath
|
||||
if !srcfi.IsDir() && !strings.HasSuffix(dest, string(os.PathSeparator)) {
|
||||
destdir = filepath.Dir(destPath)
|
||||
@ -224,7 +240,6 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch
|
||||
untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
|
||||
|
||||
if srcfi.IsDir() {
|
||||
|
||||
logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
|
||||
if destDirIsExist && !strings.HasSuffix(src, fmt.Sprintf("%s.", string(os.PathSeparator))) {
|
||||
destPath = filepath.Join(destPath, filepath.Base(srcPath))
|
||||
@ -276,3 +291,62 @@ func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping
|
||||
}
|
||||
return convertedIDMap
|
||||
}
|
||||
|
||||
func streamFileToStdout(srcPath string, srcfi os.FileInfo) error {
|
||||
if srcfi.IsDir() {
|
||||
tw := tar.NewWriter(os.Stdout)
|
||||
err := filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || !info.Mode().IsRegular() || path == srcPath {
|
||||
return err
|
||||
}
|
||||
hdr, err := tar.FileInfoHeader(info, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
fh, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
_, err = io.Copy(tw, fh)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error streaming directory %s to Stdout", srcPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error opening file %s", srcPath)
|
||||
}
|
||||
defer file.Close()
|
||||
if !archive.IsArchivePath(srcPath) {
|
||||
tw := tar.NewWriter(os.Stdout)
|
||||
hdr, err := tar.FileInfoHeader(srcfi, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(tw, file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error streaming archive %s to Stdout", srcPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = io.Copy(os.Stdout, file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error streaming file to Stdout")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ podman\-cp - Copy files/folders between a container and the local filesystem
|
||||
|
||||
## DESCRIPTION
|
||||
Copies the contents of **src_path** to the **dest_path**. You can copy from the containers's filesystem to the local machine or the reverse, from the local filesystem to the container.
|
||||
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.
|
||||
|
||||
@ -107,5 +108,7 @@ podman cp containerID:/home/myuser/. /home/myuser/
|
||||
|
||||
podman cp --extract /home/myuser/myfiles.tar.gz containerID:/myfiles
|
||||
|
||||
podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-mount(1), podman-umount(1)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
. "github.com/containers/libpod/test/utils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
@ -58,21 +59,12 @@ var _ = Describe("Podman cp", func() {
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"start", "-a", name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(Equal("copy from host to container"))
|
||||
|
||||
session = podmanTest.Podman([]string{"cp", name + ":foo", filepath.Join(path, "cp_from_container")})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
c := exec.Command("cat", filepath.Join(path, "cp_from_container"))
|
||||
output, err := c.Output()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
Expect(string(output)).To(Equal("copy from host to container"))
|
||||
|
||||
os.Remove("cp_from_container")
|
||||
os.Remove("cp_test.txt")
|
||||
})
|
||||
|
||||
It("podman cp file to dir", func() {
|
||||
@ -89,28 +81,18 @@ var _ = Describe("Podman cp", func() {
|
||||
session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foodir/"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"ps", "-a", "-q"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
name := session.OutputToString()
|
||||
|
||||
session = podmanTest.Podman([]string{"cp", filepath.Join(path, "cp_test.txt"), name + ":foodir/"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"start", "-a", name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(Equal("cp_test.txt"))
|
||||
|
||||
session = podmanTest.Podman([]string{"cp", name + ":foodir/cp_test.txt", path + "/receive/"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
c := exec.Command("cat", filepath.Join(path, "receive", "cp_test.txt"))
|
||||
output, err := c.Output()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
Expect(string(output)).To(Equal("copy from host to container directory"))
|
||||
|
||||
os.Remove("cp_test.txt")
|
||||
os.RemoveAll("receive")
|
||||
})
|
||||
|
||||
It("podman cp dir to dir", func() {
|
||||
@ -132,17 +114,50 @@ var _ = Describe("Podman cp", func() {
|
||||
session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"start", "-a", name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(len(session.OutputToStringArray())).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"start", "-a", name})
|
||||
|
||||
os.RemoveAll(testDirPath)
|
||||
})
|
||||
|
||||
It("podman cp stdin/stdout", func() {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
testDirPath := filepath.Join(path, "TestDir")
|
||||
err = os.Mkdir(testDirPath, 0777)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd := exec.Command("tar", "-zcvf", "file.tar.gz", testDirPath)
|
||||
_, err = cmd.Output()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(Equal("TestDir"))
|
||||
name := session.OutputToString()
|
||||
|
||||
data, err := ioutil.ReadFile("foo.tar.gz")
|
||||
reader := strings.NewReader(string(data))
|
||||
cmd.Stdin = reader
|
||||
session = podmanTest.Podman([]string{"cp", "-", name + ":/foo"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"cp", "file.tar.gz", name + ":/foo.tar.gz"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
session = podmanTest.Podman([]string{"cp", name + ":/foo.tar.gz", "-"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
os.Remove("file.tar.gz")
|
||||
os.RemoveAll(testDirPath)
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user