Skip to content

Commit

Permalink
podman save use named pipe
Browse files Browse the repository at this point in the history
podman save uses named pipe as output path, not directly using /dev/stdout.
fix containers#7017

Signed-off-by: Qi Wang <qiwan@redhat.com>

<MH: Corrected imports during cherry-pick>

Signed-off-by: Matt Heon <matthew.heon@pm.me>
  • Loading branch information
QiWang19 authored and mheon committed Aug 17, 2020
1 parent 263bc30 commit 41048dd
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
31 changes: 25 additions & 6 deletions cmd/podman/images/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import (
"os"
"strings"

"github.com/containers/libpod/v2/libpod/define"

"github.com/containers/libpod/v2/cmd/podman/parse"
"github.com/containers/libpod/v2/cmd/podman/registry"
"github.com/containers/libpod/v2/libpod/define"
"github.com/containers/libpod/v2/pkg/domain/entities"
"github.com/containers/libpod/v2/pkg/util"
"github.com/pkg/errors"
Expand Down Expand Up @@ -83,9 +82,10 @@ func saveFlags(flags *pflag.FlagSet) {

}

func save(cmd *cobra.Command, args []string) error {
func save(cmd *cobra.Command, args []string) (finalErr error) {
var (
tags []string
tags []string
succeeded = false
)
if cmd.Flag("compress").Changed && (saveOpts.Format != define.OCIManifestDir && saveOpts.Format != define.V2s2ManifestDir && saveOpts.Format == "") {
return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
Expand All @@ -95,13 +95,32 @@ func save(cmd *cobra.Command, args []string) error {
if terminal.IsTerminal(int(fi.Fd())) {
return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
}
saveOpts.Output = "/dev/stdout"
pipePath, cleanup, err := setupPipe()
if err != nil {
return err
}
if cleanup != nil {
defer func() {
errc := cleanup()
if succeeded {
writeErr := <-errc
if writeErr != nil && finalErr == nil {
finalErr = writeErr
}
}
}()
}
saveOpts.Output = pipePath
}
if err := parse.ValidateFileName(saveOpts.Output); err != nil {
return err
}
if len(args) > 1 {
tags = args[1:]
}
return registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
err := registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
if err == nil {
succeeded = true
}
return err
}
47 changes: 47 additions & 0 deletions cmd/podman/images/utils_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package images

import (
"io"
"io/ioutil"
"os"
"path/filepath"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

// setupPipe for fixing https://github.com/containers/podman/issues/7017
// uses named pipe since containers/image EvalSymlinks fails with /dev/stdout
// the caller should use the returned function to clean up the pipeDir
func setupPipe() (string, func() <-chan error, error) {
errc := make(chan error)
pipeDir, err := ioutil.TempDir(os.TempDir(), "pipeDir")
if err != nil {
return "", nil, err
}
pipePath := filepath.Join(pipeDir, "saveio")
err = unix.Mkfifo(pipePath, 0600)
if err != nil {
if e := os.RemoveAll(pipeDir); e != nil {
logrus.Errorf("error removing named pipe: %q", e)
}
return "", nil, errors.Wrapf(err, "error creating named pipe")
}
go func() {
fpipe, err := os.Open(pipePath)
if err != nil {
errc <- err
return
}
_, err = io.Copy(os.Stdout, fpipe)
fpipe.Close()
errc <- err
}()
return pipePath, func() <-chan error {
if e := os.RemoveAll(pipeDir); e != nil {
logrus.Errorf("error removing named pipe: %q", e)
}
return errc
}, nil
}
7 changes: 7 additions & 0 deletions cmd/podman/images/utils_unsupported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// +build !linux

package images

func setupPipe() (string, func() <-chan error, error) {
return "/dev/stdout", nil, nil
}
10 changes: 10 additions & 0 deletions test/system/120-load.bats
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ verify_iid_and_name() {
is "$new_img_name" "$1" "Name & tag of restored image"
}

@test "podman save to pipe and load" {
# We can't use run_podman because that uses the BATS 'run' function
# which redirects stdout and stderr. Here we need to guarantee
# that podman's stdout is a pipe, not any other form of redirection
$PODMAN save --format oci-archive $IMAGE | cat >$PODMAN_TMPDIR/test.tar
[ $status -eq 0 ]

run_podman load -i $PODMAN_TMPDIR/test.tar
}


@test "podman load - by image ID" {
skip_if_remote "FIXME: pending #7123"
Expand Down

0 comments on commit 41048dd

Please sign in to comment.