From 8472efdbd1efcb4eea9872baf56e2473a0dd970f Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Tue, 8 Dec 2020 14:24:34 +0100 Subject: pkg/copy: add parsing API Add an API for parsing user input into a possibly specified container and path. This allows for sharing the parsing code between the local and the remote client (and bindings) in the future. Signed-off-by: Valentin Rothberg --- pkg/domain/infra/abi/cp.go | 60 +++++++++++++++-------------------- pkg/domain/infra/tunnel/containers.go | 3 +- 2 files changed, 27 insertions(+), 36 deletions(-) (limited to 'pkg/domain') diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go index 9409df743..7cc3005c1 100644 --- a/pkg/domain/infra/abi/cp.go +++ b/pkg/domain/infra/abi/cp.go @@ -2,46 +2,53 @@ package abi import ( "context" - "strings" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/pkg/copy" "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/pkg/errors" ) func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - srcCtr, srcPath := parsePath(ic.Libpod, source) - destCtr, destPath := parsePath(ic.Libpod, dest) - - if srcCtr != nil && destCtr != nil { - return errors.Errorf("invalid arguments %q, %q: you must use just one container", source, dest) + // Parse user input. + sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest) + if err != nil { + return err } - if srcCtr == nil && destCtr == nil { - return errors.Errorf("invalid arguments %q, %q: you must specify one container", source, dest) + + // Look up containers. + var sourceContainer, destContainer *libpod.Container + if len(sourceContainerStr) > 0 { + sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr) + if err != nil { + return err + } } - if len(srcPath) == 0 || len(destPath) == 0 { - return errors.Errorf("invalid arguments %q, %q: you must specify paths", source, dest) + if len(destContainerStr) > 0 { + destContainer, err = ic.Libpod.LookupContainer(destContainerStr) + if err != nil { + return err + } } var sourceItem, destinationItem copy.CopyItem - var err error - // Copy from the container to the host. - if srcCtr != nil { - sourceItem, err = copy.CopyItemForContainer(srcCtr, srcPath, options.Pause, true) + + // Source ... container OR host. + if sourceContainer != nil { + sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true) defer sourceItem.CleanUp() if err != nil { return err } } else { - sourceItem, err = copy.CopyItemForHost(srcPath, true) + sourceItem, err = copy.CopyItemForHost(sourcePath, true) if err != nil { return err } } - if destCtr != nil { - destinationItem, err = copy.CopyItemForContainer(destCtr, destPath, options.Pause, false) + // Destination ... container OR host. + if destContainer != nil { + destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false) defer destinationItem.CleanUp() if err != nil { return err @@ -57,20 +64,3 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, // Copy from the host to the container. return copy.Copy(&sourceItem, &destinationItem, options.Extract) } - -func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) { - if len(path) == 0 { - return nil, "" - } - if path[0] == '.' || path[0] == '/' { // A path cannot point to a container. - return nil, path - } - pathArr := strings.SplitN(path, ":", 2) - if len(pathArr) == 2 { - ctr, err := runtime.LookupContainer(pathArr[0]) - if err == nil { - return ctr, pathArr[1] - } - } - return nil, path -} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 3584668c7..e65fef0a4 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -732,7 +732,8 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o } func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - return errors.New("not implemented") + return nil + // return containers.Copy(ic.ClientCxt, source, dest, options) } // Shutdown Libpod engine -- cgit v1.2.3-54-g00ecf From a12323884fd576063ba8f5785a3c9c2e48140c7f Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 9 Dec 2020 12:18:14 +0100 Subject: pkg/copy: introduce a Copier Introduce a `Copier` object to separate the copy-rule enforcement from copying. That allows for a better error reporting of the REST API. Signed-off-by: Valentin Rothberg --- pkg/api/handlers/compat/containers_archive.go | 19 ++++++-- pkg/copy/copy.go | 67 ++++++++++++++++++++------- pkg/domain/infra/abi/cp.go | 6 ++- 3 files changed, 70 insertions(+), 22 deletions(-) (limited to 'pkg/domain') diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index bb84f86be..d8197415c 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v2/pkg/copy" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func Archive(w http.ResponseWriter, r *http.Request) { @@ -86,11 +87,16 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De return } - w.WriteHeader(http.StatusOK) - if err := copy.Copy(&source, &destination, false); err != nil { + copier, err := copy.GetCopier(&source, &destination, false) + if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + w.WriteHeader(http.StatusOK) + if err := copier.Copy(); err != nil { + logrus.Errorf("Error during copy: %v", err) + return + } } func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { @@ -129,9 +135,14 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, return } - w.WriteHeader(http.StatusOK) - if err := copy.Copy(&source, &destination, false); err != nil { + copier, err := copy.GetCopier(&source, &destination, false) + if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + w.WriteHeader(http.StatusOK) + if err := copier.Copy(); err != nil { + logrus.Errorf("Error during copy: %v", err) + return + } } diff --git a/pkg/copy/copy.go b/pkg/copy/copy.go index 3993b532e..13893deb2 100644 --- a/pkg/copy/copy.go +++ b/pkg/copy/copy.go @@ -25,31 +25,61 @@ import ( // // **************************************************************************** -// Copy the source item to destination. Use extract to untar the source if -// it's a tar archive. -func Copy(source *CopyItem, destination *CopyItem, extract bool) error { +// Copier copies data from a source to a destination CopyItem. +type Copier struct { + copyFunc func() error + cleanUpFuncs []deferFunc +} + +// cleanUp releases resources the Copier may hold open. +func (c *Copier) cleanUp() { + for _, f := range c.cleanUpFuncs { + f() + } +} + +// Copy data from a source to a destination CopyItem. +func (c *Copier) Copy() error { + defer c.cleanUp() + return c.copyFunc() +} + +// GetCopiers returns a Copier to copy the source item to destination. Use +// extract to untar the source if it's a tar archive. +func GetCopier(source *CopyItem, destination *CopyItem, extract bool) (*Copier, error) { + copier := &Copier{} + // First, do the man-page dance. See podman-cp(1) for details. if err := enforceCopyRules(source, destination); err != nil { - return err + return nil, err } // Destination is a stream (e.g., stdout or an http body). if destination.info.IsStream { // Source is a stream (e.g., stdin or an http body). if source.info.IsStream { - _, err := io.Copy(destination.writer, source.reader) - return err + copier.copyFunc = func() error { + _, err := io.Copy(destination.writer, source.reader) + return err + } + return copier, nil } root, glob, err := source.buildahGlobs() if err != nil { - return err + return nil, err } - return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer) + copier.copyFunc = func() error { + return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer) + } + return copier, nil } // Destination is either a file or a directory. if source.info.IsStream { - return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader) + copier.copyFunc = func() error { + return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader) + } + return copier, nil } tarOptions := &archive.TarOptions{ @@ -71,33 +101,36 @@ func Copy(source *CopyItem, destination *CopyItem, extract bool) error { var tarReader io.ReadCloser if extract && archive.IsArchivePath(source.resolved) { if !destination.info.IsDir { - return errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original) + return nil, errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original) } reader, err := os.Open(source.resolved) if err != nil { - return err + return nil, err } - defer reader.Close() + copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) // The stream from stdin may be compressed (e.g., via gzip). decompressedStream, err := archive.DecompressStream(reader) if err != nil { - return err + return nil, err } - defer decompressedStream.Close() + copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { decompressedStream.Close() }) tarReader = decompressedStream } else { reader, err := archive.TarWithOptions(source.resolved, tarOptions) if err != nil { - return err + return nil, err } - defer reader.Close() + copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) tarReader = reader } - return buildahCopiah.Put(root, dir, source.putOptions(), tarReader) + copier.copyFunc = func() error { + return buildahCopiah.Put(root, dir, source.putOptions(), tarReader) + } + return copier, nil } // enforceCopyRules enforces the rules for copying from a source to a diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go index 7cc3005c1..362053cce 100644 --- a/pkg/domain/infra/abi/cp.go +++ b/pkg/domain/infra/abi/cp.go @@ -62,5 +62,9 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, } // Copy from the host to the container. - return copy.Copy(&sourceItem, &destinationItem, options.Extract) + copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract) + if err != nil { + return err + } + return copier.Copy() } -- cgit v1.2.3-54-g00ecf