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 <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg 2020-12-08 14:24:34 +01:00
parent dd295f297b
commit 8472efdbd1
3 changed files with 88 additions and 36 deletions

61
pkg/copy/parse.go Normal file
View File

@ -0,0 +1,61 @@
package copy
import (
"strings"
"github.com/pkg/errors"
)
// ParseSourceAndDestination parses the source and destination input into a
// possibly specified container and path. The input format is described in
// podman-cp(1) as "[nameOrID:]path". Colons in paths are supported as long
// they start with a dot or slash.
//
// It returns, in order, the source container and path, followed by the
// destination container and path, and an error. Note that exactly one
// container must be specified.
func ParseSourceAndDestination(source, destination string) (string, string, string, string, error) {
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)
}
return sourceContainer, sourcePath, destContainer, destPath, nil
}
// parseUserInput parses the input string and returns, if specified, the name
// or ID of the container and the path. The input format is described in
// podman-cp(1) as "[nameOrID:]path". Colons in paths are supported as long
// they start with a dot or slash.
func parseUserInput(input string) (container string, path string) {
if len(input) == 0 {
return
}
path = input
// If the input starts with a dot or slash, it cannot refer to a
// container.
if input[0] == '.' || input[0] == '/' {
return
}
if spl := strings.SplitN(path, ":", 2); len(spl) == 2 {
container = spl[0]
path = spl[1]
}
return
}

View File

@ -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)
// 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 use just 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 srcCtr == nil && destCtr == nil {
return errors.Errorf("invalid arguments %q, %q: you must specify one container", source, dest)
}
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
}

View File

@ -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