summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/completion.go58
-rw-r--r--cmd/podman/common/create.go10
-rw-r--r--cmd/podman/common/create_opts.go12
-rw-r--r--cmd/podman/common/netflags.go2
-rw-r--r--cmd/podman/common/specgen.go4
-rw-r--r--cmd/podman/completion/completion.go2
-rw-r--r--cmd/podman/containers/cp.go318
-rw-r--r--cmd/podman/containers/create.go4
-rw-r--r--cmd/podman/containers/kill.go8
-rw-r--r--cmd/podman/containers/logs.go3
-rw-r--r--cmd/podman/containers/mount.go8
-rw-r--r--cmd/podman/containers/run.go11
-rw-r--r--cmd/podman/images/build.go3
-rw-r--r--cmd/podman/images/pull.go2
-rw-r--r--cmd/podman/images/sign.go1
-rw-r--r--cmd/podman/inspect/inspect.go2
-rw-r--r--cmd/podman/main.go5
-rw-r--r--cmd/podman/manifest/push.go2
-rw-r--r--cmd/podman/play/kube.go2
-rw-r--r--cmd/podman/root.go4
-rw-r--r--cmd/podman/system/connection/add.go61
-rw-r--r--cmd/podman/system/prune.go27
-rw-r--r--cmd/podman/system/service_abi.go11
-rw-r--r--cmd/podman/utils/utils.go35
-rw-r--r--cmd/podman/volumes/prune.go16
25 files changed, 506 insertions, 105 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index f792b2713..83fe0723c 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -313,6 +313,10 @@ func completeKeyValues(toComplete string, k keyValueCompletion) ([]string, cobra
return suggestions, directive
}
+func getBoolCompletion(_ string) ([]string, cobra.ShellCompDirective) {
+ return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp
+}
+
/* Autocomplete Functions for cobra ValidArgsFunction */
// AutocompleteContainers - Autocomplete all container names.
@@ -797,6 +801,39 @@ func AutocompleteVolumeFlag(cmd *cobra.Command, args []string, toComplete string
return volumes, directive
}
+// AutocompleteNetworkFlag - Autocomplete network flag options.
+func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ kv := keyValueCompletion{
+ "container:": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) },
+ "ns:": func(_ string) ([]string, cobra.ShellCompDirective) {
+ return nil, cobra.ShellCompDirectiveDefault
+ },
+ "bridge": nil,
+ "none": nil,
+ "host": nil,
+ "private": nil,
+ "slirp4netns:": func(s string) ([]string, cobra.ShellCompDirective) {
+ skv := keyValueCompletion{
+ "allow_host_loopback=": getBoolCompletion,
+ "cidr=": nil,
+ "enable_ipv6=": getBoolCompletion,
+ "outbound_addr=": nil,
+ "outbound_addr6=": nil,
+ "port_handler=": func(_ string) ([]string, cobra.ShellCompDirective) {
+ return []string{"rootlesskit", "slirp4netns"}, cobra.ShellCompDirectiveNoFileComp
+ },
+ }
+ return completeKeyValues(s, skv)
+ },
+ }
+
+ networks, _ := getNetworks(cmd, toComplete)
+ suggestions, dir := completeKeyValues(toComplete, kv)
+ // add slirp4netns here it does not work correct if we add it to the kv map
+ suggestions = append(suggestions, "slirp4netns")
+ return append(networks, suggestions...), dir
+}
+
// AutocompleteJSONFormat - Autocomplete format flag option.
// -> "json"
func AutocompleteJSONFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
@@ -974,17 +1011,14 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri
// AutocompleteImageFilters - Autocomplete image ls --filter options.
func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- getBool := func(_ string) ([]string, cobra.ShellCompDirective) {
- return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp
- }
getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }
kv := keyValueCompletion{
"before=": getImg,
"since=": getImg,
"label=": nil,
"reference=": nil,
- "dangling=": getBool,
- "readonly=": getBool,
+ "dangling=": getBoolCompletion,
+ "readonly=": getBoolCompletion,
}
return completeKeyValues(toComplete, kv)
}
@@ -1004,14 +1038,12 @@ func AutocompleteVolumeFilters(cmd *cobra.Command, args []string, toComplete str
return []string{"local"}, cobra.ShellCompDirectiveNoFileComp
}
kv := keyValueCompletion{
- "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) },
- "driver=": local,
- "scope=": local,
- "label=": nil,
- "opt=": nil,
- "dangling=": func(_ string) ([]string, cobra.ShellCompDirective) {
- return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp
- },
+ "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) },
+ "driver=": local,
+ "scope=": local,
+ "label=": nil,
+ "opt=": nil,
+ "dangling=": getBoolCompletion,
}
return completeKeyValues(toComplete, kv)
}
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 14086ace4..bbd4f6bae 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -642,7 +642,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
storageOptFlagName := "storage-opt"
createFlags.StringSliceVar(
- &cf.StoreageOpt,
+ &cf.StorageOpt,
storageOptFlagName, []string{},
"Storage driver options per container",
)
@@ -671,7 +671,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
sysctlFlagName, []string{},
"Sysctl options",
)
- //TODO: Add function for systctl completion.
+ //TODO: Add function for sysctl completion.
_ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone)
systemdFlagName := "systemd"
@@ -696,13 +696,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
"Allocate a pseudo-TTY for container",
)
- timezonezFlagName := "tz"
+ timezoneFlagName := "tz"
createFlags.StringVar(
&cf.Timezone,
- timezonezFlagName, containerConfig.TZ(),
+ timezoneFlagName, containerConfig.TZ(),
"Set timezone in container",
)
- _ = cmd.RegisterFlagCompletionFunc(timezonezFlagName, completion.AutocompleteNone) //TODO: add timezone completion
+ _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion
umaskFlagName := "umask"
createFlags.StringVar(
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index e975def0a..9635f4135 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -98,7 +98,7 @@ type ContainerCLIOpts struct {
SignaturePolicy string
StopSignal string
StopTimeout uint
- StoreageOpt []string
+ StorageOpt []string
SubUIDName string
SubGIDName string
Sysctl []string
@@ -310,7 +310,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
// on speculation by Matt and I. We think that these come into play later
// like with start. We believe this is just a difference in podman/compat
cliOpts := ContainerCLIOpts{
- // Attach: nil, // dont need?
+ // Attach: nil, // don't need?
Authfile: "",
CapAdd: append(capAdd, cc.HostConfig.CapAdd...),
CapDrop: append(cappDrop, cc.HostConfig.CapDrop...),
@@ -321,11 +321,11 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod),
CPURTRuntime: cc.HostConfig.CPURealtimeRuntime,
CPUShares: uint64(cc.HostConfig.CPUShares),
- // CPUS: 0, // dont need?
+ // CPUS: 0, // don't need?
CPUSetCPUs: cc.HostConfig.CpusetCpus,
CPUSetMems: cc.HostConfig.CpusetMems,
- // Detach: false, // dont need
- // DetachKeys: "", // dont need
+ // Detach: false, // don't need
+ // DetachKeys: "", // don't need
Devices: devices,
DeviceCGroupRule: nil,
DeviceReadBPs: readBps,
@@ -359,7 +359,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
Rm: cc.HostConfig.AutoRemove,
SecurityOpt: cc.HostConfig.SecurityOpt,
StopSignal: cc.Config.StopSignal,
- StoreageOpt: stringMaptoArray(cc.HostConfig.StorageOpt),
+ StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt),
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
Systemd: "true", // podman default
TmpFS: stringMaptoArray(cc.HostConfig.Tmpfs),
diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go
index cae52ccaa..9cb4ed550 100644
--- a/cmd/podman/common/netflags.go
+++ b/cmd/podman/common/netflags.go
@@ -63,7 +63,7 @@ func DefineNetFlags(cmd *cobra.Command) {
networkFlagName, containerConfig.NetNS(),
"Connect a container to a network",
)
- _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworks)
+ _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworkFlag)
networkAliasFlagName := "network-alias"
netFlags.StringSlice(
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index c416d0d7b..287836d9f 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -488,9 +488,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.ConmonPidFile = c.ConmonPIDFile
// TODO
- // ouitside of specgen and oci though
+ // outside of specgen and oci though
// defaults to true, check spec/storage
- // s.readon = c.ReadOnlyTmpFS
+ // s.readonly = c.ReadOnlyTmpFS
// TODO convert to map?
// check if key=value and convert
sysmap := make(map[string]string)
diff --git a/cmd/podman/completion/completion.go b/cmd/podman/completion/completion.go
index 84942a508..b615b33d0 100644
--- a/cmd/podman/completion/completion.go
+++ b/cmd/podman/completion/completion.go
@@ -32,7 +32,7 @@ var (
Example: `podman completion bash
podman completion zsh -f _podman
podman completion fish --no-desc`,
- //dont show this command to users
+ //don't show this command to users
Hidden: true,
}
)
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index 9b0a01a2f..69b61a06c 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -1,19 +1,34 @@
package containers
import (
+ "io"
+ "io/ioutil"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ buildahCopiah "github.com/containers/buildah/copier"
"github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/pkg/copy"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/errorhandling"
+ "github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/idtools"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
- cpDescription = `Command copies the contents of SRC_PATH to the DEST_PATH.
+ cpDescription = `Copy the contents of SRC_PATH to the DEST_PATH.
- You can copy from the container's file system 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.
+ You can copy from the container's file system 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 a directory.
`
cpCommand = &cobra.Command{
- Use: "cp [options] [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH",
+ Use: "cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH",
Short: "Copy files/folders between a container and the local filesystem",
Long: cpDescription,
Args: cobra.ExactArgs(2),
@@ -39,19 +54,21 @@ var (
func cpFlags(cmd *cobra.Command) {
flags := cmd.Flags()
- flags.BoolVar(&cpOpts.Extract, "extract", false, "Extract the tar file into the destination directory.")
- flags.BoolVar(&cpOpts.Pause, "pause", true, "Pause the container while copying")
+ flags.BoolVar(&cpOpts.Extract, "extract", false, "Deprecated...")
+ flags.BoolVar(&cpOpts.Pause, "pause", true, "Deprecated")
+ _ = flags.MarkHidden("extract")
+ _ = flags.MarkHidden("pause")
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
- Mode: []entities.EngineMode{entities.ABIMode},
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: cpCommand,
})
cpFlags(cpCommand)
registry.Commands = append(registry.Commands, registry.CliCommand{
- Mode: []entities.EngineMode{entities.ABIMode},
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: containerCpCommand,
Parent: containerCmd,
})
@@ -59,5 +76,290 @@ func init() {
}
func cp(cmd *cobra.Command, args []string) error {
- return registry.ContainerEngine().ContainerCp(registry.GetContext(), args[0], args[1], cpOpts)
+ // Parse user input.
+ sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(args[0], args[1])
+ if err != nil {
+ return err
+ }
+
+ if len(sourceContainerStr) > 0 {
+ return copyFromContainer(sourceContainerStr, sourcePath, destPath)
+ }
+
+ return copyToContainer(destContainerStr, destPath, sourcePath)
+}
+
+// containerMustExist returns an error if the specified container does not
+// exist.
+func containerMustExist(container string) error {
+ exists, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), container, entities.ContainerExistsOptions{})
+ if err != nil {
+ return err
+ }
+ if !exists.Value {
+ return errors.Errorf("container %q does not exist", container)
+ }
+ return nil
+}
+
+// doCopy executes the two functions in parallel to copy data from A to B and
+// joins the errors if any.
+func doCopy(funcA func() error, funcB func() error) error {
+ errChan := make(chan error)
+ go func() {
+ errChan <- funcA()
+ }()
+ var copyErrors []error
+ copyErrors = append(copyErrors, funcB())
+ copyErrors = append(copyErrors, <-errChan)
+ return errorhandling.JoinErrors(copyErrors)
+}
+
+// copyFromContainer copies from the containerPath on the container to hostPath.
+func copyFromContainer(container string, containerPath string, hostPath string) error {
+ if err := containerMustExist(container); err != nil {
+ return err
+ }
+
+ if hostPath == "-" {
+ hostPath = os.Stdout.Name()
+ }
+
+ containerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
+ if err != nil {
+ return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+ }
+
+ var hostBaseName string
+ hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath)
+ if hostInfoErr != nil {
+ if strings.HasSuffix(hostPath, "/") {
+ return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath)
+ }
+ // If it doesn't exist, then let's have a look at the parent dir.
+ parentDir := filepath.Dir(hostPath)
+ hostInfo, err = copy.ResolveHostPath(parentDir)
+ if err != nil {
+ return errors.Wrapf(hostInfoErr, "%q could not be found on the host", hostPath)
+ }
+ // If the specified path does not exist, we need to assume that
+ // it'll be created while copying. Hence, we use it as the
+ // base path.
+ hostBaseName = filepath.Base(hostPath)
+ } else {
+ // If the specified path exists on the host, we must use its
+ // base path as it may have changed due to symlink evaluations.
+ hostBaseName = filepath.Base(hostInfo.LinkTarget)
+ }
+
+ reader, writer := io.Pipe()
+ hostCopy := func() error {
+ defer reader.Close()
+ if hostInfo.LinkTarget == os.Stdout.Name() {
+ _, err := io.Copy(os.Stdout, reader)
+ return err
+ }
+
+ groot, err := user.Current()
+ if err != nil {
+ return err
+ }
+
+ // Set the {G,U}ID. Let's be tolerant towards the different
+ // operating systems and only log the errors, so we can debug
+ // if necessary.
+ idPair := idtools.IDPair{}
+ if i, err := strconv.Atoi(groot.Uid); err == nil {
+ idPair.UID = i
+ } else {
+ logrus.Debugf("Error converting UID %q to int: %v", groot.Uid, err)
+ }
+ if i, err := strconv.Atoi(groot.Gid); err == nil {
+ idPair.GID = i
+ } else {
+ logrus.Debugf("Error converting GID %q to int: %v", groot.Gid, err)
+ }
+
+ putOptions := buildahCopiah.PutOptions{
+ ChownDirs: &idPair,
+ ChownFiles: &idPair,
+ }
+ if !containerInfo.IsDir && (!hostInfo.IsDir || hostInfoErr != nil) {
+ // If we're having a file-to-file copy, make sure to
+ // rename accordingly.
+ putOptions.Rename = map[string]string{filepath.Base(containerInfo.LinkTarget): hostBaseName}
+ }
+ dir := hostInfo.LinkTarget
+ if !hostInfo.IsDir {
+ dir = filepath.Dir(dir)
+ }
+ if err := buildahCopiah.Put(dir, "", putOptions, reader); err != nil {
+ return errors.Wrap(err, "error copying to host")
+ }
+ return nil
+ }
+
+ containerCopy := func() error {
+ defer writer.Close()
+ copyFunc, err := registry.ContainerEngine().ContainerCopyToArchive(registry.GetContext(), container, containerInfo.LinkTarget, writer)
+ if err != nil {
+ return err
+ }
+ if err := copyFunc(); err != nil {
+ return errors.Wrap(err, "error copying from container")
+ }
+ return nil
+ }
+ return doCopy(containerCopy, hostCopy)
+}
+
+// copyToContainer copies the hostPath to containerPath on the container.
+func copyToContainer(container string, containerPath string, hostPath string) error {
+ if err := containerMustExist(container); err != nil {
+ return err
+ }
+
+ isStdin := false
+ if hostPath == "-" {
+ hostPath = os.Stdin.Name()
+ isStdin = true
+ } else if hostPath == os.Stdin.Name() {
+ isStdin = true
+ }
+
+ // Make sure that host path exists.
+ hostInfo, err := copy.ResolveHostPath(hostPath)
+ if err != nil {
+ return errors.Wrapf(err, "%q could not be found on the host", hostPath)
+ }
+
+ // If the path on the container does not exist. We need to make sure
+ // that it's parent directory exists. The destination may be created
+ // while copying.
+ var containerBaseName string
+ containerInfo, containerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
+ if containerInfoErr != nil {
+ if strings.HasSuffix(containerPath, "/") {
+ return errors.Wrapf(containerInfoErr, "%q could not be found on container %s", containerPath, container)
+ }
+ if isStdin {
+ return errors.New("destination must be a directory when copying from stdin")
+ }
+ // NOTE: containerInfo may actually be set. That happens when
+ // the container path is a symlink into nirvana. In that case,
+ // we must use the symlinked path instead.
+ path := containerPath
+ if containerInfo != nil {
+ containerBaseName = filepath.Base(containerInfo.LinkTarget)
+ path = containerInfo.LinkTarget
+ } else {
+ containerBaseName = filepath.Base(containerPath)
+ }
+
+ parentDir, err := containerParentDir(container, path)
+ if err != nil {
+ return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
+ }
+ containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir)
+ if err != nil {
+ return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
+ }
+ } else {
+ // If the specified path exists on the container, we must use
+ // its base path as it may have changed due to symlink
+ // evaluations.
+ containerBaseName = filepath.Base(containerInfo.LinkTarget)
+ }
+
+ var stdinFile string
+ if isStdin {
+ if !containerInfo.IsDir {
+ return errors.New("destination must be a directory when copying from stdin")
+ }
+
+ // Copy from stdin to a temporary file *before* throwing it
+ // over the wire. This allows for proper client-side error
+ // reporting.
+ tmpFile, err := ioutil.TempFile("", "")
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(tmpFile, os.Stdin)
+ if err != nil {
+ return err
+ }
+ if err = tmpFile.Close(); err != nil {
+ return err
+ }
+ if !archive.IsArchivePath(tmpFile.Name()) {
+ return errors.New("source must be a (compressed) tar archive when copying from stdin")
+ }
+ stdinFile = tmpFile.Name()
+ }
+
+ reader, writer := io.Pipe()
+ hostCopy := func() error {
+ defer writer.Close()
+ if isStdin {
+ stream, err := os.Open(stdinFile)
+ if err != nil {
+ return err
+ }
+ defer stream.Close()
+ _, err = io.Copy(writer, stream)
+ return err
+ }
+
+ getOptions := buildahCopiah.GetOptions{
+ // Unless the specified path ends with ".", we want to copy the base directory.
+ KeepDirectoryNames: !strings.HasSuffix(hostPath, "."),
+ }
+ if !hostInfo.IsDir && (!containerInfo.IsDir || containerInfoErr != nil) {
+ // If we're having a file-to-file copy, make sure to
+ // rename accordingly.
+ getOptions.Rename = map[string]string{filepath.Base(hostInfo.LinkTarget): containerBaseName}
+ }
+ if err := buildahCopiah.Get("/", "", getOptions, []string{hostInfo.LinkTarget}, writer); err != nil {
+ return errors.Wrap(err, "error copying from host")
+ }
+ return nil
+ }
+
+ containerCopy := func() error {
+ defer reader.Close()
+ target := containerInfo.FileInfo.LinkTarget
+ if !containerInfo.IsDir {
+ target = filepath.Dir(target)
+ }
+
+ copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader)
+ if err != nil {
+ return err
+ }
+ if err := copyFunc(); err != nil {
+ return errors.Wrap(err, "error copying to container")
+ }
+ return nil
+ }
+
+ return doCopy(hostCopy, containerCopy)
+}
+
+// containerParentDir returns the parent directory of the specified path on the
+// container. If the path is relative, it will be resolved relative to the
+// container's working directory (or "/" if the work dir isn't set).
+func containerParentDir(container string, containerPath string) (string, error) {
+ if filepath.IsAbs(containerPath) {
+ return filepath.Dir(containerPath), nil
+ }
+ inspectData, _, err := registry.ContainerEngine().ContainerInspect(registry.GetContext(), []string{container}, entities.InspectOptions{})
+ if err != nil {
+ return "", err
+ }
+ if len(inspectData) != 1 {
+ return "", errors.Errorf("inspecting container %q: expected 1 data item but got %d", container, len(inspectData))
+ }
+ workDir := filepath.Join("/", inspectData[0].Config.WorkingDir)
+ workDir = filepath.Join(workDir, containerPath)
+ return filepath.Dir(workDir), nil
}
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 3d87c71a9..420813ba9 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -171,7 +171,7 @@ func createInit(c *cobra.Command) error {
}
cliVals.UserNS = c.Flag("userns").Value.String()
// if user did not modify --userns flag and did turn on
- // uid/gid mappsings, set userns flag to "private"
+ // uid/gid mappings, set userns flag to "private"
if !c.Flag("userns").Changed && cliVals.UserNS == "host" {
if len(cliVals.UIDMap) > 0 ||
len(cliVals.GIDMap) > 0 ||
@@ -239,7 +239,7 @@ func pullImage(imageName string) (string, error) {
if cliVals.Platform != "" {
if cliVals.OverrideArch != "" || cliVals.OverrideOS != "" {
- return "", errors.Errorf("--platform option can not be specified with --overide-arch or --override-os")
+ return "", errors.Errorf("--platform option can not be specified with --override-arch or --override-os")
}
split := strings.SplitN(cliVals.Platform, "/", 2)
cliVals.OverrideOS = split[0]
diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go
index 4640229a9..28040e08a 100644
--- a/cmd/podman/containers/kill.go
+++ b/cmd/podman/containers/kill.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
+ "github.com/containers/common/pkg/completion"
"github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
@@ -22,7 +23,7 @@ var (
Long: killDescription,
RunE: kill,
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman kill mywebserver
@@ -32,7 +33,7 @@ var (
containerKillCommand = &cobra.Command{
Args: func(cmd *cobra.Command, args []string) error {
- return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
},
Use: killCommand.Use,
Short: killCommand.Short,
@@ -57,6 +58,9 @@ func killFlags(cmd *cobra.Command) {
signalFlagName := "signal"
flags.StringVarP(&killOptions.Signal, signalFlagName, "s", "KILL", "Signal to send to the container")
_ = cmd.RegisterFlagCompletionFunc(signalFlagName, common.AutocompleteStopSignal)
+ cidfileFlagName := "cidfile"
+ flags.StringArrayVar(&killOptions.CIDFiles, cidfileFlagName, []string{}, "Read the container ID from the file")
+ _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault)
}
func init() {
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index d4ede370a..9b562afd8 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -122,6 +122,7 @@ func logs(_ *cobra.Command, args []string) error {
}
logsOptions.Since = since
}
- logsOptions.Writer = os.Stdout
+ logsOptions.StdoutWriter = os.Stdout
+ logsOptions.StderrWriter = os.Stderr
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)
}
diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go
index fb2101d64..c92b19a19 100644
--- a/cmd/podman/containers/mount.go
+++ b/cmd/podman/containers/mount.go
@@ -39,7 +39,7 @@ var (
ValidArgsFunction: common.AutocompleteContainers,
}
- containerMountCommmand = &cobra.Command{
+ containerMountCommand = &cobra.Command{
Use: mountCommand.Use,
Short: mountCommand.Short,
Long: mountCommand.Long,
@@ -76,11 +76,11 @@ func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
- Command: containerMountCommmand,
+ Command: containerMountCommand,
Parent: containerCmd,
})
- mountFlags(containerMountCommmand)
- validate.AddLatestFlag(containerMountCommmand, &mountOpts.Latest)
+ mountFlags(containerMountCommand)
+ validate.AddLatestFlag(containerMountCommand, &mountOpts.Latest)
}
func mount(_ *cobra.Command, args []string) error {
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 6ff1b929d..46bfb4143 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -3,7 +3,6 @@ package containers
import (
"fmt"
"os"
- "strconv"
"strings"
"github.com/containers/common/pkg/completion"
@@ -15,7 +14,6 @@ import (
"github.com/containers/podman/v2/pkg/errorhandling"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/specgen"
- "github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -108,15 +106,6 @@ func run(cmd *cobra.Command, args []string) error {
return err
}
- if rootless.IsRootless() && !registry.IsRemote() {
- userspec := strings.SplitN(cliVals.User, ":", 2)[0]
- if uid, err := strconv.ParseInt(userspec, 10, 32); err == nil {
- if err := util.CheckRootlessUIDRange(int(uid)); err != nil {
- return err
- }
- }
- }
-
if af := cliVals.Authfile; len(af) > 0 {
if _, err := os.Stat(af); err != nil {
return err
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index fbea1e3d8..3aca104e3 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -221,7 +221,8 @@ func build(cmd *cobra.Command, args []string) error {
var logfile *os.File
if cmd.Flag("logfile").Changed {
- logfile, err := os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
+ var err error
+ logfile, err = os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
return err
}
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index f8e1ee226..2d881a906 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -139,7 +139,7 @@ func imagePull(cmd *cobra.Command, args []string) error {
}
if platform != "" {
if pullOptions.OverrideArch != "" || pullOptions.OverrideOS != "" {
- return errors.Errorf("--platform option can not be specified with --overide-arch or --override-os")
+ return errors.Errorf("--platform option can not be specified with --override-arch or --override-os")
}
split := strings.SplitN(platform, "/", 2)
pullOptions.OverrideOS = split[0]
diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go
index 342536f7c..859d51d51 100644
--- a/cmd/podman/images/sign.go
+++ b/cmd/podman/images/sign.go
@@ -47,6 +47,7 @@ func init() {
certDirFlagName := "cert-dir"
flags.StringVar(&signOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
_ = signCommand.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault)
+ flags.BoolVarP(&signOptions.All, "all", "a", false, "Sign all the manifests of the multi-architecture image")
}
func sign(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index cc48b7ae4..76613ae71 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -51,7 +51,7 @@ func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
_ = cmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone)
typeFlagName := "type"
- flags.StringVarP(&opts.Type, typeFlagName, "t", AllType, fmt.Sprintf("Specify inspect-oject type (%q, %q or %q)", ImageType, ContainerType, AllType))
+ flags.StringVarP(&opts.Type, typeFlagName, "t", AllType, fmt.Sprintf("Specify inspect-object type (%q, %q or %q)", ImageType, ContainerType, AllType))
_ = cmd.RegisterFlagCompletionFunc(typeFlagName, common.AutocompleteInspectType)
validate.AddLatestFlag(cmd, &opts.Latest)
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index c3aaf84a8..f076d13f3 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -31,11 +31,6 @@ func main() {
return
}
- // Hard code TMPDIR functions to use /var/tmp, if user did not override
- if _, ok := os.LookupEnv("TMPDIR"); !ok {
- os.Setenv("TMPDIR", "/var/tmp")
- }
-
rootCmd = parseCommands()
Execute()
diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go
index 89faa42a2..96fea4a21 100644
--- a/cmd/podman/manifest/push.go
+++ b/cmd/podman/manifest/push.go
@@ -107,7 +107,7 @@ func push(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("tls-verify") {
manifestPushOpts.SkipTLSVerify = types.NewOptionalBool(!manifestPushOpts.TLSVerifyCLI)
}
- if err := registry.ImageEngine().ManifestPush(registry.Context(), args, manifestPushOpts.ManifestPushOptions); err != nil {
+ if err := registry.ImageEngine().ManifestPush(registry.Context(), args[0], args[1], manifestPushOpts.ManifestPushOptions); err != nil {
return err
}
return nil
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index 5e227d05a..db7280b1d 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -61,7 +61,7 @@ func init() {
networkFlagName := "network"
flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)")
- _ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworks)
+ _ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag)
logDriverFlagName := "log-driver"
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 0830a62a5..1f613a4c5 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -178,6 +178,10 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
return err
}
}
+ // Hard code TMPDIR functions to use /var/tmp, if user did not override
+ if _, ok := os.LookupEnv("TMPDIR"); !ok {
+ os.Setenv("TMPDIR", "/var/tmp")
+ }
if !registry.IsRemote() {
if cmd.Flag("cpu-profile").Changed {
diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go
index 57e747451..da5f652c8 100644
--- a/cmd/podman/system/connection/add.go
+++ b/cmd/podman/system/connection/add.go
@@ -168,19 +168,17 @@ func getUserInfo(uri *url.URL) (*url.Userinfo, error) {
}
func getUDS(cmd *cobra.Command, uri *url.URL) (string, error) {
- var authMethods []ssh.AuthMethod
- passwd, set := uri.User.Password()
- if set {
- authMethods = append(authMethods, ssh.Password(passwd))
- }
+ var signers []ssh.Signer
+ passwd, passwdSet := uri.User.Password()
if cmd.Flags().Changed("identity") {
value := cmd.Flag("identity").Value.String()
- auth, err := terminal.PublicKey(value, []byte(passwd))
+ s, err := terminal.PublicKey(value, []byte(passwd))
if err != nil {
return "", errors.Wrapf(err, "failed to read identity %q", value)
}
- authMethods = append(authMethods, auth)
+ signers = append(signers, s)
+ logrus.Debugf("SSH Ident Key %q %s %s", value, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
}
if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found {
@@ -190,16 +188,51 @@ func getUDS(cmd *cobra.Command, uri *url.URL) (string, error) {
if err != nil {
return "", err
}
- a := agent.NewClient(c)
- authMethods = append(authMethods, ssh.PublicKeysCallback(a.Signers))
- }
-
- if len(authMethods) == 0 {
- pass, err := terminal.ReadPassword(fmt.Sprintf("%s's login password:", uri.User.Username()))
+ agentSigners, err := agent.NewClient(c).Signers()
if err != nil {
return "", err
}
- authMethods = append(authMethods, ssh.Password(string(pass)))
+
+ signers = append(signers, agentSigners...)
+
+ if logrus.IsLevelEnabled(logrus.DebugLevel) {
+ for _, s := range agentSigners {
+ logrus.Debugf("SSH Agent Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
+ }
+ }
+ }
+
+ var authMethods []ssh.AuthMethod
+ if len(signers) > 0 {
+ var dedup = make(map[string]ssh.Signer)
+ // Dedup signers based on fingerprint, ssh-agent keys override CONTAINER_SSHKEY
+ for _, s := range signers {
+ fp := ssh.FingerprintSHA256(s.PublicKey())
+ if _, found := dedup[fp]; found {
+ logrus.Debugf("Dedup SSH Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type())
+ }
+ dedup[fp] = s
+ }
+
+ var uniq []ssh.Signer
+ for _, s := range dedup {
+ uniq = append(uniq, s)
+ }
+
+ authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
+ return uniq, nil
+ }))
+ }
+
+ if passwdSet {
+ authMethods = append(authMethods, ssh.Password(passwd))
+ }
+
+ if len(authMethods) == 0 {
+ authMethods = append(authMethods, ssh.PasswordCallback(func() (string, error) {
+ pass, err := terminal.ReadPassword(fmt.Sprintf("%s's login password:", uri.User.Username()))
+ return string(pass), err
+ }))
}
cfg := &ssh.ClientConfig{
diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go
index f57689584..87bb947ed 100644
--- a/cmd/podman/system/prune.go
+++ b/cmd/podman/system/prune.go
@@ -12,12 +12,14 @@ import (
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
+ dfilters "github.com/containers/podman/v2/pkg/domain/filters"
+ "github.com/docker/go-units"
"github.com/spf13/cobra"
)
var (
- pruneOptions = entities.SystemPruneOptions{}
-
+ pruneOptions = entities.SystemPruneOptions{}
+ filters []string
pruneDescription = fmt.Sprintf(`
podman system prune
@@ -46,10 +48,15 @@ func init() {
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation. The default is false")
flags.BoolVarP(&pruneOptions.All, "all", "a", false, "Remove all unused data")
flags.BoolVar(&pruneOptions.Volume, "volumes", false, "Prune volumes")
+ filterFlagName := "filter"
+ flags.StringArrayVar(&filters, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
+ _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
}
func prune(cmd *cobra.Command, args []string) error {
+ var err error
+
// Prompt for confirmation if --force is not set
if !force {
reader := bufio.NewReader(os.Stdin)
@@ -74,13 +81,17 @@ Are you sure you want to continue? [y/N] `, volumeString)
}
}
- // TODO: support for filters in system prune
+ pruneOptions.Filters, err = dfilters.ParseFilterArgumentsIntoFilters(filters)
+ if err != nil {
+ return err
+ }
+
response, err := registry.ContainerEngine().SystemPrune(context.Background(), pruneOptions)
if err != nil {
return err
}
// Print container prune results
- err = utils.PrintContainerPruneResults(response.ContainerPruneReport, true)
+ err = utils.PrintContainerPruneResults(response.ContainerPruneReports, true)
if err != nil {
return err
}
@@ -91,13 +102,17 @@ Are you sure you want to continue? [y/N] `, volumeString)
}
// Print Volume prune results
if pruneOptions.Volume {
- err = utils.PrintVolumePruneResults(response.VolumePruneReport, true)
+ err = utils.PrintVolumePruneResults(response.VolumePruneReports, true)
if err != nil {
return err
}
}
// Print Images prune results
- utils.PrintImagePruneResults(response.ImagePruneReport, true)
+ err = utils.PrintImagePruneResults(response.ImagePruneReports, true)
+ if err != nil {
+ return err
+ }
+ fmt.Printf("Total reclaimed space: %s\n", units.HumanSize((float64)(response.ReclaimedSpace)))
return nil
}
diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go
index 8c52616be..ed35fbb04 100644
--- a/cmd/podman/system/service_abi.go
+++ b/cmd/podman/system/service_abi.go
@@ -5,6 +5,7 @@ package system
import (
"context"
"net"
+ "os"
"strings"
api "github.com/containers/podman/v2/pkg/api/server"
@@ -13,6 +14,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/pflag"
+ "golang.org/x/sys/unix"
)
func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entities.PodmanConfig) error {
@@ -34,6 +36,15 @@ func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entiti
listener = &l
}
+ // Close stdin, so shortnames will not prompt
+ devNullfile, err := os.Open(os.DevNull)
+ if err != nil {
+ return err
+ }
+ defer devNullfile.Close()
+ if err := unix.Dup2(int(devNullfile.Fd()), int(os.Stdin.Fd())); err != nil {
+ return err
+ }
rt, err := infra.GetRuntime(context.Background(), flags, cfg)
if err != nil {
return err
diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go
index 2ca2c4c92..f42243f69 100644
--- a/cmd/podman/utils/utils.go
+++ b/cmd/podman/utils/utils.go
@@ -5,6 +5,7 @@ import (
"os"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/entities/reports"
)
// IsDir returns true if the specified path refers to a directory.
@@ -41,21 +42,21 @@ func PrintPodPruneResults(podPruneReports []*entities.PodPruneReport, heading bo
return errs.PrintErrors()
}
-func PrintContainerPruneResults(containerPruneReport *entities.ContainerPruneReport, heading bool) error {
+func PrintContainerPruneResults(containerPruneReports []*reports.PruneReport, heading bool) error {
var errs OutputErrors
- if heading && (len(containerPruneReport.ID) > 0 || len(containerPruneReport.Err) > 0) {
+ if heading && (len(containerPruneReports) > 0) {
fmt.Println("Deleted Containers")
}
- for k := range containerPruneReport.ID {
- fmt.Println(k)
- }
- for _, v := range containerPruneReport.Err {
- errs = append(errs, v)
+ for _, v := range containerPruneReports {
+ fmt.Println(v.Id)
+ if v.Err != nil {
+ errs = append(errs, v.Err)
+ }
}
return errs.PrintErrors()
}
-func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport, heading bool) error {
+func PrintVolumePruneResults(volumePruneReport []*reports.PruneReport, heading bool) error {
var errs OutputErrors
if heading && len(volumePruneReport) > 0 {
fmt.Println("Deleted Volumes")
@@ -70,18 +71,16 @@ func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport, he
return errs.PrintErrors()
}
-func PrintImagePruneResults(imagePruneReport *entities.ImagePruneReport, heading bool) error {
- if heading && (len(imagePruneReport.Report.Id) > 0 || len(imagePruneReport.Report.Err) > 0) {
+func PrintImagePruneResults(imagePruneReports []*reports.PruneReport, heading bool) error {
+ if heading {
fmt.Println("Deleted Images")
}
- for _, i := range imagePruneReport.Report.Id {
- fmt.Println(i)
- }
- for _, e := range imagePruneReport.Report.Err {
- fmt.Fprint(os.Stderr, e.Error()+"\n")
- }
- if imagePruneReport.Size > 0 {
- fmt.Fprintf(os.Stdout, "Size: %d\n", imagePruneReport.Size)
+ for _, r := range imagePruneReports {
+ fmt.Println(r.Id)
+ if r.Err != nil {
+ fmt.Fprint(os.Stderr, r.Err.Error()+"\n")
+ }
}
+
return nil
}
diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go
index d1370120b..0f3ba9ef6 100644
--- a/cmd/podman/volumes/prune.go
+++ b/cmd/podman/volumes/prune.go
@@ -8,10 +8,12 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/filters"
"github.com/spf13/cobra"
)
@@ -28,6 +30,7 @@ var (
RunE: prune,
ValidArgsFunction: completion.AutocompleteNone,
}
+ filter = []string{}
)
func init() {
@@ -37,10 +40,17 @@ func init() {
Parent: volumeCmd,
})
flags := pruneCommand.Flags()
+
+ filterFlagName := "filter"
+ flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
+ _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteVolumeFilters)
flags.BoolP("force", "f", false, "Do not prompt for confirmation")
}
func prune(cmd *cobra.Command, args []string) error {
+ var (
+ pruneOptions = entities.VolumePruneOptions{}
+ )
// Prompt for confirmation if --force is not set
force, err := cmd.Flags().GetBool("force")
if err != nil {
@@ -58,7 +68,11 @@ func prune(cmd *cobra.Command, args []string) error {
return nil
}
}
- responses, err := registry.ContainerEngine().VolumePrune(context.Background())
+ pruneOptions.Filters, err = filters.ParseFilterArgumentsIntoFilters(filter)
+ if err != nil {
+ return err
+ }
+ responses, err := registry.ContainerEngine().VolumePrune(context.Background(), pruneOptions)
if err != nil {
return err
}