summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/inspect.go18
-rw-r--r--cmd/podman/common/ports.go112
-rw-r--r--cmd/podman/common/specgen.go111
-rw-r--r--cmd/podman/common/util.go11
-rw-r--r--cmd/podman/containers/create.go17
-rw-r--r--cmd/podman/containers/exec.go2
-rw-r--r--cmd/podman/containers/exists.go5
-rw-r--r--cmd/podman/containers/inspect.go53
-rw-r--r--cmd/podman/containers/list.go10
-rw-r--r--cmd/podman/containers/port.go2
-rw-r--r--cmd/podman/containers/ps.go37
-rw-r--r--cmd/podman/containers/run.go8
-rw-r--r--cmd/podman/containers/start.go4
-rw-r--r--cmd/podman/generate/generate.go27
-rw-r--r--cmd/podman/generate/systemd.go57
-rw-r--r--cmd/podman/images/exists.go1
-rw-r--r--cmd/podman/images/images.go13
-rw-r--r--cmd/podman/images/inspect.go92
-rw-r--r--cmd/podman/images/list.go3
-rw-r--r--cmd/podman/images/load.go3
-rw-r--r--cmd/podman/images/prune.go3
-rw-r--r--cmd/podman/images/push.go12
-rw-r--r--cmd/podman/inspect.go42
-rw-r--r--cmd/podman/inspect/inspect.go159
-rw-r--r--cmd/podman/main.go2
-rw-r--r--cmd/podman/manifest/add.go3
-rw-r--r--cmd/podman/manifest/create.go2
-rw-r--r--cmd/podman/manifest/inspect.go2
-rw-r--r--cmd/podman/networks/network.go3
-rw-r--r--cmd/podman/pods/create.go2
-rw-r--r--cmd/podman/pods/exists.go1
-rw-r--r--cmd/podman/pods/ps.go101
-rw-r--r--cmd/podman/pods/rm.go4
-rw-r--r--cmd/podman/pods/stats.go189
-rw-r--r--cmd/podman/root.go2
-rw-r--r--cmd/podman/system/events.go3
-rw-r--r--cmd/podman/system/info.go3
-rw-r--r--cmd/podman/system/version.go3
-rw-r--r--cmd/podman/volumes/list.go20
-rw-r--r--cmd/podman/volumes/prune.go3
40 files changed, 726 insertions, 419 deletions
diff --git a/cmd/podman/common/inspect.go b/cmd/podman/common/inspect.go
deleted file mode 100644
index dfc6fe679..000000000
--- a/cmd/podman/common/inspect.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package common
-
-import (
- "github.com/containers/libpod/pkg/domain/entities"
- "github.com/spf13/cobra"
-)
-
-// AddInspectFlagSet takes a command and adds the inspect flags and returns an InspectOptions object
-// Since this cannot live in `package main` it lives here until a better home is found
-func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
- opts := entities.InspectOptions{}
-
- flags := cmd.Flags()
- flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size")
- flags.StringVarP(&opts.Format, "format", "f", "", "Change the output format to a Go template")
-
- return &opts
-}
diff --git a/cmd/podman/common/ports.go b/cmd/podman/common/ports.go
index 7e2b1e79d..a96bafabd 100644
--- a/cmd/podman/common/ports.go
+++ b/cmd/podman/common/ports.go
@@ -1,28 +1,11 @@
package common
import (
- "fmt"
- "net"
- "strconv"
-
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
-// ExposedPorts parses user and image ports and returns binding information
-func ExposedPorts(expose []string, publish []ocicni.PortMapping, publishAll bool, imageExposedPorts map[string]struct{}) ([]ocicni.PortMapping, error) {
- containerPorts := make(map[string]string)
-
- // TODO this needs to be added into a something that
- // has access to an imageengine
- // add expose ports from the image itself
- //for expose := range imageExposedPorts {
- // _, port := nat.SplitProtoPort(expose)
- // containerPorts[port] = ""
- //}
-
+func verifyExpose(expose []string) error {
// add the expose ports from the user (--expose)
// can be single or a range
for _, expose := range expose {
@@ -30,97 +13,10 @@ func ExposedPorts(expose []string, publish []ocicni.PortMapping, publishAll bool
_, port := nat.SplitProtoPort(expose)
//parse the start and end port and create a sequence of ports to expose
//if expose a port, the start and end port are the same
- start, end, err := nat.ParsePortRange(port)
+ _, _, err := nat.ParsePortRange(port)
if err != nil {
- return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
- }
- for i := start; i <= end; i++ {
- containerPorts[strconv.Itoa(int(i))] = ""
- }
- }
-
- // TODO/FIXME this is hell reencarnated
- // parse user inputted port bindings
- pbPorts, portBindings, err := nat.ParsePortSpecs([]string{})
- if err != nil {
- return nil, err
- }
-
- // delete exposed container ports if being used by -p
- for i := range pbPorts {
- delete(containerPorts, i.Port())
- }
-
- // iterate container ports and make port bindings from them
- if publishAll {
- for e := range containerPorts {
- //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
- //proto, port := nat.SplitProtoPort(e)
- p, err := nat.NewPort("tcp", e)
- if err != nil {
- return nil, err
- }
- rp, err := getRandomPort()
- if err != nil {
- return nil, err
- }
- logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
- portBindings[p] = CreatePortBinding(rp, "")
- }
- }
-
- // We need to see if any host ports are not populated and if so, we need to assign a
- // random port to them.
- for k, pb := range portBindings {
- if pb[0].HostPort == "" {
- hostPort, err := getRandomPort()
- if err != nil {
- return nil, err
- }
- logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
- pb[0].HostPort = strconv.Itoa(hostPort)
- }
- }
- var pms []ocicni.PortMapping
- for k, v := range portBindings {
- for _, pb := range v {
- hp, err := strconv.Atoi(pb.HostPort)
- if err != nil {
- return nil, err
- }
- pms = append(pms, ocicni.PortMapping{
- HostPort: int32(hp),
- ContainerPort: int32(k.Int()),
- //Protocol: "",
- HostIP: pb.HostIP,
- })
+ return errors.Wrapf(err, "invalid range format for --expose: %s", expose)
}
}
- return pms, nil
-}
-
-func getRandomPort() (int, error) {
- l, err := net.Listen("tcp", ":0")
- if err != nil {
- return 0, errors.Wrapf(err, "unable to get free port")
- }
- defer l.Close()
- _, randomPort, err := net.SplitHostPort(l.Addr().String())
- if err != nil {
- return 0, errors.Wrapf(err, "unable to determine free port")
- }
- rp, err := strconv.Atoi(randomPort)
- if err != nil {
- return 0, errors.Wrapf(err, "unable to convert random port to int")
- }
- return rp, nil
-}
-
-//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
-func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
- pb := nat.PortBinding{
- HostPort: strconv.Itoa(hostPort),
- }
- pb.HostIP = hostIP
- return []nat.PortBinding{pb}
+ return nil
}
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index b90030f7f..f8c58f1a4 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -119,13 +119,13 @@ func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (
func getPidsLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*specs.LinuxPids, error) {
pids := &specs.LinuxPids{}
hasLimits := false
+ if c.CGroupsMode == "disabled" && c.PIDsLimit > 0 {
+ return nil, nil
+ }
if c.PIDsLimit > 0 {
pids.Limit = c.PIDsLimit
hasLimits = true
}
- if c.CGroupsMode == "disabled" && c.PIDsLimit > 0 {
- s.ResourceLimits.Pids.Limit = -1
- }
if !hasLimits {
return nil, nil
}
@@ -203,23 +203,38 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.User = c.User
inputCommand := args[1:]
if len(c.HealthCmd) > 0 {
+ if c.NoHealthCheck {
+ return errors.New("Cannot specify both --no-healthcheck and --health-cmd")
+ }
s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod)
if err != nil {
return err
}
+ } else if c.NoHealthCheck {
+ s.HealthConfig = &manifest.Schema2HealthConfig{
+ Test: []string{"NONE"},
+ }
}
- s.IDMappings, err = util.ParseIDMapping(ns.UsernsMode(c.UserNS), c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName)
+ userNS := ns.UsernsMode(c.UserNS)
+ s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName)
if err != nil {
return err
}
+ // If some mappings are specified, assume a private user namespace
+ if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) {
+ s.UserNS.NSMode = specgen.Private
+ }
s.Terminal = c.TTY
- ep, err := ExposedPorts(c.Expose, c.Net.PublishPorts, c.PublishAll, nil)
- if err != nil {
+
+ if err := verifyExpose(c.Expose); err != nil {
return err
}
- s.PortMappings = ep
+ // We are not handling the Expose flag yet.
+ // s.PortsExpose = c.Expose
+ s.PortMappings = c.Net.PublishPorts
+ s.PublishImagePorts = c.PublishAll
s.Pod = c.Pod
for k, v := range map[string]*specgen.Namespace{
@@ -246,20 +261,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.NetNS = c.Net.Network
}
- // TODO this is going to have to be done the libpod/server end of things
- // USER
- //user := c.String("user")
- //if user == "" {
- // switch {
- // case usernsMode.IsKeepID():
- // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
- // case data == nil:
- // user = "0"
- // default:
- // user = data.Config.User
- // }
- //}
-
// STOP SIGNAL
signalString := "TERM"
if sig := c.StopSignal; len(sig) > 0 {
@@ -288,7 +289,23 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
if c.EnvHost {
env = envLib.Join(env, osEnv)
+ } else if c.HTTPProxy {
+ for _, envSpec := range []string{
+ "http_proxy",
+ "HTTP_PROXY",
+ "https_proxy",
+ "HTTPS_PROXY",
+ "ftp_proxy",
+ "FTP_PROXY",
+ "no_proxy",
+ "NO_PROXY",
+ } {
+ if v, ok := osEnv[envSpec]; ok {
+ env[envSpec] = v
+ }
+ }
}
+
// env-file overrides any previous variables
for _, f := range c.EnvFile {
fileEnv, err := envLib.ParseFile(f)
@@ -390,14 +407,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.DNSOptions = c.Net.DNSOptions
s.StaticIP = c.Net.StaticIP
s.StaticMAC = c.Net.StaticMAC
-
- // deferred, must be added on libpod side
- //var ImageVolumes map[string]struct{}
- //if data != nil && c.String("image-volume") != "ignore" {
- // ImageVolumes = data.Config.Volumes
- //}
+ s.UseImageHosts = c.Net.NoHosts
s.ImageVolumeMode = c.ImageVolume
+ if s.ImageVolumeMode == "bind" {
+ s.ImageVolumeMode = "anonymous"
+ }
+
systemd := c.SystemdD == "always"
if !systemd && command != nil {
x, err := strconv.ParseBool(c.SystemdD)
@@ -449,24 +465,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
s.CgroupParent = c.CGroupParent
s.CgroupsMode = c.CGroupsMode
- // TODO WTF
- //cgroup := &cc.CgroupConfig{
- // Cgroupns: c.String("cgroupns"),
- //}
- //
- //userns := &cc.UserConfig{
- // GroupAdd: c.StringSlice("group-add"),
- // IDMappings: idmappings,
- // UsernsMode: usernsMode,
- // User: user,
- //}
- //
- //uts := &cc.UtsConfig{
- // UtsMode: utsMode,
- // NoHosts: c.Bool("no-hosts"),
- // HostAdd: c.StringSlice("add-host"),
- // Hostname: c.String("hostname"),
- //}
+ s.Groups = c.GroupAdd
s.Hostname = c.Hostname
sysctl := map[string]string{}
@@ -585,7 +584,14 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
if len(split) < 2 {
return errors.Errorf("invalid log option %q", o)
}
- logOpts[split[0]] = split[1]
+ switch {
+ case split[0] == "driver":
+ s.LogConfiguration.Driver = split[1]
+ case split[0] == "path":
+ s.LogConfiguration.Path = split[1]
+ default:
+ logOpts[split[0]] = split[1]
+ }
}
s.LogConfiguration.Options = logOpts
s.Name = c.Name
@@ -608,10 +614,15 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start
// first try to parse option value as JSON array of strings...
cmd := []string{}
- err := json.Unmarshal([]byte(inCmd), &cmd)
- if err != nil {
- // ...otherwise pass it to "/bin/sh -c" inside the container
- cmd = []string{"CMD-SHELL", inCmd}
+
+ if inCmd == "none" {
+ cmd = []string{"NONE"}
+ } else {
+ err := json.Unmarshal([]byte(inCmd), &cmd)
+ if err != nil {
+ // ...otherwise pass it to "/bin/sh -c" inside the container
+ cmd = []string{"CMD-SHELL", inCmd}
+ }
}
hc := manifest.Schema2HealthConfig{
Test: cmd,
diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go
index 47bbe12fa..5b99b8398 100644
--- a/cmd/podman/common/util.go
+++ b/cmd/podman/common/util.go
@@ -1,8 +1,11 @@
package common
import (
+ "fmt"
"strconv"
+ "github.com/spf13/cobra"
+
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
@@ -41,3 +44,11 @@ func createPortBindings(ports []string) ([]ocicni.PortMapping, error) {
}
return portBindings, nil
}
+
+// NoArgs returns an error if any args are included.
+func NoArgs(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 {
+ return fmt.Errorf("`%s` takes no arguments", cmd.CommandPath())
+ }
+ return nil
+}
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 8f140e2b8..da550b606 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -75,8 +75,7 @@ func init() {
func create(cmd *cobra.Command, args []string) error {
var (
- err error
- rawImageInput string
+ err error
)
cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
if err != nil {
@@ -92,20 +91,16 @@ func create(cmd *cobra.Command, args []string) error {
defer errorhandling.SyncQuiet(cidFile)
}
- if rfs := cliVals.RootFS; !rfs {
- rawImageInput = args[0]
- }
-
if err := createInit(cmd); err != nil {
return err
}
- if err := pullImage(args[0]); err != nil {
- return err
+ if !cliVals.RootFS {
+ if err := pullImage(args[0]); err != nil {
+ return err
+ }
}
-
- //TODO rootfs still
- s := specgen.NewSpecGenerator(rawImageInput)
+ s := specgen.NewSpecGenerator(args[0], cliVals.RootFS)
if err := common.FillOutSpecGen(s, &cliVals, args); err != nil {
return err
}
diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go
index 3749c934a..2bff8ae33 100644
--- a/cmd/podman/containers/exec.go
+++ b/cmd/podman/containers/exec.go
@@ -70,7 +70,7 @@ func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: containerExecCommand,
- Parent: containerCommitCommand,
+ Parent: containerCmd,
})
containerExecFlags := containerExecCommand.Flags()
diff --git a/cmd/podman/containers/exists.go b/cmd/podman/containers/exists.go
index e640ca5e1..81ba8a282 100644
--- a/cmd/podman/containers/exists.go
+++ b/cmd/podman/containers/exists.go
@@ -17,8 +17,9 @@ var (
Long: containerExistsDescription,
Example: `podman container exists containerID
podman container exists myctr || podman run --name myctr [etc...]`,
- RunE: exists,
- Args: cobra.ExactArgs(1),
+ RunE: exists,
+ Args: cobra.ExactArgs(1),
+ DisableFlagsInUseLine: true,
}
)
diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go
index f9ef1ddbd..4549a4ef6 100644
--- a/cmd/podman/containers/inspect.go
+++ b/cmd/podman/containers/inspect.go
@@ -1,15 +1,8 @@
package containers
import (
- "context"
- "fmt"
- "os"
- "strings"
- "text/template"
-
- "github.com/containers/libpod/cmd/podman/common"
+ "github.com/containers/libpod/cmd/podman/inspect"
"github.com/containers/libpod/cmd/podman/registry"
-
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
)
@@ -20,7 +13,7 @@ var (
Use: "inspect [flags] CONTAINER",
Short: "Display the configuration of a container",
Long: `Displays the low-level information on a container identified by name or ID.`,
- RunE: inspect,
+ RunE: inspectExec,
Example: `podman container inspect myCtr
podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
}
@@ -33,45 +26,9 @@ func init() {
Command: inspectCmd,
Parent: containerCmd,
})
- inspectOpts = common.AddInspectFlagSet(inspectCmd)
- flags := inspectCmd.Flags()
-
- if !registry.IsRemote() {
- flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
- }
-
-}
-
-func inspect(cmd *cobra.Command, args []string) error {
- responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, *inspectOpts)
- if err != nil {
- return err
- }
- if inspectOpts.Format == "" {
- b, err := json.MarshalIndent(responses, "", " ")
- if err != nil {
- return err
- }
- fmt.Println(string(b))
- return nil
- }
- format := inspectOpts.Format
- if !strings.HasSuffix(format, "\n") {
- format += "\n"
- }
- tmpl, err := template.New("inspect").Parse(format)
- if err != nil {
- return err
- }
- for _, i := range responses {
- if err := tmpl.Execute(os.Stdout, i); err != nil {
- return err
- }
- }
- return nil
+ inspectOpts = inspect.AddInspectFlagSet(inspectCmd)
}
-func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
- inspectOpts = options
- return inspect(cmd, args)
+func inspectExec(cmd *cobra.Command, args []string) error {
+ return inspect.Inspect(args, *inspectOpts)
}
diff --git a/cmd/podman/containers/list.go b/cmd/podman/containers/list.go
index 938fb63d3..22fa15b7e 100644
--- a/cmd/podman/containers/list.go
+++ b/cmd/podman/containers/list.go
@@ -1,6 +1,7 @@
package containers
import (
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
@@ -11,10 +12,10 @@ var (
listCmd = &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "List containers",
Long: "Prints out information about the containers",
- RunE: containers,
+ RunE: ps,
Example: `podman container list -a
podman container list -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
podman container list --size --sort names`,
@@ -27,8 +28,5 @@ func init() {
Command: listCmd,
Parent: containerCmd,
})
-}
-
-func containers(cmd *cobra.Command, args []string) error {
- return nil
+ listFlagSet(listCmd.Flags())
}
diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go
index 0e50140ca..2e3386aa9 100644
--- a/cmd/podman/containers/port.go
+++ b/cmd/podman/containers/port.go
@@ -109,7 +109,7 @@ func port(cmd *cobra.Command, args []string) error {
fmt.Printf("%d/%s -> %s:%d\n", v.ContainerPort, v.Protocol, hostIP, v.HostPort)
continue
}
- if v == userPort {
+ if v.ContainerPort == userPort.ContainerPort {
fmt.Printf("%s:%d\n", hostIP, v.HostPort)
found = true
break
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index 49e77abd2..44f50bab2 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -12,19 +12,21 @@ import (
tm "github.com/buger/goterm"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
psDescription = "Prints out information about the containers"
psCommand = &cobra.Command{
Use: "ps",
- Args: checkFlags,
+ Args: common.NoArgs,
Short: "List containers",
Long: psDescription,
RunE: ps,
@@ -47,7 +49,10 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: psCommand,
})
- flags := psCommand.Flags()
+ listFlagSet(psCommand.Flags())
+}
+
+func listFlagSet(flags *pflag.FlagSet) {
flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers")
flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given")
flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template")
@@ -137,6 +142,9 @@ func getResponses() ([]entities.ListContainer, error) {
func ps(cmd *cobra.Command, args []string) error {
var responses []psReporter
+ if err := checkFlags(cmd, args); err != nil {
+ return err
+ }
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) == 1 {
@@ -165,14 +173,14 @@ func ps(cmd *cobra.Command, args []string) error {
responses = append(responses, psReporter{r})
}
- headers, row := createPsOut()
+ headers, format := createPsOut()
if cmd.Flag("format").Changed {
- row = listOpts.Format
- if !strings.HasPrefix(row, "\n") {
- row += "\n"
+ format = listOpts.Format
+ if !strings.HasPrefix(format, "\n") {
+ format += "\n"
}
}
- format := "{{range . }}" + row + "{{end}}"
+ format = "{{range . }}" + format + "{{end}}"
if !listOpts.Quiet && !cmd.Flag("format").Changed {
format = headers + format
}
@@ -223,7 +231,7 @@ func createPsOut() (string, string) {
}
headers := defaultHeaders
row += "{{.ID}}"
- row += "\t{{.Image}}\t{{.Command}}\t{{.CreatedHuman}}\t{{.State}}\t{{.Ports}}\t{{.Names}}"
+ row += "\t{{.Image}}\t{{.Command}}\t{{.CreatedHuman}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
if listOpts.Pod {
headers += "\tPOD ID\tPODNAME"
@@ -247,6 +255,14 @@ type psReporter struct {
entities.ListContainer
}
+// ImageID returns the ID of the container
+func (l psReporter) ImageID() string {
+ if !noTrunc {
+ return l.ListContainer.ImageID[0:12]
+ }
+ return l.ListContainer.ImageID
+}
+
// ID returns the ID of the container
func (l psReporter) ID() string {
if !noTrunc {
@@ -282,6 +298,11 @@ func (l psReporter) State() string {
return state
}
+// Status is a synonym for State()
+func (l psReporter) Status() string {
+ return l.State()
+}
+
// Command returns the container command in string format
func (l psReporter) Command() string {
return strings.Join(l.ListContainer.Command, " ")
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 409b72198..e3fe4cd0b 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -104,8 +104,10 @@ func run(cmd *cobra.Command, args []string) error {
return err
}
- if err := pullImage(args[0]); err != nil {
- return err
+ if !cliVals.RootFS {
+ if err := pullImage(args[0]); err != nil {
+ return err
+ }
}
// If -i is not set, clear stdin
@@ -136,7 +138,7 @@ func run(cmd *cobra.Command, args []string) error {
}
runOpts.Detach = cliVals.Detach
runOpts.DetachKeys = cliVals.DetachKeys
- s := specgen.NewSpecGenerator(args[0])
+ s := specgen.NewSpecGenerator(args[0], cliVals.RootFS)
if err := common.FillOutSpecGen(s, &cliVals, args); err != nil {
return err
}
diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go
index 73f37e51f..381bf8e26 100644
--- a/cmd/podman/containers/start.go
+++ b/cmd/podman/containers/start.go
@@ -20,7 +20,6 @@ var (
Short: "Start one or more containers",
Long: startDescription,
RunE: start,
- Args: cobra.MinimumNArgs(1),
Example: `podman start --latest
podman start 860a4b231279 5421ab43b45
podman start --interactive --attach imageID`,
@@ -72,6 +71,9 @@ func init() {
func start(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
+ if len(args) == 0 && !startOptions.Latest {
+ return errors.New("start requires at least one argument")
+ }
if len(args) > 1 && startOptions.Attach {
return errors.Errorf("you cannot start and attach multiple containers at once")
}
diff --git a/cmd/podman/generate/generate.go b/cmd/podman/generate/generate.go
new file mode 100644
index 000000000..f04ef58a5
--- /dev/null
+++ b/cmd/podman/generate/generate.go
@@ -0,0 +1,27 @@
+package pods
+
+import (
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _generate_
+ generateCmd = &cobra.Command{
+ Use: "generate",
+ Short: "Generate structured data based on containers and pods.",
+ Long: "Generate structured data (e.g., Kubernetes yaml or systemd units) based on containers and pods.",
+ TraverseChildren: true,
+ RunE: registry.SubCommandExists,
+ }
+ containerConfig = util.DefaultContainerConfig()
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: generateCmd,
+ })
+}
diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go
new file mode 100644
index 000000000..55d770249
--- /dev/null
+++ b/cmd/podman/generate/systemd.go
@@ -0,0 +1,57 @@
+package pods
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ systemdTimeout uint
+ systemdOptions = entities.GenerateSystemdOptions{}
+ systemdDescription = `Generate systemd units for a pod or container.
+ The generated units can later be controlled via systemctl(1).`
+
+ systemdCmd = &cobra.Command{
+ Use: "systemd [flags] CTR|POD",
+ Short: "Generate systemd units.",
+ Long: systemdDescription,
+ RunE: systemd,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman generate systemd CTR
+ podman generate systemd --new --time 10 CTR
+ podman generate systemd --files --name POD`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: systemdCmd,
+ Parent: generateCmd,
+ })
+ flags := systemdCmd.Flags()
+ flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs")
+ flags.BoolVarP(&systemdOptions.Files, "files", "f", false, "Generate .service files instead of printing to stdout")
+ flags.UintVarP(&systemdTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Stop timeout override")
+ flags.StringVar(&systemdOptions.RestartPolicy, "restart-policy", "on-failure", "Systemd restart-policy")
+ flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one")
+ flags.SetNormalizeFunc(utils.AliasFlags)
+}
+
+func systemd(cmd *cobra.Command, args []string) error {
+ if cmd.Flags().Changed("time") {
+ systemdOptions.StopTimeout = &systemdTimeout
+ }
+
+ report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println(report.Output)
+ return nil
+}
diff --git a/cmd/podman/images/exists.go b/cmd/podman/images/exists.go
index 6464e6cd8..13191113f 100644
--- a/cmd/podman/images/exists.go
+++ b/cmd/podman/images/exists.go
@@ -15,6 +15,7 @@ var (
RunE: exists,
Example: `podman image exists ID
podman image exists IMAGE && podman pull IMAGE`,
+ DisableFlagsInUseLine: true,
}
)
diff --git a/cmd/podman/images/images.go b/cmd/podman/images/images.go
index fd3ede26a..96ef344bf 100644
--- a/cmd/podman/images/images.go
+++ b/cmd/podman/images/images.go
@@ -11,12 +11,13 @@ import (
var (
// podman _images_ Alias for podman image _list_
imagesCmd = &cobra.Command{
- Use: strings.Replace(listCmd.Use, "list", "images", 1),
- Args: listCmd.Args,
- Short: listCmd.Short,
- Long: listCmd.Long,
- RunE: listCmd.RunE,
- Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
+ Use: strings.Replace(listCmd.Use, "list", "images", 1),
+ Args: listCmd.Args,
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ RunE: listCmd.RunE,
+ Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
+ DisableFlagsInUseLine: true,
}
)
diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go
index 91c9445eb..8c727eb07 100644
--- a/cmd/podman/images/inspect.go
+++ b/cmd/podman/images/inspect.go
@@ -1,18 +1,9 @@
package images
import (
- "context"
- "fmt"
- "os"
- "strings"
- "text/tabwriter"
- "text/template"
-
- "github.com/containers/buildah/pkg/formats"
- "github.com/containers/libpod/cmd/podman/common"
+ "github.com/containers/libpod/cmd/podman/inspect"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -21,8 +12,8 @@ var (
inspectCmd = &cobra.Command{
Use: "inspect [flags] IMAGE",
Short: "Display the configuration of an image",
- Long: `Displays the low-level information on an image identified by name or ID.`,
- RunE: inspect,
+ Long: `Displays the low-level information of an image identified by name or ID.`,
+ RunE: inspectExec,
Example: `podman inspect alpine
podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine
podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`,
@@ -36,78 +27,11 @@ func init() {
Command: inspectCmd,
Parent: imageCmd,
})
- inspectOpts = common.AddInspectFlagSet(inspectCmd)
-}
-
-func inspect(cmd *cobra.Command, args []string) error {
- if inspectOpts.Size {
- return fmt.Errorf("--size can only be used for containers")
- }
- if inspectOpts.Latest {
- return fmt.Errorf("--latest can only be used for containers")
- }
- if len(args) == 0 {
- return errors.Errorf("image name must be specified: podman image inspect [options [...]] name")
- }
-
- results, err := registry.ImageEngine().Inspect(context.Background(), args, *inspectOpts)
- if err != nil {
- return err
- }
-
- if len(results.Images) > 0 {
- if inspectOpts.Format == "" {
- buf, err := json.MarshalIndent(results.Images, "", " ")
- if err != nil {
- return err
- }
- fmt.Println(string(buf))
-
- for id, e := range results.Errors {
- fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
- }
- return nil
- }
- row := inspectFormat(inspectOpts.Format)
- format := "{{range . }}" + row + "{{end}}"
- tmpl, err := template.New("inspect").Parse(format)
- if err != nil {
- return err
- }
-
- w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
- defer func() { _ = w.Flush() }()
- err = tmpl.Execute(w, results.Images)
- if err != nil {
- return err
- }
- }
-
- var lastErr error
- for id, e := range results.Errors {
- if lastErr != nil {
- fmt.Fprintf(os.Stderr, "%s: %s\n", id, lastErr.Error())
- }
- lastErr = e
- }
- return lastErr
-}
-
-func inspectFormat(row string) string {
- r := strings.NewReplacer("{{.Id}}", formats.IDString,
- ".Src", ".Source",
- ".Dst", ".Destination",
- ".ImageID", ".Image",
- )
- row = r.Replace(row)
-
- if !strings.HasSuffix(row, "\n") {
- row += "\n"
- }
- return row
+ inspectOpts = inspect.AddInspectFlagSet(inspectCmd)
+ flags := inspectCmd.Flags()
+ _ = flags.MarkHidden("latest") // Shared with container-inspect but not wanted here.
}
-func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
- inspectOpts = options
- return inspect(cmd, args)
+func inspectExec(cmd *cobra.Command, args []string) error {
+ return inspect.Inspect(args, *inspectOpts)
}
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index b979cb6af..552fed804 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -32,7 +32,7 @@ type listFlagType struct {
var (
// Command: podman image _list_
listCmd = &cobra.Command{
- Use: "list [flag] [IMAGE]",
+ Use: "list [FLAGS] [IMAGE]",
Aliases: []string{"ls"},
Args: cobra.MaximumNArgs(1),
Short: "List images in local storage",
@@ -41,6 +41,7 @@ var (
Example: `podman image list --format json
podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}"
podman image list --filter dangling=true`,
+ DisableFlagsInUseLine: true,
}
// Options to pull data
diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go
index 23c657b59..f49f95002 100644
--- a/cmd/podman/images/load.go
+++ b/cmd/podman/images/load.go
@@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"os"
+ "strings"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/libpod/cmd/podman/parse"
@@ -89,6 +90,6 @@ func load(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- fmt.Println("Loaded image: " + response.Name)
+ fmt.Println("Loaded image(s): " + strings.Join(response.Names, ","))
return nil
}
diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go
index b90d889be..eb9e4a7e4 100644
--- a/cmd/podman/images/prune.go
+++ b/cmd/podman/images/prune.go
@@ -6,6 +6,7 @@ import (
"os"
"strings"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
@@ -18,7 +19,7 @@ var (
If an image is not being used by a container, it will be removed from the system.`
pruneCmd = &cobra.Command{
Use: "prune",
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "Remove unused images",
Long: pruneDescription,
RunE: prune,
diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go
index ef2ffd0d7..0b3502d61 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -98,6 +98,7 @@ func imagePush(cmd *cobra.Command, args []string) error {
switch len(args) {
case 1:
source = args[0]
+ destination = args[0]
case 2:
source = args[0]
destination = args[1]
@@ -107,22 +108,21 @@ func imagePush(cmd *cobra.Command, args []string) error {
return errors.New("push requires at least one image name, or optionally a second to specify a different destination")
}
- pushOptsAPI := pushOptions.ImagePushOptions
// TLS verification in c/image is controlled via a `types.OptionalBool`
// which allows for distinguishing among set-true, set-false, unspecified
// which is important to implement a sane way of dealing with defaults of
// boolean CLI flags.
if cmd.Flags().Changed("tls-verify") {
- pushOptsAPI.TLSVerify = types.NewOptionalBool(pushOptions.TLSVerifyCLI)
+ pushOptions.SkipTLSVerify = types.NewOptionalBool(!pushOptions.TLSVerifyCLI)
}
- if pushOptsAPI.Authfile != "" {
- if _, err := os.Stat(pushOptsAPI.Authfile); err != nil {
- return errors.Wrapf(err, "error getting authfile %s", pushOptsAPI.Authfile)
+ if pushOptions.Authfile != "" {
+ if _, err := os.Stat(pushOptions.Authfile); err != nil {
+ return errors.Wrapf(err, "error getting authfile %s", pushOptions.Authfile)
}
}
// Let's do all the remaining Yoga in the API to prevent us from scattering
// logic across (too) many parts of the code.
- return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptsAPI)
+ return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptions.ImagePushOptions)
}
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index 93bf58bdd..a5fdaedc2 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -1,31 +1,26 @@
package main
import (
- "fmt"
-
- "github.com/containers/libpod/cmd/podman/common"
- "github.com/containers/libpod/cmd/podman/containers"
- "github.com/containers/libpod/cmd/podman/images"
+ "github.com/containers/libpod/cmd/podman/inspect"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
)
-// Inspect is one of the outlier commands in that it operates on images/containers/...
-
var (
- inspectOpts *entities.InspectOptions
-
// Command: podman _inspect_ Object_ID
inspectCmd = &cobra.Command{
Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}",
Short: "Display the configuration of object denoted by ID",
Long: "Displays the low-level information on an object identified by name or ID",
TraverseChildren: true,
- RunE: inspect,
- Example: `podman inspect alpine
- podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine`,
+ RunE: inspectExec,
+ Example: `podman inspect fedora
+ podman inspect --type image fedora
+ podman inspect CtrID ImgID
+ podman inspect --format "imageId: {{.Id}} size: {{.Size}}" fedora`,
}
+ inspectOpts *entities.InspectOptions
)
func init() {
@@ -33,26 +28,9 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: inspectCmd,
})
- inspectOpts = common.AddInspectFlagSet(inspectCmd)
- flags := inspectCmd.Flags()
- flags.StringVarP(&inspectOpts.Type, "type", "t", "", "Return JSON for specified type, (image or container) (default \"all\")")
- if !registry.IsRemote() {
- flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of (containers only)")
- }
+ inspectOpts = inspect.AddInspectFlagSet(inspectCmd)
}
-func inspect(cmd *cobra.Command, args []string) error {
- switch inspectOpts.Type {
- case "image":
- return images.Inspect(cmd, args, inspectOpts)
- case "container":
- return containers.Inspect(cmd, args, inspectOpts)
- case "":
- if err := images.Inspect(cmd, args, inspectOpts); err == nil {
- return nil
- }
- return containers.Inspect(cmd, args, inspectOpts)
- default:
- return fmt.Errorf("invalid type %q is must be 'container' or 'image'", inspectOpts.Type)
- }
+func inspectExec(cmd *cobra.Command, args []string) error {
+ return inspect.Inspect(args, *inspectOpts)
}
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
new file mode 100644
index 000000000..223ce00f0
--- /dev/null
+++ b/cmd/podman/inspect/inspect.go
@@ -0,0 +1,159 @@
+package inspect
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+const (
+ // ImageType is the image type.
+ ImageType = "image"
+ // ContainerType is the container type.
+ ContainerType = "container"
+ // AllType can be of type ImageType or ContainerType.
+ AllType = "all"
+)
+
+// AddInspectFlagSet takes a command and adds the inspect flags and returns an
+// InspectOptions object.
+func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
+ opts := entities.InspectOptions{}
+
+ flags := cmd.Flags()
+ flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size")
+ flags.StringVarP(&opts.Format, "format", "f", "json", "Format the output to a Go template or json")
+ flags.StringVarP(&opts.Type, "type", "t", AllType, fmt.Sprintf("Specify inspect-oject type (%q, %q or %q)", ImageType, ContainerType, AllType))
+ flags.BoolVarP(&opts.Latest, "latest", "l", false, "Act on the latest container Podman is aware of")
+
+ return &opts
+}
+
+// Inspect inspects the specified container/image names or IDs.
+func Inspect(namesOrIDs []string, options entities.InspectOptions) error {
+ inspector, err := newInspector(options)
+ if err != nil {
+ return err
+ }
+ return inspector.inspect(namesOrIDs)
+}
+
+// inspector allows for inspecting images and containers.
+type inspector struct {
+ containerEngine entities.ContainerEngine
+ imageEngine entities.ImageEngine
+ options entities.InspectOptions
+}
+
+// newInspector creates a new inspector based on the specified options.
+func newInspector(options entities.InspectOptions) (*inspector, error) {
+ switch options.Type {
+ case ImageType, ContainerType, AllType:
+ // Valid types.
+ default:
+ return nil, errors.Errorf("invalid type %q: must be %q, %q or %q", options.Type, ImageType, ContainerType, AllType)
+ }
+ if options.Type == ImageType {
+ if options.Latest {
+ return nil, errors.Errorf("latest is not supported for type %q", ImageType)
+ }
+ if options.Size {
+ return nil, errors.Errorf("size is not supported for type %q", ImageType)
+ }
+ }
+ return &inspector{
+ containerEngine: registry.ContainerEngine(),
+ imageEngine: registry.ImageEngine(),
+ options: options,
+ }, nil
+}
+
+// inspect inspects the specified container/image names or IDs.
+func (i *inspector) inspect(namesOrIDs []string) error {
+ // data - dumping place for inspection results.
+ var data []interface{}
+ ctx := context.Background()
+
+ if len(namesOrIDs) == 0 {
+ if !i.options.Latest {
+ return errors.New("no containers or images specified")
+ }
+ }
+
+ tmpType := i.options.Type
+ if i.options.Latest {
+ if len(namesOrIDs) > 0 {
+ return errors.New("latest and containers are not allowed")
+ }
+ tmpType = ContainerType // -l works with --type=all
+ }
+
+ // Inspect - note that AllType requires us to expensively query one-by-one.
+ switch tmpType {
+ case AllType:
+ all, err := i.inspectAll(ctx, namesOrIDs)
+ if err != nil {
+ return err
+ }
+ data = all
+ case ImageType:
+ imgData, err := i.imageEngine.Inspect(ctx, namesOrIDs, i.options)
+ if err != nil {
+ return err
+ }
+ for i := range imgData {
+ data = append(data, imgData[i])
+ }
+ case ContainerType:
+ ctrData, err := i.containerEngine.ContainerInspect(ctx, namesOrIDs, i.options)
+ if err != nil {
+ return err
+ }
+ for i := range ctrData {
+ data = append(data, ctrData[i])
+ }
+ default:
+ return errors.Errorf("invalid type %q: must be %q, %q or %q", i.options.Type, ImageType, ContainerType, AllType)
+ }
+
+ var out formats.Writer
+ if i.options.Format == "json" || i.options.Format == "" { // "" for backwards compat
+ out = formats.JSONStructArray{Output: data}
+ } else {
+ out = formats.StdoutTemplateArray{Output: data, Template: inspectFormat(i.options.Format)}
+ }
+ return out.Out()
+}
+
+func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, error) {
+ var data []interface{}
+ for _, name := range namesOrIDs {
+ imgData, err := i.imageEngine.Inspect(ctx, []string{name}, i.options)
+ if err == nil {
+ data = append(data, imgData[0])
+ continue
+ }
+ ctrData, err := i.containerEngine.ContainerInspect(ctx, []string{name}, i.options)
+ if err != nil {
+ return nil, err
+ }
+ data = append(data, ctrData[0])
+ }
+ return data, nil
+}
+
+func inspectFormat(row string) string {
+ r := strings.NewReplacer(
+ "{{.Id}}", formats.IDString,
+ ".Src", ".Source",
+ ".Dst", ".Destination",
+ ".ImageID", ".Image",
+ )
+ return r.Replace(row)
+}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 8109eca2f..481214a38 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -4,10 +4,10 @@ import (
"os"
_ "github.com/containers/libpod/cmd/podman/containers"
+ _ "github.com/containers/libpod/cmd/podman/generate"
_ "github.com/containers/libpod/cmd/podman/healthcheck"
_ "github.com/containers/libpod/cmd/podman/images"
_ "github.com/containers/libpod/cmd/podman/manifest"
- _ "github.com/containers/libpod/cmd/podman/networks"
_ "github.com/containers/libpod/cmd/podman/pods"
"github.com/containers/libpod/cmd/podman/registry"
_ "github.com/containers/libpod/cmd/podman/system"
diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go
index 20251ca87..38f832fad 100644
--- a/cmd/podman/manifest/add.go
+++ b/cmd/podman/manifest/add.go
@@ -13,7 +13,7 @@ import (
var (
manifestAddOpts = entities.ManifestAddOptions{}
addCmd = &cobra.Command{
- Use: "add",
+ Use: "add [flags] LIST LIST",
Short: "Add images to a manifest list or image index",
Long: "Adds an image to a manifest list or image index.",
RunE: add,
@@ -34,6 +34,7 @@ func init() {
flags.StringSliceVar(&manifestAddOpts.Annotation, "annotation", nil, "set an `annotation` for the specified image")
flags.StringVar(&manifestAddOpts.Arch, "arch", "", "override the `architecture` of the specified image")
flags.StringSliceVar(&manifestAddOpts.Features, "features", nil, "override the `features` of the specified image")
+ flags.StringVar(&manifestAddOpts.OS, "os", "", "override the `OS` of the specified image")
flags.StringVar(&manifestAddOpts.OSVersion, "os-version", "", "override the OS `version` of the specified image")
flags.StringVar(&manifestAddOpts.Variant, "variant", "", "override the `Variant` of the specified image")
}
diff --git a/cmd/podman/manifest/create.go b/cmd/podman/manifest/create.go
index 4f3e27774..9c0097b90 100644
--- a/cmd/podman/manifest/create.go
+++ b/cmd/podman/manifest/create.go
@@ -13,7 +13,7 @@ import (
var (
manifestCreateOpts = entities.ManifestCreateOptions{}
createCmd = &cobra.Command{
- Use: "create",
+ Use: "create [flags] LIST [IMAGE]",
Short: "Create manifest list or image index",
Long: "Creates manifest lists or image indexes.",
RunE: create,
diff --git a/cmd/podman/manifest/inspect.go b/cmd/podman/manifest/inspect.go
index 36ecdc87b..5112aa5b2 100644
--- a/cmd/podman/manifest/inspect.go
+++ b/cmd/podman/manifest/inspect.go
@@ -12,7 +12,7 @@ import (
var (
inspectCmd = &cobra.Command{
- Use: "inspect IMAGE",
+ Use: "inspect [flags] IMAGE",
Short: "Display the contents of a manifest list or image index",
Long: "Display the contents of a manifest list or image index.",
RunE: inspect,
diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go
index 3cee86bcc..a0e412098 100644
--- a/cmd/podman/networks/network.go
+++ b/cmd/podman/networks/network.go
@@ -17,6 +17,9 @@ var (
}
)
+// TODO add the following to main.go to get networks back onto the
+// command list.
+//_ "github.com/containers/libpod/cmd/podman/networks"
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index ff21166f3..0c0d07b3e 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -24,7 +24,7 @@ var (
createCommand = &cobra.Command{
Use: "create",
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "Create a new empty pod",
Long: podCreateDescription,
RunE: create,
diff --git a/cmd/podman/pods/exists.go b/cmd/podman/pods/exists.go
index 5a94bf150..cf3e3eae5 100644
--- a/cmd/podman/pods/exists.go
+++ b/cmd/podman/pods/exists.go
@@ -19,6 +19,7 @@ var (
Args: cobra.ExactArgs(1),
Example: `podman pod exists podID
podman pod exists mypod || podman pod create --name mypod`,
+ DisableFlagsInUseLine: true,
}
)
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 808980eff..8ae1f91a8 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -5,11 +5,13 @@ import (
"fmt"
"io"
"os"
+ "sort"
"strings"
"text/tabwriter"
"text/template"
"time"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/docker/go-units"
@@ -27,12 +29,13 @@ var (
Short: "list pods",
Long: psDescription,
RunE: pods,
+ Args: common.NoArgs,
}
)
var (
defaultHeaders string = "POD ID\tNAME\tSTATUS\tCREATED"
- inputFilters string
+ inputFilters []string
noTrunc bool
psInput entities.PodPSOptions
)
@@ -48,7 +51,7 @@ func init() {
flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated")
flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status")
// TODO should we make this a [] ?
- flags.StringVarP(&inputFilters, "filter", "f", "", "Filter output based on conditions given")
+ flags.StringSliceVarP(&inputFilters, "filter", "f", []string{}, "Filter output based on conditions given")
flags.StringVar(&psInput.Format, "format", "", "Pretty-print pods to JSON or using a Go template")
flags.BoolVarP(&psInput.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod")
@@ -67,8 +70,13 @@ func pods(cmd *cobra.Command, args []string) error {
row string
lpr []ListPodReporter
)
+
+ if psInput.Quiet && len(psInput.Format) > 0 {
+ return errors.New("quiet and format cannot be used together")
+ }
if cmd.Flag("filter").Changed {
- for _, f := range strings.Split(inputFilters, ",") {
+ psInput.Filters = make(map[string][]string)
+ for _, f := range inputFilters {
split := strings.Split(f, "=")
if len(split) < 2 {
return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
@@ -81,6 +89,10 @@ func pods(cmd *cobra.Command, args []string) error {
return err
}
+ if err := sortPodPsOutput(psInput.Sort, responses); err != nil {
+ return err
+ }
+
if psInput.Format == "json" {
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
@@ -95,11 +107,7 @@ func pods(cmd *cobra.Command, args []string) error {
}
headers, row := createPodPsOut()
if psInput.Quiet {
- if noTrunc {
- row = "{{.Id}}\n"
- } else {
- row = "{{slice .Id 0 12}}\n"
- }
+ row = "{{.Id}}\n"
}
if cmd.Flag("format").Changed {
row = psInput.Format
@@ -130,11 +138,7 @@ func pods(cmd *cobra.Command, args []string) error {
func createPodPsOut() (string, string) {
var row string
headers := defaultHeaders
- if noTrunc {
- row += "{{.Id}}"
- } else {
- row += "{{slice .Id 0 12}}"
- }
+ row += "{{.Id}}"
row += "\t{{.Name}}\t{{.Status}}\t{{.Created}}"
@@ -160,11 +164,7 @@ func createPodPsOut() (string, string) {
}
headers += "\tINFRA ID\n"
- if noTrunc {
- row += "\t{{.InfraId}}\n"
- } else {
- row += "\t{{slice .InfraId 0 12}}\n"
- }
+ row += "\t{{.InfraId}}\n"
return headers, row
}
@@ -184,6 +184,19 @@ func (l ListPodReporter) NumberOfContainers() int {
return len(l.Containers)
}
+// ID is a wrapper to Id for compat, typos
+func (l ListPodReporter) ID() string {
+ return l.Id()
+}
+
+// Id returns the Pod id
+func (l ListPodReporter) Id() string {
+ if noTrunc {
+ return l.ListPodsReport.Id
+ }
+ return l.ListPodsReport.Id[0:12]
+}
+
// Added for backwards compatibility with podmanv1
func (l ListPodReporter) InfraID() string {
return l.InfraId()
@@ -192,6 +205,9 @@ func (l ListPodReporter) InfraID() string {
// InfraId returns the infra container id for the pod
// depending on trunc
func (l ListPodReporter) InfraId() string {
+ if len(l.ListPodsReport.InfraId) == 0 {
+ return ""
+ }
if noTrunc {
return l.ListPodsReport.InfraId
}
@@ -225,3 +241,52 @@ func (l ListPodReporter) ContainerStatuses() string {
}
return strings.Join(statuses, ",")
}
+
+func sortPodPsOutput(sortBy string, lprs []*entities.ListPodsReport) error {
+ switch sortBy {
+ case "created":
+ sort.Sort(podPsSortedCreated{lprs})
+ case "id":
+ sort.Sort(podPsSortedId{lprs})
+ case "name":
+ sort.Sort(podPsSortedName{lprs})
+ case "number":
+ sort.Sort(podPsSortedNumber{lprs})
+ case "status":
+ sort.Sort(podPsSortedStatus{lprs})
+ default:
+ return errors.Errorf("invalid option for --sort, options are: id, names, or number")
+ }
+ return nil
+}
+
+type lprSort []*entities.ListPodsReport
+
+func (a lprSort) Len() int { return len(a) }
+func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type podPsSortedCreated struct{ lprSort }
+
+func (a podPsSortedCreated) Less(i, j int) bool {
+ return a.lprSort[i].Created.After(a.lprSort[j].Created)
+}
+
+type podPsSortedId struct{ lprSort }
+
+func (a podPsSortedId) Less(i, j int) bool { return a.lprSort[i].Id < a.lprSort[j].Id }
+
+type podPsSortedNumber struct{ lprSort }
+
+func (a podPsSortedNumber) Less(i, j int) bool {
+ return len(a.lprSort[i].Containers) < len(a.lprSort[j].Containers)
+}
+
+type podPsSortedName struct{ lprSort }
+
+func (a podPsSortedName) Less(i, j int) bool { return a.lprSort[i].Name < a.lprSort[j].Name }
+
+type podPsSortedStatus struct{ lprSort }
+
+func (a podPsSortedStatus) Less(i, j int) bool {
+ return a.lprSort[i].Status < a.lprSort[j].Status
+}
diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go
index ea3a6476a..4b9882f8a 100644
--- a/cmd/podman/pods/rm.go
+++ b/cmd/podman/pods/rm.go
@@ -41,10 +41,10 @@ func init() {
})
flags := rmCommand.Flags()
- flags.BoolVarP(&rmOptions.All, "all", "a", false, "Restart all running pods")
+ flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all running pods")
flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false")
flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing")
- flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of")
+ flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Remove the latest pod podman is aware of")
if registry.IsRemote() {
_ = flags.MarkHidden("latest")
_ = flags.MarkHidden("ignore")
diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go
new file mode 100644
index 000000000..7c3597d9a
--- /dev/null
+++ b/cmd/podman/pods/stats.go
@@ -0,0 +1,189 @@
+package pods
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "reflect"
+ "strings"
+ "text/tabwriter"
+ "text/template"
+ "time"
+
+ "github.com/buger/goterm"
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util/camelcase"
+ "github.com/spf13/cobra"
+)
+
+type podStatsOptionsWrapper struct {
+ entities.PodStatsOptions
+
+ // Format - pretty-print to JSON or a go template.
+ Format string
+ // NoReset - do not reset the screen when streaming.
+ NoReset bool
+ // NoStream - do not stream stats but write them once.
+ NoStream bool
+}
+
+var (
+ statsOptions = podStatsOptionsWrapper{}
+ statsDescription = `Display the containers' resource-usage statistics of one or more running pod`
+ // Command: podman pod _pod_
+ statsCmd = &cobra.Command{
+ Use: "stats [flags] [POD...]",
+ Short: "Display resource-usage statistics of pods",
+ Long: statsDescription,
+ RunE: stats,
+ Example: `podman pod stats
+ podman pod stats a69b23034235 named-pod
+ podman pod stats --latest
+ podman pod stats --all`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: statsCmd,
+ Parent: podCmd,
+ })
+
+ flags := statsCmd.Flags()
+ flags.BoolVarP(&statsOptions.All, "all", "a", false, "Provide stats for all pods")
+ flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template")
+ flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Provide stats on the latest pod Podman is aware of")
+ flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen when streaming")
+ flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result")
+
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func stats(cmd *cobra.Command, args []string) error {
+ // Validate input.
+ if err := entities.ValidatePodStatsOptions(args, &statsOptions.PodStatsOptions); err != nil {
+ return err
+ }
+
+ format := statsOptions.Format
+ doJson := strings.ToLower(format) == formats.JSONString
+ header := getPodStatsHeader(format)
+
+ for {
+ reports, err := registry.ContainerEngine().PodStats(context.Background(), args, statsOptions.PodStatsOptions)
+ if err != nil {
+ return err
+ }
+ // Print the stats in the requested format and configuration.
+ if doJson {
+ if err := printJSONPodStats(reports); err != nil {
+ return err
+ }
+ } else {
+ if !statsOptions.NoReset {
+ goterm.Clear()
+ goterm.MoveCursor(1, 1)
+ goterm.Flush()
+ }
+ if len(format) == 0 {
+ printPodStatsLines(reports)
+ } else if err := printFormattedPodStatsLines(format, reports, header); err != nil {
+ return err
+ }
+ }
+ if statsOptions.NoStream {
+ break
+ }
+ time.Sleep(time.Second)
+ }
+
+ return nil
+}
+
+func printJSONPodStats(stats []*entities.PodStatsReport) error {
+ b, err := json.MarshalIndent(&stats, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(os.Stdout, "%s\n", string(b))
+ return nil
+}
+
+func printPodStatsLines(stats []*entities.PodStatsReport) {
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+ outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
+ fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS")
+ for _, i := range stats {
+ if len(stats) == 0 {
+ fmt.Fprintf(w, outFormat, i.Pod, "--", "--", "--", "--", "--", "--", "--", "--")
+ } else {
+ fmt.Fprintf(w, outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS)
+ }
+ }
+ w.Flush()
+}
+
+func printFormattedPodStatsLines(format string, stats []*entities.PodStatsReport, headerNames map[string]string) error {
+ if len(stats) == 0 {
+ return nil
+ }
+
+ // Use a tabwriter to align column format
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ // Spit out the header if "table" is present in the format
+ if strings.HasPrefix(format, "table") {
+ hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1)
+ format = hformat
+ headerTmpl, err := template.New("header").Parse(hformat)
+ if err != nil {
+ return err
+ }
+ if err := headerTmpl.Execute(w, headerNames); err != nil {
+ return err
+ }
+ fmt.Fprintln(w, "")
+ }
+
+ // Spit out the data rows now
+ dataTmpl, err := template.New("data").Parse(format)
+ if err != nil {
+ return err
+ }
+ for _, s := range stats {
+ if err := dataTmpl.Execute(w, s); err != nil {
+ return err
+ }
+ fmt.Fprintln(w, "")
+ }
+ // Flush the writer
+ return w.Flush()
+
+}
+
+// getPodStatsHeader returns the stats header for the specified options.
+func getPodStatsHeader(format string) map[string]string {
+ headerNames := make(map[string]string)
+ if format == "" {
+ return headerNames
+ }
+ // Make a map of the field names for the headers
+ v := reflect.ValueOf(entities.PodStatsReport{})
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ split := camelcase.Split(t.Field(i).Name)
+ value := strings.ToUpper(strings.Join(split, " "))
+ switch value {
+ case "CPU", "MEM":
+ value += " %"
+ case "MEM USAGE":
+ value = "MEM USAGE / LIMIT"
+ }
+ headerNames[t.Field(i).Name] = value
+ }
+ return headerNames
+}
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 84c3867f2..56ca549b6 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -34,7 +34,7 @@ Description:
// UsageTemplate is the usage template for podman commands
// This blocks the displaying of the global options. The main podman
// command should not use this.
-const usageTemplate = `Usage(v2):{{if (and .Runnable (not .HasAvailableSubCommands))}}
+const usageTemplate = `Usage:{{if (and .Runnable (not .HasAvailableSubCommands))}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go
index 3c1943b55..31dd9aa77 100644
--- a/cmd/podman/system/events.go
+++ b/cmd/podman/system/events.go
@@ -7,6 +7,7 @@ import (
"os"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/domain/entities"
@@ -18,7 +19,7 @@ var (
eventsDescription = "Monitor podman events"
eventsCommand = &cobra.Command{
Use: "events",
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "Show podman events",
Long: eventsDescription,
RunE: eventsCmd,
diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go
index 8b36ef549..143796938 100644
--- a/cmd/podman/system/info.go
+++ b/cmd/podman/system/info.go
@@ -5,6 +5,7 @@ import (
"os"
"text/template"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/ghodss/yaml"
@@ -18,7 +19,7 @@ var (
`
infoCommand = &cobra.Command{
Use: "info",
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Long: infoDescription,
Short: "Display podman system information",
RunE: info,
diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go
index 5d3874de3..b0f4eb528 100644
--- a/cmd/podman/system/version.go
+++ b/cmd/podman/system/version.go
@@ -9,6 +9,7 @@ import (
"time"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/domain/entities"
@@ -19,7 +20,7 @@ import (
var (
versionCommand = &cobra.Command{
Use: "version",
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "Display the Podman Version Information",
RunE: version,
Annotations: map[string]string{
diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go
index f75de6b4b..8cc6fb301 100644
--- a/cmd/podman/volumes/list.go
+++ b/cmd/podman/volumes/list.go
@@ -2,12 +2,14 @@ package volumes
import (
"context"
+ "fmt"
"html/template"
"io"
"os"
"strings"
"text/tabwriter"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
@@ -23,7 +25,7 @@ and the output format can be changed to JSON or a user specified Go template.`
lsCommand = &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "List volumes",
Long: volumeLsDescription,
RunE: list,
@@ -57,6 +59,9 @@ func list(cmd *cobra.Command, args []string) error {
if cliOpts.Quiet && cmd.Flag("format").Changed {
return errors.New("quiet and format flags cannot be used together")
}
+ if len(cliOpts.Filter) > 0 {
+ lsOpts.Filter = make(map[string][]string)
+ }
for _, f := range cliOpts.Filter {
filterSplit := strings.Split(f, "=")
if len(filterSplit) < 2 {
@@ -68,6 +73,10 @@ func list(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ if cliOpts.Format == "json" {
+ return outputJSON(responses)
+ }
+
if len(responses) < 1 {
return nil
}
@@ -99,3 +108,12 @@ func list(cmd *cobra.Command, args []string) error {
}
return nil
}
+
+func outputJSON(vols []*entities.VolumeListReport) error {
+ b, err := json.MarshalIndent(vols, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go
index 197a9da9b..77138f4b7 100644
--- a/cmd/podman/volumes/prune.go
+++ b/cmd/podman/volumes/prune.go
@@ -7,6 +7,7 @@ import (
"os"
"strings"
+ "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/cmd/podman/utils"
"github.com/containers/libpod/pkg/domain/entities"
@@ -21,7 +22,7 @@ var (
Note all data will be destroyed.`
pruneCommand = &cobra.Command{
Use: "prune",
- Args: cobra.NoArgs,
+ Args: common.NoArgs,
Short: "Remove all unused volumes",
Long: volumePruneDescription,
RunE: prune,