summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/README.md66
-rw-r--r--cmd/podman/common/create.go4
-rw-r--r--cmd/podman/common/specgen.go10
-rw-r--r--cmd/podman/common/util.go216
-rw-r--r--cmd/podman/containers/logs.go2
-rw-r--r--cmd/podman/containers/prune.go15
-rw-r--r--cmd/podman/containers/ps.go4
-rw-r--r--cmd/podman/containers/rm.go2
-rw-r--r--cmd/podman/containers/run.go6
-rw-r--r--cmd/podman/healthcheck/healthcheck.go4
-rw-r--r--cmd/podman/images/build.go (renamed from cmd/podman/build.go)25
-rw-r--r--cmd/podman/images/diff.go5
-rw-r--r--cmd/podman/images/history.go21
-rw-r--r--cmd/podman/images/import.go23
-rw-r--r--cmd/podman/images/load.go19
-rw-r--r--cmd/podman/images/prune.go15
-rw-r--r--cmd/podman/images/pull.go4
-rw-r--r--cmd/podman/images/push.go4
-rw-r--r--cmd/podman/images/rm.go7
-rw-r--r--cmd/podman/images/save.go23
-rw-r--r--cmd/podman/images/search.go8
-rw-r--r--cmd/podman/images/tag.go16
-rw-r--r--cmd/podman/images/untag.go16
-rw-r--r--cmd/podman/login.go2
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/networks/create.go81
-rw-r--r--cmd/podman/networks/inspect.go46
-rw-r--r--cmd/podman/networks/list.go133
-rw-r--r--cmd/podman/networks/network.go9
-rw-r--r--cmd/podman/networks/rm.go63
-rw-r--r--cmd/podman/pods/pod.go2
-rw-r--r--cmd/podman/pods/prune.go13
-rw-r--r--cmd/podman/pods/ps.go2
-rw-r--r--cmd/podman/pods/stats.go2
-rw-r--r--cmd/podman/pods/top.go2
-rw-r--r--cmd/podman/root.go2
-rw-r--r--cmd/podman/system/info.go22
-rw-r--r--cmd/podman/system/prune.go103
-rw-r--r--cmd/podman/utils/utils.go55
-rw-r--r--cmd/podman/volumes/prune.go12
40 files changed, 916 insertions, 149 deletions
diff --git a/cmd/podman/README.md b/cmd/podman/README.md
index c1b8f48a7..da92d0216 100644
--- a/cmd/podman/README.md
+++ b/cmd/podman/README.md
@@ -1,23 +1,16 @@
-# Adding a podman V2 commands
+# Podman CLI
-## Build podman V2
+The following is an example of how to add a new primary command (`manifest`) and a sub-command (`inspect`) to the Podman CLI.
+This is example code, the production code has additional error checking and the business logic provided.
-```shell script
-$ cd $GOPATH/src/github.com/containers/libpod/cmd/podmanV2
-```
-If you wish to include the libpod library in your program,
-```shell script
-$ go build -tags 'ABISupport' .
-```
-The `--remote` flag may be used to connect to the Podman service using the API.
-Otherwise, direct calls will be made to the Libpod library.
-```shell script
-$ go build -tags '!ABISupport' .
-```
-The Libpod library is not linked into the executable.
-All calls are made via the API and `--remote=False` is an error condition.
+See items below for details on building, installing, contributing to Podman:
+ - [Readme](README.md)
+ - [Contributing](CONTRIBUTING.md)
+ - [Podman Usage](transfer.md)
+ - [Trouble Shooting](troubleshooting.md)
+ - [Code Of Conduct](CODE-OF-CONDUCT.md)
-## Adding a new command `podman manifests`
+## Adding a new command `podman manifest`
```shell script
$ mkdir -p $GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests
```
@@ -27,6 +20,7 @@ package manifests
import (
"github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
)
@@ -36,11 +30,11 @@ var (
manifestCmd = &cobra.Command{
Use: "manifest",
Short: "Manage manifests",
+ Args: cobra.ExactArgs(1),
Long: "Manage manifests",
- Example: "podman manifests IMAGE",
+ Example: "podman manifest IMAGE",
TraverseChildren: true,
- PersistentPreRunE: preRunE,
- RunE: registry.SubCommandExists, // Report error if there is no sub command given
+ RunE: validate.SubCommandExists, // Report error if there is no sub command given
}
)
func init() {
@@ -51,15 +45,6 @@ func init() {
// The definition for this command
Command: manifestCmd,
})
- // Setup cobra templates, sub commands will inherit
- manifestCmd.SetHelpTemplate(registry.HelpTemplate())
- manifestCmd.SetUsageTemplate(registry.UsageTemplate())
-}
-
-// preRunE populates the image engine for sub commands
-func preRunE(cmd *cobra.Command, args []string) error {
- _, err := registry.NewImageEngine(cmd, args)
- return err
}
```
To "wire" in the `manifest` command, edit the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/main.go``` to add:
@@ -87,7 +72,11 @@ var (
Short: "Display manifest from image",
Long: "Displays the low-level information on a manifest identified by image name or ID",
RunE: inspect,
- Example: "podman manifest DEADBEEF",
+ Annotations: map[string]string{
+ // Add this annotation if this command cannot be run rootless
+ // registry.ParentNSRequired: "",
+ },
+ Example: "podman manifest inspect DEADBEEF",
}
)
@@ -98,6 +87,7 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
// The definition for this command
Command: inspectCmd,
+ // The parent command to proceed this command on the CLI
Parent: manifestCmd,
})
@@ -106,8 +96,22 @@ func init() {
// Business logic: cmd is inspectCmd, args is the positional arguments from os.Args
func inspect(cmd *cobra.Command, args []string) error {
- // Business logic using registry.ImageEngine
+ // Business logic using registry.ImageEngine()
// Do not pull from libpod directly use the domain objects and types
return nil
}
```
+
+## Helper functions
+
+The complete set can be found in the `validate` package, here are some examples:
+
+ - `cobra.Command{ Args: validate.NoArgs }` used when the command does not accept errors
+ - `cobra.Command{ Args: validate.IdOrLatestArgs }` used to ensure either a list of ids given or the --latest flag
+ - `cobra.Command{ RunE: validate.SubCommandExists }` used to validate a subcommand given to a command
+ - `validate.ChoiceValue` used to create a `pflag.Value` that validate user input against a provided slice of values. For example:
+ ```go
+ flags := cobraCommand.Flags()
+ created := validate.ChoiceValue(&opts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status")
+ flags.Var(created, "sort", "Sort output by: "+created.Choices())
+ ```
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 0f9476754..53f4a8fa2 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -3,7 +3,7 @@ package common
import (
"fmt"
- buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/spf13/pflag"
)
@@ -26,7 +26,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
)
createFlags.StringVar(
&cf.Authfile,
- "authfile", buildahcli.GetDefaultAuthFile(),
+ "authfile", auth.GetDefaultAuthFile(),
"Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override",
)
createFlags.StringVar(
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 7250f88bb..7467c184a 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -192,7 +192,6 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []strin
func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error {
var (
err error
- // namespaces map[string]string
)
// validate flags as needed
@@ -234,9 +233,15 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
// We are not handling the Expose flag yet.
// s.PortsExpose = c.Expose
s.PortMappings = c.Net.PublishPorts
- s.PublishImagePorts = c.PublishAll
+ s.PublishExposedPorts = c.PublishAll
s.Pod = c.Pod
+ expose, err := createExpose(c.Expose)
+ if err != nil {
+ return err
+ }
+ s.Expose = expose
+
for k, v := range map[string]*specgen.Namespace{
c.IPC: &s.IpcNS,
c.PID: &s.PidNS,
@@ -403,6 +408,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
s.ShmSize = &shmSize
}
+ s.CNINetworks = c.Net.CNINetworks
s.HostAdd = c.Net.AddHosts
s.UseImageResolvConf = c.Net.UseImageResolvConf
s.DNSServers = c.Net.DNSServers
diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go
index 47bbe12fa..a3626b4e4 100644
--- a/cmd/podman/common/util.go
+++ b/cmd/podman/common/util.go
@@ -1,43 +1,201 @@
package common
import (
+ "net"
"strconv"
+ "strings"
- "github.com/cri-o/ocicni/pkg/ocicni"
- "github.com/docker/go-connections/nat"
+ "github.com/containers/libpod/pkg/specgen"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
-// createPortBindings iterates ports mappings and exposed ports into a format CNI understands
-func createPortBindings(ports []string) ([]ocicni.PortMapping, error) {
- // TODO wants someone to rewrite this code in the future
- var portBindings []ocicni.PortMapping
- // The conversion from []string to natBindings is temporary while mheon reworks the port
- // deduplication code. Eventually that step will not be required.
- _, natBindings, err := nat.ParsePortSpecs(ports)
- if err != nil {
- return nil, err
- }
- for containerPb, hostPb := range natBindings {
- var pm ocicni.PortMapping
- pm.ContainerPort = int32(containerPb.Int())
- for _, i := range hostPb {
- var hostPort int
- var err error
- pm.HostIP = i.HostIP
- if i.HostPort == "" {
- hostPort = containerPb.Int()
+// createExpose parses user-provided exposed port definitions and converts them
+// into SpecGen format.
+// TODO: The SpecGen format should really handle ranges more sanely - we could
+// be massively inflating what is sent over the wire with a large range.
+func createExpose(expose []string) (map[uint16]string, error) {
+ toReturn := make(map[uint16]string)
+
+ for _, e := range expose {
+ // Check for protocol
+ proto := "tcp"
+ splitProto := strings.Split(e, "/")
+ if len(splitProto) > 2 {
+ return nil, errors.Errorf("invalid expose format - protocol can only be specified once")
+ } else if len(splitProto) == 2 {
+ proto = splitProto[1]
+ }
+
+ // Check for a range
+ start, len, err := parseAndValidateRange(splitProto[0])
+ if err != nil {
+ return nil, err
+ }
+
+ var index uint16
+ for index = 0; index < len; index++ {
+ portNum := start + index
+ protocols, ok := toReturn[portNum]
+ if !ok {
+ toReturn[portNum] = proto
} else {
- hostPort, err = strconv.Atoi(i.HostPort)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to convert host port to integer")
- }
+ newProto := strings.Join(append(strings.Split(protocols, ","), strings.Split(proto, ",")...), ",")
+ toReturn[portNum] = newProto
}
+ }
+ }
+
+ return toReturn, nil
+}
+
+// createPortBindings iterates ports mappings into SpecGen format.
+func createPortBindings(ports []string) ([]specgen.PortMapping, error) {
+ // --publish is formatted as follows:
+ // [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol]
+ toReturn := make([]specgen.PortMapping, 0, len(ports))
+
+ for _, p := range ports {
+ var (
+ ctrPort string
+ proto, hostIP, hostPort *string
+ )
+
+ splitProto := strings.Split(p, "/")
+ switch len(splitProto) {
+ case 1:
+ // No protocol was provided
+ case 2:
+ proto = &(splitProto[1])
+ default:
+ return nil, errors.Errorf("invalid port format - protocol can only be specified once")
+ }
- pm.HostPort = int32(hostPort)
- pm.Protocol = containerPb.Proto()
- portBindings = append(portBindings, pm)
+ splitPort := strings.Split(splitProto[0], ":")
+ switch len(splitPort) {
+ case 1:
+ ctrPort = splitPort[0]
+ case 2:
+ hostPort = &(splitPort[0])
+ ctrPort = splitPort[1]
+ case 3:
+ hostIP = &(splitPort[0])
+ hostPort = &(splitPort[1])
+ ctrPort = splitPort[2]
+ default:
+ return nil, errors.Errorf("invalid port format - format is [[hostIP:]hostPort:]containerPort")
+ }
+
+ newPort, err := parseSplitPort(hostIP, hostPort, ctrPort, proto)
+ if err != nil {
+ return nil, err
+ }
+
+ toReturn = append(toReturn, newPort)
+ }
+
+ return toReturn, nil
+}
+
+// parseSplitPort parses individual components of the --publish flag to produce
+// a single port mapping in SpecGen format.
+func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) (specgen.PortMapping, error) {
+ newPort := specgen.PortMapping{}
+ if ctrPort == "" {
+ return newPort, errors.Errorf("must provide a non-empty container port to publish")
+ }
+ ctrStart, ctrLen, err := parseAndValidateRange(ctrPort)
+ if err != nil {
+ return newPort, errors.Wrapf(err, "error parsing container port")
+ }
+ newPort.ContainerPort = ctrStart
+ newPort.Range = ctrLen
+
+ if protocol != nil {
+ if *protocol == "" {
+ return newPort, errors.Errorf("must provide a non-empty protocol to publish")
+ }
+ newPort.Protocol = *protocol
+ }
+ if hostIP != nil {
+ if *hostIP == "" {
+ return newPort, errors.Errorf("must provide a non-empty container host IP to publish")
}
+ testIP := net.ParseIP(*hostIP)
+ if testIP == nil {
+ return newPort, errors.Errorf("cannot parse %q as an IP address", *hostIP)
+ }
+ newPort.HostIP = testIP.String()
+ }
+ if hostPort != nil {
+ if *hostPort == "" {
+ return newPort, errors.Errorf("must provide a non-empty container host port to publish")
+ }
+ hostStart, hostLen, err := parseAndValidateRange(*hostPort)
+ if err != nil {
+ return newPort, errors.Wrapf(err, "error parsing host port")
+ }
+ if hostLen != ctrLen {
+ return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen)
+ }
+ newPort.HostPort = hostStart
+ }
+
+ hport := newPort.HostPort
+ if hport == 0 {
+ hport = newPort.ContainerPort
+ }
+ logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol)
+
+ return newPort, nil
+}
+
+// Parse and validate a port range.
+// Returns start port, length of range, error.
+func parseAndValidateRange(portRange string) (uint16, uint16, error) {
+ splitRange := strings.Split(portRange, "-")
+ if len(splitRange) > 2 {
+ return 0, 0, errors.Errorf("invalid port format - port ranges are formatted as startPort-stopPort")
+ }
+
+ if splitRange[0] == "" {
+ return 0, 0, errors.Errorf("port numbers cannot be negative")
+ }
+
+ startPort, err := parseAndValidatePort(splitRange[0])
+ if err != nil {
+ return 0, 0, err
+ }
+
+ var rangeLen uint16 = 1
+ if len(splitRange) == 2 {
+ if splitRange[1] == "" {
+ return 0, 0, errors.Errorf("must provide ending number for port range")
+ }
+ endPort, err := parseAndValidatePort(splitRange[1])
+ if err != nil {
+ return 0, 0, err
+ }
+ if endPort <= startPort {
+ return 0, 0, errors.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort)
+ }
+ // Our range is the total number of ports
+ // involved, so we need to add 1 (8080:8081 is
+ // 2 ports, for example, not 1)
+ rangeLen = endPort - startPort + 1
+ }
+
+ return startPort, rangeLen, nil
+}
+
+// Turn a single string into a valid U16 port.
+func parseAndValidatePort(port string) (uint16, error) {
+ num, err := strconv.Atoi(port)
+ if err != nil {
+ return 0, errors.Wrapf(err, "cannot parse %q as a port number", port)
+ }
+ if num < 1 || num > 65535 {
+ return 0, errors.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num)
}
- return portBindings, nil
+ return uint16(num), nil
}
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 5dec71fdd..2b8c3ed5f 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -27,7 +27,7 @@ var (
`
logsCommand = &cobra.Command{
Use: "logs [flags] CONTAINER [CONTAINER...]",
- Short: "Fetch the logs of one or more container",
+ Short: "Fetch the logs of one or more containers",
Long: logsDescription,
RunE: logs,
Example: `podman logs ctrID
diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go
index df627259c..38168a6e4 100644
--- a/cmd/podman/containers/prune.go
+++ b/cmd/podman/containers/prune.go
@@ -18,10 +18,10 @@ import (
var (
pruneDescription = fmt.Sprintf(`podman container prune
- Removes all stopped | exited containers`)
+ Removes all non running containers`)
pruneCommand = &cobra.Command{
Use: "prune [flags]",
- Short: "Remove all stopped | exited containers",
+ Short: "Remove all non running containers",
Long: pruneDescription,
RunE: prune,
Example: `podman container prune`,
@@ -43,7 +43,6 @@ func init() {
func prune(cmd *cobra.Command, args []string) error {
var (
- errs utils.OutputErrors
pruneOptions = entities.ContainerPruneOptions{}
)
if len(args) > 0 {
@@ -51,7 +50,7 @@ func prune(cmd *cobra.Command, args []string) error {
}
if !force {
reader := bufio.NewReader(os.Stdin)
- fmt.Println("WARNING! This will remove all stopped containers.")
+ fmt.Println("WARNING! This will remove all non running containers.")
fmt.Print("Are you sure you want to continue? [y/N] ")
answer, err := reader.ReadString('\n')
if err != nil {
@@ -76,11 +75,5 @@ func prune(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- for k := range responses.ID {
- fmt.Println(k)
- }
- for _, v := range responses.Err {
- errs = append(errs, v)
- }
- return errs.PrintErrors()
+ return utils.PrintContainerPruneResults(responses)
}
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index fd0b68195..4d12d2534 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -67,8 +67,8 @@ func listFlagSet(flags *pflag.FlagSet) {
flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime")
flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds")
- created := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status")
- flags.Var(created, "sort", "Sort output by: "+created.Choices())
+ sort := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status")
+ flags.Var(sort, "sort", "Sort output by: "+sort.Choices())
if registry.IsRemote() {
_ = flags.MarkHidden("latest")
diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go
index 96549cead..2a0f9cc6a 100644
--- a/cmd/podman/containers/rm.go
+++ b/cmd/podman/containers/rm.go
@@ -35,7 +35,7 @@ var (
containerRmCommand = &cobra.Command{
Use: rmCommand.Use,
- Short: rmCommand.Use,
+ Short: rmCommand.Short,
Long: rmCommand.Long,
RunE: rmCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index b13983e37..f72446cb6 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -170,9 +170,9 @@ func run(cmd *cobra.Command, args []string) error {
return nil
}
if runRmi {
- _, err := registry.ImageEngine().Remove(registry.GetContext(), []string{args[0]}, entities.ImageRemoveOptions{})
- if err != nil {
- logrus.Errorf("%s", errors.Wrapf(err, "failed removing image"))
+ _, rmErrors := registry.ImageEngine().Remove(registry.GetContext(), []string{args[0]}, entities.ImageRemoveOptions{})
+ if len(rmErrors) > 0 {
+ logrus.Errorf("%s", errors.Wrapf(errorhandling.JoinErrors(rmErrors), "failed removing image"))
}
}
return nil
diff --git a/cmd/podman/healthcheck/healthcheck.go b/cmd/podman/healthcheck/healthcheck.go
index ce90dba31..f48701624 100644
--- a/cmd/podman/healthcheck/healthcheck.go
+++ b/cmd/podman/healthcheck/healthcheck.go
@@ -11,8 +11,8 @@ var (
// Command: healthcheck
healthCmd = &cobra.Command{
Use: "healthcheck",
- Short: "Manage Healthcheck",
- Long: "Manage Healthcheck",
+ Short: "Manage health checks on containers",
+ Long: "Run health checks on containers",
TraverseChildren: true,
RunE: validate.SubCommandExists,
}
diff --git a/cmd/podman/build.go b/cmd/podman/images/build.go
index 43a2f7ab5..06a7efd25 100644
--- a/cmd/podman/build.go
+++ b/cmd/podman/images/build.go
@@ -1,4 +1,4 @@
-package main
+package images
import (
"os"
@@ -17,6 +17,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
// buildFlagsWrapper are local to cmd/ as the build code is using Buildah-internal
@@ -48,6 +49,17 @@ var (
podman build --layers --force-rm --tag imageName .`,
}
+ imageBuildCmd = &cobra.Command{
+ Args: buildCmd.Args,
+ Use: buildCmd.Use,
+ Short: buildCmd.Short,
+ Long: buildCmd.Long,
+ RunE: buildCmd.RunE,
+ Example: `podman image build .
+ podman image build --creds=username:password -t imageName -f Containerfile.simple .
+ podman image build --layers --force-rm --tag imageName .`,
+ }
+
buildOpts = buildFlagsWrapper{}
)
@@ -66,8 +78,17 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: buildCmd,
})
- flags := buildCmd.Flags()
+ buildFlags(buildCmd.Flags())
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageBuildCmd,
+ Parent: imageCmd,
+ })
+ buildFlags(imageBuildCmd.Flags())
+}
+func buildFlags(flags *pflag.FlagSet) {
// Podman flags
flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer")
diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go
index 7cfacfc6c..c24f98369 100644
--- a/cmd/podman/images/diff.go
+++ b/cmd/podman/images/diff.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -28,9 +29,11 @@ func init() {
Command: diffCmd,
Parent: imageCmd,
})
+ diffFlags(diffCmd.Flags())
+}
+func diffFlags(flags *pflag.FlagSet) {
diffOpts = &entities.DiffOptions{}
- flags := diffCmd.Flags()
flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive")
_ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.")
flags.StringVar(&diffOpts.Format, "format", "", "Change the output format")
diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go
index ce153aa46..17a80557e 100644
--- a/cmd/podman/images/history.go
+++ b/cmd/podman/images/history.go
@@ -15,6 +15,7 @@ import (
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -32,6 +33,15 @@ var (
RunE: history,
}
+ imageHistoryCmd = &cobra.Command{
+ Args: historyCmd.Args,
+ Use: historyCmd.Use,
+ Short: historyCmd.Short,
+ Long: historyCmd.Long,
+ RunE: historyCmd.RunE,
+ Example: `podman image history imageID`,
+ }
+
opts = struct {
human bool
noTrunc bool
@@ -45,8 +55,17 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: historyCmd,
})
+ historyFlags(historyCmd.Flags())
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageHistoryCmd,
+ Parent: imageCmd,
+ })
+ historyFlags(imageHistoryCmd.Flags())
+}
- flags := historyCmd.Flags()
+func historyFlags(flags *pflag.FlagSet) {
flags.StringVar(&opts.format, "format", "", "Change the output to JSON or a Go template")
flags.BoolVarP(&opts.human, "human", "H", true, "Display sizes and dates in human readable format")
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output")
diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go
index 1c0568762..0e16128ce 100644
--- a/cmd/podman/images/import.go
+++ b/cmd/podman/images/import.go
@@ -10,6 +10,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -26,6 +27,17 @@ var (
cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported
cat ctr.tar | podman import -`,
}
+
+ imageImportCommand = &cobra.Command{
+ Args: cobra.MinimumNArgs(1),
+ Use: importCommand.Use,
+ Short: importCommand.Short,
+ Long: importCommand.Long,
+ RunE: importCommand.RunE,
+ Example: `podman image import http://example.com/ctr.tar url-image
+ cat ctr.tar | podman -q image import --message "importing the ctr.tar tarball" - image-imported
+ cat ctr.tar | podman image import -`,
+ }
)
var (
@@ -37,8 +49,17 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: importCommand,
})
+ importFlags(importCommand.Flags())
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageImportCommand,
+ Parent: imageCmd,
+ })
+ importFlags(imageImportCommand.Flags())
+}
- flags := importCommand.Flags()
+func importFlags(flags *pflag.FlagSet) {
flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR")
flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image")
flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output")
diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go
index f49f95002..d34c794c6 100644
--- a/cmd/podman/images/load.go
+++ b/cmd/podman/images/load.go
@@ -15,6 +15,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
"golang.org/x/crypto/ssh/terminal"
)
@@ -27,6 +28,14 @@ var (
RunE: load,
Args: cobra.MaximumNArgs(1),
}
+
+ imageLoadCommand = &cobra.Command{
+ Args: cobra.MinimumNArgs(1),
+ Use: loadCommand.Use,
+ Short: loadCommand.Short,
+ Long: loadCommand.Long,
+ RunE: loadCommand.RunE,
+ }
)
var (
@@ -38,8 +47,16 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: loadCommand,
})
+ loadFlags(loadCommand.Flags())
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageLoadCommand,
+ Parent: imageCmd,
+ })
+ loadFlags(imageLoadCommand.Flags())
+}
- flags := loadCommand.Flags()
+func loadFlags(flags *pflag.FlagSet) {
flags.StringVarP(&loadOpts.Input, "input", "i", "", "Read from specified archive file (default: stdin)")
flags.BoolVarP(&loadOpts.Quiet, "quiet", "q", false, "Suppress the output")
flags.StringVar(&loadOpts.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file")
diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go
index 53a1966c1..7c9e3eb61 100644
--- a/cmd/podman/images/prune.go
+++ b/cmd/podman/images/prune.go
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
"github.com/containers/libpod/cmd/podman/validate"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
@@ -71,17 +72,5 @@ Are you sure you want to continue? [y/N] `)
return err
}
- for _, i := range results.Report.Id {
- fmt.Println(i)
- }
-
- for _, e := range results.Report.Err {
- fmt.Fprint(os.Stderr, e.Error()+"\n")
- }
-
- if results.Size > 0 {
- fmt.Fprintf(os.Stdout, "Size: %d\n", results.Size)
- }
-
- return nil
+ return utils.PrintImagePruneResults(results)
}
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index 9f4cbc50e..9e883703f 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -4,7 +4,7 @@ import (
"fmt"
"os"
- buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -75,7 +75,7 @@ func init() {
// pullFlags set the flags for the pull command.
func pullFlags(flags *pflag.FlagSet) {
flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled")
- flags.StringVar(&pullOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
flags.StringVar(&pullOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images")
diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go
index 0b3502d61..dd536213f 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -3,7 +3,7 @@ package images
import (
"os"
- buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -70,7 +70,7 @@ func init() {
// pushFlags set the flags for the push command.
func pushFlags(flags *pflag.FlagSet) {
- flags.StringVar(&pushOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pushOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys")
flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&pushOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go
index 1cf5fa365..4b9920532 100644
--- a/cmd/podman/images/rm.go
+++ b/cmd/podman/images/rm.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -48,7 +49,9 @@ func rm(cmd *cobra.Command, args []string) error {
return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
}
- report, err := registry.ImageEngine().Remove(registry.GetContext(), args, imageOpts)
+ // Note: certain image-removal errors are non-fatal. Hence, the report
+ // might be set even if err != nil.
+ report, rmErrors := registry.ImageEngine().Remove(registry.GetContext(), args, imageOpts)
if report != nil {
for _, u := range report.Untagged {
fmt.Println("Untagged: " + u)
@@ -62,5 +65,5 @@ func rm(cmd *cobra.Command, args []string) error {
registry.SetExitCode(report.ExitCode)
}
- return err
+ return errorhandling.JoinErrors(rmErrors)
}
diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go
index 8f7832074..56953e41c 100644
--- a/cmd/podman/images/save.go
+++ b/cmd/podman/images/save.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
"golang.org/x/crypto/ssh/terminal"
)
@@ -43,6 +44,16 @@ var (
podman save --format docker-dir -o ubuntu-dir ubuntu
podman save > alpine-all.tar alpine:latest`,
}
+ imageSaveCommand = &cobra.Command{
+ Args: saveCommand.Args,
+ Use: saveCommand.Use,
+ Short: saveCommand.Short,
+ Long: saveCommand.Long,
+ RunE: saveCommand.RunE,
+ Example: `podman image save --quiet -o myimage.tar imageID
+ podman image save --format docker-dir -o ubuntu-dir ubuntu
+ podman image save > alpine-all.tar alpine:latest`,
+ }
)
var (
@@ -54,7 +65,17 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: saveCommand,
})
- flags := saveCommand.Flags()
+ saveFlags(saveCommand.Flags())
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageSaveCommand,
+ Parent: imageCmd,
+ })
+ saveFlags(imageSaveCommand.Flags())
+}
+
+func saveFlags(flags *pflag.FlagSet) {
flags.BoolVar(&saveOpts.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&saveOpts.Format, "format", define.V2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
flags.StringVarP(&saveOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go
index a8abfb339..640d497c3 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -5,8 +5,8 @@ import (
"reflect"
"strings"
- buildahcli "github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -87,7 +87,7 @@ func searchFlags(flags *pflag.FlagSet) {
flags.StringVar(&searchOptions.Format, "format", "", "Change the output format to a Go template")
flags.IntVar(&searchOptions.Limit, "limit", 0, "Limit the number of results")
flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output")
- flags.StringVar(&searchOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&searchOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
if registry.IsRemote() {
_ = flags.MarkHidden("authfile")
@@ -105,6 +105,10 @@ func imageSearch(cmd *cobra.Command, args []string) error {
return errors.Errorf("search requires exactly one argument")
}
+ if searchOptions.Limit > 100 {
+ return errors.Errorf("Limit %d is outside the range of [1, 100]", searchOptions.Limit)
+ }
+
// 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
diff --git a/cmd/podman/images/tag.go b/cmd/podman/images/tag.go
index 411313a9b..dae3416c4 100644
--- a/cmd/podman/images/tag.go
+++ b/cmd/podman/images/tag.go
@@ -18,6 +18,17 @@ var (
podman tag imageID:latest myNewImage:newTag
podman tag httpd myregistryhost:5000/fedora/httpd:v2`,
}
+
+ imageTagCommand = &cobra.Command{
+ Args: tagCommand.Args,
+ Use: tagCommand.Use,
+ Short: tagCommand.Short,
+ Long: tagCommand.Long,
+ RunE: tagCommand.RunE,
+ Example: `podman image tag 0e3bbc2 fedora:latest
+ podman image tag imageID:latest myNewImage:newTag
+ podman image tag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
)
func init() {
@@ -25,6 +36,11 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: tagCommand,
})
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageTagCommand,
+ Parent: imageCmd,
+ })
}
func tag(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/images/untag.go b/cmd/podman/images/untag.go
index 3218844b7..266a3f115 100644
--- a/cmd/podman/images/untag.go
+++ b/cmd/podman/images/untag.go
@@ -17,6 +17,17 @@ var (
podman untag imageID:latest otherImageName:latest
podman untag httpd myregistryhost:5000/fedora/httpd:v2`,
}
+
+ imageUntagCommand = &cobra.Command{
+ Args: untagCommand.Args,
+ Use: untagCommand.Use,
+ Short: untagCommand.Short,
+ Long: untagCommand.Long,
+ RunE: untagCommand.RunE,
+ Example: `podman image untag 0e3bbc2
+ podman image untag imageID:latest otherImageName:latest
+ podman image untag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
)
func init() {
@@ -24,6 +35,11 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: untagCommand,
})
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageUntagCommand,
+ Parent: imageCmd,
+ })
}
func untag(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/login.go b/cmd/podman/login.go
index 9de805d15..dc57758ab 100644
--- a/cmd/podman/login.go
+++ b/cmd/podman/login.go
@@ -63,6 +63,6 @@ func login(cmd *cobra.Command, args []string) error {
DockerCertPath: loginOptions.CertDir,
DockerInsecureSkipTLSVerify: skipTLS,
}
-
+ loginOptions.GetLoginSet = cmd.Flag("get-login").Changed
return auth.Login(context.Background(), &sysCtx, &loginOptions.LoginOptions, args[0])
}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 3a8958b6d..422dee90b 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -9,6 +9,7 @@ import (
_ "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/networks/create.go b/cmd/podman/networks/create.go
new file mode 100644
index 000000000..2bb75ea9e
--- /dev/null
+++ b/cmd/podman/networks/create.go
@@ -0,0 +1,81 @@
+package network
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networkCreateDescription = `create CNI networks for containers and pods`
+ networkCreateCommand = &cobra.Command{
+ Use: "create [flags] [NETWORK]",
+ Short: "network create",
+ Long: networkCreateDescription,
+ RunE: networkCreate,
+ Example: `podman network create podman1`,
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ networkCreateOptions entities.NetworkCreateOptions
+)
+
+func networkCreateFlags(flags *pflag.FlagSet) {
+ flags.StringVarP(&networkCreateOptions.Driver, "driver", "d", "bridge", "driver to manage the network")
+ flags.IPVar(&networkCreateOptions.Gateway, "gateway", nil, "IPv4 or IPv6 gateway for the subnet")
+ flags.BoolVar(&networkCreateOptions.Internal, "internal", false, "restrict external access from this network")
+ flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range")
+ flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device")
+ // TODO not supported yet
+ //flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver")
+ // TODO enable when IPv6 is working
+ //flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking")
+ flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format")
+ flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin")
+}
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networkCreateCommand,
+ Parent: networkCmd,
+ })
+ flags := networkCreateCommand.Flags()
+ networkCreateFlags(flags)
+
+}
+
+func networkCreate(cmd *cobra.Command, args []string) error {
+ var (
+ name string
+ )
+ if err := network.IsSupportedDriver(networkCreateOptions.Driver); err != nil {
+ return err
+ }
+ if len(args) > 1 {
+ return errors.Errorf("only one network can be created at a time")
+ }
+ if len(args) > 0 && !libpod.NameRegex.MatchString(args[0]) {
+ return libpod.RegexError
+ }
+
+ if len(args) > 0 {
+ name = args[0]
+ }
+ response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.Filename)
+ return nil
+}
diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go
new file mode 100644
index 000000000..0bc73579a
--- /dev/null
+++ b/cmd/podman/networks/inspect.go
@@ -0,0 +1,46 @@
+package network
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ networkinspectDescription = `Inspect network`
+ networkinspectCommand = &cobra.Command{
+ Use: "inspect NETWORK [NETWORK...] [flags] ",
+ Short: "network inspect",
+ Long: networkinspectDescription,
+ RunE: networkInspect,
+ Example: `podman network inspect podman`,
+ Args: cobra.MinimumNArgs(1),
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networkinspectCommand,
+ Parent: networkCmd,
+ })
+}
+
+func networkInspect(cmd *cobra.Command, args []string) error {
+ responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{})
+ if err != nil {
+ return err
+ }
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go
new file mode 100644
index 000000000..e27062255
--- /dev/null
+++ b/cmd/podman/networks/list.go
@@ -0,0 +1,133 @@
+package network
+
+import (
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "io"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podman/validate"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networklistDescription = `List networks`
+ networklistCommand = &cobra.Command{
+ Use: "ls",
+ Args: validate.NoArgs,
+ Short: "network list",
+ Long: networklistDescription,
+ RunE: networkList,
+ Example: `podman network list`,
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ networkListOptions entities.NetworkListOptions
+ headers string = "NAME\tVERSION\tPLUGINS\n"
+ defaultListRow string = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
+)
+
+func networkListFlags(flags *pflag.FlagSet) {
+ // TODO enable filters based on something
+ //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers")
+ flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print containers to JSON or using a Go template")
+ flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networklistCommand,
+ Parent: networkCmd,
+ })
+ flags := networklistCommand.Flags()
+ networkListFlags(flags)
+}
+
+func networkList(cmd *cobra.Command, args []string) error {
+ var (
+ w io.Writer = os.Stdout
+ nlprs []NetworkListPrintReports
+ )
+
+ responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions)
+ if err != nil {
+ return err
+ }
+
+ // quiet means we only print the network names
+ if networkListOptions.Quiet {
+ return quietOut(responses)
+ }
+
+ if networkListOptions.Format == "json" {
+ return jsonOut(responses)
+ }
+
+ for _, r := range responses {
+ nlprs = append(nlprs, NetworkListPrintReports{r})
+ }
+
+ row := networkListOptions.Format
+ if len(row) < 1 {
+ row = defaultListRow
+ }
+ if !strings.HasSuffix(row, "\n") {
+ row += "\n"
+ }
+
+ format := "{{range . }}" + row + "{{end}}"
+ if !cmd.Flag("format").Changed {
+ format = headers + format
+ }
+ tmpl, err := template.New("listNetworks").Parse(format)
+ if err != nil {
+ return err
+ }
+ if err := tmpl.Execute(w, nlprs); err != nil {
+ return err
+ }
+ if flusher, ok := w.(interface{ Flush() error }); ok {
+ return flusher.Flush()
+ }
+ return nil
+}
+
+func quietOut(responses []*entities.NetworkListReport) error {
+ for _, r := range responses {
+ fmt.Println(r.Name)
+ }
+ return nil
+}
+
+func jsonOut(responses []*entities.NetworkListReport) error {
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
+
+type NetworkListPrintReports struct {
+ *entities.NetworkListReport
+}
+
+func (n NetworkListPrintReports) Version() string {
+ return n.CNIVersion
+}
+
+func (n NetworkListPrintReports) Plugins() string {
+ return network.GetCNIPlugins(n.NetworkConfigList)
+}
diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go
index e2a928312..56dd390ea 100644
--- a/cmd/podman/networks/network.go
+++ b/cmd/podman/networks/network.go
@@ -1,4 +1,4 @@
-package images
+package network
import (
"github.com/containers/libpod/cmd/podman/registry"
@@ -9,7 +9,7 @@ import (
var (
// Command: podman _network_
- cmd = &cobra.Command{
+ networkCmd = &cobra.Command{
Use: "network",
Short: "Manage networks",
Long: "Manage networks",
@@ -18,12 +18,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},
- Command: cmd,
+ Command: networkCmd,
})
}
diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go
new file mode 100644
index 000000000..dc1eb9909
--- /dev/null
+++ b/cmd/podman/networks/rm.go
@@ -0,0 +1,63 @@
+package network
+
+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"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networkrmDescription = `Remove networks`
+ networkrmCommand = &cobra.Command{
+ Use: "rm [flags] NETWORK [NETWORK...]",
+ Short: "network rm",
+ Long: networkrmDescription,
+ RunE: networkRm,
+ Example: `podman network rm podman`,
+ Args: cobra.MinimumNArgs(1),
+ Annotations: map[string]string{
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ networkRmOptions entities.NetworkRmOptions
+)
+
+func networkRmFlags(flags *pflag.FlagSet) {
+ flags.BoolVarP(&networkRmOptions.Force, "force", "f", false, "remove any containers using network")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: networkrmCommand,
+ Parent: networkCmd,
+ })
+ flags := networkrmCommand.Flags()
+ networkRmFlags(flags)
+}
+
+func networkRm(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+
+ responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Name)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podman/pods/pod.go b/cmd/podman/pods/pod.go
index edca08202..ed265ef90 100644
--- a/cmd/podman/pods/pod.go
+++ b/cmd/podman/pods/pod.go
@@ -16,7 +16,7 @@ var (
podCmd = &cobra.Command{
Use: "pod",
Short: "Manage pods",
- Long: "Manage pods",
+ Long: "Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.",
TraverseChildren: true,
RunE: validate.SubCommandExists,
}
diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go
index 091e3375b..bc15d8035 100644
--- a/cmd/podman/pods/prune.go
+++ b/cmd/podman/pods/prune.go
@@ -41,9 +41,6 @@ func init() {
}
func prune(cmd *cobra.Command, args []string) error {
- var (
- errs utils.OutputErrors
- )
if len(args) > 0 {
return errors.Errorf("`%s` takes no arguments", cmd.CommandPath())
}
@@ -60,16 +57,8 @@ func prune(cmd *cobra.Command, args []string) error {
}
}
responses, err := registry.ContainerEngine().PodPrune(context.Background(), pruneOptions)
-
if err != nil {
return err
}
- for _, r := range responses {
- if r.Err == nil {
- fmt.Println(r.Id)
- } else {
- errs = append(errs, r.Err)
- }
- }
- return errs.PrintErrors()
+ return utils.PrintPodPruneResults(responses)
}
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index b97dfeb66..5703bd172 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -26,7 +26,7 @@ var (
psCmd = &cobra.Command{
Use: "ps",
Aliases: []string{"ls", "list"},
- Short: "list pods",
+ Short: "List pods",
Long: psDescription,
RunE: pods,
Args: validate.NoArgs,
diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go
index 7c3597d9a..d3950fdbc 100644
--- a/cmd/podman/pods/stats.go
+++ b/cmd/podman/pods/stats.go
@@ -35,7 +35,7 @@ var (
// Command: podman pod _pod_
statsCmd = &cobra.Command{
Use: "stats [flags] [POD...]",
- Short: "Display resource-usage statistics of pods",
+ Short: "Display a live stream of resource usage statistics for the containers in one or more pods",
Long: statsDescription,
RunE: stats,
Example: `podman pod stats
diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go
index ad602f4ea..9cf2bd525 100644
--- a/cmd/podman/pods/top.go
+++ b/cmd/podman/pods/top.go
@@ -25,7 +25,7 @@ var (
topCommand = &cobra.Command{
Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS]",
- Short: "Display the running processes in a pod",
+ Short: "Display the running processes of containers in a pod",
Long: topDescription,
RunE: top,
Args: cobra.ArbitraryArgs,
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 375faf8b1..502b6c03c 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -212,7 +212,7 @@ func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) {
flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file")
cfg := opts.Config
- flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, opts.CGroupUsage)
+ flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")")
flags.StringVar(&opts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results")
flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary")
flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network")
diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go
index 26be794c5..dad63bcd4 100644
--- a/cmd/podman/system/info.go
+++ b/cmd/podman/system/info.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/libpod/pkg/domain/entities"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -25,6 +26,15 @@ var (
RunE: info,
Example: `podman info`,
}
+
+ systemInfoCommand = &cobra.Command{
+ Args: infoCommand.Args,
+ Use: infoCommand.Use,
+ Short: infoCommand.Short,
+ Long: infoCommand.Long,
+ RunE: infoCommand.RunE,
+ Example: `podman system info`,
+ }
)
var (
@@ -37,7 +47,17 @@ func init() {
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: infoCommand,
})
- flags := infoCommand.Flags()
+ infoFlags(infoCommand.Flags())
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: systemInfoCommand,
+ Parent: systemCmd,
+ })
+ infoFlags(systemInfoCommand.Flags())
+}
+
+func infoFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&debug, "debug", "D", false, "Display additional debug information")
flags.StringVarP(&inFormat, "format", "f", "", "Change the output format to JSON or a Go template")
}
diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go
new file mode 100644
index 000000000..cc9b473f1
--- /dev/null
+++ b/cmd/podman/system/prune.go
@@ -0,0 +1,103 @@
+package system
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
+ "github.com/containers/libpod/cmd/podman/validate"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ pruneOptions = entities.SystemPruneOptions{}
+
+ pruneDescription = fmt.Sprintf(`
+ podman system prune
+
+ Remove unused data
+`)
+
+ pruneCommand = &cobra.Command{
+ Use: "prune [flags]",
+ Short: "Remove unused data",
+ Args: validate.NoArgs,
+ Long: pruneDescription,
+ RunE: prune,
+ Example: `podman system prune`,
+ }
+ force bool
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pruneCommand,
+ Parent: systemCmd,
+ })
+ flags := pruneCommand.Flags()
+ 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")
+
+}
+
+func prune(cmd *cobra.Command, args []string) error {
+ // Prompt for confirmation if --force is not set
+ if !force {
+ reader := bufio.NewReader(os.Stdin)
+ volumeString := ""
+ if pruneOptions.Volume {
+ volumeString = `
+ - all volumes not used by at least one container`
+ }
+ fmt.Printf(`
+WARNING! This will remove:
+ - all stopped containers%s
+ - all stopped pods
+ - all dangling images
+ - all build cache
+Are you sure you want to continue? [y/N] `, volumeString)
+ answer, err := reader.ReadString('\n')
+ if err != nil {
+ return errors.Wrapf(err, "error reading input")
+ }
+ if strings.ToLower(answer)[0] != 'y' {
+ return nil
+ }
+ }
+ // TODO: support for filters in system prune
+ response, err := registry.ContainerEngine().SystemPrune(context.Background(), pruneOptions)
+ if err != nil {
+ return err
+ }
+ // Print pod prune results
+ fmt.Println("Deleted Pods")
+ err = utils.PrintPodPruneResults(response.PodPruneReport)
+ if err != nil {
+ return err
+ }
+ // Print container prune results
+ fmt.Println("Deleted Containers")
+ err = utils.PrintContainerPruneResults(response.ContainerPruneReport)
+ if err != nil {
+ return err
+ }
+ // Print Volume prune results
+ if pruneOptions.Volume {
+ fmt.Println("Deleted Volumes")
+ err = utils.PrintVolumePruneResults(response.VolumePruneReport)
+ if err != nil {
+ return err
+ }
+ }
+ // Print Images prune results
+ fmt.Println("Deleted Images")
+ return utils.PrintImagePruneResults(response.ImagePruneReport)
+}
diff --git a/cmd/podman/utils/utils.go b/cmd/podman/utils/utils.go
index c7d105ba4..f4c704628 100644
--- a/cmd/podman/utils/utils.go
+++ b/cmd/podman/utils/utils.go
@@ -1,6 +1,11 @@
package utils
-import "os"
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+)
// IsDir returns true if the specified path refers to a directory.
func IsDir(path string) bool {
@@ -20,3 +25,51 @@ func FileExists(path string) bool {
}
return !file.IsDir()
}
+
+func PrintPodPruneResults(podPruneReports []*entities.PodPruneReport) error {
+ var errs OutputErrors
+ for _, r := range podPruneReports {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
+
+func PrintContainerPruneResults(containerPruneReport *entities.ContainerPruneReport) error {
+ var errs OutputErrors
+ for k := range containerPruneReport.ID {
+ fmt.Println(k)
+ }
+ for _, v := range containerPruneReport.Err {
+ errs = append(errs, v)
+ }
+ return errs.PrintErrors()
+}
+
+func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport) error {
+ var errs OutputErrors
+ for _, r := range volumePruneReport {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
+
+func PrintImagePruneResults(imagePruneReport *entities.ImagePruneReport) error {
+ 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)
+ }
+ return nil
+}
diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go
index 2c3ed88f3..57344385b 100644
--- a/cmd/podman/volumes/prune.go
+++ b/cmd/podman/volumes/prune.go
@@ -44,9 +44,6 @@ func init() {
}
func prune(cmd *cobra.Command, args []string) error {
- var (
- errs utils.OutputErrors
- )
// Prompt for confirmation if --force is not set
if !pruneOptions.Force {
reader := bufio.NewReader(os.Stdin)
@@ -64,12 +61,5 @@ func prune(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- for _, r := range responses {
- if r.Err == nil {
- fmt.Println(r.Id)
- } else {
- errs = append(errs, r.Err)
- }
- }
- return errs.PrintErrors()
+ return utils.PrintVolumePruneResults(responses)
}