summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/shared/create.go8
-rw-r--r--cmd/podmanV2/containers/init.go60
-rw-r--r--cmd/podmanV2/containers/mount.go124
-rw-r--r--cmd/podmanV2/containers/ps.go205
-rw-r--r--cmd/podmanV2/containers/stop.go2
-rw-r--r--cmd/podmanV2/containers/unmount.go65
-rw-r--r--cmd/podmanV2/containers/wait.go2
-rw-r--r--cmd/podmanV2/images/history.go9
-rw-r--r--cmd/podmanV2/images/inspect.go3
-rw-r--r--cmd/podmanV2/images/rm.go8
-rw-r--r--cmd/podmanV2/images/rmi.go1
-rw-r--r--cmd/podmanV2/images/search.go157
-rw-r--r--cmd/podmanV2/main.go33
-rw-r--r--cmd/podmanV2/registry/config.go59
-rw-r--r--cmd/podmanV2/registry/registry.go38
-rw-r--r--cmd/podmanV2/registry/remote.go2
-rw-r--r--cmd/podmanV2/root.go168
-rw-r--r--cmd/podmanV2/system/events.go104
18 files changed, 934 insertions, 114 deletions
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 68a36d967..94b1e63dc 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -783,10 +783,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
Sysctl: sysctl,
}
+ var securityOpt []string
if c.Changed("security-opt") {
- if err := secConfig.SetSecurityOpts(runtime, c.StringArray("security-opt")); err != nil {
- return nil, err
- }
+ securityOpt = c.StringArray("security-opt")
+ }
+ if err := secConfig.SetSecurityOpts(runtime, securityOpt); err != nil {
+ return nil, err
}
// SECCOMP
diff --git a/cmd/podmanV2/containers/init.go b/cmd/podmanV2/containers/init.go
new file mode 100644
index 000000000..dd1e1d21b
--- /dev/null
+++ b/cmd/podmanV2/containers/init.go
@@ -0,0 +1,60 @@
+package containers
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ initDescription = `Initialize one or more containers, creating the OCI spec and mounts for inspection. Container names or IDs can be used.`
+
+ initCommand = &cobra.Command{
+ Use: "init [flags] CONTAINER [CONTAINER...]",
+ Short: "Initialize one or more containers",
+ Long: initDescription,
+ PreRunE: preRunE,
+ RunE: initContainer,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman init --latest
+ podman init 3c45ef19d893
+ podman init test1`,
+ }
+)
+
+var (
+ initOptions entities.ContainerInitOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: initCommand,
+ })
+ flags := initCommand.Flags()
+ flags.BoolVarP(&initOptions.All, "all", "a", false, "Initialize all containers")
+ flags.BoolVarP(&initOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ _ = flags.MarkHidden("latest")
+}
+
+func initContainer(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ report, err := registry.ContainerEngine().ContainerInit(registry.GetContext(), args, initOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range report {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/containers/mount.go b/cmd/podmanV2/containers/mount.go
new file mode 100644
index 000000000..4f7b95d98
--- /dev/null
+++ b/cmd/podmanV2/containers/mount.go
@@ -0,0 +1,124 @@
+package containers
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "text/tabwriter"
+ "text/template"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ mountDescription = `podman mount
+ Lists all mounted containers mount points if no container is specified
+
+ podman mount CONTAINER-NAME-OR-ID
+ Mounts the specified container and outputs the mountpoint
+`
+
+ mountCommand = &cobra.Command{
+ Use: "mount [flags] [CONTAINER]",
+ Short: "Mount a working container's root filesystem",
+ Long: mountDescription,
+ PreRunE: preRunE,
+ RunE: mount,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ },
+ Annotations: map[string]string{
+ registry.RootRequired: "true",
+ },
+ }
+)
+
+var (
+ mountOpts entities.ContainerMountOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: mountCommand,
+ })
+ flags := mountCommand.Flags()
+ flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers")
+ flags.StringVar(&mountOpts.Format, "format", "", "Change the output format to Go template")
+ flags.BoolVarP(&mountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output")
+}
+
+func mount(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ mrs []mountReporter
+ )
+ reports, err := registry.ContainerEngine().ContainerMount(registry.GetContext(), args, mountOpts)
+ if err != nil {
+ return err
+ }
+ if len(args) > 0 || mountOpts.Latest || mountOpts.All {
+ for _, r := range reports {
+ if r.Err == nil {
+ fmt.Println(r.Path)
+ continue
+ }
+ errs = append(errs, r.Err)
+ }
+ return errs.PrintErrors()
+ }
+ if mountOpts.Format == "json" {
+ return printJSON(reports)
+ }
+ for _, r := range reports {
+ mrs = append(mrs, mountReporter{r})
+ }
+ row := "{{.ID}} {{.Path}}"
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("mounts").Parse(format)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ defer w.Flush()
+ return tmpl.Execute(w, mrs)
+}
+
+func printJSON(reports []*entities.ContainerMountReport) error {
+ type jreport struct {
+ ID string `json:"id"`
+ Names []string
+ Mountpoint string `json:"mountpoint"`
+ }
+ var jreports []jreport
+
+ for _, r := range reports {
+ jreports = append(jreports, jreport{
+ ID: r.Id,
+ Names: []string{r.Name},
+ Mountpoint: r.Path,
+ })
+ }
+ b, err := json.MarshalIndent(jreports, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
+
+type mountReporter struct {
+ *entities.ContainerMountReport
+}
+
+func (m mountReporter) ID() string {
+ if mountOpts.NoTruncate {
+ return m.Id
+ }
+ return m.Id[0:12]
+}
diff --git a/cmd/podmanV2/containers/ps.go b/cmd/podmanV2/containers/ps.go
index 8c1d44842..8ebbf6ebf 100644
--- a/cmd/podmanV2/containers/ps.go
+++ b/cmd/podmanV2/containers/ps.go
@@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"os"
+ "sort"
+ "strconv"
"strings"
"text/tabwriter"
"text/template"
@@ -13,6 +15,8 @@ import (
"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -134,6 +138,7 @@ func getResponses() ([]entities.ListContainer, error) {
}
func ps(cmd *cobra.Command, args []string) error {
+ var responses []psReporter
for _, f := range filters {
split := strings.SplitN(f, "=", 2)
if len(split) == 1 {
@@ -141,22 +146,27 @@ func ps(cmd *cobra.Command, args []string) error {
}
listOpts.Filters[split[0]] = append(listOpts.Filters[split[0]], split[1])
}
- responses, err := getResponses()
+ listContainers, err := getResponses()
if err != nil {
return err
}
if len(listOpts.Sort) > 0 {
- responses, err = entities.SortPsOutput(listOpts.Sort, responses)
+ listContainers, err = entities.SortPsOutput(listOpts.Sort, listContainers)
if err != nil {
return err
}
}
if listOpts.Format == "json" {
- return jsonOut(responses)
+ return jsonOut(listContainers)
}
if listOpts.Quiet {
- return quietOut(responses)
+ return quietOut(listContainers)
+ }
+
+ for _, r := range listContainers {
+ responses = append(responses, psReporter{r})
}
+
headers, row := createPsOut()
if cmd.Flag("format").Changed {
row = listOpts.Format
@@ -175,10 +185,14 @@ func ps(cmd *cobra.Command, args []string) error {
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
if listOpts.Watch > 0 {
for {
+ var responses []psReporter
tm.Clear()
tm.MoveCursor(1, 1)
tm.Flush()
- responses, err := getResponses()
+ listContainers, err := getResponses()
+ for _, r := range listContainers {
+ responses = append(responses, psReporter{r})
+ }
if err != nil {
return err
}
@@ -210,21 +224,12 @@ func createPsOut() (string, string) {
return headers, row
}
headers := defaultHeaders
- if noTrunc {
- row += "{{.ID}}"
- } else {
- row += "{{slice .ID 0 12}}"
- }
+ row += "{{.ID}}"
row += "\t{{.Image}}\t{{.Command}}\t{{.CreatedHuman}}\t{{.State}}\t{{.Ports}}\t{{.Names}}"
if listOpts.Pod {
headers += "\tPOD ID\tPODNAME"
- if noTrunc {
- row += "\t{{.Pod}}"
- } else {
- row += "\t{{slice .Pod 0 12}}"
- }
- row += "\t{{.PodName}}"
+ row += "\t{{.Pod}}\t{{.PodName}}"
}
if listOpts.Size {
@@ -239,3 +244,171 @@ func createPsOut() (string, string) {
}
return headers, row
}
+
+type psReporter struct {
+ entities.ListContainer
+}
+
+// ID returns the ID of the container
+func (l psReporter) ID() string {
+ if !noTrunc {
+ return l.ListContainer.ID[0:12]
+ }
+ return l.ListContainer.ID
+}
+
+// Pod returns the ID of the pod the container
+// belongs to and appropriately truncates the ID
+func (l psReporter) Pod() string {
+ if !noTrunc && len(l.ListContainer.Pod) > 0 {
+ return l.ListContainer.Pod[0:12]
+ }
+ return l.ListContainer.Pod
+}
+
+// State returns the container state in human duration
+func (l psReporter) State() string {
+ var state string
+ switch l.ListContainer.State {
+ case "running":
+ t := units.HumanDuration(time.Since(time.Unix(l.StartedAt, 0)))
+ state = "Up " + t + " ago"
+ case "configured":
+ state = "Created"
+ case "exited", "stopped":
+ t := units.HumanDuration(time.Since(time.Unix(l.ExitedAt, 0)))
+ state = fmt.Sprintf("Exited (%d) %s ago", l.ExitCode, t)
+ default:
+ state = l.ListContainer.State
+ }
+ return state
+}
+
+// Command returns the container command in string format
+func (l psReporter) Command() string {
+ return strings.Join(l.ListContainer.Command, " ")
+}
+
+// Size returns the rootfs and virtual sizes in human duration in
+// and output form (string) suitable for ps
+func (l psReporter) Size() string {
+ virt := units.HumanSizeWithPrecision(float64(l.ListContainer.Size.RootFsSize), 3)
+ s := units.HumanSizeWithPrecision(float64(l.ListContainer.Size.RwSize), 3)
+ return fmt.Sprintf("%s (virtual %s)", s, virt)
+}
+
+// Names returns the container name in string format
+func (l psReporter) Names() string {
+ return l.ListContainer.Names[0]
+}
+
+// Ports converts from Portmappings to the string form
+// required by ps
+func (l psReporter) Ports() string {
+ if len(l.ListContainer.Ports) < 1 {
+ return ""
+ }
+ return portsToString(l.ListContainer.Ports)
+}
+
+// CreatedAt returns the container creation time in string format. podman
+// and docker both return a timestamped value for createdat
+func (l psReporter) CreatedAt() string {
+ return time.Unix(l.Created, 0).String()
+}
+
+// CreateHuman allows us to output the created time in human readable format
+func (l psReporter) CreatedHuman() string {
+ return units.HumanDuration(time.Since(time.Unix(l.Created, 0))) + " ago"
+}
+
+// portsToString converts the ports used to a string of the from "port1, port2"
+// and also groups a continuous list of ports into a readable format.
+func portsToString(ports []ocicni.PortMapping) string {
+ type portGroup struct {
+ first int32
+ last int32
+ }
+ var portDisplay []string
+ if len(ports) == 0 {
+ return ""
+ }
+ //Sort the ports, so grouping continuous ports become easy.
+ sort.Slice(ports, func(i, j int) bool {
+ return comparePorts(ports[i], ports[j])
+ })
+
+ // portGroupMap is used for grouping continuous ports.
+ portGroupMap := make(map[string]*portGroup)
+ var groupKeyList []string
+
+ for _, v := range ports {
+
+ hostIP := v.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ // If hostPort and containerPort are not same, consider as individual port.
+ if v.ContainerPort != v.HostPort {
+ portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
+ continue
+ }
+
+ portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
+
+ portgroup, ok := portGroupMap[portMapKey]
+ if !ok {
+ portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
+ // This list is required to traverse portGroupMap.
+ groupKeyList = append(groupKeyList, portMapKey)
+ continue
+ }
+
+ if portgroup.last == (v.ContainerPort - 1) {
+ portgroup.last = v.ContainerPort
+ continue
+ }
+ }
+ // For each portMapKey, format group list and appned to output string.
+ for _, portKey := range groupKeyList {
+ group := portGroupMap[portKey]
+ portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
+ }
+ return strings.Join(portDisplay, ", ")
+}
+
+func comparePorts(i, j ocicni.PortMapping) bool {
+ if i.ContainerPort != j.ContainerPort {
+ return i.ContainerPort < j.ContainerPort
+ }
+
+ if i.HostIP != j.HostIP {
+ return i.HostIP < j.HostIP
+ }
+
+ if i.HostPort != j.HostPort {
+ return i.HostPort < j.HostPort
+ }
+
+ return i.Protocol < j.Protocol
+}
+
+// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
+// e.g 0.0.0.0:1000-1006->1000-1006/tcp.
+func formatGroup(key string, start, last int32) string {
+ parts := strings.Split(key, "/")
+ groupType := parts[0]
+ var ip string
+ if len(parts) > 1 {
+ ip = parts[0]
+ groupType = parts[1]
+ }
+ group := strconv.Itoa(int(start))
+ if start != last {
+ group = fmt.Sprintf("%s-%d", group, last)
+ }
+ if ip != "" {
+ group = fmt.Sprintf("%s:%s->%s", ip, group, group)
+ }
+ return fmt.Sprintf("%s/%s", group, groupType)
+}
diff --git a/cmd/podmanV2/containers/stop.go b/cmd/podmanV2/containers/stop.go
index d6f31352f..53ec2934d 100644
--- a/cmd/podmanV2/containers/stop.go
+++ b/cmd/podmanV2/containers/stop.go
@@ -46,7 +46,7 @@ func init() {
flags.StringArrayVarP(&stopOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file")
flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.UintVarP(&stopTimeout, "time", "t", defaultContainerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container")
- if registry.EngineOptions.EngineMode == entities.ABIMode {
+ if registry.PodmanOptions.EngineMode == entities.ABIMode {
_ = flags.MarkHidden("latest")
_ = flags.MarkHidden("cidfile")
_ = flags.MarkHidden("ignore")
diff --git a/cmd/podmanV2/containers/unmount.go b/cmd/podmanV2/containers/unmount.go
new file mode 100644
index 000000000..2a6ef14b0
--- /dev/null
+++ b/cmd/podmanV2/containers/unmount.go
@@ -0,0 +1,65 @@
+package containers
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ description = `Container storage increments a mount counter each time a container is mounted.
+
+ When a container is unmounted, the mount counter is decremented. The container's root filesystem is physically unmounted only when the mount counter reaches zero indicating no other processes are using the mount.
+
+ An unmount can be forced with the --force flag.
+`
+ umountCommand = &cobra.Command{
+ Use: "umount [flags] CONTAINER [CONTAINER...]",
+ Aliases: []string{"unmount"},
+ Short: "Unmounts working container's root filesystem",
+ Long: description,
+ RunE: unmount,
+ PreRunE: preRunE,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman umount ctrID
+ podman umount ctrID1 ctrID2 ctrID3
+ podman umount --all`,
+ }
+)
+
+var (
+ unmountOpts entities.ContainerUnmountOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: umountCommand,
+ })
+ flags := umountCommand.Flags()
+ flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Umount all of the currently mounted containers")
+ flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers")
+ flags.BoolVarP(&unmountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+}
+
+func unmount(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ reports, err := registry.ContainerEngine().ContainerUnmount(registry.GetContext(), args, unmountOpts)
+ if err != nil {
+ return err
+ }
+ for _, r := range reports {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go
index 2171f2073..3d11c581e 100644
--- a/cmd/podmanV2/containers/wait.go
+++ b/cmd/podmanV2/containers/wait.go
@@ -44,7 +44,7 @@ func init() {
flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion")
flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on")
- if registry.EngineOptions.EngineMode == entities.ABIMode {
+ if registry.PodmanOptions.EngineMode == entities.ABIMode {
// TODO: This is the same as V1. We could skip creating the flag altogether in V2...
_ = flags.MarkHidden("latest")
}
diff --git a/cmd/podmanV2/images/history.go b/cmd/podmanV2/images/history.go
index 48575b33a..e3bb7a051 100644
--- a/cmd/podmanV2/images/history.go
+++ b/cmd/podmanV2/images/history.go
@@ -53,7 +53,7 @@ func init() {
flags := historyCmd.Flags()
flags.StringVar(&opts.format, "format", "", "Change the output to JSON or a Go template")
- flags.BoolVarP(&opts.human, "human", "H", false, "Display sizes and dates in human readable format")
+ 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")
flags.BoolVar(&opts.noTrunc, "notruncate", false, "Do not truncate the output")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Display the numeric IDs only")
@@ -79,7 +79,7 @@ func history(cmd *cobra.Command, args []string) error {
layers := make([]layer, len(results.Layers))
for i, l := range results.Layers {
layers[i].ImageHistoryLayer = l
- layers[i].Created = time.Unix(l.Created, 0).Format(time.RFC3339)
+ layers[i].Created = l.Created.Format(time.RFC3339)
}
json := jsoniter.ConfigCompatibleWithStandardLibrary
enc := json.NewEncoder(os.Stdout)
@@ -129,7 +129,10 @@ type historyreporter struct {
}
func (h historyreporter) Created() string {
- return units.HumanDuration(time.Since(time.Unix(h.ImageHistoryLayer.Created, 0))) + " ago"
+ if opts.human {
+ return units.HumanDuration(time.Since(h.ImageHistoryLayer.Created)) + " ago"
+ }
+ return h.ImageHistoryLayer.Created.Format(time.RFC3339)
}
func (h historyreporter) Size() string {
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
index d7f6b0ee1..2ee2d86ee 100644
--- a/cmd/podmanV2/images/inspect.go
+++ b/cmd/podmanV2/images/inspect.go
@@ -67,7 +67,6 @@ func inspect(cmd *cobra.Command, args []string) error {
}
return nil
}
-
row := inspectFormat(inspectOpts.Format)
format := "{{range . }}" + row + "{{end}}"
tmpl, err := template.New("inspect").Parse(format)
@@ -77,7 +76,7 @@ func inspect(cmd *cobra.Command, args []string) error {
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
defer func() { _ = w.Flush() }()
- err = tmpl.Execute(w, results)
+ err = tmpl.Execute(w, results.Images)
if err != nil {
return err
}
diff --git a/cmd/podmanV2/images/rm.go b/cmd/podmanV2/images/rm.go
index bb5880de3..6784182d9 100644
--- a/cmd/podmanV2/images/rm.go
+++ b/cmd/podmanV2/images/rm.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -33,11 +34,13 @@ func init() {
Parent: imageCmd,
})
- flags := rmCmd.Flags()
+ imageRemoveFlagSet(rmCmd.Flags())
+}
+
+func imageRemoveFlagSet(flags *pflag.FlagSet) {
flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images")
flags.BoolVarP(&imageOpts.Force, "force", "f", false, "Force Removal of the image")
}
-
func rm(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !imageOpts.All {
@@ -46,7 +49,6 @@ func rm(cmd *cobra.Command, args []string) error {
if len(args) > 0 && imageOpts.All {
return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
}
-
report, err := registry.ImageEngine().Delete(registry.GetContext(), args, imageOpts)
if err != nil {
switch {
diff --git a/cmd/podmanV2/images/rmi.go b/cmd/podmanV2/images/rmi.go
index 7f9297bc9..973763966 100644
--- a/cmd/podmanV2/images/rmi.go
+++ b/cmd/podmanV2/images/rmi.go
@@ -27,4 +27,5 @@ func init() {
})
rmiCmd.SetHelpTemplate(registry.HelpTemplate())
rmiCmd.SetUsageTemplate(registry.UsageTemplate())
+ imageRemoveFlagSet(rmiCmd.Flags())
}
diff --git a/cmd/podmanV2/images/search.go b/cmd/podmanV2/images/search.go
new file mode 100644
index 000000000..2ab9735ec
--- /dev/null
+++ b/cmd/podmanV2/images/search.go
@@ -0,0 +1,157 @@
+package images
+
+import (
+ "reflect"
+ "strings"
+
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util/camelcase"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+// searchOptionsWrapper wraps entities.ImagePullOptions and prevents leaking
+// CLI-only fields into the API types.
+type searchOptionsWrapper struct {
+ entities.ImageSearchOptions
+ // CLI only flags
+ TLSVerifyCLI bool // Used to convert to an optional bool later
+ Format string // For go templating
+}
+
+var (
+ searchOptions = searchOptionsWrapper{}
+ searchDescription = `Search registries for a given image. Can search all the default registries or a specific registry.
+
+ Users can limit the number of results, and filter the output based on certain conditions.`
+
+ // Command: podman search
+ searchCmd = &cobra.Command{
+ Use: "search [flags] TERM",
+ Short: "Search registry for image",
+ Long: searchDescription,
+ PreRunE: preRunE,
+ RunE: imageSearch,
+ Args: cobra.ExactArgs(1),
+ Example: `podman search --filter=is-official --limit 3 alpine
+ podman search registry.fedoraproject.org/ # only works with v2 registries
+ podman search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`,
+ }
+
+ // Command: podman image search
+ imageSearchCmd = &cobra.Command{
+ Use: searchCmd.Use,
+ Short: searchCmd.Short,
+ Long: searchCmd.Long,
+ PreRunE: searchCmd.PreRunE,
+ RunE: searchCmd.RunE,
+ Args: searchCmd.Args,
+ Example: `podman image search --filter=is-official --limit 3 alpine
+ podman image search registry.fedoraproject.org/ # only works with v2 registries
+ podman image search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`,
+ }
+)
+
+func init() {
+ // search
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: searchCmd,
+ })
+
+ searchCmd.SetHelpTemplate(registry.HelpTemplate())
+ searchCmd.SetUsageTemplate(registry.UsageTemplate())
+ flags := searchCmd.Flags()
+ searchFlags(flags)
+
+ // images search
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageSearchCmd,
+ Parent: imageCmd,
+ })
+
+ imageSearchFlags := imageSearchCmd.Flags()
+ searchFlags(imageSearchFlags)
+}
+
+// searchFlags set the flags for the pull command.
+func searchFlags(flags *pflag.FlagSet) {
+ flags.StringSliceVarP(&searchOptions.Filters, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
+ 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.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("tls-verify")
+ }
+}
+
+// imageSearch implements the command for searching images.
+func imageSearch(cmd *cobra.Command, args []string) error {
+ searchTerm := ""
+ switch len(args) {
+ case 1:
+ searchTerm = args[0]
+ default:
+ return errors.Errorf("search requires exactly one argument")
+ }
+
+ sarchOptsAPI := searchOptions.ImageSearchOptions
+ // 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") {
+ sarchOptsAPI.TLSVerify = types.NewOptionalBool(pullOptions.TLSVerifyCLI)
+ }
+
+ searchReport, err := registry.ImageEngine().Search(registry.GetContext(), searchTerm, sarchOptsAPI)
+ if err != nil {
+ return err
+ }
+
+ format := genSearchFormat(searchOptions.Format)
+ if len(searchReport) == 0 {
+ return nil
+ }
+ out := formats.StdoutTemplateArray{Output: searchToGeneric(searchReport), Template: format, Fields: searchHeaderMap()}
+ return out.Out()
+}
+
+// searchHeaderMap returns the headers of a SearchResult.
+func searchHeaderMap() map[string]string {
+ s := new(entities.ImageSearchReport)
+ v := reflect.Indirect(reflect.ValueOf(s))
+ values := make(map[string]string, v.NumField())
+
+ for i := 0; i < v.NumField(); i++ {
+ key := v.Type().Field(i).Name
+ value := key
+ values[key] = strings.ToUpper(strings.Join(camelcase.Split(value), " "))
+ }
+ return values
+}
+
+func genSearchFormat(format string) string {
+ if format != "" {
+ // "\t" from the command line is not being recognized as a tab
+ // replacing the string "\t" to a tab character if the user passes in "\t"
+ return strings.Replace(format, `\t`, "\t", -1)
+ }
+ return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t"
+}
+
+func searchToGeneric(params []entities.ImageSearchReport) (genericParams []interface{}) {
+ for _, v := range params {
+ genericParams = append(genericParams, interface{}(v))
+ }
+ return genericParams
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
index fe3cd9f16..cfe20d1c1 100644
--- a/cmd/podmanV2/main.go
+++ b/cmd/podmanV2/main.go
@@ -3,8 +3,6 @@ package main
import (
"os"
"reflect"
- "runtime"
- "strings"
_ "github.com/containers/libpod/cmd/podmanV2/containers"
_ "github.com/containers/libpod/cmd/podmanV2/healthcheck"
@@ -14,36 +12,13 @@ import (
"github.com/containers/libpod/cmd/podmanV2/registry"
_ "github.com/containers/libpod/cmd/podmanV2/system"
_ "github.com/containers/libpod/cmd/podmanV2/volumes"
- "github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/storage/pkg/reexec"
- "github.com/sirupsen/logrus"
)
func init() {
- if err := libpod.SetXdgDirs(); err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
-
- switch runtime.GOOS {
- case "darwin":
- fallthrough
- case "windows":
- registry.EngineOptions.EngineMode = entities.TunnelMode
- case "linux":
- registry.EngineOptions.EngineMode = entities.ABIMode
- default:
- logrus.Errorf("%s is not a supported OS", runtime.GOOS)
- os.Exit(1)
- }
-
- // TODO: Is there a Cobra way to "peek" at os.Args?
- for _, v := range os.Args {
- if strings.HasPrefix(v, "--remote") {
- registry.EngineOptions.EngineMode = entities.TunnelMode
- }
- }
+ // This is the bootstrap configuration, if user gives
+ // CLI flags parts of this configuration may be overwritten
+ registry.PodmanOptions = registry.NewPodmanConfig()
}
func main() {
@@ -53,7 +28,7 @@ func main() {
return
}
for _, c := range registry.Commands {
- if Contains(registry.EngineOptions.EngineMode, c.Mode) {
+ if Contains(registry.PodmanOptions.EngineMode, c.Mode) {
parent := rootCmd
if c.Parent != nil {
parent = c.Parent
diff --git a/cmd/podmanV2/registry/config.go b/cmd/podmanV2/registry/config.go
new file mode 100644
index 000000000..e68009a50
--- /dev/null
+++ b/cmd/podmanV2/registry/config.go
@@ -0,0 +1,59 @@
+package registry
+
+import (
+ "os"
+ "runtime"
+ "strings"
+
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ RootRequired = "RootRequired"
+)
+
+var (
+ PodmanOptions entities.PodmanConfig
+)
+
+// NewPodmanConfig creates a PodmanConfig from the environment
+func NewPodmanConfig() entities.PodmanConfig {
+ if err := libpod.SetXdgDirs(); err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+
+ var mode entities.EngineMode
+ switch runtime.GOOS {
+ case "darwin":
+ fallthrough
+ case "windows":
+ mode = entities.TunnelMode
+ case "linux":
+ mode = entities.ABIMode
+ default:
+ logrus.Errorf("%s is not a supported OS", runtime.GOOS)
+ os.Exit(1)
+ }
+
+ // cobra.Execute() may not be called yet, so we peek at os.Args.
+ for _, v := range os.Args {
+ // Prefix checking works because of how default EngineMode's
+ // have been defined.
+ if strings.HasPrefix(v, "--remote=") {
+ mode = entities.TunnelMode
+ }
+ }
+
+ // FIXME: for rootless, where to get the path
+ // TODO:
+ cfg, err := config.NewConfig("")
+ if err != nil {
+ logrus.Error("Failed to obtain podman configuration")
+ os.Exit(1)
+ }
+ return entities.PodmanConfig{Config: cfg, EngineMode: mode}
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
index 07c2b33ff..5ef6a10d8 100644
--- a/cmd/podmanV2/registry/registry.go
+++ b/cmd/podmanV2/registry/registry.go
@@ -29,8 +29,9 @@ var (
exitCode = ExecErrorCodeGeneric
imageEngine entities.ImageEngine
- Commands []CliCommand
- EngineOptions entities.EngineOptions
+ // Commands holds the cobra.Commands to present to the user, including
+ // parent if not a child of "root"
+ Commands []CliCommand
)
func SetExitCode(code int) {
@@ -83,8 +84,8 @@ func ImageEngine() entities.ImageEngine {
// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions
func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) {
if imageEngine == nil {
- EngineOptions.FlagSet = cmd.Flags()
- engine, err := infra.NewImageEngine(EngineOptions)
+ PodmanOptions.FlagSet = cmd.Flags()
+ engine, err := infra.NewImageEngine(PodmanOptions)
if err != nil {
return nil, err
}
@@ -100,8 +101,8 @@ func ContainerEngine() entities.ContainerEngine {
// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions
func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) {
if containerEngine == nil {
- EngineOptions.FlagSet = cmd.Flags()
- engine, err := infra.NewContainerEngine(EngineOptions)
+ PodmanOptions.FlagSet = cmd.Flags()
+ engine, err := infra.NewContainerEngine(PodmanOptions)
if err != nil {
return nil, err
}
@@ -125,24 +126,17 @@ func IdOrLatestArgs(cmd *cobra.Command, args []string) error {
return nil
}
-type podmanContextKey string
-
-var podmanFactsKey = podmanContextKey("engineOptions")
-
-func NewOptions(ctx context.Context, facts *entities.EngineOptions) context.Context {
- return context.WithValue(ctx, podmanFactsKey, facts)
-}
-
-func Options(cmd *cobra.Command) (*entities.EngineOptions, error) {
- if f, ok := cmd.Context().Value(podmanFactsKey).(*entities.EngineOptions); ok {
- return f, errors.New("Command Context ")
- }
- return nil, nil
-}
-
func GetContext() context.Context {
if cliCtx == nil {
- cliCtx = context.TODO()
+ cliCtx = context.Background()
}
return cliCtx
}
+
+type ContextOptionsKey string
+
+const PodmanOptionsKey ContextOptionsKey = "PodmanOptions"
+
+func GetContextWithOptions() context.Context {
+ return context.WithValue(GetContext(), PodmanOptionsKey, PodmanOptions)
+}
diff --git a/cmd/podmanV2/registry/remote.go b/cmd/podmanV2/registry/remote.go
index 32a231ac4..5378701e7 100644
--- a/cmd/podmanV2/registry/remote.go
+++ b/cmd/podmanV2/registry/remote.go
@@ -5,5 +5,5 @@ import (
)
func IsRemote() bool {
- return EngineOptions.EngineMode == entities.TunnelMode
+ return PodmanOptions.EngineMode == entities.TunnelMode
}
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
index 6fc12f57e..0639257ea 100644
--- a/cmd/podmanV2/root.go
+++ b/cmd/podmanV2/root.go
@@ -1,29 +1,37 @@
package main
import (
+ "context"
"fmt"
"log/syslog"
"os"
"path"
+ "runtime/pprof"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/tracing"
"github.com/containers/libpod/version"
+ "github.com/opentracing/opentracing-go"
+ "github.com/pkg/errors"
"github.com/sirupsen/logrus"
logrusSyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
rootCmd = &cobra.Command{
- Use: path.Base(os.Args[0]),
- Long: "Manage pods, containers and images",
- SilenceUsage: true,
- SilenceErrors: true,
- TraverseChildren: true,
- PersistentPreRunE: preRunE,
- RunE: registry.SubCommandExists,
- Version: version.Version,
+ Use: path.Base(os.Args[0]),
+ Long: "Manage pods, containers and images",
+ SilenceUsage: true,
+ SilenceErrors: true,
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ PersistentPostRunE: postRunE,
+ Version: version.Version,
}
logLevels = entities.NewStringSet("debug", "info", "warn", "error", "fatal", "panic")
@@ -32,30 +40,73 @@ var (
)
func init() {
- // Override default --help information of `--version` global flag}
- var dummyVersion bool
- // TODO had to disable shorthand -v for version due to -v rm with volume
- rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version of Podman")
- rootCmd.PersistentFlags().StringVarP(&registry.EngineOptions.Uri, "remote", "r", "", "URL to access Podman service")
- rootCmd.PersistentFlags().StringSliceVar(&registry.EngineOptions.Identities, "identity", []string{}, "path to SSH identity file")
- rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "error", fmt.Sprintf("Log messages above specified level (%s)", logLevels.String()))
- rootCmd.PersistentFlags().BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
-
cobra.OnInitialize(
- logging,
+ rootlessHook,
+ loggingHook,
syslogHook,
)
+
+ rootFlags(registry.PodmanOptions, rootCmd.PersistentFlags())
+}
+
+func Execute() {
+ if err := rootCmd.ExecuteContext(registry.GetContextWithOptions()); err != nil {
+ logrus.Error(err)
+ } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
+ // The exitCode modified from registry.ExecErrorCodeGeneric,
+ // indicates an application
+ // running inside of a container failed, as opposed to the
+ // podman command failed. Must exit with that exit code
+ // otherwise command exited correctly.
+ registry.SetExitCode(0)
+ }
+ os.Exit(registry.GetExitCode())
}
-func preRunE(cmd *cobra.Command, args []string) error {
+func preRunE(cmd *cobra.Command, _ []string) error {
+ // Update PodmanOptions now that we "know" more
+ // TODO: pass in path overriding configuration file
+ registry.PodmanOptions = registry.NewPodmanConfig()
+
cmd.SetHelpTemplate(registry.HelpTemplate())
cmd.SetUsageTemplate(registry.UsageTemplate())
+
+ if cmd.Flag("cpu-profile").Changed {
+ f, err := os.Create(registry.PodmanOptions.CpuProfile)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create cpu profiling file %s",
+ registry.PodmanOptions.CpuProfile)
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ return err
+ }
+ }
+
+ if cmd.Flag("trace").Changed {
+ tracer, closer := tracing.Init("podman")
+ opentracing.SetGlobalTracer(tracer)
+ registry.PodmanOptions.SpanCloser = closer
+
+ registry.PodmanOptions.Span = tracer.StartSpan("before-context")
+ registry.PodmanOptions.SpanCtx = opentracing.ContextWithSpan(context.Background(), registry.PodmanOptions.Span)
+ }
return nil
}
-func logging() {
+func postRunE(cmd *cobra.Command, args []string) error {
+ if cmd.Flag("cpu-profile").Changed {
+ pprof.StopCPUProfile()
+ }
+ if cmd.Flag("trace").Changed {
+ registry.PodmanOptions.Span.Finish()
+ registry.PodmanOptions.SpanCloser.Close()
+ }
+ return nil
+}
+
+func loggingHook() {
if !logLevels.Contains(logLevel) {
- fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, logLevels.String())
+ logrus.Errorf("Log Level \"%s\" is not supported, choose from: %s", logLevel, logLevels.String())
os.Exit(1)
}
@@ -83,17 +134,68 @@ func syslogHook() {
}
}
-func Execute() {
- o := registry.NewOptions(rootCmd.Context(), &registry.EngineOptions)
- if err := rootCmd.ExecuteContext(o); err != nil {
- fmt.Fprintln(os.Stderr, "Error:", err.Error())
- } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
- // The exitCode modified from registry.ExecErrorCodeGeneric,
- // indicates an application
- // running inside of a container failed, as opposed to the
- // podman command failed. Must exit with that exit code
- // otherwise command exited correctly.
- registry.SetExitCode(0)
+func rootlessHook() {
+ if rootless.IsRootless() {
+ logrus.Error("rootless mode is currently not supported. Support will return ASAP.")
}
- os.Exit(registry.GetExitCode())
+ // ce, err := registry.NewContainerEngine(rootCmd, []string{})
+ // if err != nil {
+ // logrus.WithError(err).Fatal("failed to obtain container engine")
+ // }
+ // ce.SetupRootLess(rootCmd)
+}
+
+func rootFlags(opts entities.PodmanConfig, flags *pflag.FlagSet) {
+ // V2 flags
+ flags.StringVarP(&opts.Uri, "remote", "r", "", "URL to access Podman service")
+ flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file")
+
+ // Override default --help information of `--version` global flag
+ // TODO: restore -v option for version without breaking -v for volumes
+ var dummyVersion bool
+ flags.BoolVar(&dummyVersion, "version", false, "Version of Podman")
+
+ cfg := opts.Config
+ flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, opts.CGroupUsage)
+ 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")
+ flags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks")
+ flags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file")
+ flags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`)
+ flags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)")
+ flags.IntVar(&opts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations")
+ flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
+ flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored")
+ flags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
+ flags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
+ // -s is deprecated due to conflict with -s on subcommands
+ flags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
+ flags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
+
+ flags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
+ flags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)")
+
+ // Override default --help information of `--help` global flag
+ var dummyHelp bool
+ flags.BoolVar(&dummyHelp, "help", false, "Help for podman")
+ flags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", logLevels.String()))
+
+ // Hide these flags for both ABI and Tunneling
+ for _, f := range []string{
+ "cpu-profile",
+ "default-mounts-file",
+ "max-workers",
+ "trace",
+ } {
+ if err := flags.MarkHidden(f); err != nil {
+ logrus.Warnf("unable to mark %s flag as hidden", f)
+ }
+ }
+
+ // Only create these flags for ABI connections
+ if !registry.IsRemote() {
+ flags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
+ }
+
}
diff --git a/cmd/podmanV2/system/events.go b/cmd/podmanV2/system/events.go
new file mode 100644
index 000000000..9fd27e2c1
--- /dev/null
+++ b/cmd/podmanV2/system/events.go
@@ -0,0 +1,104 @@
+package system
+
+import (
+ "bufio"
+ "context"
+ "html/template"
+ "os"
+
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ eventsDescription = "Monitor podman events"
+ eventsCommand = &cobra.Command{
+ Use: "events",
+ Args: cobra.NoArgs,
+ Short: "Show podman events",
+ Long: eventsDescription,
+ PersistentPreRunE: preRunE,
+ RunE: eventsCmd,
+ Example: `podman events
+ podman events --filter event=create
+ podman events --since 1h30s`,
+ }
+)
+
+var (
+ eventOptions entities.EventsOptions
+ eventFormat string
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: eventsCommand,
+ })
+ flags := eventsCommand.Flags()
+ flags.StringArrayVar(&eventOptions.Filter, "filter", []string{}, "filter output")
+ flags.StringVar(&eventFormat, "format", "", "format the output using a Go template")
+ flags.BoolVar(&eventOptions.Stream, "stream", true, "stream new events; for testing only")
+ flags.StringVar(&eventOptions.Since, "since", "", "show all events created since timestamp")
+ flags.StringVar(&eventOptions.Until, "until", "", "show all events until timestamp")
+ _ = flags.MarkHidden("stream")
+}
+
+func eventsCmd(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ eventsError error
+ tmpl *template.Template
+ )
+ if eventFormat != formats.JSONString {
+ tmpl, err = template.New("events").Parse(eventFormat)
+ if err != nil {
+ return err
+ }
+ }
+ if len(eventOptions.Since) > 0 || len(eventOptions.Until) > 0 {
+ eventOptions.FromStart = true
+ }
+ eventChannel := make(chan *events.Event)
+ eventOptions.EventChan = eventChannel
+
+ go func() {
+ eventsError = registry.ContainerEngine().Events(context.Background(), eventOptions)
+ }()
+ if eventsError != nil {
+ return eventsError
+ }
+
+ w := bufio.NewWriter(os.Stdout)
+ for event := range eventChannel {
+ switch {
+ case eventFormat == formats.JSONString:
+ jsonStr, err := event.ToJSONString()
+ if err != nil {
+ return errors.Wrapf(err, "unable to format json")
+ }
+ if _, err := w.Write([]byte(jsonStr)); err != nil {
+ return err
+ }
+ case len(eventFormat) > 0:
+ if err := tmpl.Execute(w, event); err != nil {
+ return err
+ }
+ default:
+ if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil {
+ return err
+ }
+ }
+ if _, err := w.Write([]byte("\n")); err != nil {
+ return err
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ }
+ return nil
+}