summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/common/create.go3
-rw-r--r--cmd/podman/common/create_opts.go2
-rw-r--r--cmd/podman/common/specgen.go63
-rw-r--r--cmd/podman/common/types.go3
-rw-r--r--cmd/podman/common/util.go216
-rw-r--r--cmd/podman/containers/create.go12
-rw-r--r--cmd/podman/containers/logs.go2
-rw-r--r--cmd/podman/containers/port.go38
-rw-r--r--cmd/podman/containers/prune.go6
-rw-r--r--cmd/podman/containers/ps.go2
-rw-r--r--cmd/podman/containers/rm.go2
-rw-r--r--cmd/podman/containers/run.go8
-rw-r--r--cmd/podman/containers/stats.go244
-rw-r--r--cmd/podman/containers/unpause.go2
-rw-r--r--cmd/podman/generate/generate.go2
-rw-r--r--cmd/podman/generate/kube.go68
-rw-r--r--cmd/podman/generate/systemd.go2
-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.go20
-rw-r--r--cmd/podman/images/rm.go7
-rw-r--r--cmd/podman/images/save.go23
-rw-r--r--cmd/podman/images/tag.go16
-rw-r--r--cmd/podman/images/trust.go27
-rw-r--r--cmd/podman/images/trust_set.go56
-rw-r--r--cmd/podman/images/trust_show.go77
-rw-r--r--cmd/podman/images/untag.go16
-rw-r--r--cmd/podman/login.go7
-rw-r--r--cmd/podman/logout.go17
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/manifest/annotate.go56
-rw-r--r--cmd/podman/manifest/manifest.go8
-rw-r--r--cmd/podman/manifest/push.go66
-rw-r--r--cmd/podman/manifest/remove.go47
-rw-r--r--cmd/podman/parse/common.go15
-rw-r--r--cmd/podman/play/kube.go101
-rw-r--r--cmd/podman/play/play.go26
-rw-r--r--cmd/podman/pods/create.go3
-rw-r--r--cmd/podman/pods/pod.go2
-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/df.go282
-rw-r--r--cmd/podman/system/info.go22
-rw-r--r--cmd/podman/system/migrate.go63
-rw-r--r--cmd/podman/system/renumber.go57
-rw-r--r--cmd/podman/system/reset.go82
51 files changed, 1757 insertions, 101 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 53f4a8fa2..7086dc839 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -156,8 +156,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
createFlags.String("entrypoint", "",
"Overwrite the default ENTRYPOINT of the image",
)
- createFlags.StringArrayVarP(
- &cf.env,
+ createFlags.StringArrayP(
"env", "e", containerConfig.Env(),
"Set environment variables in container",
)
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index c275b1674..8b38e3b47 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -32,7 +32,7 @@ type ContainerCLIOpts struct {
DeviceWriteBPs []string
DeviceWriteIOPs []string
Entrypoint *string
- env []string
+ Env []string
EnvHost bool
EnvFile []string
Expose []string
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 33cba30cd..9a2345064 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -26,6 +26,16 @@ func getCPULimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string)
cpu := &specs.LinuxCPU{}
hasLimits := false
+ const cpuPeriod = 100000
+
+ if c.CPUS > 0 {
+ quota := int64(c.CPUS * cpuPeriod)
+ period := uint64(cpuPeriod)
+
+ cpu.Period = &period
+ cpu.Quota = &quota
+ hasLimits = true
+ }
if c.CPUShares > 0 {
cpu.Shares = &c.CPUShares
hasLimits = true
@@ -142,6 +152,10 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []strin
return nil, errors.Wrapf(err, "invalid value for memory")
}
memory.Limit = &ml
+ if c.MemorySwap == "" {
+ limit := 2 * ml
+ memory.Swap = &(limit)
+ }
hasLimits = true
}
if m := c.MemoryReservation; len(m) > 0 {
@@ -192,7 +206,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 +247,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,
@@ -316,15 +335,12 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
env = envLib.Join(env, fileEnv)
}
- // env overrides any previous variables
- if cmdLineEnv := c.env; len(cmdLineEnv) > 0 {
- parsedEnv, err := envLib.ParseSlice(cmdLineEnv)
- if err != nil {
- return err
- }
- env = envLib.Join(env, parsedEnv)
+ parsedEnv, err := envLib.ParseSlice(c.Env)
+ if err != nil {
+ return err
}
- s.Env = env
+
+ s.Env = envLib.Join(env, parsedEnv)
// LABEL VARIABLES
labels, err := parse.GetAllLabels(c.LabelFile, c.Label)
@@ -515,10 +531,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
case "label":
// TODO selinux opts and label opts are the same thing
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
+ s.Annotations[define.InspectAnnotationLabel] = con[1]
case "apparmor":
s.ContainerSecurityConfig.ApparmorProfile = con[1]
+ s.Annotations[define.InspectAnnotationApparmor] = con[1]
case "seccomp":
s.SeccompProfilePath = con[1]
+ s.Annotations[define.InspectAnnotationSeccomp] = con[1]
default:
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
@@ -601,7 +620,29 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Name = c.Name
s.OOMScoreAdj = &c.OOMScoreAdj
- s.RestartPolicy = c.Restart
+ if c.Restart != "" {
+ splitRestart := strings.Split(c.Restart, ":")
+ switch len(splitRestart) {
+ case 1:
+ // No retries specified
+ case 2:
+ if strings.ToLower(splitRestart[0]) != "on-failure" {
+ return errors.Errorf("restart policy retries can only be specified with on-failure restart policy")
+ }
+ retries, err := strconv.Atoi(splitRestart[1])
+ if err != nil {
+ return errors.Wrapf(err, "error parsing restart policy retry count")
+ }
+ if retries < 0 {
+ return errors.Errorf("must specify restart policy retry count as a number greater than 0")
+ }
+ var retriesUint uint = uint(retries)
+ s.RestartRetries = &retriesUint
+ default:
+ return errors.Errorf("invalid restart policy: may specify retries at most once")
+ }
+ s.RestartPolicy = splitRestart[0]
+ }
s.Remove = c.Rm
s.StopTimeout = &c.StopTimeout
diff --git a/cmd/podman/common/types.go b/cmd/podman/common/types.go
deleted file mode 100644
index 2427ae975..000000000
--- a/cmd/podman/common/types.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package common
-
-var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
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/create.go b/cmd/podman/containers/create.go
index 7927da04d..2ecdda2e0 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -55,6 +55,11 @@ func createFlags(flags *pflag.FlagSet) {
flags.AddFlagSet(common.GetCreateFlags(&cliVals))
flags.AddFlagSet(common.GetNetFlags())
flags.SetNormalizeFunc(common.AliasFlags)
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("env-host")
+ _ = flags.MarkHidden("http-proxy")
+ }
}
func init() {
@@ -170,6 +175,13 @@ func createInit(c *cobra.Command) error {
val := c.Flag("entrypoint").Value.String()
cliVals.Entrypoint = &val
}
+ if c.Flags().Changed("env") {
+ env, err := c.Flags().GetStringArray("env")
+ if err != nil {
+ return errors.Wrapf(err, "retrieve env flag")
+ }
+ cliVals.Env = env
+ }
// Docker-compatibility: the "-h" flag for run/create is reserved for
// the hostname (see https://github.com/containers/libpod/issues/1367).
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/port.go b/cmd/podman/containers/port.go
index 2e3386aa9..ec0ddf838 100644
--- a/cmd/podman/containers/port.go
+++ b/cmd/podman/containers/port.go
@@ -11,6 +11,7 @@ import (
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -28,23 +29,50 @@ var (
podman port ctrID 80/tcp
podman port --latest 80`,
}
+
+ containerPortCommand = &cobra.Command{
+ Use: "port [flags] CONTAINER [PORT]",
+ Short: portCommand.Short,
+ Long: portDescription,
+ RunE: portCommand.RunE,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ },
+ Example: `podman container port --all
+ podman container port --latest 80`,
+ }
)
var (
portOpts entities.ContainerPortOptions
)
+func portFlags(flags *pflag.FlagSet) {
+ flags.BoolVarP(&portOpts.All, "all", "a", false, "Display port information for all containers")
+ flags.BoolVarP(&portOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode},
Command: portCommand,
})
+
flags := portCommand.Flags()
- flags.BoolVarP(&portOpts.All, "all", "a", false, "Display port information for all containers")
- flags.BoolVarP(&portOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
- if registry.IsRemote() {
- _ = flags.MarkHidden("latest")
- }
+ portFlags(flags)
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: containerPortCommand,
+ Parent: containerCmd,
+ })
+
+ containerPortflags := containerPortCommand.Flags()
+ portFlags(containerPortflags)
+
}
func port(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go
index d4bea48f9..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`,
@@ -50,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 {
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index e9146bda7..4d12d2534 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -206,7 +206,7 @@ func ps(cmd *cobra.Command, args []string) error {
return err
}
if err := tmpl.Execute(w, responses); err != nil {
- return nil
+ return err
}
if err := w.Flush(); err != nil {
return err
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..5f3ea9ef4 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -60,6 +60,8 @@ func runFlags(flags *pflag.FlagSet) {
flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers")
if registry.IsRemote() {
_ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("env-host")
+ _ = flags.MarkHidden("http-proxy")
}
}
func init() {
@@ -170,9 +172,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/containers/stats.go b/cmd/podman/containers/stats.go
new file mode 100644
index 000000000..3f9db671f
--- /dev/null
+++ b/cmd/podman/containers/stats.go
@@ -0,0 +1,244 @@
+package containers
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+ "text/template"
+
+ tm "github.com/buger/goterm"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/utils"
+ "github.com/docker/go-units"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ statsDescription = "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers."
+ statsCommand = &cobra.Command{
+ Use: "stats [flags] [CONTAINER...]",
+ Short: "Display a live stream of container resource usage statistics",
+ Long: statsDescription,
+ RunE: stats,
+ Args: checkStatOptions,
+ Example: `podman stats --all --no-stream
+ podman stats ctrID
+ podman stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`,
+ }
+
+ containerStatsCommand = &cobra.Command{
+ Use: statsCommand.Use,
+ Short: statsCommand.Short,
+ Long: statsCommand.Long,
+ RunE: statsCommand.RunE,
+ Args: checkStatOptions,
+ Example: `podman container stats --all --no-stream
+ podman container stats ctrID
+ podman container stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`,
+ }
+)
+
+var (
+ statsOptions entities.ContainerStatsOptions
+ defaultStatsRow = "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\n"
+ defaultStatsHeader = "ID\tNAME\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET IO\tBLOCK IO\tPIDS\n"
+)
+
+func statFlags(flags *pflag.FlagSet) {
+ flags.BoolVarP(&statsOptions.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false")
+ flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template")
+ flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Act on the latest container Podman is aware of")
+ flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals")
+ flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: statsCommand,
+ })
+ flags := statsCommand.Flags()
+ statFlags(flags)
+
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: containerStatsCommand,
+ Parent: containerCmd,
+ })
+
+ containerStatsFlags := containerStatsCommand.Flags()
+ statFlags(containerStatsFlags)
+}
+
+// stats is different in that it will assume running containers if
+// no input is given, so we need to validate differently
+func checkStatOptions(cmd *cobra.Command, args []string) error {
+ opts := 0
+ if statsOptions.All {
+ opts += 1
+ }
+ if statsOptions.Latest {
+ opts += 1
+ }
+ if len(args) > 0 {
+ opts += 1
+ }
+ if opts > 1 {
+ return errors.Errorf("--all, --latest and containers cannot be used together")
+ }
+ return nil
+}
+
+func stats(cmd *cobra.Command, args []string) error {
+ if rootless.IsRootless() {
+ unified, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return err
+ }
+ if !unified {
+ return errors.New("stats is not supported in rootless mode without cgroups v2")
+ }
+ }
+ statsOptions.StatChan = make(chan []*define.ContainerStats, 1)
+ go func() {
+ for reports := range statsOptions.StatChan {
+ if err := outputStats(reports); err != nil {
+ logrus.Error(err)
+ }
+ }
+ }()
+ return registry.ContainerEngine().ContainerStats(registry.Context(), args, statsOptions)
+}
+
+func outputStats(reports []*define.ContainerStats) error {
+ if len(statsOptions.Format) < 1 && !statsOptions.NoReset {
+ tm.Clear()
+ tm.MoveCursor(1, 1)
+ tm.Flush()
+ }
+ var stats []*containerStats
+ for _, r := range reports {
+ stats = append(stats, &containerStats{r})
+ }
+ if statsOptions.Format == "json" {
+ return outputJSON(stats)
+ }
+ format := defaultStatsRow
+ if len(statsOptions.Format) > 0 {
+ format = statsOptions.Format
+ if !strings.HasSuffix(format, "\n") {
+ format += "\n"
+ }
+ }
+ format = "{{range . }}" + format + "{{end}}"
+ if len(statsOptions.Format) < 1 {
+ format = defaultStatsHeader + format
+ }
+ tmpl, err := template.New("stats").Parse(format)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ if err := tmpl.Execute(w, stats); err != nil {
+ return err
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ return nil
+}
+
+type containerStats struct {
+ *define.ContainerStats
+}
+
+func (s *containerStats) ID() string {
+ return s.ContainerID[0:12]
+}
+
+func (s *containerStats) CPUPerc() string {
+ return floatToPercentString(s.CPU)
+}
+
+func (s *containerStats) MemPerc() string {
+ return floatToPercentString(s.ContainerStats.MemPerc)
+}
+
+func (s *containerStats) NetIO() string {
+ return combineHumanValues(s.NetInput, s.NetOutput)
+}
+
+func (s *containerStats) BlockIO() string {
+ return combineHumanValues(s.BlockInput, s.BlockOutput)
+}
+
+func (s *containerStats) PIDS() string {
+ if s.PIDs == 0 {
+ // If things go bazinga, return a safe value
+ return "--"
+ }
+ return fmt.Sprintf("%d", s.PIDs)
+}
+func (s *containerStats) MemUsage() string {
+ return combineHumanValues(s.ContainerStats.MemUsage, s.ContainerStats.MemLimit)
+}
+
+func floatToPercentString(f float64) string {
+ strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
+ if err != nil || strippedFloat == 0 {
+ // If things go bazinga, return a safe value
+ return "--"
+ }
+ return fmt.Sprintf("%.2f", strippedFloat) + "%"
+}
+
+func combineHumanValues(a, b uint64) string {
+ if a == 0 && b == 0 {
+ return "-- / --"
+ }
+ return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
+}
+
+func outputJSON(stats []*containerStats) error {
+ type jstat struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ CpuPercent string `json:"cpu_percent"`
+ MemUsage string `json:"mem_usage"`
+ MemPerc string `json:"mem_percent"`
+ NetIO string `json:"net_io"`
+ BlockIO string `json:"block_io"`
+ Pids string `json:"pids"`
+ }
+ var jstats []jstat
+ for _, j := range stats {
+ jstats = append(jstats, jstat{
+ Id: j.ID(),
+ Name: j.Name,
+ CpuPercent: j.CPUPerc(),
+ MemUsage: j.MemPerc(),
+ MemPerc: j.MemUsage(),
+ NetIO: j.NetIO(),
+ BlockIO: j.BlockIO(),
+ Pids: j.PIDS(),
+ })
+ }
+
+ b, err := json.MarshalIndent(jstats, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
diff --git a/cmd/podman/containers/unpause.go b/cmd/podman/containers/unpause.go
index adf8d12ee..7ea8e13c1 100644
--- a/cmd/podman/containers/unpause.go
+++ b/cmd/podman/containers/unpause.go
@@ -49,7 +49,7 @@ func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
- Command: unpauseCommand,
+ Command: containerUnpauseCommand,
Parent: containerCmd,
})
diff --git a/cmd/podman/generate/generate.go b/cmd/podman/generate/generate.go
index b112e666a..7803c0c78 100644
--- a/cmd/podman/generate/generate.go
+++ b/cmd/podman/generate/generate.go
@@ -22,7 +22,7 @@ var (
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
- Mode: []entities.EngineMode{entities.ABIMode},
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: generateCmd,
})
}
diff --git a/cmd/podman/generate/kube.go b/cmd/podman/generate/kube.go
new file mode 100644
index 000000000..86a9cc686
--- /dev/null
+++ b/cmd/podman/generate/kube.go
@@ -0,0 +1,68 @@
+package pods
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ kubeOptions = entities.GenerateKubeOptions{}
+ kubeFile = ""
+ kubeDescription = `Command generates Kubernetes pod and service YAML (v1 specification) from a Podman container or pod.
+
+Whether the input is for a container or pod, Podman will always generate the specification as a pod.`
+
+ kubeCmd = &cobra.Command{
+ Use: "kube [flags] CONTAINER | POD",
+ Short: "Generate Kubernetes YAML from a container or pod.",
+ Long: kubeDescription,
+ RunE: kube,
+ Args: cobra.ExactArgs(1),
+ Example: `podman generate kube ctrID
+ podman generate kube podID
+ podman generate kube --service podID`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: kubeCmd,
+ Parent: generateCmd,
+ })
+ flags := kubeCmd.Flags()
+ flags.BoolVarP(&kubeOptions.Service, "service", "s", false, "Generate YAML for a Kubernetes service object")
+ flags.StringVarP(&kubeFile, "filename", "f", "", "Write output to the specified path")
+ flags.SetNormalizeFunc(utils.AliasFlags)
+}
+
+func kube(cmd *cobra.Command, args []string) error {
+ report, err := registry.ContainerEngine().GenerateKube(registry.GetContext(), args[0], kubeOptions)
+ if err != nil {
+ return err
+ }
+
+ content, err := ioutil.ReadAll(report.Reader)
+ if err != nil {
+ return err
+ }
+ if cmd.Flags().Changed("filename") {
+ if _, err := os.Stat(kubeFile); err == nil {
+ return errors.Errorf("cannot write to %q", kubeFile)
+ }
+ if err := ioutil.WriteFile(kubeFile, content, 0644); err != nil {
+ return errors.Wrapf(err, "cannot write to %q", kubeFile)
+ }
+ return nil
+ }
+
+ fmt.Println(string(content))
+ return nil
+}
diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go
index 55d770249..20d9748d4 100644
--- a/cmd/podman/generate/systemd.go
+++ b/cmd/podman/generate/systemd.go
@@ -29,7 +29,7 @@ var (
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
- Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Mode: []entities.EngineMode{entities.ABIMode},
Command: systemdCmd,
Parent: generateCmd,
})
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..4bbffd432 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")
@@ -61,7 +78,6 @@ func load(cmd *cobra.Command, args []string) error {
loadOpts.Tag = "latest"
}
if r, ok := ref.(reference.Named); ok {
- fmt.Println(r.Name())
loadOpts.Name = r.Name()
}
}
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/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/trust.go b/cmd/podman/images/trust.go
new file mode 100644
index 000000000..88a567871
--- /dev/null
+++ b/cmd/podman/images/trust.go
@@ -0,0 +1,27 @@
+package images
+
+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"
+)
+
+var (
+ trustDescription = `Manages which registries you trust as a source of container images based on their location.
+ The location is determined by the transport and the registry host of the image. Using this container image docker://quay.io/podman/stable as an example, docker is the transport and quay.io is the registry host.`
+ trustCmd = &cobra.Command{
+ Use: "trust",
+ Short: "Manage container image trust policy",
+ Long: trustDescription,
+ RunE: validate.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: trustCmd,
+ Parent: imageCmd,
+ })
+}
diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go
new file mode 100644
index 000000000..5868f5546
--- /dev/null
+++ b/cmd/podman/images/trust_set.go
@@ -0,0 +1,56 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ setTrustDescription = "Set default trust policy or add a new trust policy for a registry"
+ setTrustCommand = &cobra.Command{
+ Use: "set [flags] REGISTRY",
+ Short: "Set default trust policy or a new trust policy for a registry",
+ Long: setTrustDescription,
+ Example: "",
+ RunE: setTrust,
+ Args: cobra.ExactArgs(1),
+ }
+)
+
+var (
+ setOptions entities.SetTrustOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: setTrustCommand,
+ Parent: trustCmd,
+ })
+ setFlags := setTrustCommand.Flags()
+ setFlags.StringVar(&setOptions.PolicyPath, "policypath", "", "")
+ _ = setFlags.MarkHidden("policypath")
+ setFlags.StringSliceVarP(&setOptions.PubKeysFile, "pubkeysfile", "f", []string{}, `Path of installed public key(s) to trust for TARGET.
+Absolute path to keys is added to policy.json. May
+used multiple times to define multiple public keys.
+File(s) must exist before using this command`)
+ setFlags.StringVarP(&setOptions.Type, "type", "t", "signedBy", "Trust type, accept values: signedBy(default), accept, reject")
+}
+
+func setTrust(cmd *cobra.Command, args []string) error {
+ validTrustTypes := []string{"accept", "insecureAcceptAnything", "reject", "signedBy"}
+
+ valid, err := image.IsValidImageURI(args[0])
+ if err != nil || !valid {
+ return errors.Wrapf(err, "invalid image uri %s", args[0])
+ }
+
+ if !util.StringInSlice(setOptions.Type, validTrustTypes) {
+ return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", setOptions.Type)
+ }
+ return registry.ImageEngine().SetTrust(registry.Context(), args, setOptions)
+}
diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go
new file mode 100644
index 000000000..23ee6c709
--- /dev/null
+++ b/cmd/podman/images/trust_show.go
@@ -0,0 +1,77 @@
+package images
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+ "text/template"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ showTrustDescription = "Display trust policy for the system"
+ showTrustCommand = &cobra.Command{
+ Use: "show [flags] [REGISTRY]",
+ Short: "Display trust policy for the system",
+ Long: showTrustDescription,
+ RunE: showTrust,
+ Example: "",
+ }
+)
+
+var (
+ showTrustOptions entities.ShowTrustOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: showTrustCommand,
+ Parent: trustCmd,
+ })
+ showFlags := showTrustCommand.Flags()
+ showFlags.BoolVarP(&showTrustOptions.JSON, "json", "j", false, "Output as json")
+ showFlags.StringVar(&showTrustOptions.PolicyPath, "policypath", "", "")
+ showFlags.BoolVar(&showTrustOptions.Raw, "raw", false, "Output raw policy file")
+ _ = showFlags.MarkHidden("policypath")
+ showFlags.StringVar(&showTrustOptions.RegistryPath, "registrypath", "", "")
+ _ = showFlags.MarkHidden("registrypath")
+
+}
+
+func showTrust(cmd *cobra.Command, args []string) error {
+ report, err := registry.ImageEngine().ShowTrust(registry.Context(), args, showTrustOptions)
+ if err != nil {
+ return err
+ }
+ if showTrustOptions.Raw {
+ fmt.Println(report.Raw)
+ return nil
+ }
+ if showTrustOptions.JSON {
+ b, err := json.MarshalIndent(report.Policies, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+
+ row := "{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n"
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("listContainers").Parse(format)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ if err := tmpl.Execute(w, report.Policies); err != nil {
+ return err
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ return nil
+}
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 dc57758ab..8413861f5 100644
--- a/cmd/podman/login.go
+++ b/cmd/podman/login.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/registries"
"github.com/spf13/cobra"
)
@@ -23,7 +24,7 @@ var (
Short: "Login to a container registry",
Long: "Login to a container registry on a specified server.",
RunE: login,
- Args: cobra.ExactArgs(1),
+ Args: cobra.MaximumNArgs(1),
Example: `podman login quay.io
podman login --username ... --password ... quay.io
podman login --authfile dir/auth.json quay.io`,
@@ -48,6 +49,7 @@ func init() {
flags.BoolVarP(&loginOptions.GetLoginSet, "get-login", "", false, "Return the current login user for the registry")
loginOptions.Stdin = os.Stdin
loginOptions.Stdout = os.Stdout
+ loginOptions.AcceptUnspecifiedRegistry = true
}
// Implementation of podman-login.
@@ -62,7 +64,8 @@ func login(cmd *cobra.Command, args []string) error {
AuthFilePath: loginOptions.AuthFile,
DockerCertPath: loginOptions.CertDir,
DockerInsecureSkipTLSVerify: skipTLS,
+ SystemRegistriesConfPath: registries.SystemRegistriesConfPath(),
}
loginOptions.GetLoginSet = cmd.Flag("get-login").Changed
- return auth.Login(context.Background(), &sysCtx, &loginOptions.LoginOptions, args[0])
+ return auth.Login(context.Background(), &sysCtx, &loginOptions.LoginOptions, args)
}
diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go
index c21711fc0..d0afc21b4 100644
--- a/cmd/podman/logout.go
+++ b/cmd/podman/logout.go
@@ -7,7 +7,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/pkg/errors"
+ "github.com/containers/libpod/pkg/registries"
"github.com/spf13/cobra"
)
@@ -39,19 +39,14 @@ func init() {
flags.AddFlagSet(auth.GetLogoutFlags(&logoutOptions))
logoutOptions.Stdin = os.Stdin
logoutOptions.Stdout = os.Stdout
+ logoutOptions.AcceptUnspecifiedRegistry = true
}
// Implementation of podman-logout.
func logout(cmd *cobra.Command, args []string) error {
- sysCtx := types.SystemContext{AuthFilePath: logoutOptions.AuthFile}
-
- registry := ""
- if len(args) > 0 {
- if logoutOptions.All {
- return errors.New("--all takes no arguments")
- }
- registry = args[0]
+ sysCtx := types.SystemContext{
+ AuthFilePath: logoutOptions.AuthFile,
+ SystemRegistriesConfPath: registries.SystemRegistriesConfPath(),
}
-
- return auth.Logout(&sysCtx, &logoutOptions, registry)
+ return auth.Logout(&sysCtx, &logoutOptions, args)
}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 422dee90b..76ec7bc8e 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -10,6 +10,7 @@ import (
_ "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/play"
_ "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/annotate.go b/cmd/podman/manifest/annotate.go
new file mode 100644
index 000000000..21d4fb747
--- /dev/null
+++ b/cmd/podman/manifest/annotate.go
@@ -0,0 +1,56 @@
+package manifest
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ manifestAnnotateOpts = entities.ManifestAnnotateOptions{}
+ annotateCmd = &cobra.Command{
+ Use: "annotate [flags] LIST IMAGE",
+ Short: "Add or update information about an entry in a manifest list or image index",
+ Long: "Adds or updates information about an entry in a manifest list or image index.",
+ RunE: annotate,
+ Example: `podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64`,
+ Args: cobra.ExactArgs(2),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: annotateCmd,
+ Parent: manifestCmd,
+ })
+ flags := annotateCmd.Flags()
+ flags.StringSliceVar(&manifestAnnotateOpts.Annotation, "annotation", nil, "set an `annotation` for the specified image")
+ flags.StringVar(&manifestAnnotateOpts.Arch, "arch", "", "override the `architecture` of the specified image")
+ flags.StringSliceVar(&manifestAnnotateOpts.Features, "features", nil, "override the `features` of the specified image")
+ flags.StringVar(&manifestAnnotateOpts.OS, "os", "", "override the `OS` of the specified image")
+ flags.StringSliceVar(&manifestAnnotateOpts.OSFeatures, "os-features", nil, "override the OS `features` of the specified image")
+ flags.StringVar(&manifestAnnotateOpts.OSVersion, "os-version", "", "override the OS `version` of the specified image")
+ flags.StringVar(&manifestAnnotateOpts.Variant, "variant", "", "override the `variant` of the specified image")
+}
+
+func annotate(cmd *cobra.Command, args []string) error {
+ listImageSpec := args[0]
+ instanceSpec := args[1]
+ if listImageSpec == "" {
+ return errors.Errorf(`invalid image name "%s"`, listImageSpec)
+ }
+ if instanceSpec == "" {
+ return errors.Errorf(`invalid image digest "%s"`, instanceSpec)
+ }
+ updatedListID, err := registry.ImageEngine().ManifestAnnotate(context.Background(), args, manifestAnnotateOpts)
+ if err != nil {
+ return errors.Wrapf(err, "error removing from manifest list %s", listImageSpec)
+ }
+ fmt.Printf("%s\n", updatedListID)
+ return nil
+}
diff --git a/cmd/podman/manifest/manifest.go b/cmd/podman/manifest/manifest.go
index b78879b34..d7f042a56 100644
--- a/cmd/podman/manifest/manifest.go
+++ b/cmd/podman/manifest/manifest.go
@@ -15,8 +15,12 @@ var (
Long: manifestDescription,
TraverseChildren: true,
RunE: validate.SubCommandExists,
- Example: `podman manifest create localhost/list
- podman manifest inspect localhost/list`,
+ Example: `podman manifest add mylist:v1.11 image:v1.11-amd64
+ podman manifest create localhost/list
+ podman manifest inspect localhost/list
+ podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64
+ podman manifest push mylist:v1.11 quay.io/myimagelist
+ podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`,
}
)
diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go
new file mode 100644
index 000000000..49c76f40b
--- /dev/null
+++ b/cmd/podman/manifest/push.go
@@ -0,0 +1,66 @@
+package manifest
+
+import (
+ "context"
+
+ "github.com/containers/common/pkg/auth"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ manifestPushOpts = entities.ManifestPushOptions{}
+ pushCmd = &cobra.Command{
+ Use: "push [flags] SOURCE DESTINATION",
+ Short: "Push a manifest list or image index to a registry",
+ Long: "Pushes manifest lists and image indexes to registries.",
+ RunE: push,
+ Example: `podman manifest push mylist:v1.11 quay.io/myimagelist`,
+ Args: cobra.ExactArgs(2),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pushCmd,
+ Parent: manifestCmd,
+ })
+ flags := pushCmd.Flags()
+ flags.BoolVar(&manifestPushOpts.Purge, "purge", false, "remove the manifest list if push succeeds")
+ flags.BoolVar(&manifestPushOpts.All, "all", false, "also push the images in the list")
+ flags.StringVar(&manifestPushOpts.Authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&manifestPushOpts.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
+ flags.StringVar(&manifestPushOpts.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
+ flags.StringVar(&manifestPushOpts.DigestFile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file")
+ flags.StringVarP(&manifestPushOpts.Format, "format", "f", "", "manifest type (oci or v2s2) to attempt to use when pushing the manifest list (default is manifest type of source)")
+ flags.BoolVarP(&manifestPushOpts.RemoveSignatures, "remove-signatures", "", false, "don't copy signatures when pushing images")
+ flags.StringVar(&manifestPushOpts.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`")
+ flags.BoolVar(&manifestPushOpts.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
+ flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("cert-dir")
+ _ = flags.MarkHidden("tls-verify")
+ }
+}
+
+func push(cmd *cobra.Command, args []string) error {
+ if err := auth.CheckAuthFile(manifestPushOpts.Authfile); err != nil {
+ return err
+ }
+ listImageSpec := args[0]
+ destSpec := args[1]
+ if listImageSpec == "" {
+ return errors.Errorf(`invalid image name "%s"`, listImageSpec)
+ }
+ if destSpec == "" {
+ return errors.Errorf(`invalid destination "%s"`, destSpec)
+ }
+ if err := registry.ImageEngine().ManifestPush(context.Background(), args, manifestPushOpts); err != nil {
+ return errors.Wrapf(err, "error pushing manifest %s to %s", listImageSpec, destSpec)
+ }
+ return nil
+}
diff --git a/cmd/podman/manifest/remove.go b/cmd/podman/manifest/remove.go
new file mode 100644
index 000000000..4d345efc0
--- /dev/null
+++ b/cmd/podman/manifest/remove.go
@@ -0,0 +1,47 @@
+package manifest
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ removeCmd = &cobra.Command{
+ Use: "remove [flags] LIST IMAGE",
+ Short: "Remove an entry from a manifest list or image index",
+ Long: "Removes an image from a manifest list or image index.",
+ RunE: remove,
+ Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`,
+ Args: cobra.ExactArgs(2),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: removeCmd,
+ Parent: manifestCmd,
+ })
+}
+
+func remove(cmd *cobra.Command, args []string) error {
+ listImageSpec := args[0]
+ instanceSpec := args[1]
+ if listImageSpec == "" {
+ return errors.Errorf(`invalid image name "%s"`, listImageSpec)
+ }
+ if instanceSpec == "" {
+ return errors.Errorf(`invalid image digest "%s"`, instanceSpec)
+ }
+ updatedListID, err := registry.ImageEngine().ManifestRemove(context.Background(), args)
+ if err != nil {
+ return errors.Wrapf(err, "error removing from manifest list %s", listImageSpec)
+ }
+ fmt.Printf("%s\n", updatedListID)
+ return nil
+}
diff --git a/cmd/podman/parse/common.go b/cmd/podman/parse/common.go
index a5e9b4fc2..13f425b6d 100644
--- a/cmd/podman/parse/common.go
+++ b/cmd/podman/parse/common.go
@@ -30,13 +30,20 @@ func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool
return errors.Errorf("--all and --latest cannot be used together")
}
+ if (argLen > 0) && specifiedAll {
+ return errors.Errorf("no arguments are needed with --all")
+ }
+
if ignoreArgLen {
return nil
}
- if (argLen > 0) && (specifiedAll || specifiedLatest) {
- return errors.Errorf("no arguments are needed with --all or --latest")
- } else if cidfile && (argLen > 0) && (specifiedAll || specifiedLatest || specifiedCIDFile) {
- return errors.Errorf("no arguments are needed with --all, --latest or --cidfile")
+
+ if argLen > 0 {
+ if specifiedLatest {
+ return errors.Errorf("no arguments are needed with --latest")
+ } else if cidfile && (specifiedLatest || specifiedCIDFile) {
+ return errors.Errorf("no arguments are needed with --latest or --cidfile")
+ }
}
if specifiedCIDFile {
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
new file mode 100644
index 000000000..2499b54b9
--- /dev/null
+++ b/cmd/podman/play/kube.go
@@ -0,0 +1,101 @@
+package pods
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/common/pkg/auth"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+// playKubeOptionsWrapper allows for separating CLI-only fields from API-only
+// fields.
+type playKubeOptionsWrapper struct {
+ entities.PlayKubeOptions
+
+ TLSVerifyCLI bool
+}
+
+var (
+ // https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
+ defaultSeccompRoot = "/var/lib/kubelet/seccomp"
+ kubeOptions = playKubeOptionsWrapper{}
+ kubeDescription = `Command reads in a structured file of Kubernetes YAML.
+
+ It creates the pod and containers described in the YAML. The containers within the pod are then started and the ID of the new Pod is output.`
+
+ kubeCmd = &cobra.Command{
+ Use: "kube [flags] KUBEFILE",
+ Short: "Play a pod based on Kubernetes YAML.",
+ Long: kubeDescription,
+ RunE: kube,
+ Args: cobra.ExactArgs(1),
+ Example: `podman play kube nginx.yml
+ podman play kube --creds user:password --seccomp-profile-root /custom/path apache.yml`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: kubeCmd,
+ Parent: playCmd,
+ })
+
+ flags := kubeCmd.Flags()
+ flags.SetNormalizeFunc(utils.AliasFlags)
+ flags.StringVar(&kubeOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
+ flags.StringVar(&kubeOptions.Network, "network", "", "Connect pod to CNI network(s)")
+ flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
+ if !registry.IsRemote() {
+ flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&kubeOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
+ flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+ flags.StringVar(&kubeOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
+ flags.StringVar(&kubeOptions.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles")
+ }
+}
+
+func kube(cmd *cobra.Command, args []string) error {
+ // 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") {
+ kubeOptions.SkipTLSVerify = types.NewOptionalBool(!kubeOptions.TLSVerifyCLI)
+ }
+ if kubeOptions.Authfile != "" {
+ if _, err := os.Stat(kubeOptions.Authfile); err != nil {
+ return errors.Wrapf(err, "error getting authfile %s", kubeOptions.Authfile)
+ }
+ }
+
+ report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), args[0], kubeOptions.PlayKubeOptions)
+ if err != nil {
+ return err
+ }
+
+ for _, l := range report.Logs {
+ fmt.Fprintf(os.Stderr, l)
+ }
+
+ fmt.Printf("Pod:\n%s\n", report.Pod)
+ switch len(report.Containers) {
+ case 0:
+ return nil
+ case 1:
+ fmt.Printf("Container:\n")
+ default:
+ fmt.Printf("Containers:\n")
+ }
+ for _, ctr := range report.Containers {
+ fmt.Println(ctr)
+ }
+
+ return nil
+}
diff --git a/cmd/podman/play/play.go b/cmd/podman/play/play.go
new file mode 100644
index 000000000..b151e5f5d
--- /dev/null
+++ b/cmd/podman/play/play.go
@@ -0,0 +1,26 @@
+package pods
+
+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"
+)
+
+var (
+ // Command: podman _play_
+ playCmd = &cobra.Command{
+ Use: "play",
+ Short: "Play a pod and its containers from a structured file.",
+ Long: "Play structured data (e.g., Kubernetes pod or service yaml) based on containers and pods.",
+ TraverseChildren: true,
+ RunE: validate.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: playCmd,
+ })
+}
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 85b96d37b..0a2016496 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/libpod/cmd/podman/validate"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/errorhandling"
+ createconfig "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
@@ -57,7 +58,7 @@ func init() {
flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod")
flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod")
flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file")
- flags.StringVar(&share, "share", common.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
+ flags.StringVar(&share, "share", createconfig.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
}
func create(cmd *cobra.Command, args []string) error {
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/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/df.go b/cmd/podman/system/df.go
new file mode 100644
index 000000000..7caa8e39a
--- /dev/null
+++ b/cmd/podman/system/df.go
@@ -0,0 +1,282 @@
+package system
+
+import (
+ "fmt"
+ "html/template"
+ "io"
+ "os"
+ "strings"
+ "text/tabwriter"
+ "time"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/docker/go-units"
+ "github.com/spf13/cobra"
+)
+
+var (
+ dfSystemDescription = `
+ podman system df
+
+ Show podman disk usage
+ `
+ dfSystemCommand = &cobra.Command{
+ Use: "df",
+ Args: validate.NoArgs,
+ Short: "Show podman disk usage",
+ Long: dfSystemDescription,
+ RunE: df,
+ }
+)
+
+var (
+ dfOptions entities.SystemDfOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: dfSystemCommand,
+ Parent: systemCmd,
+ })
+ flags := dfSystemCommand.Flags()
+ flags.BoolVarP(&dfOptions.Verbose, "verbose", "v", false, "Show detailed information on disk usage")
+ flags.StringVar(&dfOptions.Format, "format", "", "Pretty-print images using a Go template")
+}
+
+func df(cmd *cobra.Command, args []string) error {
+ reports, err := registry.ContainerEngine().SystemDf(registry.Context(), dfOptions)
+ if err != nil {
+ return err
+ }
+ if dfOptions.Verbose {
+ return printVerbose(reports)
+ }
+ return printSummary(reports, dfOptions.Format)
+}
+
+func printSummary(reports *entities.SystemDfReport, userFormat string) error {
+
+ var (
+ dfSummaries []*dfSummary
+ active int
+ size, reclaimable int64
+ format string = "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n"
+ w io.Writer = os.Stdout
+ )
+
+ // Images
+ if len(userFormat) > 0 {
+ format = userFormat
+ }
+
+ for _, i := range reports.Images {
+ if i.Containers > 0 {
+ active += 1
+ }
+ size += i.Size
+ if i.Containers < 1 {
+ reclaimable += i.Size
+ }
+ }
+
+ imageSummary := dfSummary{
+ Type: "Images",
+ Total: len(reports.Images),
+ Active: active,
+ size: size,
+ reclaimable: reclaimable,
+ }
+ dfSummaries = append(dfSummaries, &imageSummary)
+
+ // Containers
+
+ var (
+ conActive int
+ conSize, conReclaimable int64
+ )
+ for _, c := range reports.Containers {
+ if c.Status == "running" {
+ conActive += 1
+ } else {
+ conReclaimable += c.RWSize
+ }
+ conSize += c.RWSize
+ }
+
+ containerSummary := dfSummary{
+ Type: "Containers",
+ Total: len(reports.Containers),
+ Active: conActive,
+ size: conSize,
+ reclaimable: conReclaimable,
+ }
+
+ dfSummaries = append(dfSummaries, &containerSummary)
+
+ // Volumes
+ var (
+ activeVolumes int
+ volumesSize, volumesReclaimable int64
+ )
+
+ for _, v := range reports.Volumes {
+ activeVolumes += v.Links
+ volumesSize += v.Size
+ volumesReclaimable += v.Size
+ }
+ volumeSummary := dfSummary{
+ Type: "Local Volumes",
+ Total: len(reports.Volumes),
+ Active: activeVolumes,
+ size: volumesSize,
+ reclaimable: volumesReclaimable,
+ }
+
+ dfSummaries = append(dfSummaries, &volumeSummary)
+
+ headers := "TYPE\tTOTAL\tACTIVE\tSIZE\tRECLAIMABLE\n"
+ format = "{{range . }}" + format + "{{end}}"
+ if len(userFormat) == 0 {
+ format = headers + format
+ }
+ return writeTemplate(w, format, dfSummaries)
+}
+
+func printVerbose(reports *entities.SystemDfReport) error {
+ var (
+ dfImages []*dfImage
+ dfContainers []*dfContainer
+ dfVolumes []*dfVolume
+ w io.Writer = os.Stdout
+ )
+
+ // Images
+ fmt.Print("\nImages space usage:\n\n")
+ // convert to dfImage for output
+ for _, d := range reports.Images {
+ dfImages = append(dfImages, &dfImage{SystemDfImageReport: d})
+ }
+ imageHeaders := "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE\tSHARED SIZE\tUNIQUE SIZE\tCONTAINERS\n"
+ imageRow := "{{.Repository}}\t{{.Tag}}\t{{.ImageID}}\t{{.Created}}\t{{.Size}}\t{{.SharedSize}}\t{{.UniqueSize}}\t{{.Containers}}\n"
+ format := imageHeaders + "{{range . }}" + imageRow + "{{end}}"
+ if err := writeTemplate(w, format, dfImages); err != nil {
+ return nil
+ }
+
+ // Containers
+ fmt.Print("\nContainers space usage:\n\n")
+
+ // convert to dfContainers for output
+ for _, d := range reports.Containers {
+ dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d})
+ }
+ containerHeaders := "CONTAINER ID\tIMAGE\tCOMMAND\tLOCAL VOLUMES\tSIZE\tCREATED\tSTATUS\tNAMES\n"
+ containerRow := "{{.ContainerID}}\t{{.Image}}\t{{.Command}}\t{{.LocalVolumes}}\t{{.Size}}\t{{.Created}}\t{{.Status}}\t{{.Names}}\n"
+ format = containerHeaders + "{{range . }}" + containerRow + "{{end}}"
+ if err := writeTemplate(w, format, dfContainers); err != nil {
+ return nil
+ }
+
+ // Volumes
+ fmt.Print("\nLocal Volumes space usage:\n\n")
+
+ // convert to dfVolume for output
+ for _, d := range reports.Volumes {
+ dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d})
+ }
+ volumeHeaders := "VOLUME NAME\tLINKS\tSIZE\n"
+ volumeRow := "{{.VolumeName}}\t{{.Links}}\t{{.Size}}\n"
+ format = volumeHeaders + "{{range . }}" + volumeRow + "{{end}}"
+ return writeTemplate(w, format, dfVolumes)
+}
+
+func writeTemplate(w io.Writer, format string, output interface{}) error {
+ tmpl, err := template.New("dfout").Parse(format)
+ if err != nil {
+ return err
+ }
+ w = tabwriter.NewWriter(w, 8, 2, 2, ' ', 0) //nolint
+ if err := tmpl.Execute(w, output); err != nil {
+ return err
+ }
+ if flusher, ok := w.(interface{ Flush() error }); ok {
+ return flusher.Flush()
+ }
+ return nil
+}
+
+type dfImage struct {
+ *entities.SystemDfImageReport
+}
+
+func (d *dfImage) ImageID() string {
+ return d.SystemDfImageReport.ImageID[0:12]
+}
+
+func (d *dfImage) Created() string {
+ return units.HumanDuration(time.Since(d.SystemDfImageReport.Created))
+}
+
+func (d *dfImage) Size() string {
+ return units.HumanSize(float64(d.SystemDfImageReport.Size))
+}
+
+func (d *dfImage) SharedSize() string {
+ return units.HumanSize(float64(d.SystemDfImageReport.SharedSize))
+}
+
+func (d *dfImage) UniqueSize() string {
+ return units.HumanSize(float64(d.SystemDfImageReport.UniqueSize))
+}
+
+type dfContainer struct {
+ *entities.SystemDfContainerReport
+}
+
+func (d *dfContainer) ContainerID() string {
+ return d.SystemDfContainerReport.ContainerID[0:12]
+}
+
+func (d *dfContainer) Image() string {
+ return d.SystemDfContainerReport.Image[0:12]
+}
+
+func (d *dfContainer) Command() string {
+ return strings.Join(d.SystemDfContainerReport.Command, " ")
+}
+
+func (d *dfContainer) Size() string {
+ return units.HumanSize(float64(d.SystemDfContainerReport.Size))
+}
+
+func (d *dfContainer) Created() string {
+ return units.HumanDuration(time.Since(d.SystemDfContainerReport.Created))
+}
+
+type dfVolume struct {
+ *entities.SystemDfVolumeReport
+}
+
+func (d *dfVolume) Size() string {
+ return units.HumanSize(float64(d.SystemDfVolumeReport.Size))
+}
+
+type dfSummary struct {
+ Type string
+ Total int
+ Active int
+ size int64
+ reclaimable int64
+}
+
+func (d *dfSummary) Size() string {
+ return units.HumanSize(float64(d.size))
+}
+
+func (d *dfSummary) Reclaimable() string {
+ percent := int(float64(d.reclaimable)/float64(d.size)) * 100
+ return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.reclaimable)), percent)
+}
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/migrate.go b/cmd/podman/system/migrate.go
new file mode 100644
index 000000000..13aa162c7
--- /dev/null
+++ b/cmd/podman/system/migrate.go
@@ -0,0 +1,63 @@
+package system
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra"
+ "github.com/spf13/cobra"
+)
+
+var (
+ migrateDescription = `
+ podman system migrate
+
+ Migrate existing containers to a new version of Podman.
+`
+
+ migrateCommand = &cobra.Command{
+ Use: "migrate",
+ Args: validate.NoArgs,
+ Short: "Migrate containers",
+ Long: migrateDescription,
+ Run: migrate,
+ }
+)
+
+var (
+ migrateOptions entities.SystemMigrateOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: migrateCommand,
+ Parent: systemCmd,
+ })
+
+ flags := migrateCommand.Flags()
+ flags.StringVar(&migrateOptions.NewRuntime, "new-runtime", "", "Specify a new runtime for all containers")
+}
+
+func migrate(cmd *cobra.Command, args []string) {
+ // Shutdown all running engines, `renumber` will hijack repository
+ registry.ContainerEngine().Shutdown(registry.Context())
+ registry.ImageEngine().Shutdown(registry.Context())
+
+ engine, err := infra.NewSystemEngine(entities.MigrateMode, registry.PodmanConfig())
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(125)
+ }
+ defer engine.Shutdown(registry.Context())
+
+ err = engine.Migrate(registry.Context(), cmd.Flags(), registry.PodmanConfig(), migrateOptions)
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(125)
+ }
+ os.Exit(0)
+}
diff --git a/cmd/podman/system/renumber.go b/cmd/podman/system/renumber.go
new file mode 100644
index 000000000..5ee6b3be6
--- /dev/null
+++ b/cmd/podman/system/renumber.go
@@ -0,0 +1,57 @@
+package system
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra"
+ "github.com/spf13/cobra"
+)
+
+var (
+ renumberDescription = `
+ podman system renumber
+
+ Migrate lock numbers to handle a change in maximum number of locks.
+ Mandatory after the number of locks in libpod.conf is changed.
+`
+
+ renumberCommand = &cobra.Command{
+ Use: "renumber",
+ Args: validate.NoArgs,
+ Short: "Migrate lock numbers",
+ Long: renumberDescription,
+ Run: renumber,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: renumberCommand,
+ Parent: systemCmd,
+ })
+
+}
+func renumber(cmd *cobra.Command, args []string) {
+ // Shutdown all running engines, `renumber` will hijack all methods
+ registry.ContainerEngine().Shutdown(registry.Context())
+ registry.ImageEngine().Shutdown(registry.Context())
+
+ engine, err := infra.NewSystemEngine(entities.RenumberMode, registry.PodmanConfig())
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(125)
+ }
+ defer engine.Shutdown(registry.Context())
+
+ err = engine.Renumber(registry.Context(), cmd.Flags(), registry.PodmanConfig())
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(125)
+ }
+ os.Exit(0)
+}
diff --git a/cmd/podman/system/reset.go b/cmd/podman/system/reset.go
new file mode 100644
index 000000000..22ddc7529
--- /dev/null
+++ b/cmd/podman/system/reset.go
@@ -0,0 +1,82 @@
+package system
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ systemResetDescription = `Reset podman storage back to default state"
+
+ All containers will be stopped and removed, and all images, volumes and container content will be removed.
+`
+ systemResetCommand = &cobra.Command{
+ Use: "reset",
+ Args: validate.NoArgs,
+ Short: "Reset podman storage",
+ Long: systemResetDescription,
+ Run: reset,
+ }
+)
+
+var (
+ systemResetOptions entities.SystemResetOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: systemResetCommand,
+ Parent: systemCmd,
+ })
+ flags := systemResetCommand.Flags()
+ flags.BoolVarP(&systemResetOptions.Force, "force", "f", false, "Do not prompt for confirmation")
+}
+
+func reset(cmd *cobra.Command, args []string) {
+ // Prompt for confirmation if --force is not set
+ if !systemResetOptions.Force {
+ reader := bufio.NewReader(os.Stdin)
+ fmt.Print(`
+WARNING! This will remove:
+ - all containers
+ - all pods
+ - all images
+ - all build cache
+Are you sure you want to continue? [y/N] `)
+ answer, err := reader.ReadString('\n')
+ if err != nil {
+ fmt.Println(errors.Wrapf(err, "error reading input"))
+ os.Exit(1)
+ }
+ if strings.ToLower(answer)[0] != 'y' {
+ os.Exit(0)
+ }
+ }
+
+ // Shutdown all running engines, `reset` will hijack repository
+ registry.ContainerEngine().Shutdown(registry.Context())
+ registry.ImageEngine().Shutdown(registry.Context())
+
+ engine, err := infra.NewSystemEngine(entities.ResetMode, registry.PodmanConfig())
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(125)
+ }
+ defer engine.Shutdown(registry.Context())
+
+ if err := engine.Reset(registry.Context(), systemResetOptions); err != nil {
+ fmt.Println(err)
+ os.Exit(125)
+ }
+ os.Exit(0)
+}