summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQi Wang <qiwan@redhat.com>2019-04-26 17:27:06 -0400
committerQi Wang <qiwan@redhat.com>2019-05-09 10:12:39 -0400
commit4479b8eec3558daedfd519d577bf63a17f4b2642 (patch)
treeafd9fda59818e1f52345c35c012ad2c32a450b09
parent83700a74f82f849146a890ca28a8968107e79910 (diff)
downloadpodman-4479b8eec3558daedfd519d577bf63a17f4b2642.tar.gz
podman-4479b8eec3558daedfd519d577bf63a17f4b2642.tar.bz2
podman-4479b8eec3558daedfd519d577bf63a17f4b2642.zip
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>
-rw-r--r--cmd/podman/cp.go80
-rw-r--r--docs/podman-cp.1.md3
-rw-r--r--test/e2e/cp_test.go75
3 files changed, 125 insertions, 33 deletions
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go
index 82f2d3f20..4cb8a8c54 100644
--- a/cmd/podman/cp.go
+++ b/cmd/podman/cp.go
@@ -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
+}
diff --git a/docs/podman-cp.1.md b/docs/podman-cp.1.md
index 44612003d..406dd51df 100644
--- a/docs/podman-cp.1.md
+++ b/docs/podman-cp.1.md
@@ -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)
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index 591f533d6..273668f35 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -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 = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(len(session.OutputToStringArray())).To(Equal(0))
- session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
+ 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))
+ 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{"start", "-a", name})
+
+ session = podmanTest.Podman([]string{"cp", "file.tar.gz", name + ":/foo.tar.gz"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(Equal("TestDir"))
+ 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)
})
})