summaryrefslogtreecommitdiff
path: root/cmd/podmanV2
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podmanV2')
-rw-r--r--cmd/podmanV2/Makefile2
-rw-r--r--cmd/podmanV2/common/create.go534
-rw-r--r--cmd/podmanV2/common/create_opts.go103
-rw-r--r--cmd/podmanV2/common/createparse.go51
-rw-r--r--cmd/podmanV2/common/default.go121
-rw-r--r--cmd/podmanV2/common/inspect.go18
-rw-r--r--cmd/podmanV2/common/netflags.go108
-rw-r--r--cmd/podmanV2/common/ports.go126
-rw-r--r--cmd/podmanV2/common/specgen.go647
-rw-r--r--cmd/podmanV2/common/types.go3
-rw-r--r--cmd/podmanV2/common/util.go43
-rw-r--r--cmd/podmanV2/containers/attach.go60
-rw-r--r--cmd/podmanV2/containers/checkpoint.go79
-rw-r--r--cmd/podmanV2/containers/commit.go79
-rw-r--r--cmd/podmanV2/containers/container.go15
-rw-r--r--cmd/podmanV2/containers/create.go102
-rw-r--r--cmd/podmanV2/containers/exec.go93
-rw-r--r--cmd/podmanV2/containers/export.go57
-rw-r--r--cmd/podmanV2/containers/inspect.go53
-rw-r--r--cmd/podmanV2/containers/restart.go11
-rw-r--r--cmd/podmanV2/containers/restore.go104
-rw-r--r--cmd/podmanV2/containers/start.go87
-rw-r--r--cmd/podmanV2/containers/stop.go19
-rw-r--r--cmd/podmanV2/containers/top.go91
-rw-r--r--cmd/podmanV2/healthcheck/healthcheck.go33
-rw-r--r--cmd/podmanV2/healthcheck/run.go42
-rw-r--r--cmd/podmanV2/images/exists.go40
-rw-r--r--cmd/podmanV2/images/image.go6
-rw-r--r--cmd/podmanV2/images/images.go14
-rw-r--r--cmd/podmanV2/images/import.go87
-rw-r--r--cmd/podmanV2/images/inspect.go141
-rw-r--r--cmd/podmanV2/images/list.go4
-rw-r--r--cmd/podmanV2/images/load.go61
-rw-r--r--cmd/podmanV2/images/prune.go86
-rw-r--r--cmd/podmanV2/images/pull.go140
-rw-r--r--cmd/podmanV2/images/push.go127
-rw-r--r--cmd/podmanV2/images/rm.go70
-rw-r--r--cmd/podmanV2/images/rmi.go30
-rw-r--r--cmd/podmanV2/images/save.go87
-rw-r--r--cmd/podmanV2/images/tag.go34
-rw-r--r--cmd/podmanV2/images/untag.go33
-rw-r--r--cmd/podmanV2/inspect.go62
-rw-r--r--cmd/podmanV2/main.go16
-rw-r--r--cmd/podmanV2/parse/common.go50
-rw-r--r--cmd/podmanV2/parse/net.go (renamed from cmd/podmanV2/parse/parse.go)45
-rw-r--r--cmd/podmanV2/parse/net_test.go (renamed from cmd/podmanV2/parse/parse_test.go)0
-rw-r--r--cmd/podmanV2/pods/create.go132
-rw-r--r--cmd/podmanV2/pods/inspect.go64
-rw-r--r--cmd/podmanV2/pods/kill.go68
-rw-r--r--cmd/podmanV2/pods/pause.go66
-rw-r--r--cmd/podmanV2/pods/pod.go30
-rw-r--r--cmd/podmanV2/pods/ps.go134
-rw-r--r--cmd/podmanV2/pods/restart.go68
-rw-r--r--cmd/podmanV2/pods/rm.go71
-rw-r--r--cmd/podmanV2/pods/start.go68
-rw-r--r--cmd/podmanV2/pods/stop.go79
-rw-r--r--cmd/podmanV2/pods/top.go90
-rw-r--r--cmd/podmanV2/pods/unpause.go66
-rw-r--r--cmd/podmanV2/registry/registry.go19
-rw-r--r--cmd/podmanV2/report/templates.go3
-rw-r--r--cmd/podmanV2/root.go77
-rw-r--r--cmd/podmanV2/system/system.go2
-rw-r--r--cmd/podmanV2/system/version.go119
-rw-r--r--cmd/podmanV2/utils/alias.go24
64 files changed, 4805 insertions, 189 deletions
diff --git a/cmd/podmanV2/Makefile b/cmd/podmanV2/Makefile
new file mode 100644
index 000000000..b847a9385
--- /dev/null
+++ b/cmd/podmanV2/Makefile
@@ -0,0 +1,2 @@
+all:
+ CGO_ENABLED=1 GO111MODULE=off go build -tags 'ABISupport systemd seccomp'
diff --git a/cmd/podmanV2/common/create.go b/cmd/podmanV2/common/create.go
new file mode 100644
index 000000000..f81d021c8
--- /dev/null
+++ b/cmd/podmanV2/common/create.go
@@ -0,0 +1,534 @@
+package common
+
+import (
+ "fmt"
+ "os"
+
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/pflag"
+)
+
+const (
+ sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))"
+)
+
+var (
+ defaultContainerConfig = getDefaultContainerConfig()
+)
+
+func getDefaultContainerConfig() *config.Config {
+ defaultContainerConfig, err := config.Default()
+ if err != nil {
+ logrus.Error(err)
+ os.Exit(1)
+ }
+ return defaultContainerConfig
+}
+
+func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
+ //createFlags := c.Flags()
+ createFlags := pflag.FlagSet{}
+ createFlags.StringSliceVar(
+ &cf.Annotation,
+ "annotation", []string{},
+ "Add annotations to container (key:value)",
+ )
+ createFlags.StringSliceVarP(
+ &cf.Attach,
+ "attach", "a", []string{},
+ "Attach to STDIN, STDOUT or STDERR",
+ )
+ createFlags.StringVar(
+ &cf.Authfile,
+ "authfile", buildahcli.GetDefaultAuthFile(),
+ "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override",
+ )
+ createFlags.StringVar(
+ &cf.BlkIOWeight,
+ "blkio-weight", "",
+ "Block IO weight (relative weight) accepts a weight value between 10 and 1000.",
+ )
+ createFlags.StringSliceVar(
+ &cf.BlkIOWeightDevice,
+ "blkio-weight-device", []string{},
+ "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)",
+ )
+ createFlags.StringSliceVar(
+ &cf.CapAdd,
+ "cap-add", []string{},
+ "Add capabilities to the container",
+ )
+ createFlags.StringSliceVar(
+ &cf.CapDrop,
+ "cap-drop", []string{},
+ "Drop capabilities from the container",
+ )
+ createFlags.StringVar(
+ &cf.CGroupsNS,
+ "cgroupns", getDefaultCgroupNS(),
+ "cgroup namespace to use",
+ )
+ createFlags.StringVar(
+ &cf.CGroups,
+ "cgroups", "enabled",
+ `control container cgroup configuration ("enabled"|"disabled"|"no-conmon")`,
+ )
+ createFlags.StringVar(
+ &cf.CGroupParent,
+ "cgroup-parent", "",
+ "Optional parent cgroup for the container",
+ )
+ createFlags.StringVar(
+ &cf.CIDFile,
+ "cidfile", "",
+ "Write the container ID to the file",
+ )
+ createFlags.StringVar(
+ &cf.ConmonPIDFile,
+ "conmon-pidfile", "",
+ "Path to the file that will receive the PID of conmon",
+ )
+ createFlags.Uint64Var(
+ &cf.CPUPeriod,
+ "cpu-period", 0,
+ "Limit the CPU CFS (Completely Fair Scheduler) period",
+ )
+ createFlags.Int64Var(
+ &cf.CPUQuota,
+ "cpu-quota", 0,
+ "Limit the CPU CFS (Completely Fair Scheduler) quota",
+ )
+ createFlags.Uint64Var(
+ &cf.CPURTPeriod,
+ "cpu-rt-period", 0,
+ "Limit the CPU real-time period in microseconds",
+ )
+ createFlags.Int64Var(
+ &cf.CPURTRuntime,
+ "cpu-rt-runtime", 0,
+ "Limit the CPU real-time runtime in microseconds",
+ )
+ createFlags.Uint64Var(
+ &cf.CPUShares,
+ "cpu-shares", 0,
+ "CPU shares (relative weight)",
+ )
+ createFlags.Float64Var(
+ &cf.CPUS,
+ "cpus", 0,
+ "Number of CPUs. The default is 0.000 which means no limit",
+ )
+ createFlags.StringVar(
+ &cf.CPUSetCPUs,
+ "cpuset-cpus", "",
+ "CPUs in which to allow execution (0-3, 0,1)",
+ )
+ createFlags.StringVar(
+ &cf.CPUSetMems,
+ "cpuset-mems", "",
+ "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.",
+ )
+ createFlags.BoolVarP(
+ &cf.Detach,
+ "detach", "d", false,
+ "Run container in background and print container ID",
+ )
+ createFlags.StringVar(
+ &cf.DetachKeys,
+ "detach-keys", GetDefaultDetachKeys(),
+ "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`",
+ )
+ createFlags.StringSliceVar(
+ &cf.Device,
+ "device", getDefaultDevices(),
+ fmt.Sprintf("Add a host device to the container"),
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceCGroupRule,
+ "device-cgroup-rule", []string{},
+ "Add a rule to the cgroup allowed devices list",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceReadBPs,
+ "device-read-bps", []string{},
+ "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceReadIOPs,
+ "device-read-iops", []string{},
+ "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceWriteBPs,
+ "device-write-bps", []string{},
+ "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)",
+ )
+ createFlags.StringSliceVar(
+ &cf.DeviceWriteIOPs,
+ "device-write-iops", []string{},
+ "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
+ )
+ createFlags.StringVar(
+ &cf.Entrypoint,
+ "entrypoint", "",
+ "Overwrite the default ENTRYPOINT of the image",
+ )
+ createFlags.StringArrayVarP(
+ &cf.env,
+ "env", "e", getDefaultEnv(),
+ "Set environment variables in container",
+ )
+ createFlags.BoolVar(
+ &cf.EnvHost,
+ "env-host", false, "Use all current host environment variables in container",
+ )
+ createFlags.StringSliceVar(
+ &cf.EnvFile,
+ "env-file", []string{},
+ "Read in a file of environment variables",
+ )
+ createFlags.StringSliceVar(
+ &cf.Expose,
+ "expose", []string{},
+ "Expose a port or a range of ports",
+ )
+ createFlags.StringSliceVar(
+ &cf.GIDMap,
+ "gidmap", []string{},
+ "GID map to use for the user namespace",
+ )
+ createFlags.StringSliceVar(
+ &cf.GroupAdd,
+ "group-add", []string{},
+ "Add additional groups to join",
+ )
+ createFlags.Bool(
+ "help", false, "",
+ )
+ createFlags.StringVar(
+ &cf.HealthCmd,
+ "health-cmd", "",
+ "set a healthcheck command for the container ('none' disables the existing healthcheck)",
+ )
+ createFlags.StringVar(
+ &cf.HealthInterval,
+ "health-interval", cliconfig.DefaultHealthCheckInterval,
+ "set an interval for the healthchecks (a value of disable results in no automatic timer setup)",
+ )
+ createFlags.UintVar(
+ &cf.HealthRetries,
+ "health-retries", cliconfig.DefaultHealthCheckRetries,
+ "the number of retries allowed before a healthcheck is considered to be unhealthy",
+ )
+ createFlags.StringVar(
+ &cf.HealthStartPeriod,
+ "health-start-period", cliconfig.DefaultHealthCheckStartPeriod,
+ "the initialization time needed for a container to bootstrap",
+ )
+ createFlags.StringVar(
+ &cf.HealthTimeout,
+ "health-timeout", cliconfig.DefaultHealthCheckTimeout,
+ "the maximum time allowed to complete the healthcheck before an interval is considered failed",
+ )
+ createFlags.StringVarP(
+ &cf.Hostname,
+ "hostname", "h", "",
+ "Set container hostname",
+ )
+ createFlags.BoolVar(
+ &cf.HTTPProxy,
+ "http-proxy", true,
+ "Set proxy environment variables in the container based on the host proxy vars",
+ )
+ createFlags.StringVar(
+ &cf.ImageVolume,
+ "image-volume", cliconfig.DefaultImageVolume,
+ `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`,
+ )
+ createFlags.BoolVar(
+ &cf.Init,
+ "init", false,
+ "Run an init binary inside the container that forwards signals and reaps processes",
+ )
+ createFlags.StringVar(
+ &cf.InitPath,
+ "init-path", getDefaultInitPath(),
+ // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string)
+ fmt.Sprintf("Path to the container-init binary"),
+ )
+ createFlags.BoolVarP(
+ &cf.Interactive,
+ "interactive", "i", false,
+ "Keep STDIN open even if not attached",
+ )
+ createFlags.StringVar(
+ &cf.IPC,
+ "ipc", getDefaultIPCNS(),
+ "IPC namespace to use",
+ )
+ createFlags.StringVar(
+ &cf.KernelMemory,
+ "kernel-memory", "",
+ "Kernel memory limit "+sizeWithUnitFormat,
+ )
+ createFlags.StringArrayVarP(
+ &cf.Label,
+ "label", "l", []string{},
+ "Set metadata on container",
+ )
+ createFlags.StringSliceVar(
+ &cf.LabelFile,
+ "label-file", []string{},
+ "Read in a line delimited file of labels",
+ )
+ createFlags.StringVar(
+ &cf.LogDriver,
+ "log-driver", "",
+ "Logging driver for the container",
+ )
+ createFlags.StringSliceVar(
+ &cf.LogOptions,
+ "log-opt", []string{},
+ "Logging driver options",
+ )
+ createFlags.StringVarP(
+ &cf.Memory,
+ "memory", "m", "",
+ "Memory limit "+sizeWithUnitFormat,
+ )
+ createFlags.StringVar(
+ &cf.MemoryReservation,
+ "memory-reservation", "",
+ "Memory soft limit "+sizeWithUnitFormat,
+ )
+ createFlags.StringVar(
+ &cf.MemorySwap,
+ "memory-swap", "",
+ "Swap limit equal to memory plus swap: '-1' to enable unlimited swap",
+ )
+ createFlags.Int64Var(
+ &cf.MemorySwappiness,
+ "memory-swappiness", -1,
+ "Tune container memory swappiness (0 to 100, or -1 for system default)",
+ )
+ createFlags.StringVar(
+ &cf.Name,
+ "name", "",
+ "Assign a name to the container",
+ )
+ createFlags.BoolVar(
+ &cf.NoHealthCheck,
+ "no-healthcheck", false,
+ "Disable healthchecks on container",
+ )
+ createFlags.BoolVar(
+ &cf.OOMKillDisable,
+ "oom-kill-disable", false,
+ "Disable OOM Killer",
+ )
+ createFlags.IntVar(
+ &cf.OOMScoreAdj,
+ "oom-score-adj", 0,
+ "Tune the host's OOM preferences (-1000 to 1000)",
+ )
+ createFlags.StringVar(
+ &cf.OverrideArch,
+ "override-arch", "",
+ "use `ARCH` instead of the architecture of the machine for choosing images",
+ )
+ //markFlagHidden(createFlags, "override-arch")
+ createFlags.StringVar(
+ &cf.OverrideOS,
+ "override-os", "",
+ "use `OS` instead of the running OS for choosing images",
+ )
+ //markFlagHidden(createFlags, "override-os")
+ createFlags.StringVar(
+ &cf.PID,
+ "pid", getDefaultPidNS(),
+ "PID namespace to use",
+ )
+ createFlags.Int64Var(
+ &cf.PIDsLimit,
+ "pids-limit", getDefaultPidsLimit(),
+ getDefaultPidsDescription(),
+ )
+ createFlags.StringVar(
+ &cf.Pod,
+ "pod", "",
+ "Run container in an existing pod",
+ )
+ createFlags.BoolVar(
+ &cf.Privileged,
+ "privileged", false,
+ "Give extended privileges to container",
+ )
+ createFlags.BoolVarP(
+ &cf.PublishAll,
+ "publish-all", "P", false,
+ "Publish all exposed ports to random ports on the host interface",
+ )
+ createFlags.StringVar(
+ &cf.Pull,
+ "pull", "missing",
+ `Pull image before creating ("always"|"missing"|"never")`,
+ )
+ createFlags.BoolVarP(
+ &cf.Quiet,
+ "quiet", "q", false,
+ "Suppress output information when pulling images",
+ )
+ createFlags.BoolVar(
+ &cf.ReadOnly,
+ "read-only", false,
+ "Make containers root filesystem read-only",
+ )
+ createFlags.BoolVar(
+ &cf.ReadOnlyTmpFS,
+ "read-only-tmpfs", true,
+ "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp",
+ )
+ createFlags.StringVar(
+ &cf.Restart,
+ "restart", "",
+ `Restart policy to apply when a container exits ("always"|"no"|"on-failure")`,
+ )
+ createFlags.BoolVar(
+ &cf.Rm,
+ "rm", false,
+ "Remove container (and pod if created) after exit",
+ )
+ createFlags.BoolVar(
+ &cf.RootFS,
+ "rootfs", false,
+ "The first argument is not an image but the rootfs to the exploded container",
+ )
+ createFlags.StringArrayVar(
+ &cf.SecurityOpt,
+ "security-opt", getDefaultSecurityOptions(),
+ fmt.Sprintf("Security Options"),
+ )
+ createFlags.StringVar(
+ &cf.ShmSize,
+ "shm-size", getDefaultShmSize(),
+ "Size of /dev/shm "+sizeWithUnitFormat,
+ )
+ createFlags.StringVar(
+ &cf.StopSignal,
+ "stop-signal", "",
+ "Signal to stop a container. Default is SIGTERM",
+ )
+ createFlags.UintVar(
+ &cf.StopTimeout,
+ "stop-timeout", defaultContainerConfig.Engine.StopTimeout,
+ "Timeout (in seconds) to stop a container. Default is 10",
+ )
+ createFlags.StringSliceVar(
+ &cf.StoreageOpt,
+ "storage-opt", []string{},
+ "Storage driver options per container",
+ )
+ createFlags.StringVar(
+ &cf.SubUIDName,
+ "subgidname", "",
+ "Name of range listed in /etc/subgid for use in user namespace",
+ )
+ createFlags.StringVar(
+ &cf.SubGIDName,
+ "subuidname", "",
+ "Name of range listed in /etc/subuid for use in user namespace",
+ )
+
+ createFlags.StringSliceVar(
+ &cf.Sysctl,
+ "sysctl", getDefaultSysctls(),
+ "Sysctl options",
+ )
+ createFlags.StringVar(
+ &cf.SystemdD,
+ "systemd", "true",
+ `Run container in systemd mode ("true"|"false"|"always")`,
+ )
+ createFlags.StringArrayVar(
+ &cf.TmpFS,
+ "tmpfs", []string{},
+ "Mount a temporary filesystem (`tmpfs`) into a container",
+ )
+ createFlags.BoolVarP(
+ &cf.TTY,
+ "tty", "t", false,
+ "Allocate a pseudo-TTY for container",
+ )
+ createFlags.StringSliceVar(
+ &cf.UIDMap,
+ "uidmap", []string{},
+ "UID map to use for the user namespace",
+ )
+ createFlags.StringSliceVar(
+ &cf.Ulimit,
+ "ulimit", getDefaultUlimits(),
+ "Ulimit options",
+ )
+ createFlags.StringVarP(
+ &cf.User,
+ "user", "u", "",
+ "Username or UID (format: <name|uid>[:<group|gid>])",
+ )
+ createFlags.StringVar(
+ &cf.UserNS,
+ "userns", getDefaultUserNS(),
+ "User namespace to use",
+ )
+ createFlags.StringVar(
+ &cf.UTS,
+ "uts", getDefaultUTSNS(),
+ "UTS namespace to use",
+ )
+ createFlags.StringArrayVar(
+ &cf.Mount,
+ "mount", []string{},
+ "Attach a filesystem mount to the container",
+ )
+ createFlags.StringArrayVarP(
+ &cf.Volume,
+ "volume", "v", getDefaultVolumes(),
+ "Bind mount a volume into the container",
+ )
+ createFlags.StringSliceVar(
+ &cf.VolumesFrom,
+ "volumes-from", []string{},
+ "Mount volumes from the specified container(s)",
+ )
+ createFlags.StringVarP(
+ &cf.Workdir,
+ "workdir", "w", "",
+ "Working directory inside the container",
+ )
+ createFlags.StringVar(
+ &cf.SeccompPolicy,
+ "seccomp-policy", "default",
+ "Policy for selecting a seccomp profile (experimental)",
+ )
+ return &createFlags
+}
+
+func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
+ switch name {
+ case "healthcheck-command":
+ name = "health-cmd"
+ case "healthcheck-interval":
+ name = "health-interval"
+ case "healthcheck-retries":
+ name = "health-retries"
+ case "healthcheck-start-period":
+ name = "health-start-period"
+ case "healthcheck-timeout":
+ name = "health-timeout"
+ case "net":
+ name = "network"
+ }
+ return pflag.NormalizedName(name)
+}
diff --git a/cmd/podmanV2/common/create_opts.go b/cmd/podmanV2/common/create_opts.go
new file mode 100644
index 000000000..9d12e4b26
--- /dev/null
+++ b/cmd/podmanV2/common/create_opts.go
@@ -0,0 +1,103 @@
+package common
+
+import "github.com/containers/libpod/pkg/domain/entities"
+
+type ContainerCLIOpts struct {
+ Annotation []string
+ Attach []string
+ Authfile string
+ BlkIOWeight string
+ BlkIOWeightDevice []string
+ CapAdd []string
+ CapDrop []string
+ CGroupsNS string
+ CGroups string
+ CGroupParent string
+ CIDFile string
+ ConmonPIDFile string
+ CPUPeriod uint64
+ CPUQuota int64
+ CPURTPeriod uint64
+ CPURTRuntime int64
+ CPUShares uint64
+ CPUS float64
+ CPUSetCPUs string
+ CPUSetMems string
+ Detach bool
+ DetachKeys string
+ Device []string
+ DeviceCGroupRule []string
+ DeviceReadBPs []string
+ DeviceReadIOPs []string
+ DeviceWriteBPs []string
+ DeviceWriteIOPs []string
+ Entrypoint string
+ env []string
+ EnvHost bool
+ EnvFile []string
+ Expose []string
+ GIDMap []string
+ GroupAdd []string
+ HealthCmd string
+ HealthInterval string
+ HealthRetries uint
+ HealthStartPeriod string
+ HealthTimeout string
+ Hostname string
+ HTTPProxy bool
+ ImageVolume string
+ Init bool
+ InitPath string
+ Interactive bool
+ IPC string
+ KernelMemory string
+ Label []string
+ LabelFile []string
+ LogDriver string
+ LogOptions []string
+ Memory string
+ MemoryReservation string
+ MemorySwap string
+ MemorySwappiness int64
+ Name string
+ NoHealthCheck bool
+ OOMKillDisable bool
+ OOMScoreAdj int
+ OverrideArch string
+ OverrideOS string
+ PID string
+ PIDsLimit int64
+ Pod string
+ Privileged bool
+ PublishAll bool
+ Pull string
+ Quiet bool
+ ReadOnly bool
+ ReadOnlyTmpFS bool
+ Restart string
+ Rm bool
+ RootFS bool
+ SecurityOpt []string
+ ShmSize string
+ StopSignal string
+ StopTimeout uint
+ StoreageOpt []string
+ SubUIDName string
+ SubGIDName string
+ Sysctl []string
+ SystemdD string
+ TmpFS []string
+ TTY bool
+ UIDMap []string
+ Ulimit []string
+ User string
+ UserNS string
+ UTS string
+ Mount []string
+ Volume []string
+ VolumesFrom []string
+ Workdir string
+ SeccompPolicy string
+
+ Net *entities.NetOptions
+}
diff --git a/cmd/podmanV2/common/createparse.go b/cmd/podmanV2/common/createparse.go
new file mode 100644
index 000000000..89524a04b
--- /dev/null
+++ b/cmd/podmanV2/common/createparse.go
@@ -0,0 +1,51 @@
+package common
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+// validate determines if the flags and values given by the user are valid. things checked
+// by validate must not need any state information on the flag (i.e. changed)
+func (c *ContainerCLIOpts) validate() error {
+ var ()
+ if c.Rm && c.Restart != "" && c.Restart != "no" {
+ return errors.Errorf("the --rm option conflicts with --restart")
+ }
+
+ if _, err := util.ValidatePullType(c.Pull); err != nil {
+ return err
+ }
+ // Verify the additional hosts are in correct format
+ for _, host := range c.Net.AddHosts {
+ if _, err := parse.ValidateExtraHost(host); err != nil {
+ return err
+ }
+ }
+
+ if dnsSearches := c.Net.DNSSearch; len(dnsSearches) > 0 {
+ // Validate domains are good
+ for _, dom := range dnsSearches {
+ if dom == "." {
+ if len(dnsSearches) > 1 {
+ return errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ }
+ continue
+ }
+ if _, err := parse.ValidateDomain(dom); err != nil {
+ return err
+ }
+ }
+ }
+ var imageVolType = map[string]string{
+ "bind": "",
+ "tmpfs": "",
+ "ignore": "",
+ }
+ if _, ok := imageVolType[c.ImageVolume]; !ok {
+ return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume)
+ }
+ return nil
+
+}
diff --git a/cmd/podmanV2/common/default.go b/cmd/podmanV2/common/default.go
new file mode 100644
index 000000000..b71fcb6f0
--- /dev/null
+++ b/cmd/podmanV2/common/default.go
@@ -0,0 +1,121 @@
+package common
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/libpod/pkg/apparmor"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/sysinfo"
+ "github.com/opencontainers/selinux/go-selinux"
+)
+
+// TODO these options are directly embedded into many of the CLI cobra values, as such
+// this approach will not work in a remote client. so we will need to likely do something like a
+// supported and unsupported approach here and backload these options into the specgen
+// once we are "on" the host system.
+func getDefaultSecurityOptions() []string {
+ securityOpts := []string{}
+ if defaultContainerConfig.Containers.SeccompProfile != "" && defaultContainerConfig.Containers.SeccompProfile != parse.SeccompDefaultPath {
+ securityOpts = append(securityOpts, fmt.Sprintf("seccomp=%s", defaultContainerConfig.Containers.SeccompProfile))
+ }
+ if apparmor.IsEnabled() && defaultContainerConfig.Containers.ApparmorProfile != "" {
+ securityOpts = append(securityOpts, fmt.Sprintf("apparmor=%s", defaultContainerConfig.Containers.ApparmorProfile))
+ }
+ if selinux.GetEnabled() && !defaultContainerConfig.Containers.EnableLabeling {
+ securityOpts = append(securityOpts, fmt.Sprintf("label=%s", selinux.DisableSecOpt()[0]))
+ }
+ return securityOpts
+}
+
+// getDefaultSysctls
+func getDefaultSysctls() []string {
+ return defaultContainerConfig.Containers.DefaultSysctls
+}
+
+func getDefaultVolumes() []string {
+ return defaultContainerConfig.Containers.Volumes
+}
+
+func getDefaultDevices() []string {
+ return defaultContainerConfig.Containers.Devices
+}
+
+func getDefaultDNSServers() []string { //nolint
+ return defaultContainerConfig.Containers.DNSServers
+}
+
+func getDefaultDNSSearches() []string { //nolint
+ return defaultContainerConfig.Containers.DNSSearches
+}
+
+func getDefaultDNSOptions() []string { //nolint
+ return defaultContainerConfig.Containers.DNSOptions
+}
+
+func getDefaultEnv() []string {
+ return defaultContainerConfig.Containers.Env
+}
+
+func getDefaultInitPath() string {
+ return defaultContainerConfig.Containers.InitPath
+}
+
+func getDefaultIPCNS() string {
+ return defaultContainerConfig.Containers.IPCNS
+}
+
+func getDefaultPidNS() string {
+ return defaultContainerConfig.Containers.PidNS
+}
+
+func getDefaultNetNS() string { //nolint
+ if defaultContainerConfig.Containers.NetNS == "private" && rootless.IsRootless() {
+ return "slirp4netns"
+ }
+ return defaultContainerConfig.Containers.NetNS
+}
+
+func getDefaultCgroupNS() string {
+ return defaultContainerConfig.Containers.CgroupNS
+}
+
+func getDefaultUTSNS() string {
+ return defaultContainerConfig.Containers.UTSNS
+}
+
+func getDefaultShmSize() string {
+ return defaultContainerConfig.Containers.ShmSize
+}
+
+func getDefaultUlimits() []string {
+ return defaultContainerConfig.Containers.DefaultUlimits
+}
+
+func getDefaultUserNS() string {
+ userns := os.Getenv("PODMAN_USERNS")
+ if userns != "" {
+ return userns
+ }
+ return defaultContainerConfig.Containers.UserNS
+}
+
+func getDefaultPidsLimit() int64 {
+ if rootless.IsRootless() {
+ cgroup2, _ := cgroups.IsCgroup2UnifiedMode()
+ if cgroup2 {
+ return defaultContainerConfig.Containers.PidsLimit
+ }
+ }
+ return sysinfo.GetDefaultPidsLimit()
+}
+
+func getDefaultPidsDescription() string {
+ return "Tune container pids limit (set 0 for unlimited)"
+}
+
+func GetDefaultDetachKeys() string {
+ return defaultContainerConfig.Engine.DetachKeys
+}
diff --git a/cmd/podmanV2/common/inspect.go b/cmd/podmanV2/common/inspect.go
new file mode 100644
index 000000000..dfc6fe679
--- /dev/null
+++ b/cmd/podmanV2/common/inspect.go
@@ -0,0 +1,18 @@
+package common
+
+import (
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+// AddInspectFlagSet takes a command and adds the inspect flags and returns an InspectOptions object
+// Since this cannot live in `package main` it lives here until a better home is found
+func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
+ opts := entities.InspectOptions{}
+
+ flags := cmd.Flags()
+ flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size")
+ flags.StringVarP(&opts.Format, "format", "f", "", "Change the output format to a Go template")
+
+ return &opts
+}
diff --git a/cmd/podmanV2/common/netflags.go b/cmd/podmanV2/common/netflags.go
new file mode 100644
index 000000000..758f155c8
--- /dev/null
+++ b/cmd/podmanV2/common/netflags.go
@@ -0,0 +1,108 @@
+package common
+
+import (
+ "net"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+func getDefaultNetwork() string {
+ if rootless.IsRootless() {
+ return "slirp4netns"
+ }
+ return "bridge"
+}
+
+func GetNetFlags() *pflag.FlagSet {
+ netFlags := pflag.FlagSet{}
+ netFlags.StringSlice(
+ "add-host", []string{},
+ "Add a custom host-to-IP mapping (host:ip) (default [])",
+ )
+ netFlags.StringSlice(
+ "dns", []string{},
+ "Set custom DNS servers",
+ )
+ netFlags.StringSlice(
+ "dns-opt", []string{},
+ "Set custom DNS options",
+ )
+ netFlags.StringSlice(
+ "dns-search", []string{},
+ "Set custom DNS search domains",
+ )
+ netFlags.String(
+ "ip", "",
+ "Specify a static IPv4 address for the container",
+ )
+ netFlags.String(
+ "mac-address", "",
+ "Container MAC address (e.g. 92:d0:c6:0a:29:33)",
+ )
+ netFlags.String(
+ "network", getDefaultNetwork(),
+ "Connect a container to a network",
+ )
+ netFlags.StringSliceP(
+ "publish", "p", []string{},
+ "Publish a container's port, or a range of ports, to the host (default [])",
+ )
+ netFlags.Bool(
+ "no-hosts", false,
+ "Do not create /etc/hosts within the container, instead use the version from the image",
+ )
+ return &netFlags
+}
+
+func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
+ var (
+ err error
+ )
+ opts := entities.NetOptions{}
+ opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host")
+ if err != nil {
+ return nil, err
+ }
+ servers, err := cmd.Flags().GetStringSlice("dns")
+ if err != nil {
+ return nil, err
+ }
+ for _, d := range servers {
+ if d == "none" {
+ opts.DNSHost = true
+ break
+ }
+ opts.DNSServers = append(opts.DNSServers, net.ParseIP(d))
+ }
+ opts.DNSSearch, err = cmd.Flags().GetStringSlice("dns-search")
+ if err != nil {
+ return nil, err
+ }
+
+ m, err := cmd.Flags().GetString("mac-address")
+ if err != nil {
+ return nil, err
+ }
+ if len(m) > 0 {
+ mac, err := net.ParseMAC(m)
+ if err != nil {
+ return nil, err
+ }
+ opts.StaticMAC = &mac
+ }
+ inputPorts, err := cmd.Flags().GetStringSlice("publish")
+ if err != nil {
+ return nil, err
+ }
+ if len(inputPorts) > 0 {
+ opts.PublishPorts, err = createPortBindings(inputPorts)
+ if err != nil {
+ return nil, err
+ }
+ }
+ opts.NoHosts, err = cmd.Flags().GetBool("no-hosts")
+ return &opts, err
+}
diff --git a/cmd/podmanV2/common/ports.go b/cmd/podmanV2/common/ports.go
new file mode 100644
index 000000000..7e2b1e79d
--- /dev/null
+++ b/cmd/podmanV2/common/ports.go
@@ -0,0 +1,126 @@
+package common
+
+import (
+ "fmt"
+ "net"
+ "strconv"
+
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// ExposedPorts parses user and image ports and returns binding information
+func ExposedPorts(expose []string, publish []ocicni.PortMapping, publishAll bool, imageExposedPorts map[string]struct{}) ([]ocicni.PortMapping, error) {
+ containerPorts := make(map[string]string)
+
+ // TODO this needs to be added into a something that
+ // has access to an imageengine
+ // add expose ports from the image itself
+ //for expose := range imageExposedPorts {
+ // _, port := nat.SplitProtoPort(expose)
+ // containerPorts[port] = ""
+ //}
+
+ // add the expose ports from the user (--expose)
+ // can be single or a range
+ for _, expose := range expose {
+ //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
+ _, port := nat.SplitProtoPort(expose)
+ //parse the start and end port and create a sequence of ports to expose
+ //if expose a port, the start and end port are the same
+ start, end, err := nat.ParsePortRange(port)
+ if err != nil {
+ return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
+ }
+ for i := start; i <= end; i++ {
+ containerPorts[strconv.Itoa(int(i))] = ""
+ }
+ }
+
+ // TODO/FIXME this is hell reencarnated
+ // parse user inputted port bindings
+ pbPorts, portBindings, err := nat.ParsePortSpecs([]string{})
+ if err != nil {
+ return nil, err
+ }
+
+ // delete exposed container ports if being used by -p
+ for i := range pbPorts {
+ delete(containerPorts, i.Port())
+ }
+
+ // iterate container ports and make port bindings from them
+ if publishAll {
+ for e := range containerPorts {
+ //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
+ //proto, port := nat.SplitProtoPort(e)
+ p, err := nat.NewPort("tcp", e)
+ if err != nil {
+ return nil, err
+ }
+ rp, err := getRandomPort()
+ if err != nil {
+ return nil, err
+ }
+ logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
+ portBindings[p] = CreatePortBinding(rp, "")
+ }
+ }
+
+ // We need to see if any host ports are not populated and if so, we need to assign a
+ // random port to them.
+ for k, pb := range portBindings {
+ if pb[0].HostPort == "" {
+ hostPort, err := getRandomPort()
+ if err != nil {
+ return nil, err
+ }
+ logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
+ pb[0].HostPort = strconv.Itoa(hostPort)
+ }
+ }
+ var pms []ocicni.PortMapping
+ for k, v := range portBindings {
+ for _, pb := range v {
+ hp, err := strconv.Atoi(pb.HostPort)
+ if err != nil {
+ return nil, err
+ }
+ pms = append(pms, ocicni.PortMapping{
+ HostPort: int32(hp),
+ ContainerPort: int32(k.Int()),
+ //Protocol: "",
+ HostIP: pb.HostIP,
+ })
+ }
+ }
+ return pms, nil
+}
+
+func getRandomPort() (int, error) {
+ l, err := net.Listen("tcp", ":0")
+ if err != nil {
+ return 0, errors.Wrapf(err, "unable to get free port")
+ }
+ defer l.Close()
+ _, randomPort, err := net.SplitHostPort(l.Addr().String())
+ if err != nil {
+ return 0, errors.Wrapf(err, "unable to determine free port")
+ }
+ rp, err := strconv.Atoi(randomPort)
+ if err != nil {
+ return 0, errors.Wrapf(err, "unable to convert random port to int")
+ }
+ return rp, nil
+}
+
+//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
+func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
+ pb := nat.PortBinding{
+ HostPort: strconv.Itoa(hostPort),
+ }
+ pb.HostIP = hostIP
+ return []nat.PortBinding{pb}
+}
diff --git a/cmd/podmanV2/common/specgen.go b/cmd/podmanV2/common/specgen.go
new file mode 100644
index 000000000..5245e206e
--- /dev/null
+++ b/cmd/podmanV2/common/specgen.go
@@ -0,0 +1,647 @@
+package common
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/libpod"
+ ann "github.com/containers/libpod/pkg/annotations"
+ envLib "github.com/containers/libpod/pkg/env"
+ ns "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/specgen"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/docker/go-units"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+)
+
+func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error {
+ var (
+ err error
+ //namespaces map[string]string
+ )
+
+ // validate flags as needed
+ if err := c.validate(); err != nil {
+ return nil
+ }
+
+ inputCommand := args[1:]
+ if len(c.HealthCmd) > 0 {
+ s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod)
+ if err != nil {
+ return err
+ }
+ }
+
+ s.IDMappings, err = util.ParseIDMapping(ns.UsernsMode(c.UserNS), c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName)
+ if err != nil {
+ return err
+ }
+ if m := c.Memory; len(m) > 0 {
+ ml, err := units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for memory")
+ }
+ s.ResourceLimits.Memory.Limit = &ml
+ }
+ if m := c.MemoryReservation; len(m) > 0 {
+ mr, err := units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for memory")
+ }
+ s.ResourceLimits.Memory.Reservation = &mr
+ }
+ if m := c.MemorySwap; len(m) > 0 {
+ var ms int64
+ if m == "-1" {
+ ms = int64(-1)
+ s.ResourceLimits.Memory.Swap = &ms
+ } else {
+ ms, err = units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for memory")
+ }
+ }
+ s.ResourceLimits.Memory.Swap = &ms
+ }
+ if m := c.KernelMemory; len(m) > 0 {
+ mk, err := units.RAMInBytes(m)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for kernel-memory")
+ }
+ s.ResourceLimits.Memory.Kernel = &mk
+ }
+ if b := c.BlkIOWeight; len(b) > 0 {
+ u, err := strconv.ParseUint(b, 10, 16)
+ if err != nil {
+ return errors.Wrapf(err, "invalid value for blkio-weight")
+ }
+ nu := uint16(u)
+ s.ResourceLimits.BlockIO.Weight = &nu
+ }
+
+ s.Terminal = c.TTY
+ ep, err := ExposedPorts(c.Expose, c.Net.PublishPorts, c.PublishAll, nil)
+ if err != nil {
+ return err
+ }
+ s.PortMappings = ep
+ s.Pod = c.Pod
+
+ //s.CgroupNS = specgen.Namespace{
+ // NSMode: ,
+ // Value: "",
+ //}
+
+ //s.UserNS = specgen.Namespace{}
+
+ // Kernel Namespaces
+ // TODO Fix handling of namespace from pod
+ // Instead of integrating here, should be done in libpod
+ // However, that also involves setting up security opts
+ // when the pod's namespace is integrated
+ //namespaces = map[string]string{
+ // "cgroup": c.CGroupsNS,
+ // "pid": c.PID,
+ // //"net": c.Net.Network.Value, // TODO need help here
+ // "ipc": c.IPC,
+ // "user": c.User,
+ // "uts": c.UTS,
+ //}
+ //
+ //if len(c.PID) > 0 {
+ // split := strings.SplitN(c.PID, ":", 2)
+ // // need a way to do thsi
+ // specgen.Namespace{
+ // NSMode: split[0],
+ // }
+ // //Value: split1 if len allows
+ //}
+ // TODO this is going to have be done after things like pod creation are done because
+ // pod creation changes these values.
+ //pidMode := ns.PidMode(namespaces["pid"])
+ //usernsMode := ns.UsernsMode(namespaces["user"])
+ //utsMode := ns.UTSMode(namespaces["uts"])
+ //cgroupMode := ns.CgroupMode(namespaces["cgroup"])
+ //ipcMode := ns.IpcMode(namespaces["ipc"])
+ //// Make sure if network is set to container namespace, port binding is not also being asked for
+ //netMode := ns.NetworkMode(namespaces["net"])
+ //if netMode.IsContainer() {
+ // if len(portBindings) > 0 {
+ // return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
+ // }
+ //}
+
+ // TODO Remove when done with namespaces for realz
+ // Setting a default for IPC to get this working
+ s.IpcNS = specgen.Namespace{
+ NSMode: specgen.Private,
+ Value: "",
+ }
+
+ // TODO this is going to have to be done the libpod/server end of things
+ // USER
+ //user := c.String("user")
+ //if user == "" {
+ // switch {
+ // case usernsMode.IsKeepID():
+ // user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID())
+ // case data == nil:
+ // user = "0"
+ // default:
+ // user = data.Config.User
+ // }
+ //}
+
+ // STOP SIGNAL
+ signalString := "TERM"
+ if sig := c.StopSignal; len(sig) > 0 {
+ signalString = sig
+ }
+ stopSignal, err := util.ParseSignal(signalString)
+ if err != nil {
+ return err
+ }
+ s.StopSignal = &stopSignal
+
+ // ENVIRONMENT VARIABLES
+ //
+ // Precedence order (higher index wins):
+ // 1) env-host, 2) image data, 3) env-file, 4) env
+ env := map[string]string{
+ "container": "podman",
+ }
+
+ // First transform the os env into a map. We need it for the labels later in
+ // any case.
+ osEnv, err := envLib.ParseSlice(os.Environ())
+ if err != nil {
+ return errors.Wrap(err, "error parsing host environment variables")
+ }
+
+ if c.EnvHost {
+ env = envLib.Join(env, osEnv)
+ }
+ // env-file overrides any previous variables
+ for _, f := range c.EnvFile {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return err
+ }
+ // File env is overridden by env.
+ 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)
+ }
+ s.Env = env
+
+ // LABEL VARIABLES
+ labels, err := parse.GetAllLabels(c.LabelFile, c.Label)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+
+ if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
+ labels[systemdGen.EnvVariable] = systemdUnit
+ }
+
+ s.Labels = labels
+
+ // ANNOTATIONS
+ annotations := make(map[string]string)
+
+ // First, add our default annotations
+ annotations[ann.TTY] = "false"
+ if c.TTY {
+ annotations[ann.TTY] = "true"
+ }
+
+ // Last, add user annotations
+ for _, annotation := range c.Annotation {
+ splitAnnotation := strings.SplitN(annotation, "=", 2)
+ if len(splitAnnotation) < 2 {
+ return errors.Errorf("Annotations must be formatted KEY=VALUE")
+ }
+ annotations[splitAnnotation[0]] = splitAnnotation[1]
+ }
+ s.Annotations = annotations
+
+ workDir := "/"
+ if wd := c.Workdir; len(wd) > 0 {
+ workDir = wd
+ }
+ s.WorkDir = workDir
+ entrypoint := []string{}
+ userCommand := []string{}
+ if ep := c.Entrypoint; len(ep) > 0 {
+ // Check if entrypoint specified is json
+ if err := json.Unmarshal([]byte(c.Entrypoint), &entrypoint); err != nil {
+ entrypoint = append(entrypoint, ep)
+ }
+ }
+
+ var command []string
+
+ // Build the command
+ // If we have an entry point, it goes first
+ if len(entrypoint) > 0 {
+ command = entrypoint
+ }
+ if len(inputCommand) > 0 {
+ // User command overrides data CMD
+ command = append(command, inputCommand...)
+ userCommand = append(userCommand, inputCommand...)
+ }
+
+ if len(inputCommand) > 0 {
+ s.Command = userCommand
+ } else {
+ s.Command = command
+ }
+
+ // SHM Size
+ shmSize, err := units.FromHumanSize(c.ShmSize)
+ if err != nil {
+ return errors.Wrapf(err, "unable to translate --shm-size")
+ }
+ s.ShmSize = &shmSize
+ s.HostAdd = c.Net.AddHosts
+ s.DNSServer = c.Net.DNSServers
+ s.DNSSearch = c.Net.DNSSearch
+ s.DNSOption = c.Net.DNSOptions
+
+ // deferred, must be added on libpod side
+ //var ImageVolumes map[string]struct{}
+ //if data != nil && c.String("image-volume") != "ignore" {
+ // ImageVolumes = data.Config.Volumes
+ //}
+
+ s.ImageVolumeMode = c.ImageVolume
+ systemd := c.SystemdD == "always"
+ if !systemd && command != nil {
+ x, err := strconv.ParseBool(c.SystemdD)
+ if err != nil {
+ return errors.Wrapf(err, "cannot parse bool %s", c.SystemdD)
+ }
+ if x && (command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd")) {
+ systemd = true
+ }
+ }
+ if systemd {
+ if s.StopSignal == nil {
+ stopSignal, err = util.ParseSignal("RTMIN+3")
+ if err != nil {
+ return errors.Wrapf(err, "error parsing systemd signal")
+ }
+ s.StopSignal = &stopSignal
+ }
+ }
+ swappiness := uint64(c.MemorySwappiness)
+ if s.ResourceLimits == nil {
+ s.ResourceLimits = &specs.LinuxResources{}
+ }
+ if s.ResourceLimits.Memory == nil {
+ s.ResourceLimits.Memory = &specs.LinuxMemory{}
+ }
+ s.ResourceLimits.Memory.Swappiness = &swappiness
+
+ if s.LogConfiguration == nil {
+ s.LogConfiguration = &specgen.LogConfig{}
+ }
+ s.LogConfiguration.Driver = libpod.KubernetesLogging
+ if ld := c.LogDriver; len(ld) > 0 {
+ s.LogConfiguration.Driver = ld
+ }
+ if s.ResourceLimits.Pids == nil {
+ s.ResourceLimits.Pids = &specs.LinuxPids{}
+ }
+ s.ResourceLimits.Pids.Limit = c.PIDsLimit
+ if c.CGroups == "disabled" && c.PIDsLimit > 0 {
+ s.ResourceLimits.Pids.Limit = -1
+ }
+ // TODO WTF
+ //cgroup := &cc.CgroupConfig{
+ // Cgroups: c.String("cgroups"),
+ // Cgroupns: c.String("cgroupns"),
+ // CgroupParent: c.String("cgroup-parent"),
+ // CgroupMode: cgroupMode,
+ //}
+ //
+ //userns := &cc.UserConfig{
+ // GroupAdd: c.StringSlice("group-add"),
+ // IDMappings: idmappings,
+ // UsernsMode: usernsMode,
+ // User: user,
+ //}
+ //
+ //uts := &cc.UtsConfig{
+ // UtsMode: utsMode,
+ // NoHosts: c.Bool("no-hosts"),
+ // HostAdd: c.StringSlice("add-host"),
+ // Hostname: c.String("hostname"),
+ //}
+
+ sysctl := map[string]string{}
+ if ctl := c.Sysctl; len(ctl) > 0 {
+ sysctl, err = util.ValidateSysctls(ctl)
+ if err != nil {
+ return err
+ }
+ }
+ s.Sysctl = sysctl
+
+ s.CapAdd = c.CapAdd
+ s.CapDrop = c.CapDrop
+ s.Privileged = c.Privileged
+ s.ReadOnlyFilesystem = c.ReadOnly
+
+ // TODO
+ // ouitside of specgen and oci though
+ // defaults to true, check spec/storage
+ //s.readon = c.ReadOnlyTmpFS
+ // TODO convert to map?
+ // check if key=value and convert
+ sysmap := make(map[string]string)
+ for _, ctl := range c.Sysctl {
+ splitCtl := strings.SplitN(ctl, "=", 2)
+ if len(splitCtl) < 2 {
+ return errors.Errorf("invalid sysctl value %q", ctl)
+ }
+ sysmap[splitCtl[0]] = splitCtl[1]
+ }
+ s.Sysctl = sysmap
+
+ for _, opt := range c.SecurityOpt {
+ if opt == "no-new-privileges" {
+ s.ContainerSecurityConfig.NoNewPrivileges = true
+ } else {
+ con := strings.SplitN(opt, "=", 2)
+ if len(con) != 2 {
+ return fmt.Errorf("invalid --security-opt 1: %q", opt)
+ }
+
+ switch con[0] {
+ case "label":
+ // TODO selinux opts and label opts are the same thing
+ s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
+ case "apparmor":
+ s.ContainerSecurityConfig.ApparmorProfile = con[1]
+ case "seccomp":
+ s.SeccompProfilePath = con[1]
+ default:
+ return fmt.Errorf("invalid --security-opt 2: %q", opt)
+ }
+ }
+ }
+
+ // TODO any idea why this was done
+ // storage.go from spec/
+ // grab it
+ //volumes := rtc.Containers.Volumes
+ // TODO conflict on populate?
+ //if v := c.Volume; len(v)> 0 {
+ // s.Volumes = append(volumes, c.StringSlice("volume")...)
+ //}
+ //s.volu
+
+ //s.Mounts = c.Mount
+ s.VolumesFrom = c.VolumesFrom
+
+ // TODO any idea why this was done
+ //devices := rtc.Containers.Devices
+ // TODO conflict on populate?
+ //
+ //if c.Changed("device") {
+ // devices = append(devices, c.StringSlice("device")...)
+ //}
+
+ // TODO things i cannot find in spec
+ // we dont think these are in the spec
+ // init - initbinary
+ // initpath
+ s.Stdin = c.Interactive
+ // quiet
+ //DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
+
+ if bps := c.DeviceReadBPs; len(bps) > 0 {
+ if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil {
+ return err
+ }
+ }
+
+ if bps := c.DeviceWriteBPs; len(bps) > 0 {
+ if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil {
+ return err
+ }
+ }
+
+ if iops := c.DeviceReadIOPs; len(iops) > 0 {
+ if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil {
+ return err
+ }
+ }
+
+ if iops := c.DeviceWriteIOPs; len(iops) > 0 {
+ if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil {
+ return err
+ }
+ }
+
+ s.ResourceLimits.Memory.DisableOOMKiller = &c.OOMKillDisable
+
+ // Rlimits/Ulimits
+ for _, u := range c.Ulimit {
+ if u == "host" {
+ s.Rlimits = nil
+ break
+ }
+ ul, err := units.ParseUlimit(u)
+ if err != nil {
+ return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
+ }
+ rl := specs.POSIXRlimit{
+ Type: ul.Name,
+ Hard: uint64(ul.Hard),
+ Soft: uint64(ul.Soft),
+ }
+ s.Rlimits = append(s.Rlimits, rl)
+ }
+
+ //Tmpfs: c.StringArray("tmpfs"),
+
+ // TODO how to handle this?
+ //Syslog: c.Bool("syslog"),
+
+ logOpts := make(map[string]string)
+ for _, o := range c.LogOptions {
+ split := strings.SplitN(o, "=", 2)
+ if len(split) < 2 {
+ return errors.Errorf("invalid log option %q", o)
+ }
+ logOpts[split[0]] = split[1]
+ }
+ s.LogConfiguration.Options = logOpts
+ s.Name = c.Name
+
+ if err := parseWeightDevices(c.BlkIOWeightDevice, s); err != nil {
+ return err
+ }
+
+ if s.ResourceLimits.CPU == nil {
+ s.ResourceLimits.CPU = &specs.LinuxCPU{}
+ }
+ s.ResourceLimits.CPU.Shares = &c.CPUShares
+ s.ResourceLimits.CPU.Period = &c.CPUPeriod
+
+ // TODO research these
+ //s.ResourceLimits.CPU.Cpus = c.CPUS
+ //s.ResourceLimits.CPU.Cpus = c.CPUSetCPUs
+
+ //s.ResourceLimits.CPU. = c.CPUSetCPUs
+ s.ResourceLimits.CPU.Mems = c.CPUSetMems
+ s.ResourceLimits.CPU.Quota = &c.CPUQuota
+ s.ResourceLimits.CPU.RealtimePeriod = &c.CPURTPeriod
+ s.ResourceLimits.CPU.RealtimeRuntime = &c.CPURTRuntime
+ s.OOMScoreAdj = &c.OOMScoreAdj
+ s.RestartPolicy = c.Restart
+ s.Remove = c.Rm
+ s.StopTimeout = &c.StopTimeout
+
+ // TODO where should we do this?
+ //func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) {
+ return nil
+}
+
+func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) {
+ // Every healthcheck requires a command
+ if len(inCmd) == 0 {
+ return nil, errors.New("Must define a healthcheck command for all healthchecks")
+ }
+
+ // first try to parse option value as JSON array of strings...
+ cmd := []string{}
+ err := json.Unmarshal([]byte(inCmd), &cmd)
+ if err != nil {
+ // ...otherwise pass it to "/bin/sh -c" inside the container
+ cmd = []string{"CMD-SHELL", inCmd}
+ }
+ hc := manifest.Schema2HealthConfig{
+ Test: cmd,
+ }
+
+ if interval == "disable" {
+ interval = "0"
+ }
+ intervalDuration, err := time.ParseDuration(interval)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-interval %s ", interval)
+ }
+
+ hc.Interval = intervalDuration
+
+ if retries < 1 {
+ return nil, errors.New("healthcheck-retries must be greater than 0.")
+ }
+ hc.Retries = int(retries)
+ timeoutDuration, err := time.ParseDuration(timeout)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-timeout %s", timeout)
+ }
+ if timeoutDuration < time.Duration(1) {
+ return nil, errors.New("healthcheck-timeout must be at least 1 second")
+ }
+ hc.Timeout = timeoutDuration
+
+ startPeriodDuration, err := time.ParseDuration(startPeriod)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid healthcheck-start-period %s", startPeriod)
+ }
+ if startPeriodDuration < time.Duration(0) {
+ return nil, errors.New("healthcheck-start-period must be 0 seconds or greater")
+ }
+ hc.StartPeriod = startPeriodDuration
+
+ return &hc, nil
+}
+
+func parseWeightDevices(weightDevs []string, s *specgen.SpecGenerator) error {
+ for _, val := range weightDevs {
+ split := strings.SplitN(val, ":", 2)
+ if len(split) != 2 {
+ return fmt.Errorf("bad format: %s", val)
+ }
+ if !strings.HasPrefix(split[0], "/dev/") {
+ return fmt.Errorf("bad format for device path: %s", val)
+ }
+ weight, err := strconv.ParseUint(split[1], 10, 0)
+ if err != nil {
+ return fmt.Errorf("invalid weight for device: %s", val)
+ }
+ if weight > 0 && (weight < 10 || weight > 1000) {
+ return fmt.Errorf("invalid weight for device: %s", val)
+ }
+ w := uint16(weight)
+ s.WeightDevice[split[0]] = specs.LinuxWeightDevice{
+ Weight: &w,
+ LeafWeight: nil,
+ }
+ }
+ return nil
+}
+
+func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
+ td := make(map[string]specs.LinuxThrottleDevice)
+ for _, val := range bpsDevices {
+ split := strings.SplitN(val, ":", 2)
+ if len(split) != 2 {
+ return nil, fmt.Errorf("bad format: %s", val)
+ }
+ if !strings.HasPrefix(split[0], "/dev/") {
+ return nil, fmt.Errorf("bad format for device path: %s", val)
+ }
+ rate, err := units.RAMInBytes(split[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
+ }
+ if rate < 0 {
+ return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
+ }
+ td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)}
+ }
+ return td, nil
+}
+
+func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) {
+ td := make(map[string]specs.LinuxThrottleDevice)
+ for _, val := range iopsDevices {
+ split := strings.SplitN(val, ":", 2)
+ if len(split) != 2 {
+ return nil, fmt.Errorf("bad format: %s", val)
+ }
+ if !strings.HasPrefix(split[0], "/dev/") {
+ return nil, fmt.Errorf("bad format for device path: %s", val)
+ }
+ rate, err := strconv.ParseUint(split[1], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
+ }
+ td[split[0]] = specs.LinuxThrottleDevice{Rate: rate}
+ }
+ return td, nil
+}
diff --git a/cmd/podmanV2/common/types.go b/cmd/podmanV2/common/types.go
new file mode 100644
index 000000000..2427ae975
--- /dev/null
+++ b/cmd/podmanV2/common/types.go
@@ -0,0 +1,3 @@
+package common
+
+var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
diff --git a/cmd/podmanV2/common/util.go b/cmd/podmanV2/common/util.go
new file mode 100644
index 000000000..47bbe12fa
--- /dev/null
+++ b/cmd/podmanV2/common/util.go
@@ -0,0 +1,43 @@
+package common
+
+import (
+ "strconv"
+
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ "github.com/pkg/errors"
+)
+
+// 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()
+ } else {
+ hostPort, err = strconv.Atoi(i.HostPort)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert host port to integer")
+ }
+ }
+
+ pm.HostPort = int32(hostPort)
+ pm.Protocol = containerPb.Proto()
+ portBindings = append(portBindings, pm)
+ }
+ }
+ return portBindings, nil
+}
diff --git a/cmd/podmanV2/containers/attach.go b/cmd/podmanV2/containers/attach.go
new file mode 100644
index 000000000..d62dcff86
--- /dev/null
+++ b/cmd/podmanV2/containers/attach.go
@@ -0,0 +1,60 @@
+package containers
+
+import (
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively."
+ attachCommand = &cobra.Command{
+ Use: "attach [flags] CONTAINER",
+ Short: "Attach to a running container",
+ Long: attachDescription,
+ RunE: attach,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) {
+ return errors.Errorf("attach requires the name or id of one running container or the latest flag")
+ }
+ return nil
+ },
+ PreRunE: preRunE,
+ Example: `podman attach ctrID
+ podman attach 1234
+ podman attach --no-stdin foobar`,
+ }
+)
+
+var (
+ attachOpts entities.AttachOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: attachCommand,
+ })
+ flags := attachCommand.Flags()
+ flags.StringVar(&attachOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
+ flags.BoolVar(&attachOpts.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false")
+ flags.BoolVar(&attachOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
+ flags.BoolVarP(&attachOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func attach(cmd *cobra.Command, args []string) error {
+ attachOpts.Stdin = os.Stdin
+ if attachOpts.NoStdin {
+ attachOpts.Stdin = nil
+ }
+ attachOpts.Stdout = os.Stdout
+ attachOpts.Stderr = os.Stderr
+ return registry.ContainerEngine().ContainerAttach(registry.GetContext(), args[0], attachOpts)
+}
diff --git a/cmd/podmanV2/containers/checkpoint.go b/cmd/podmanV2/containers/checkpoint.go
new file mode 100644
index 000000000..7c3e551bc
--- /dev/null
+++ b/cmd/podmanV2/containers/checkpoint.go
@@ -0,0 +1,79 @@
+package containers
+
+import (
+ "context"
+ "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/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ checkpointDescription = `
+ podman container checkpoint
+
+ Checkpoints one or more running containers. The container name or ID can be used.
+`
+ checkpointCommand = &cobra.Command{
+ Use: "checkpoint [flags] CONTAINER [CONTAINER...]",
+ Short: "Checkpoints one or more containers",
+ Long: checkpointDescription,
+ RunE: checkpoint,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman container checkpoint --keep ctrID
+ podman container checkpoint --all
+ podman container checkpoint --leave-running --latest`,
+ }
+)
+
+var (
+ checkpointOptions entities.CheckpointOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: checkpointCommand,
+ Parent: containerCmd,
+ })
+ flags := checkpointCommand.Flags()
+ flags.BoolVarP(&checkpointOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
+ flags.BoolVarP(&checkpointOptions.LeaveRuninng, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk")
+ flags.BoolVar(&checkpointOptions.TCPEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
+ flags.BoolVarP(&checkpointOptions.All, "all", "a", false, "Checkpoint all running containers")
+ flags.BoolVarP(&checkpointOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.StringVarP(&checkpointOptions.Export, "export", "e", "", "Export the checkpoint image to a tar.gz")
+ flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func checkpoint(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if rootless.IsRootless() {
+ return errors.New("checkpointing a container requires root")
+ }
+ if checkpointOptions.Export == "" && checkpointOptions.IgnoreRootFS {
+ return errors.Errorf("--ignore-rootfs can only be used with --export")
+ }
+ responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/containers/commit.go b/cmd/podmanV2/containers/commit.go
new file mode 100644
index 000000000..28eb42f33
--- /dev/null
+++ b/cmd/podmanV2/containers/commit.go
@@ -0,0 +1,79 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ commitDescription = `Create an image from a container's changes. Optionally tag the image created, set the author with the --author flag, set the commit message with the --message flag, and make changes to the instructions with the --change flag.`
+
+ commitCommand = &cobra.Command{
+ Use: "commit [flags] CONTAINER [IMAGE]",
+ Short: "Create new image based on the changed container",
+ Long: commitDescription,
+ RunE: commit,
+ PreRunE: preRunE,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman commit -q --message "committing container to image" reverent_golick image-committed
+ podman commit -q --author "firstName lastName" reverent_golick image-committed
+ podman commit -q --pause=false containerID image-committed
+ podman commit containerID`,
+ }
+
+ // ChangeCmds is the list of valid Changes commands to passed to the Commit call
+ ChangeCmds = []string{"CMD", "ENTRYPOINT", "ENV", "EXPOSE", "LABEL", "ONBUILD", "STOPSIGNAL", "USER", "VOLUME", "WORKDIR"}
+)
+
+var (
+ commitOptions = entities.CommitOptions{
+ ImageName: "",
+ }
+ iidFile string
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: commitCommand,
+ })
+ flags := commitCommand.Flags()
+ flags.StringArrayVarP(&commitOptions.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): "+strings.Join(ChangeCmds, " | "))
+ flags.StringVarP(&commitOptions.Format, "format", "f", "oci", "`Format` of the image manifest and metadata")
+ flags.StringVarP(&iidFile, "iidfile", "", "", "`file` to write the image ID to")
+ flags.StringVarP(&commitOptions.Message, "message", "m", "", "Set commit message for imported image")
+ flags.StringVarP(&commitOptions.Author, "author", "a", "", "Set the author for the image committed")
+ flags.BoolVarP(&commitOptions.Pause, "pause", "p", false, "Pause container during commit")
+ flags.BoolVarP(&commitOptions.Quiet, "quiet", "q", false, "Suppress output")
+ flags.BoolVar(&commitOptions.IncludeVolumes, "include-volumes", false, "Include container volumes as image volumes")
+
+}
+func commit(cmd *cobra.Command, args []string) error {
+ container := args[0]
+ if len(args) > 1 {
+ commitOptions.ImageName = args[1]
+ }
+ if !commitOptions.Quiet {
+ commitOptions.Writer = os.Stderr
+ }
+
+ response, err := registry.ContainerEngine().ContainerCommit(context.Background(), container, commitOptions)
+ if err != nil {
+ return err
+ }
+ if len(iidFile) > 0 {
+ if err = ioutil.WriteFile(iidFile, []byte(response.Id), 0644); err != nil {
+ return errors.Wrapf(err, "failed to write image ID to file %q", iidFile)
+ }
+ }
+ fmt.Println(response.Id)
+ return nil
+}
diff --git a/cmd/podmanV2/containers/container.go b/cmd/podmanV2/containers/container.go
index 6b44f2a3e..b922eea05 100644
--- a/cmd/podmanV2/containers/container.go
+++ b/cmd/podmanV2/containers/container.go
@@ -1,8 +1,12 @@
package containers
import (
+ "os"
+
+ "github.com/containers/common/pkg/config"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -16,6 +20,8 @@ var (
PersistentPreRunE: preRunE,
RunE: registry.SubCommandExists,
}
+
+ defaultContainerConfig = getDefaultContainerConfig()
)
func init() {
@@ -31,3 +37,12 @@ func preRunE(cmd *cobra.Command, args []string) error {
_, err := registry.NewContainerEngine(cmd, args)
return err
}
+
+func getDefaultContainerConfig() *config.Config {
+ defaultContainerConfig, err := config.Default()
+ if err != nil {
+ logrus.Error(err)
+ os.Exit(1)
+ }
+ return defaultContainerConfig
+}
diff --git a/cmd/podmanV2/containers/create.go b/cmd/podmanV2/containers/create.go
new file mode 100644
index 000000000..fd5300966
--- /dev/null
+++ b/cmd/podmanV2/containers/create.go
@@ -0,0 +1,102 @@
+package containers
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+)
+
+var (
+ createDescription = `Creates a new container from the given image or storage and prepares it for running the specified command.
+
+ The container ID is then printed to stdout. You can then start it at any time with the podman start <container_id> command. The container will be created with the initial state 'created'.`
+ createCommand = &cobra.Command{
+ Use: "create [flags] IMAGE [COMMAND [ARG...]]",
+ Short: "Create but do not start a container",
+ Long: createDescription,
+ RunE: create,
+ PersistentPreRunE: preRunE,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman create alpine ls
+ podman create --annotation HELLO=WORLD alpine ls
+ podman create -t -i --name myctr alpine ls`,
+ }
+)
+
+var (
+ cliVals common.ContainerCLIOpts
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: createCommand,
+ })
+ //common.GetCreateFlags(createCommand)
+ flags := createCommand.Flags()
+ flags.AddFlagSet(common.GetCreateFlags(&cliVals))
+ flags.AddFlagSet(common.GetNetFlags())
+ flags.SetNormalizeFunc(common.AliasFlags)
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ rawImageInput string
+ )
+ cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
+ if err != nil {
+ return err
+ }
+ if rfs := cliVals.RootFS; !rfs {
+ rawImageInput = args[0]
+ }
+
+ if err := createInit(cmd); err != nil {
+ return err
+ }
+ //TODO rootfs still
+ s := specgen.NewSpecGenerator(rawImageInput)
+ if err := common.FillOutSpecGen(s, &cliVals, args); err != nil {
+ return err
+ }
+
+ report, err := registry.ContainerEngine().ContainerCreate(registry.GetContext(), s)
+ if err != nil {
+ return err
+ }
+ fmt.Println(report.Id)
+ return nil
+}
+
+func createInit(c *cobra.Command) error {
+ if c.Flag("privileged").Changed && c.Flag("security-opt").Changed {
+ logrus.Warn("setting security options with --privileged has no effect")
+ }
+
+ if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && (cliVals.Net.Network.NSMode == specgen.NoNetwork || cliVals.Net.Network.IsContainer()) {
+ return errors.Errorf("conflicting options: dns and the network mode.")
+ }
+
+ if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed {
+ return errors.Errorf("--cpu-period and --cpus cannot be set together")
+ }
+ if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed {
+ return errors.Errorf("--cpu-quota and --cpus cannot be set together")
+ }
+
+ if c.Flag("no-hosts").Changed && c.Flag("add-host").Changed {
+ return errors.Errorf("--no-hosts and --add-host cannot be set together")
+ }
+
+ // Docker-compatibility: the "-h" flag for run/create is reserved for
+ // the hostname (see https://github.com/containers/libpod/issues/1367).
+
+ return nil
+}
diff --git a/cmd/podmanV2/containers/exec.go b/cmd/podmanV2/containers/exec.go
new file mode 100644
index 000000000..4bff57dbb
--- /dev/null
+++ b/cmd/podmanV2/containers/exec.go
@@ -0,0 +1,93 @@
+package containers
+
+import (
+ "bufio"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ envLib "github.com/containers/libpod/pkg/env"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ execDescription = `Execute the specified command inside a running container.
+`
+ execCommand = &cobra.Command{
+ Use: "exec [flags] CONTAINER [COMMAND [ARG...]]",
+ Short: "Run a process in a running container",
+ Long: execDescription,
+ PreRunE: preRunE,
+ RunE: exec,
+ Example: `podman exec -it ctrID ls
+ podman exec -it -w /tmp myCtr pwd
+ podman exec --user root ctrID ls`,
+ }
+)
+
+var (
+ envInput, envFile []string
+ execOpts entities.ExecOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: execCommand,
+ })
+ flags := execCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.StringVar(&execOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
+ flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables")
+ flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables")
+ flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
+ flags.BoolVarP(&execOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&execOpts.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false")
+ flags.BoolVarP(&execOpts.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false")
+ flags.StringVarP(&execOpts.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command")
+ flags.UintVar(&execOpts.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container")
+ flags.StringVarP(&execOpts.WorkDir, "workdir", "w", "", "Working directory inside the container")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ _ = flags.MarkHidden("preserve-fds")
+ }
+
+}
+func exec(cmd *cobra.Command, args []string) error {
+ var nameOrId string
+ execOpts.Cmd = args
+ if !execOpts.Latest {
+ execOpts.Cmd = args[1:]
+ nameOrId = args[0]
+ }
+ // Validate given environment variables
+ execOpts.Envs = make(map[string]string)
+ for _, f := range envFile {
+ fileEnv, err := envLib.ParseFile(f)
+ if err != nil {
+ return err
+ }
+ execOpts.Envs = envLib.Join(execOpts.Envs, fileEnv)
+ }
+
+ cliEnv, err := envLib.ParseSlice(envInput)
+ if err != nil {
+ return errors.Wrap(err, "error parsing environment variables")
+ }
+
+ execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv)
+ execOpts.Streams.OutputStream = os.Stdout
+ execOpts.Streams.ErrorStream = os.Stderr
+ if execOpts.Interactive {
+ execOpts.Streams.InputStream = bufio.NewReader(os.Stdin)
+ execOpts.Streams.AttachInput = true
+ }
+ execOpts.Streams.AttachOutput = true
+ execOpts.Streams.AttachError = true
+
+ exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrId, execOpts)
+ registry.SetExitCode(exitCode)
+ return err
+}
diff --git a/cmd/podmanV2/containers/export.go b/cmd/podmanV2/containers/export.go
new file mode 100644
index 000000000..b93b60878
--- /dev/null
+++ b/cmd/podmanV2/containers/export.go
@@ -0,0 +1,57 @@
+package containers
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+var (
+ exportDescription = "Exports container's filesystem contents as a tar archive" +
+ " and saves it on the local machine."
+
+ exportCommand = &cobra.Command{
+ Use: "export [flags] CONTAINER",
+ Short: "Export container's filesystem contents as a tar archive",
+ Long: exportDescription,
+ PersistentPreRunE: preRunE,
+ RunE: export,
+ Args: cobra.ExactArgs(1),
+ Example: `podman export ctrID > myCtr.tar
+ podman export --output="myCtr.tar" ctrID`,
+ }
+)
+
+var (
+ exportOpts entities.ContainerExportOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: exportCommand,
+ })
+ exportCommand.SetHelpTemplate(registry.HelpTemplate())
+ exportCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := exportCommand.Flags()
+ flags.StringVarP(&exportOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
+}
+
+func export(cmd *cobra.Command, args []string) error {
+ if len(exportOpts.Output) == 0 {
+ file := os.Stdout
+ if terminal.IsTerminal(int(file.Fd())) {
+ return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
+ }
+ exportOpts.Output = "/dev/stdout"
+ } else if err := parse.ValidateFileName(exportOpts.Output); err != nil {
+ return err
+ }
+ return registry.ContainerEngine().ContainerExport(context.Background(), args[0], exportOpts)
+}
diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go
index 635be4789..3147426cb 100644
--- a/cmd/podmanV2/containers/inspect.go
+++ b/cmd/podmanV2/containers/inspect.go
@@ -1,8 +1,17 @@
package containers
import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+ "text/template"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
"github.com/containers/libpod/cmd/podmanV2/registry"
+
"github.com/containers/libpod/pkg/domain/entities"
+ json "github.com/json-iterator/go"
"github.com/spf13/cobra"
)
@@ -12,11 +21,12 @@ var (
Use: "inspect [flags] CONTAINER",
Short: "Display the configuration of a container",
Long: `Displays the low-level information on a container identified by name or ID.`,
- PreRunE: inspectPreRunE,
+ PreRunE: preRunE,
RunE: inspect,
Example: `podman container inspect myCtr
podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
}
+ inspectOpts *entities.InspectOptions
)
func init() {
@@ -25,18 +35,45 @@ func init() {
Command: inspectCmd,
Parent: containerCmd,
})
-}
+ inspectOpts = common.AddInspectFlagSet(inspectCmd)
+ flags := inspectCmd.Flags()
-func inspectPreRunE(cmd *cobra.Command, args []string) (err error) {
- err = preRunE(cmd, args)
- if err != nil {
- return
+ if !registry.IsRemote() {
+ flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
}
- _, err = registry.NewImageEngine(cmd, args)
- return err
}
func inspect(cmd *cobra.Command, args []string) error {
+ responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, *inspectOpts)
+ if err != nil {
+ return err
+ }
+ if inspectOpts.Format == "" {
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+ format := inspectOpts.Format
+ if !strings.HasSuffix(format, "\n") {
+ format += "\n"
+ }
+ tmpl, err := template.New("inspect").Parse(format)
+ if err != nil {
+ return err
+ }
+ for _, i := range responses {
+ if err := tmpl.Execute(os.Stdout, i); err != nil {
+ return err
+ }
+ }
return nil
}
+
+func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
+ inspectOpts = options
+ return inspect(cmd, args)
+}
diff --git a/cmd/podmanV2/containers/restart.go b/cmd/podmanV2/containers/restart.go
index 053891f79..5f1d3fe51 100644
--- a/cmd/podmanV2/containers/restart.go
+++ b/cmd/podmanV2/containers/restart.go
@@ -14,9 +14,10 @@ import (
)
var (
- restartDescription = `Restarts one or more running containers. The container ID or name can be used.
+ restartDescription = fmt.Sprintf(`Restarts one or more running containers. The container ID or name can be used.
+
+ A timeout before forcibly stopping can be set, but defaults to %d seconds.`, defaultContainerConfig.Engine.StopTimeout)
- A timeout before forcibly stopping can be set, but defaults to 10 seconds.`
restartCommand = &cobra.Command{
Use: "restart [flags] CONTAINER [CONTAINER...]",
Short: "Restart one or more containers",
@@ -46,11 +47,11 @@ func init() {
flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all non-running containers")
flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&restartOptions.Running, "running", false, "Restart only running containers when --all is used")
- flags.UintVarP(&restartTimeout, "timeout", "t", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
- flags.UintVar(&restartTimeout, "time", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
+ flags.UintVarP(&restartTimeout, "time", "t", defaultContainerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container")
if registry.IsRemote() {
_ = flags.MarkHidden("latest")
}
+ flags.SetNormalizeFunc(utils.AliasFlags)
}
func restart(cmd *cobra.Command, args []string) error {
@@ -61,7 +62,7 @@ func restart(cmd *cobra.Command, args []string) error {
return errors.Wrapf(define.ErrInvalidArg, "you must provide at least one container name or ID")
}
- if cmd.Flag("timeout").Changed || cmd.Flag("time").Changed {
+ if cmd.Flag("time").Changed {
restartOptions.Timeout = &restartTimeout
}
responses, err := registry.ContainerEngine().ContainerRestart(context.Background(), args, restartOptions)
diff --git a/cmd/podmanV2/containers/restore.go b/cmd/podmanV2/containers/restore.go
new file mode 100644
index 000000000..6cab6ab50
--- /dev/null
+++ b/cmd/podmanV2/containers/restore.go
@@ -0,0 +1,104 @@
+package containers
+
+import (
+ "context"
+ "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/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ restoreDescription = `
+ podman container restore
+
+ Restores a container from a checkpoint. The container name or ID can be used.
+`
+ restoreCommand = &cobra.Command{
+ Use: "restore [flags] CONTAINER [CONTAINER...]",
+ Short: "Restores one or more containers from a checkpoint",
+ Long: restoreDescription,
+ RunE: restore,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, true, false)
+ },
+ Example: `podman container restore ctrID
+ podman container restore --latest
+ podman container restore --all`,
+ }
+)
+
+var (
+ restoreOptions entities.RestoreOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: restoreCommand,
+ Parent: containerCmd,
+ })
+ flags := restoreCommand.Flags()
+ flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers")
+ flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
+ flags.BoolVarP(&restoreOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&restoreOptions.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections")
+ flags.StringVarP(&restoreOptions.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)")
+ flags.StringVarP(&restoreOptions.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
+ flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")
+ flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
+ flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func restore(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if rootless.IsRootless() {
+ return errors.New("restoring a container requires root")
+ }
+ if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS {
+ return errors.Errorf("--ignore-rootfs can only be used with --import")
+ }
+ if restoreOptions.Import == "" && restoreOptions.Name != "" {
+ return errors.Errorf("--name can only be used with --import")
+ }
+ if restoreOptions.Name != "" && restoreOptions.TCPEstablished {
+ return errors.Errorf("--tcp-established cannot be used with --name")
+ }
+
+ argLen := len(args)
+ if restoreOptions.Import != "" {
+ if restoreOptions.All || restoreOptions.Latest {
+ return errors.Errorf("Cannot use --import with --all or --latest")
+ }
+ if argLen > 0 {
+ return errors.Errorf("Cannot use --import with positional arguments")
+ }
+ }
+ if (restoreOptions.All || restoreOptions.Latest) && argLen > 0 {
+ return errors.Errorf("no arguments are needed with --all or --latest")
+ }
+ if argLen < 1 && !restoreOptions.All && !restoreOptions.Latest && restoreOptions.Import == "" {
+ return errors.Errorf("you must provide at least one name or id")
+ }
+ responses, err := registry.ContainerEngine().ContainerRestore(context.Background(), args, restoreOptions)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+
+}
diff --git a/cmd/podmanV2/containers/start.go b/cmd/podmanV2/containers/start.go
new file mode 100644
index 000000000..0ae2f6d50
--- /dev/null
+++ b/cmd/podmanV2/containers/start.go
@@ -0,0 +1,87 @@
+package containers
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ startDescription = `Starts one or more containers. The container name or ID can be used.`
+ startCommand = &cobra.Command{
+ Use: "start [flags] CONTAINER [CONTAINER...]",
+ Short: "Start one or more containers",
+ Long: startDescription,
+ RunE: start,
+ PreRunE: preRunE,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman start --latest
+ podman start 860a4b231279 5421ab43b45
+ podman start --interactive --attach imageID`,
+ }
+)
+
+var (
+ startOptions entities.ContainerStartOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: startCommand,
+ })
+ flags := startCommand.Flags()
+ flags.BoolVarP(&startOptions.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR")
+ flags.StringVar(&startOptions.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
+ flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
+ flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ _ = flags.MarkHidden("sig-proxy")
+ }
+
+}
+
+func start(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if len(args) > 1 && startOptions.Attach {
+ return errors.Errorf("you cannot start and attach multiple containers at once")
+ }
+
+ sigProxy := startOptions.SigProxy || startOptions.Attach
+ if cmd.Flag("sig-proxy").Changed {
+ sigProxy = startOptions.SigProxy
+ }
+
+ if sigProxy && !startOptions.Attach {
+ return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ }
+ if startOptions.Attach {
+ startOptions.Stdin = os.Stdin
+ startOptions.Stderr = os.Stderr
+ startOptions.Stdout = os.Stdout
+ }
+
+ responses, err := registry.ContainerEngine().ContainerStart(registry.GetContext(), args, startOptions)
+ if err != nil {
+ return err
+ }
+
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ // TODO need to understand an implement exitcodes
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/containers/stop.go b/cmd/podmanV2/containers/stop.go
index 58d47fd52..d6f31352f 100644
--- a/cmd/podmanV2/containers/stop.go
+++ b/cmd/podmanV2/containers/stop.go
@@ -7,16 +7,14 @@ import (
"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/libpod/define"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
- stopDescription = `Stops one or more running containers. The container name or ID can be used.
+ stopDescription = fmt.Sprintf(`Stops one or more running containers. The container name or ID can be used.
- A timeout to forcibly stop the container can also be set but defaults to 10 seconds otherwise.`
+ A timeout to forcibly stop the container can also be set but defaults to %d seconds otherwise.`, defaultContainerConfig.Engine.StopTimeout)
stopCommand = &cobra.Command{
Use: "stop [flags] CONTAINER [CONTAINER...]",
Short: "Stop one or more containers",
@@ -28,7 +26,7 @@ var (
},
Example: `podman stop ctrID
podman stop --latest
- podman stop --timeout 2 mywebserver 6e534f14da9d`,
+ podman stop --time 2 mywebserver 6e534f14da9d`,
}
)
@@ -47,24 +45,21 @@ func init() {
flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing")
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.UintVar(&stopTimeout, "time", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
- flags.UintVarP(&stopTimeout, "timeout", "t", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
+ flags.UintVarP(&stopTimeout, "time", "t", defaultContainerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container")
if registry.EngineOptions.EngineMode == entities.ABIMode {
_ = flags.MarkHidden("latest")
_ = flags.MarkHidden("cidfile")
_ = flags.MarkHidden("ignore")
}
+ flags.SetNormalizeFunc(utils.AliasFlags)
}
func stop(cmd *cobra.Command, args []string) error {
var (
errs utils.OutputErrors
)
- if cmd.Flag("timeout").Changed && cmd.Flag("time").Changed {
- return errors.New("the --timeout and --time flags are mutually exclusive")
- }
- stopOptions.Timeout = define.CtrRemoveTimeout
- if cmd.Flag("timeout").Changed || cmd.Flag("time").Changed {
+ stopOptions.Timeout = defaultContainerConfig.Engine.StopTimeout
+ if cmd.Flag("time").Changed {
stopOptions.Timeout = stopTimeout
}
diff --git a/cmd/podmanV2/containers/top.go b/cmd/podmanV2/containers/top.go
new file mode 100644
index 000000000..a86c12e2a
--- /dev/null
+++ b/cmd/podmanV2/containers/top.go
@@ -0,0 +1,91 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/psgo"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ topDescription = fmt.Sprintf(`Similar to system "top" command.
+
+ Specify format descriptors to alter the output.
+
+ Running "podman top -l pid pcpu seccomp" will print the process ID, the CPU percentage and the seccomp mode of each process of the latest container.
+ Format Descriptors:
+ %s`, strings.Join(psgo.ListDescriptors(), ","))
+
+ topOptions = entities.TopOptions{}
+
+ topCommand = &cobra.Command{
+ Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS]",
+ Short: "Display the running processes of a container",
+ Long: topDescription,
+ PersistentPreRunE: preRunE,
+ RunE: top,
+ Args: cobra.ArbitraryArgs,
+ Example: `podman top ctrID
+podman top --latest
+podman top ctrID pid seccomp args %C
+podman top ctrID -eo user,pid,comm`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: topCommand,
+ })
+
+ topCommand.SetHelpTemplate(registry.HelpTemplate())
+ topCommand.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := topCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "")
+ flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+
+ _ = flags.MarkHidden("list-descriptors") // meant only for bash completion
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func top(cmd *cobra.Command, args []string) error {
+ if topOptions.ListDescriptors {
+ fmt.Println(strings.Join(psgo.ListDescriptors(), "\n"))
+ return nil
+ }
+
+ if len(args) < 1 && !topOptions.Latest {
+ return errors.Errorf("you must provide the name or id of a running container")
+ }
+
+ if topOptions.Latest {
+ topOptions.Descriptors = args
+ } else {
+ topOptions.NameOrID = args[0]
+ topOptions.Descriptors = args[1:]
+ }
+
+ topResponse, err := registry.ContainerEngine().ContainerTop(context.Background(), topOptions)
+ if err != nil {
+ return err
+ }
+
+ w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
+ for _, proc := range topResponse.Value {
+ if _, err := fmt.Fprintln(w, proc); err != nil {
+ return err
+ }
+ }
+ return w.Flush()
+}
diff --git a/cmd/podmanV2/healthcheck/healthcheck.go b/cmd/podmanV2/healthcheck/healthcheck.go
new file mode 100644
index 000000000..2af398ff0
--- /dev/null
+++ b/cmd/podmanV2/healthcheck/healthcheck.go
@@ -0,0 +1,33 @@
+package healthcheck
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: healthcheck
+ healthCmd = &cobra.Command{
+ Use: "healthcheck",
+ Short: "Manage Healthcheck",
+ Long: "Manage Healthcheck",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: healthCmd,
+ })
+ healthCmd.SetHelpTemplate(registry.HelpTemplate())
+ healthCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/healthcheck/run.go b/cmd/podmanV2/healthcheck/run.go
new file mode 100644
index 000000000..bb2962eaf
--- /dev/null
+++ b/cmd/podmanV2/healthcheck/run.go
@@ -0,0 +1,42 @@
+package healthcheck
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ healthcheckRunDescription = "run the health check of a container"
+ healthcheckrunCommand = &cobra.Command{
+ Use: "run [flags] CONTAINER",
+ Short: "run the health check of a container",
+ Long: healthcheckRunDescription,
+ Example: `podman healthcheck run mywebapp`,
+ RunE: run,
+ Args: cobra.ExactArgs(1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: healthcheckrunCommand,
+ Parent: healthCmd,
+ })
+}
+
+func run(cmd *cobra.Command, args []string) error {
+ response, err := registry.ContainerEngine().HealthCheckRun(context.Background(), args[0], entities.HealthCheckOptions{})
+ if err != nil {
+ return err
+ }
+ if response.Status == "unhealthy" {
+ registry.SetExitCode(1)
+ }
+ fmt.Println(response.Status)
+ return err
+}
diff --git a/cmd/podmanV2/images/exists.go b/cmd/podmanV2/images/exists.go
new file mode 100644
index 000000000..d35d6825e
--- /dev/null
+++ b/cmd/podmanV2/images/exists.go
@@ -0,0 +1,40 @@
+package images
+
+import (
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ existsCmd = &cobra.Command{
+ Use: "exists IMAGE",
+ Short: "Check if an image exists in local storage",
+ Long: `If the named image exists in local storage, podman image exists exits with 0, otherwise the exit code will be 1.`,
+ Args: cobra.ExactArgs(1),
+ RunE: exists,
+ Example: `podman image exists ID
+ podman image exists IMAGE && podman pull IMAGE`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: existsCmd,
+ Parent: imageCmd,
+ })
+}
+
+func exists(cmd *cobra.Command, args []string) error {
+ found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0])
+ if err != nil {
+ return err
+ }
+ if !found.Value {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/images/image.go b/cmd/podmanV2/images/image.go
index a15c3e826..9fc7b21d1 100644
--- a/cmd/podmanV2/images/image.go
+++ b/cmd/podmanV2/images/image.go
@@ -28,6 +28,8 @@ func init() {
}
func preRunE(cmd *cobra.Command, args []string) error {
- _, err := registry.NewImageEngine(cmd, args)
- return err
+ if _, err := registry.NewImageEngine(cmd, args); err != nil {
+ return err
+ }
+ return nil
}
diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go
index 719846b4c..d00f0996e 100644
--- a/cmd/podmanV2/images/images.go
+++ b/cmd/podmanV2/images/images.go
@@ -11,13 +11,13 @@ import (
var (
// podman _images_ Alias for podman image _list_
imagesCmd = &cobra.Command{
- Use: strings.Replace(listCmd.Use, "list", "images", 1),
- Args: listCmd.Args,
- Short: listCmd.Short,
- Long: listCmd.Long,
- PersistentPreRunE: preRunE,
- RunE: listCmd.RunE,
- Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
+ Use: strings.Replace(listCmd.Use, "list", "images", 1),
+ Args: listCmd.Args,
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ PreRunE: preRunE,
+ RunE: listCmd.RunE,
+ Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
}
)
diff --git a/cmd/podmanV2/images/import.go b/cmd/podmanV2/images/import.go
new file mode 100644
index 000000000..09a15585f
--- /dev/null
+++ b/cmd/podmanV2/images/import.go
@@ -0,0 +1,87 @@
+package images
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/hashicorp/go-multierror"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ importDescription = `Create a container image from the contents of the specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).
+
+ Note remote tar balls can be specified, via web address.
+ Optionally tag the image. You can specify the instructions using the --change option.`
+ importCommand = &cobra.Command{
+ Use: "import [flags] PATH [REFERENCE]",
+ Short: "Import a tarball to create a filesystem image",
+ Long: importDescription,
+ RunE: importCon,
+ PersistentPreRunE: preRunE,
+ Example: `podman import http://example.com/ctr.tar url-image
+ cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported
+ cat ctr.tar | podman import -`,
+ }
+)
+
+var (
+ importOpts entities.ImageImportOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: importCommand,
+ })
+
+ importCommand.SetHelpTemplate(registry.HelpTemplate())
+ importCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := importCommand.Flags()
+ 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")
+}
+
+func importCon(cmd *cobra.Command, args []string) error {
+ var (
+ source string
+ reference string
+ )
+ switch len(args) {
+ case 0:
+ return errors.Errorf("need to give the path to the tarball, or must specify a tarball of '-' for stdin")
+ case 1:
+ source = args[0]
+ case 2:
+ source = args[0]
+ // TODO when save is merged, we need to process reference
+ // like it is done in there or we end up with docker.io prepends
+ // instead of the localhost ones
+ reference = args[1]
+ default:
+ return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]")
+ }
+ errFileName := parse.ValidateFileName(source)
+ errURL := parse.ValidURL(source)
+ if errURL == nil {
+ importOpts.SourceIsURL = true
+ }
+ if errFileName != nil && errURL != nil {
+ return multierror.Append(errFileName, errURL)
+ }
+
+ importOpts.Source = source
+ importOpts.Reference = reference
+
+ response, err := registry.ImageEngine().Import(context.Background(), importOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.Id)
+ return nil
+}
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
index f8fd44571..d7f6b0ee1 100644
--- a/cmd/podmanV2/images/inspect.go
+++ b/cmd/podmanV2/images/inspect.go
@@ -1,71 +1,44 @@
package images
import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
"strings"
+ "text/tabwriter"
+ "text/template"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podmanV2/common"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
- inspectOpts = entities.ImageInspectOptions{}
-
// Command: podman image _inspect_
inspectCmd = &cobra.Command{
Use: "inspect [flags] IMAGE",
Short: "Display the configuration of an image",
Long: `Displays the low-level information on an image identified by name or ID.`,
- PreRunE: populateEngines,
- RunE: imageInspect,
+ RunE: inspect,
Example: `podman image inspect alpine`,
}
-
- containerEngine entities.ContainerEngine
+ inspectOpts *entities.InspectOptions
)
-// Inspect is unique in that it needs both an ImageEngine and a ContainerEngine
-func populateEngines(cmd *cobra.Command, args []string) (err error) {
- // Populate registry.ImageEngine
- err = preRunE(cmd, args)
- if err != nil {
- return
- }
-
- // Populate registry.ContainerEngine
- containerEngine, err = registry.NewContainerEngine(cmd, args)
- return
-}
-
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: inspectCmd,
Parent: imageCmd,
})
-
- flags := inspectCmd.Flags()
- flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
- flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size")
- flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template")
-
- if registry.EngineOptions.EngineMode == entities.ABIMode {
- // TODO: This is the same as V1. We could skip creating the flag altogether in V2...
- _ = flags.MarkHidden("latest")
- }
+ inspectOpts = common.AddInspectFlagSet(inspectCmd)
}
-const (
- inspectTypeContainer = "container"
- inspectTypeImage = "image"
- inspectAll = "all"
-)
-
-func imageInspect(cmd *cobra.Command, args []string) error {
- inspectType := inspectTypeImage
+func inspect(cmd *cobra.Command, args []string) error {
latestContainer := inspectOpts.Latest
if len(args) == 0 && !latestContainer {
@@ -76,49 +49,61 @@ func imageInspect(cmd *cobra.Command, args []string) error {
return errors.Errorf("you cannot provide additional arguments with --latest")
}
- if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) {
- return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
+ results, err := registry.ImageEngine().Inspect(context.Background(), args, *inspectOpts)
+ if err != nil {
+ return err
}
- outputFormat := inspectOpts.Format
- if strings.Contains(outputFormat, "{{.Id}}") {
- outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1)
- }
- // These fields were renamed, so we need to provide backward compat for
- // the old names.
- if strings.Contains(outputFormat, ".Src") {
- outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1)
+ if len(results.Images) > 0 {
+ if inspectOpts.Format == "" {
+ buf, err := json.MarshalIndent(results.Images, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(buf))
+
+ for id, e := range results.Errors {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
+ }
+ return nil
+ }
+
+ row := inspectFormat(inspectOpts.Format)
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("inspect").Parse(format)
+ if err != nil {
+ return err
+ }
+
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ defer func() { _ = w.Flush() }()
+ err = tmpl.Execute(w, results)
+ if err != nil {
+ return err
+ }
}
- if strings.Contains(outputFormat, ".Dst") {
- outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1)
- }
- if strings.Contains(outputFormat, ".ImageID") {
- outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1)
+
+ for id, e := range results.Errors {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
}
- _ = outputFormat
- // if latestContainer {
- // lc, err := ctnrRuntime.GetLatestContainer()
- // if err != nil {
- // return err
- // }
- // args = append(args, lc.ID())
- // inspectType = inspectTypeContainer
- // }
-
- // inspectedObjects, iterateErr := iterateInput(getContext(), c.Size, args, runtime, inspectType)
- // if iterateErr != nil {
- // return iterateErr
- // }
- //
- // var out formats.Writer
- // if outputFormat != "" && outputFormat != formats.JSONString {
- // // template
- // out = formats.StdoutTemplateArray{Output: inspectedObjects, Template: outputFormat}
- // } else {
- // // default is json output
- // out = formats.JSONStructArray{Output: inspectedObjects}
- // }
- //
- // return out.Out()
return nil
}
+
+func inspectFormat(row string) string {
+ r := strings.NewReplacer("{{.Id}}", formats.IDString,
+ ".Src", ".Source",
+ ".Dst", ".Destination",
+ ".ImageID", ".Image",
+ )
+ row = r.Replace(row)
+
+ if !strings.HasSuffix(row, "\n") {
+ row += "\n"
+ }
+ return row
+}
+
+func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
+ inspectOpts = options
+ return inspect(cmd, args)
+}
diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go
index 4714af3e4..2d6cb3596 100644
--- a/cmd/podmanV2/images/list.go
+++ b/cmd/podmanV2/images/list.go
@@ -152,7 +152,7 @@ func writeTemplate(imageS []*entities.ImageSummary, err error) error {
hdr, row := imageListFormat(listFlag)
format := hdr + "{{range . }}" + row + "{{end}}"
- tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format))
+ tmpl := template.Must(template.New("list").Funcs(report.PodmanTemplateFuncs()).Parse(format))
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
defer w.Flush()
return tmpl.Execute(w, imgs)
@@ -212,7 +212,7 @@ func imageListFormat(flags listFlagType) (string, string) {
row += "\t{{.Digest}}"
}
- hdr += "\tID"
+ hdr += "\tIMAGE ID"
if flags.noTrunc {
row += "\tsha256:{{.ID}}"
} else {
diff --git a/cmd/podmanV2/images/load.go b/cmd/podmanV2/images/load.go
new file mode 100644
index 000000000..f60dc4908
--- /dev/null
+++ b/cmd/podmanV2/images/load.go
@@ -0,0 +1,61 @@
+package images
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ loadDescription = "Loads an image from a locally stored archive (tar file) into container storage."
+ loadCommand = &cobra.Command{
+ Use: "load [flags] [NAME[:TAG]]",
+ Short: "Load an image from container archive",
+ Long: loadDescription,
+ RunE: load,
+ Args: cobra.MaximumNArgs(1),
+ PersistentPreRunE: preRunE,
+ }
+)
+
+var (
+ loadOpts entities.ImageLoadOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: loadCommand,
+ })
+
+ loadCommand.SetHelpTemplate(registry.HelpTemplate())
+ loadCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := loadCommand.Flags()
+ 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")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("signature-policy")
+ }
+
+}
+
+func load(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 {
+ repo, err := image.NormalizedTag(args[0])
+ if err != nil {
+ return err
+ }
+ loadOpts.Name = repo.Name()
+ }
+ response, err := registry.ImageEngine().Load(context.Background(), loadOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println("Loaded image: " + response.Name)
+ return nil
+}
diff --git a/cmd/podmanV2/images/prune.go b/cmd/podmanV2/images/prune.go
new file mode 100644
index 000000000..6577c458e
--- /dev/null
+++ b/cmd/podmanV2/images/prune.go
@@ -0,0 +1,86 @@
+package images
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ pruneDescription = `Removes all unnamed images from local storage.
+
+ If an image is not being used by a container, it will be removed from the system.`
+ pruneCmd = &cobra.Command{
+ Use: "prune",
+ Args: cobra.NoArgs,
+ Short: "Remove unused images",
+ Long: pruneDescription,
+ RunE: prune,
+ Example: `podman image prune`,
+ }
+
+ pruneOpts = entities.ImagePruneOptions{}
+ force bool
+ filter = []string{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pruneCmd,
+ Parent: imageCmd,
+ })
+
+ flags := pruneCmd.Flags()
+ flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all unused images, not just dangling ones")
+ flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")
+ flags.StringArrayVar(&filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
+
+}
+
+func prune(cmd *cobra.Command, args []string) error {
+ if !force {
+ reader := bufio.NewReader(os.Stdin)
+ fmt.Printf(`
+WARNING! This will remove all dangling images.
+Are you sure you want to continue? [y/N] `)
+ answer, err := reader.ReadString('\n')
+ if err != nil {
+ return errors.Wrapf(err, "error reading input")
+ }
+ if strings.ToLower(answer)[0] != 'y' {
+ return nil
+ }
+ }
+
+ // TODO Remove once filter refactor is finished and url.Values rules :)
+ for _, f := range filter {
+ t := strings.SplitN(f, "=", 2)
+ pruneOpts.Filters.Add(t[0], t[1])
+ }
+
+ results, err := registry.ImageEngine().Prune(registry.GetContext(), pruneOpts)
+ if err != nil {
+ return err
+ }
+
+ for _, i := range results.Report.Id {
+ fmt.Println(i)
+ }
+
+ for _, e := range results.Report.Err {
+ fmt.Fprint(os.Stderr, e.Error()+"\n")
+ }
+
+ if results.Size > 0 {
+ fmt.Fprintf(os.Stdout, "Size: %d\n", results.Size)
+ }
+
+ return nil
+}
diff --git a/cmd/podmanV2/images/pull.go b/cmd/podmanV2/images/pull.go
new file mode 100644
index 000000000..c7e325409
--- /dev/null
+++ b/cmd/podmanV2/images/pull.go
@@ -0,0 +1,140 @@
+package images
+
+import (
+ "fmt"
+
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/opentracing/opentracing-go"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+// pullOptionsWrapper wraps entities.ImagePullOptions and prevents leaking
+// CLI-only fields into the API types.
+type pullOptionsWrapper struct {
+ entities.ImagePullOptions
+ TLSVerifyCLI bool // CLI only
+}
+
+var (
+ pullOptions = pullOptionsWrapper{}
+ pullDescription = `Pulls an image from a registry and stores it locally.
+
+ An image can be pulled by tag or digest. If a tag is not specified, the image with the 'latest' tag is pulled.`
+
+ // Command: podman pull
+ pullCmd = &cobra.Command{
+ Use: "pull [flags] IMAGE",
+ Short: "Pull an image from a registry",
+ Long: pullDescription,
+ PreRunE: preRunE,
+ RunE: imagePull,
+ Example: `podman pull imageName
+ podman pull fedora:latest`,
+ }
+
+ // Command: podman image pull
+ // It's basically a clone of `pullCmd` with the exception of being a
+ // child of the images command.
+ imagesPullCmd = &cobra.Command{
+ Use: pullCmd.Use,
+ Short: pullCmd.Short,
+ Long: pullCmd.Long,
+ PreRunE: pullCmd.PreRunE,
+ RunE: pullCmd.RunE,
+ Example: `podman image pull imageName
+ podman image pull fedora:latest`,
+ }
+)
+
+func init() {
+ // pull
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pullCmd,
+ })
+
+ pullCmd.SetHelpTemplate(registry.HelpTemplate())
+ pullCmd.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := pullCmd.Flags()
+ pullFlags(flags)
+
+ // images pull
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imagesPullCmd,
+ Parent: imageCmd,
+ })
+
+ imagesPullCmd.SetHelpTemplate(registry.HelpTemplate())
+ imagesPullCmd.SetUsageTemplate(registry.UsageTemplate())
+ imagesPullFlags := imagesPullCmd.Flags()
+ pullFlags(imagesPullFlags)
+}
+
+// pullFlags set the flags for the pull command.
+func pullFlags(flags *pflag.FlagSet) {
+ flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled")
+ flags.StringVar(&pullOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
+ flags.StringVar(&pullOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
+ flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images")
+ flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images")
+ flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
+ flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
+ flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("cert-dir")
+ _ = flags.MarkHidden("signature-policy")
+ _ = flags.MarkHidden("tls-verify")
+ }
+}
+
+// imagePull is implement the command for pulling images.
+func imagePull(cmd *cobra.Command, args []string) error {
+ // Sanity check input.
+ if len(args) == 0 {
+ return errors.Errorf("an image name must be specified")
+ }
+ if len(args) > 1 {
+ return errors.Errorf("too many arguments. Requires exactly 1")
+ }
+
+ // Start tracing if requested.
+ if cmd.Flags().Changed("trace") {
+ span, _ := opentracing.StartSpanFromContext(registry.GetContext(), "pullCmd")
+ defer span.Finish()
+ }
+
+ pullOptsAPI := pullOptions.ImagePullOptions
+ // 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") {
+ pullOptsAPI.TLSVerify = types.NewOptionalBool(pullOptions.TLSVerifyCLI)
+ }
+
+ // Let's do all the remaining Yoga in the API to prevent us from
+ // scattering logic across (too) many parts of the code.
+ pullReport, err := registry.ImageEngine().Pull(registry.GetContext(), args[0], pullOptsAPI)
+ if err != nil {
+ return err
+ }
+
+ if len(pullReport.Images) > 1 {
+ fmt.Println("Pulled Images:")
+ }
+ for _, img := range pullReport.Images {
+ fmt.Println(img)
+ }
+
+ return nil
+}
diff --git a/cmd/podmanV2/images/push.go b/cmd/podmanV2/images/push.go
new file mode 100644
index 000000000..82cc0c486
--- /dev/null
+++ b/cmd/podmanV2/images/push.go
@@ -0,0 +1,127 @@
+package images
+
+import (
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+// pushOptionsWrapper wraps entities.ImagepushOptions and prevents leaking
+// CLI-only fields into the API types.
+type pushOptionsWrapper struct {
+ entities.ImagePushOptions
+ TLSVerifyCLI bool // CLI only
+}
+
+var (
+ pushOptions = pushOptionsWrapper{}
+ pushDescription = `Pushes a source image to a specified destination.
+
+ The Image "DESTINATION" uses a "transport":"details" format. See podman-push(1) section "DESTINATION" for the expected format.`
+
+ // Command: podman push
+ pushCmd = &cobra.Command{
+ Use: "push [flags] SOURCE DESTINATION",
+ Short: "Push an image to a specified destination",
+ Long: pushDescription,
+ PreRunE: preRunE,
+ RunE: imagePush,
+ Example: `podman push imageID docker://registry.example.com/repository:tag
+ podman push imageID oci-archive:/path/to/layout:image:tag`,
+ }
+
+ // Command: podman image push
+ // It's basically a clone of `pushCmd` with the exception of being a
+ // child of the images command.
+ imagePushCmd = &cobra.Command{
+ Use: pushCmd.Use,
+ Short: pushCmd.Short,
+ Long: pushCmd.Long,
+ PreRunE: pushCmd.PreRunE,
+ RunE: pushCmd.RunE,
+ Example: `podman image push imageID docker://registry.example.com/repository:tag
+ podman image push imageID oci-archive:/path/to/layout:image:tag`,
+ }
+)
+
+func init() {
+ // push
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pushCmd,
+ })
+
+ pushCmd.SetHelpTemplate(registry.HelpTemplate())
+ pushCmd.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := pushCmd.Flags()
+ pushFlags(flags)
+
+ // images push
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imagePushCmd,
+ Parent: imageCmd,
+ })
+
+ imagePushCmd.SetHelpTemplate(registry.HelpTemplate())
+ imagePushCmd.SetUsageTemplate(registry.UsageTemplate())
+ pushFlags(imagePushCmd.Flags())
+}
+
+// pushFlags set the flags for the push command.
+func pushFlags(flags *pflag.FlagSet) {
+ flags.StringVar(&pushOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys")
+ flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
+ flags.StringVar(&pushOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
+ flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
+ flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)")
+ flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images")
+ flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
+ flags.StringVar(&pushOptions.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file")
+ flags.StringVar(&pushOptions.SignBy, "sign-by", "", "Add a signature at the destination using the specified key")
+ flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("cert-dir")
+ _ = flags.MarkHidden("compress")
+ _ = flags.MarkHidden("quiet")
+ _ = flags.MarkHidden("signature-policy")
+ _ = flags.MarkHidden("tls-verify")
+ }
+}
+
+// imagePush is implement the command for pushing images.
+func imagePush(cmd *cobra.Command, args []string) error {
+ var source, destination string
+ switch len(args) {
+ case 1:
+ source = args[0]
+ case 2:
+ source = args[0]
+ destination = args[1]
+ case 0:
+ fallthrough
+ default:
+ return errors.New("push requires at least one image name, or optionally a second to specify a different destination")
+ }
+
+ pushOptsAPI := pushOptions.ImagePushOptions
+ // TLS verification in c/image is controlled via a `types.OptionalBool`
+ // which allows for distinguishing among set-true, set-false, unspecified
+ // which is important to implement a sane way of dealing with defaults of
+ // boolean CLI flags.
+ if cmd.Flags().Changed("tls-verify") {
+ pushOptsAPI.TLSVerify = types.NewOptionalBool(pushOptions.TLSVerifyCLI)
+ }
+
+ // Let's do all the remaining Yoga in the API to prevent us from scattering
+ // logic across (too) many parts of the code.
+ return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptsAPI)
+}
diff --git a/cmd/podmanV2/images/rm.go b/cmd/podmanV2/images/rm.go
new file mode 100644
index 000000000..bb5880de3
--- /dev/null
+++ b/cmd/podmanV2/images/rm.go
@@ -0,0 +1,70 @@
+package images
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ rmDescription = "Removes one or more previously pulled or locally created images."
+ rmCmd = &cobra.Command{
+ Use: "rm [flags] IMAGE [IMAGE...]",
+ Short: "Removes one or more images from local storage",
+ Long: rmDescription,
+ PreRunE: preRunE,
+ RunE: rm,
+ Example: `podman image rm imageID
+ podman image rm --force alpine
+ podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`,
+ }
+
+ imageOpts = entities.ImageDeleteOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: rmCmd,
+ Parent: imageCmd,
+ })
+
+ flags := rmCmd.Flags()
+ 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 {
+ return errors.Errorf("image name or ID must be specified")
+ }
+ 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 {
+ case report != nil && report.ImageNotFound != nil:
+ fmt.Fprintln(os.Stderr, err.Error())
+ registry.SetExitCode(2)
+ case report != nil && report.ImageInUse != nil:
+ fmt.Fprintln(os.Stderr, err.Error())
+ default:
+ return err
+ }
+ }
+
+ for _, u := range report.Untagged {
+ fmt.Println("Untagged: " + u)
+ }
+ for _, d := range report.Deleted {
+ fmt.Println("Deleted: " + d)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/images/rmi.go b/cmd/podmanV2/images/rmi.go
new file mode 100644
index 000000000..7f9297bc9
--- /dev/null
+++ b/cmd/podmanV2/images/rmi.go
@@ -0,0 +1,30 @@
+package images
+
+import (
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ rmiCmd = &cobra.Command{
+ Use: strings.Replace(rmCmd.Use, "rm ", "rmi ", 1),
+ Args: rmCmd.Args,
+ Short: rmCmd.Short,
+ Long: rmCmd.Long,
+ PreRunE: rmCmd.PreRunE,
+ RunE: rmCmd.RunE,
+ Example: strings.Replace(rmCmd.Example, "podman image rm", "podman rmi", -1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: rmiCmd,
+ })
+ rmiCmd.SetHelpTemplate(registry.HelpTemplate())
+ rmiCmd.SetUsageTemplate(registry.UsageTemplate())
+}
diff --git a/cmd/podmanV2/images/save.go b/cmd/podmanV2/images/save.go
new file mode 100644
index 000000000..ae39b7bce
--- /dev/null
+++ b/cmd/podmanV2/images/save.go
@@ -0,0 +1,87 @@
+package images
+
+import (
+ "context"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/libpod/define"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+var validFormats = []string{define.OCIManifestDir, define.OCIArchive, define.V2s2ManifestDir, define.V2s2Archive}
+
+var (
+ saveDescription = `Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.`
+
+ saveCommand = &cobra.Command{
+ Use: "save [flags] IMAGE",
+ Short: "Save image to an archive",
+ Long: saveDescription,
+ PersistentPreRunE: preRunE,
+ RunE: save,
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) == 0 {
+ return errors.Errorf("need at least 1 argument")
+ }
+ format, err := cmd.Flags().GetString("format")
+ if err != nil {
+ return err
+ }
+ if !util.StringInSlice(format, validFormats) {
+ return errors.Errorf("format value must be one of %s", strings.Join(validFormats, " "))
+ }
+ return nil
+ },
+ Example: `podman save --quiet -o myimage.tar imageID
+ podman save --format docker-dir -o ubuntu-dir ubuntu
+ podman save > alpine-all.tar alpine:latest`,
+ }
+)
+
+var (
+ saveOpts entities.ImageSaveOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: saveCommand,
+ })
+ flags := saveCommand.Flags()
+ 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)")
+ flags.BoolVarP(&saveOpts.Quiet, "quiet", "q", false, "Suppress the output")
+
+}
+
+func save(cmd *cobra.Command, args []string) error {
+ var (
+ tags []string
+ )
+ if cmd.Flag("compress").Changed && (saveOpts.Format != define.OCIManifestDir && saveOpts.Format != define.V2s2ManifestDir && saveOpts.Format == "") {
+ return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
+ }
+ if len(saveOpts.Output) == 0 {
+ fi := os.Stdout
+ if terminal.IsTerminal(int(fi.Fd())) {
+ return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
+ }
+ saveOpts.Output = "/dev/stdout"
+ }
+ if err := parse.ValidateFileName(saveOpts.Output); err != nil {
+ return err
+ }
+ if len(args) > 1 {
+ tags = args[1:]
+ }
+ return registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
+}
diff --git a/cmd/podmanV2/images/tag.go b/cmd/podmanV2/images/tag.go
new file mode 100644
index 000000000..f66fe7857
--- /dev/null
+++ b/cmd/podmanV2/images/tag.go
@@ -0,0 +1,34 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ tagDescription = "Adds one or more additional names to locally-stored image."
+ tagCommand = &cobra.Command{
+ Use: "tag [flags] IMAGE TARGET_NAME [TARGET_NAME...]",
+ Short: "Add an additional name to a local image",
+ Long: tagDescription,
+ RunE: tag,
+ Args: cobra.MinimumNArgs(2),
+ Example: `podman tag 0e3bbc2 fedora:latest
+ podman tag imageID:latest myNewImage:newTag
+ podman tag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: tagCommand,
+ })
+ tagCommand.SetHelpTemplate(registry.HelpTemplate())
+ tagCommand.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func tag(cmd *cobra.Command, args []string) error {
+ return registry.ImageEngine().Tag(registry.GetContext(), args[0], args[1:], entities.ImageTagOptions{})
+}
diff --git a/cmd/podmanV2/images/untag.go b/cmd/podmanV2/images/untag.go
new file mode 100644
index 000000000..c84827bb3
--- /dev/null
+++ b/cmd/podmanV2/images/untag.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ untagCommand = &cobra.Command{
+ Use: "untag [flags] IMAGE [NAME...]",
+ Short: "Remove a name from a local image",
+ Long: "Removes one or more names from a locally-stored image.",
+ RunE: untag,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman untag 0e3bbc2
+ podman untag imageID:latest otherImageName:latest
+ podman untag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: untagCommand,
+ })
+ untagCommand.SetHelpTemplate(registry.HelpTemplate())
+ untagCommand.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func untag(cmd *cobra.Command, args []string) error {
+ return registry.ImageEngine().Untag(registry.GetContext(), args[0], args[1:], entities.ImageUntagOptions{})
+}
diff --git a/cmd/podmanV2/inspect.go b/cmd/podmanV2/inspect.go
new file mode 100644
index 000000000..4975cf632
--- /dev/null
+++ b/cmd/podmanV2/inspect.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/containers"
+ "github.com/containers/libpod/cmd/podmanV2/images"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+// Inspect is one of the out layer commands in that it operates on images/containers/...
+
+var (
+ inspectOpts *entities.InspectOptions
+
+ // Command: podman _inspect_ Object_ID
+ inspectCmd = &cobra.Command{
+ Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}",
+ Args: cobra.ExactArgs(1),
+ Short: "Display the configuration of object denoted by ID",
+ Long: "Displays the low-level information on an object identified by name or ID",
+ TraverseChildren: true,
+ RunE: inspect,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: inspectCmd,
+ })
+ inspectOpts = common.AddInspectFlagSet(inspectCmd)
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+ ie, err := registry.NewImageEngine(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ if found, err := ie.Exists(context.Background(), args[0]); err != nil {
+ return err
+ } else if found.Value {
+ return images.Inspect(cmd, args, inspectOpts)
+ }
+
+ ce, err := registry.NewContainerEngine(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ if found, err := ce.ContainerExists(context.Background(), args[0]); err != nil {
+ return err
+ } else if found.Value {
+ return containers.Inspect(cmd, args, inspectOpts)
+ }
+ return fmt.Errorf("%s not found on system", args[0])
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
index dc96c26d0..fe3cd9f16 100644
--- a/cmd/podmanV2/main.go
+++ b/cmd/podmanV2/main.go
@@ -1,22 +1,23 @@
package main
import (
- "fmt"
"os"
"reflect"
"runtime"
"strings"
_ "github.com/containers/libpod/cmd/podmanV2/containers"
+ _ "github.com/containers/libpod/cmd/podmanV2/healthcheck"
_ "github.com/containers/libpod/cmd/podmanV2/images"
_ "github.com/containers/libpod/cmd/podmanV2/networks"
_ "github.com/containers/libpod/cmd/podmanV2/pods"
"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"
- "github.com/spf13/cobra"
)
func init() {
@@ -24,10 +25,7 @@ func init() {
logrus.Errorf(err.Error())
os.Exit(1)
}
- initCobra()
-}
-func initCobra() {
switch runtime.GOOS {
case "darwin":
fallthrough
@@ -46,12 +44,14 @@ func initCobra() {
registry.EngineOptions.EngineMode = entities.TunnelMode
}
}
-
- cobra.OnInitialize(func() {})
}
func main() {
- fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands))
+ if reexec.Init() {
+ // We were invoked with a different argv[0] indicating that we
+ // had a specific job to do as a subprocess, and it's done.
+ return
+ }
for _, c := range registry.Commands {
if Contains(registry.EngineOptions.EngineMode, c.Mode) {
parent := rootCmd
diff --git a/cmd/podmanV2/parse/common.go b/cmd/podmanV2/parse/common.go
new file mode 100644
index 000000000..a5e9b4fc2
--- /dev/null
+++ b/cmd/podmanV2/parse/common.go
@@ -0,0 +1,50 @@
+package parse
+
+import (
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly.
+// If cidfile is set, also check for the --cidfile flag.
+func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
+ argLen := len(args)
+ if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
+ if !cidfile {
+ return errors.New("unable to lookup values for 'latest' or 'all'")
+ } else if c.Flags().Lookup("cidfile") == nil {
+ return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
+ }
+ }
+
+ specifiedAll, _ := c.Flags().GetBool("all")
+ specifiedLatest, _ := c.Flags().GetBool("latest")
+ specifiedCIDFile := false
+ if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
+ specifiedCIDFile = true
+ }
+
+ if specifiedCIDFile && (specifiedAll || specifiedLatest) {
+ return errors.Errorf("--all, --latest and --cidfile cannot be used together")
+ } else if specifiedAll && specifiedLatest {
+ return errors.Errorf("--all and --latest cannot be used together")
+ }
+
+ 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 specifiedCIDFile {
+ return nil
+ }
+
+ if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
+ return errors.Errorf("you must provide at least one name or id")
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/parse/parse.go b/cmd/podmanV2/parse/net.go
index 10d2146fa..03cda268c 100644
--- a/cmd/podmanV2/parse/parse.go
+++ b/cmd/podmanV2/parse/net.go
@@ -13,7 +13,6 @@ import (
"strings"
"github.com/pkg/errors"
- "github.com/spf13/cobra"
)
const (
@@ -187,47 +186,3 @@ func ValidURL(urlStr string) error {
}
return nil
}
-
-// checkAllLatestAndCIDFile checks that --all and --latest are used correctly.
-// If cidfile is set, also check for the --cidfile flag.
-func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
- argLen := len(args)
- if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
- if !cidfile {
- return errors.New("unable to lookup values for 'latest' or 'all'")
- } else if c.Flags().Lookup("cidfile") == nil {
- return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
- }
- }
-
- specifiedAll, _ := c.Flags().GetBool("all")
- specifiedLatest, _ := c.Flags().GetBool("latest")
- specifiedCIDFile := false
- if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
- specifiedCIDFile = true
- }
-
- if specifiedCIDFile && (specifiedAll || specifiedLatest) {
- return errors.Errorf("--all, --latest and --cidfile cannot be used together")
- } else if specifiedAll && specifiedLatest {
- return errors.Errorf("--all and --latest cannot be used together")
- }
-
- 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 specifiedCIDFile {
- return nil
- }
-
- if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
- return errors.Errorf("you must provide at least one name or id")
- }
- return nil
-}
diff --git a/cmd/podmanV2/parse/parse_test.go b/cmd/podmanV2/parse/net_test.go
index a6ddc2be9..a6ddc2be9 100644
--- a/cmd/podmanV2/parse/parse_test.go
+++ b/cmd/podmanV2/parse/net_test.go
diff --git a/cmd/podmanV2/pods/create.go b/cmd/podmanV2/pods/create.go
new file mode 100644
index 000000000..ab8957ee3
--- /dev/null
+++ b/cmd/podmanV2/pods/create.go
@@ -0,0 +1,132 @@
+package pods
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/errorhandling"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ podCreateDescription = `After creating the pod, the pod ID is printed to stdout.
+
+ You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.`
+
+ createCommand = &cobra.Command{
+ Use: "create",
+ Args: cobra.NoArgs,
+ Short: "Create a new empty pod",
+ Long: podCreateDescription,
+ RunE: create,
+ }
+)
+
+var (
+ createOptions entities.PodCreateOptions
+ labels, labelFile []string
+ podIDFile string
+ share string
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: createCommand,
+ Parent: podCmd,
+ })
+ flags := createCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.AddFlagSet(common.GetNetFlags())
+ flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod")
+ flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with")
+ flags.StringVar(&createOptions.InfraImage, "infra-image", define.DefaultInfraImage, "The image of the infra container to associate with the pod")
+ flags.StringVar(&createOptions.InfraCommand, "infra-command", define.DefaultInfraCommand, "The command to run on the infra container when the pod is started")
+ flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels")
+ flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])")
+ 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")
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ podIdFile *os.File
+ )
+ createOptions.Labels, err = parse.GetAllLabels(labelFile, labels)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+
+ if !createOptions.Infra && cmd.Flag("share").Changed && share != "none" && share != "" {
+ return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
+ }
+ createOptions.Share = strings.Split(share, ",")
+ if cmd.Flag("pod-id-file").Changed {
+ podIdFile, err = util.OpenExclusiveFile(podIDFile)
+ if err != nil && os.IsExist(err) {
+ return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile)
+ }
+ if err != nil {
+ return errors.Errorf("error opening pod-id-file %s", podIDFile)
+ }
+ defer errorhandling.CloseQuiet(podIdFile)
+ defer errorhandling.SyncQuiet(podIdFile)
+ }
+
+ createOptions.Net, err = common.NetFlagsToNetOptions(cmd)
+ if err != nil {
+ return err
+ }
+ netInput, err := cmd.Flags().GetString("network")
+ if err != nil {
+ return err
+ }
+ n := specgen.Namespace{}
+ switch netInput {
+ case "bridge":
+ n.NSMode = specgen.Bridge
+ case "host":
+ n.NSMode = specgen.Host
+ case "slip4netns":
+ n.NSMode = specgen.Slirp
+ default:
+ if strings.HasPrefix(netInput, "container:") { //nolint
+ split := strings.Split(netInput, ":")
+ if len(split) != 2 {
+ return errors.Errorf("invalid network paramater: %q", netInput)
+ }
+ n.NSMode = specgen.FromContainer
+ n.Value = split[1]
+ } else if strings.HasPrefix(netInput, "ns:") {
+ return errors.New("the ns: network option is not supported for pods")
+ } else {
+ n.NSMode = specgen.Bridge
+ createOptions.Net.CNINetworks = strings.Split(netInput, ",")
+ }
+ }
+ if len(createOptions.Net.PublishPorts) > 0 {
+ if !createOptions.Infra {
+ return errors.Errorf("you must have an infra container to publish port bindings to the host")
+ }
+ }
+
+ response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.Id)
+ return nil
+}
diff --git a/cmd/podmanV2/pods/inspect.go b/cmd/podmanV2/pods/inspect.go
new file mode 100644
index 000000000..9aab610f2
--- /dev/null
+++ b/cmd/podmanV2/pods/inspect.go
@@ -0,0 +1,64 @@
+package pods
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ inspectOptions = entities.PodInspectOptions{}
+)
+
+var (
+ inspectDescription = fmt.Sprintf(`Display the configuration for a pod by name or id
+
+ By default, this will render all results in a JSON array.`)
+
+ inspectCmd = &cobra.Command{
+ Use: "inspect [flags] POD [POD...]",
+ Short: "Displays a pod configuration",
+ Long: inspectDescription,
+ RunE: inspect,
+ Example: `podman pod inspect podID`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: inspectCmd,
+ Parent: podCmd,
+ })
+ flags := inspectCmd.Flags()
+ flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+
+ if len(args) < 1 && !inspectOptions.Latest {
+ return errors.Errorf("you must provide the name or id of a running pod")
+ }
+
+ if !inspectOptions.Latest {
+ inspectOptions.NameOrID = args[0]
+ }
+ responses, err := registry.ContainerEngine().PodInspect(context.Background(), inspectOptions)
+ if err != nil {
+ return err
+ }
+ b, err := jsoniter.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
diff --git a/cmd/podmanV2/pods/kill.go b/cmd/podmanV2/pods/kill.go
new file mode 100644
index 000000000..06cca916c
--- /dev/null
+++ b/cmd/podmanV2/pods/kill.go
@@ -0,0 +1,68 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podKillDescription = `Signals are sent to the main process of each container inside the specified pod.
+
+ The default signal is SIGKILL, or any signal specified with option --signal.`
+ killCommand = &cobra.Command{
+ Use: "kill [flags] POD [POD...]",
+ Short: "Send the specified signal or SIGKILL to containers in pod",
+ Long: podKillDescription,
+ RunE: kill,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod kill podID
+ podman pod kill --signal TERM mywebserver
+ podman pod kill --latest`,
+ }
+)
+
+var (
+ killOpts entities.PodKillOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: killCommand,
+ Parent: podCmd,
+ })
+ flags := killCommand.Flags()
+ flags.BoolVarP(&killOpts.All, "all", "a", false, "Kill all containers in all pods")
+ flags.BoolVarP(&killOpts.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ flags.StringVarP(&killOpts.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+
+}
+func kill(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ responses, err := registry.ContainerEngine().PodKill(context.Background(), args, killOpts)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if len(r.Errs) == 0 {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Errs...)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/pause.go b/cmd/podmanV2/pods/pause.go
new file mode 100644
index 000000000..dc86e534d
--- /dev/null
+++ b/cmd/podmanV2/pods/pause.go
@@ -0,0 +1,66 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podPauseDescription = `The pod name or ID can be used.
+
+ All running containers within each specified pod will then be paused.`
+ pauseCommand = &cobra.Command{
+ Use: "pause [flags] POD [POD...]",
+ Short: "Pause one or more pods",
+ Long: podPauseDescription,
+ RunE: pause,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod pause podID1 podID2
+ podman pod pause --latest
+ podman pod pause --all`,
+ }
+)
+
+var (
+ pauseOptions entities.PodPauseOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pauseCommand,
+ Parent: podCmd,
+ })
+ flags := pauseCommand.Flags()
+ flags.BoolVarP(&pauseOptions.All, "all", "a", false, "Pause all running pods")
+ flags.BoolVarP(&pauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+func pause(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ responses, err := registry.ContainerEngine().PodPause(context.Background(), args, pauseOptions)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if len(r.Errs) == 0 {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Errs...)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/pod.go b/cmd/podmanV2/pods/pod.go
index 81c0d33e1..3766893bb 100644
--- a/cmd/podmanV2/pods/pod.go
+++ b/cmd/podmanV2/pods/pod.go
@@ -1,6 +1,9 @@
package pods
import (
+ "strings"
+ "text/template"
+
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
@@ -18,6 +21,33 @@ var (
}
)
+var podFuncMap = template.FuncMap{
+ "numCons": func(cons []*entities.ListPodContainer) int {
+ return len(cons)
+ },
+ "podcids": func(cons []*entities.ListPodContainer) string {
+ var ctrids []string
+ for _, c := range cons {
+ ctrids = append(ctrids, c.Id[:12])
+ }
+ return strings.Join(ctrids, ",")
+ },
+ "podconnames": func(cons []*entities.ListPodContainer) string {
+ var ctrNames []string
+ for _, c := range cons {
+ ctrNames = append(ctrNames, c.Names[:12])
+ }
+ return strings.Join(ctrNames, ",")
+ },
+ "podconstatuses": func(cons []*entities.ListPodContainer) string {
+ var statuses []string
+ for _, c := range cons {
+ statuses = append(statuses, c.Status)
+ }
+ return strings.Join(statuses, ",")
+ },
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go
index d4c625b2e..9546dff9e 100644
--- a/cmd/podmanV2/pods/ps.go
+++ b/cmd/podmanV2/pods/ps.go
@@ -1,8 +1,19 @@
package pods
import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "text/tabwriter"
+ "text/template"
+
"github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/report"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -19,14 +30,137 @@ var (
}
)
+var (
+ defaultHeaders string = "POD ID\tNAME\tSTATUS\tCREATED"
+ inputFilters string
+ noTrunc bool
+ psInput entities.PodPSOptions
+)
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: psCmd,
Parent: podCmd,
})
+ flags := psCmd.Flags()
+ flags.BoolVar(&psInput.CtrNames, "ctr-names", false, "Display the container names")
+ flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated")
+ flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status")
+ // TODO should we make this a [] ?
+ flags.StringVarP(&inputFilters, "filter", "f", "", "Filter output based on conditions given")
+ flags.StringVar(&psInput.Format, "format", "", "Pretty-print pods to JSON or using a Go template")
+ flags.BoolVarP(&psInput.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod")
+ flags.BoolVar(&psInput.Namespace, "ns", false, "Display namespace information of the pod")
+ flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate pod and container IDs")
+ flags.BoolVarP(&psInput.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only")
+ flags.StringVar(&psInput.Sort, "sort", "created", "Sort output by created, id, name, or number")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
}
func pods(cmd *cobra.Command, args []string) error {
+ var (
+ w io.Writer = os.Stdout
+ row string
+ )
+ if cmd.Flag("filter").Changed {
+ for _, f := range strings.Split(inputFilters, ",") {
+ split := strings.Split(f, "=")
+ if len(split) < 2 {
+ return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+ psInput.Filters[split[0]] = append(psInput.Filters[split[0]], split[1])
+ }
+ }
+ responses, err := registry.ContainerEngine().PodPs(context.Background(), psInput)
+ if err != nil {
+ return err
+ }
+
+ if psInput.Format == "json" {
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+ headers, row := createPodPsOut(cmd)
+ if psInput.Quiet {
+ if noTrunc {
+ row = "{{.Id}}\n"
+ } else {
+ row = "{{slice .Id 0 12}}\n"
+ }
+ }
+ if cmd.Flag("format").Changed {
+ row = psInput.Format
+ if !strings.HasPrefix(row, "\n") {
+ row += "\n"
+ }
+ }
+ format := "{{range . }}" + row + "{{end}}"
+ if !psInput.Quiet && !cmd.Flag("format").Changed {
+ format = headers + format
+ }
+ funcs := report.AppendFuncMap(podFuncMap)
+ tmpl, err := template.New("listPods").Funcs(funcs).Parse(format)
+ if err != nil {
+ return err
+ }
+ if !psInput.Quiet {
+ w = tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ }
+ if err := tmpl.Execute(w, responses); err != nil {
+ return err
+ }
+ if flusher, ok := w.(interface{ Flush() error }); ok {
+ return flusher.Flush()
+ }
return nil
}
+
+func createPodPsOut(cmd *cobra.Command) (string, string) {
+ var row string
+ headers := defaultHeaders
+ if noTrunc {
+ row += "{{.Id}}"
+ } else {
+ row += "{{slice .Id 0 12}}"
+ }
+
+ row += "\t{{.Name}}\t{{.Status}}\t{{humanDurationFromTime .Created}}"
+
+ //rowFormat string = "{{slice .Id 0 12}}\t{{.Name}}\t{{.Status}}\t{{humanDurationFromTime .Created}}"
+ if psInput.CtrIds {
+ headers += "\tIDS"
+ row += "\t{{podcids .Containers}}"
+ }
+ if psInput.CtrNames {
+ headers += "\tNAMES"
+ row += "\t{{podconnames .Containers}}"
+ }
+ if psInput.CtrStatus {
+ headers += "\tSTATUS"
+ row += "\t{{podconstatuses .Containers}}"
+ }
+ if psInput.Namespace {
+ headers += "\tCGROUP\tNAMESPACES"
+ row += "\t{{.Cgroup}}\t{{.Namespace}}"
+ }
+ if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
+ headers += "\t# OF CONTAINERS"
+ row += "\t{{numCons .Containers}}"
+
+ }
+ headers += "\tINFRA ID\n"
+ if noTrunc {
+ row += "\t{{.InfraId}}\n"
+ } else {
+ row += "\t{{slice .InfraId 0 12}}\n"
+ }
+ return headers, row
+}
diff --git a/cmd/podmanV2/pods/restart.go b/cmd/podmanV2/pods/restart.go
new file mode 100644
index 000000000..1c8709704
--- /dev/null
+++ b/cmd/podmanV2/pods/restart.go
@@ -0,0 +1,68 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podRestartDescription = `The pod ID or name can be used.
+
+ All of the containers within each of the specified pods will be restarted. If a container in a pod is not currently running it will be started.`
+ restartCommand = &cobra.Command{
+ Use: "restart [flags] POD [POD...]",
+ Short: "Restart one or more pods",
+ Long: podRestartDescription,
+ RunE: restart,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod restart podID1 podID2
+ podman pod restart --latest
+ podman pod restart --all`,
+ }
+)
+
+var (
+ restartOptions = entities.PodRestartOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: restartCommand,
+ Parent: podCmd,
+ })
+
+ flags := restartCommand.Flags()
+ flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all running pods")
+ flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func restart(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ responses, err := registry.ContainerEngine().PodRestart(context.Background(), args, restartOptions)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if len(r.Errs) == 0 {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Errs...)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/rm.go b/cmd/podmanV2/pods/rm.go
new file mode 100644
index 000000000..b43dd2d6c
--- /dev/null
+++ b/cmd/podmanV2/pods/rm.go
@@ -0,0 +1,71 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host.
+
+ The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`)
+ rmCommand = &cobra.Command{
+ Use: "rm [flags] POD [POD...]",
+ Short: "Remove one or more pods",
+ Long: podRmDescription,
+ RunE: rm,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod rm mywebserverpod
+ podman pod rm -f 860a4b23
+ podman pod rm -f -a`,
+ }
+)
+
+var (
+ rmOptions = entities.PodRmOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: rmCommand,
+ Parent: podCmd,
+ })
+
+ flags := rmCommand.Flags()
+ flags.BoolVarP(&rmOptions.All, "all", "a", false, "Restart all running pods")
+ flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false")
+ flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing")
+ flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ _ = flags.MarkHidden("ignore")
+ }
+}
+
+func rm(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ responses, err := registry.ContainerEngine().PodRm(context.Background(), args, rmOptions)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/start.go b/cmd/podmanV2/pods/start.go
new file mode 100644
index 000000000..11ac312f9
--- /dev/null
+++ b/cmd/podmanV2/pods/start.go
@@ -0,0 +1,68 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podStartDescription = `The pod name or ID can be used.
+
+ All containers defined in the pod will be started.`
+ startCommand = &cobra.Command{
+ Use: "start [flags] POD [POD...]",
+ Short: "Start one or more pods",
+ Long: podStartDescription,
+ RunE: start,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod start podID
+ podman pod start --latest
+ podman pod start --all`,
+ }
+)
+
+var (
+ startOptions = entities.PodStartOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: startCommand,
+ Parent: podCmd,
+ })
+
+ flags := startCommand.Flags()
+ flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods")
+ flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func start(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ responses, err := registry.ContainerEngine().PodStart(context.Background(), args, startOptions)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if len(r.Errs) == 0 {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Errs...)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/stop.go b/cmd/podmanV2/pods/stop.go
new file mode 100644
index 000000000..403c7d95d
--- /dev/null
+++ b/cmd/podmanV2/pods/stop.go
@@ -0,0 +1,79 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podStopDescription = `The pod name or ID can be used.
+
+ This command will stop all running containers in each of the specified pods.`
+
+ stopCommand = &cobra.Command{
+ Use: "stop [flags] POD [POD...]",
+ Short: "Stop one or more pods",
+ Long: podStopDescription,
+ RunE: stop,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod stop mywebserverpod
+ podman pod stop --latest
+ podman pod stop --time 0 490eb 3557fb`,
+ }
+)
+
+var (
+ stopOptions = entities.PodStopOptions{
+ Timeout: -1,
+ }
+ timeout uint
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: stopCommand,
+ Parent: podCmd,
+ })
+ flags := stopCommand.Flags()
+ flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running pods")
+ flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing")
+ flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Stop the latest pod podman is aware of")
+ flags.UintVarP(&timeout, "time", "t", 0, "Seconds to wait for pod stop before killing the container")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ _ = flags.MarkHidden("ignore")
+
+ }
+ flags.SetNormalizeFunc(utils.AliasFlags)
+}
+
+func stop(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ if cmd.Flag("time").Changed {
+ stopOptions.Timeout = int(timeout)
+ }
+ responses, err := registry.ContainerEngine().PodStop(context.Background(), args, stopOptions)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if len(r.Errs) == 0 {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Errs...)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/top.go b/cmd/podmanV2/pods/top.go
new file mode 100644
index 000000000..5ef282238
--- /dev/null
+++ b/cmd/podmanV2/pods/top.go
@@ -0,0 +1,90 @@
+package pods
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/psgo"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ topDescription = fmt.Sprintf(`Specify format descriptors to alter the output.
+
+ You may run "podman pod top -l pid pcpu seccomp" to print the process ID, the CPU percentage and the seccomp mode of each process of the latest pod.
+ Format Descriptors:
+ %s`, strings.Join(psgo.ListDescriptors(), ","))
+
+ topOptions = entities.PodTopOptions{}
+
+ topCommand = &cobra.Command{
+ Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS]",
+ Short: "Display the running processes in a pod",
+ Long: topDescription,
+ PersistentPreRunE: preRunE,
+ RunE: top,
+ Args: cobra.ArbitraryArgs,
+ Example: `podman pod top podID
+podman pod top --latest
+podman pod top podID pid seccomp args %C
+podman pod top podID -eo user,pid,comm`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: topCommand,
+ Parent: podCmd,
+ })
+
+ topCommand.SetHelpTemplate(registry.HelpTemplate())
+ topCommand.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := topCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "")
+ flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+
+ _ = flags.MarkHidden("list-descriptors") // meant only for bash completion
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func top(cmd *cobra.Command, args []string) error {
+ if topOptions.ListDescriptors {
+ fmt.Println(strings.Join(psgo.ListDescriptors(), "\n"))
+ return nil
+ }
+
+ if len(args) < 1 && !topOptions.Latest {
+ return errors.Errorf("you must provide the name or id of a running pod")
+ }
+
+ if topOptions.Latest {
+ topOptions.Descriptors = args
+ } else {
+ topOptions.NameOrID = args[0]
+ topOptions.Descriptors = args[1:]
+ }
+
+ topResponse, err := registry.ContainerEngine().PodTop(context.Background(), topOptions)
+ if err != nil {
+ return err
+ }
+
+ w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
+ for _, proc := range topResponse.Value {
+ if _, err := fmt.Fprintln(w, proc); err != nil {
+ return err
+ }
+ }
+ return w.Flush()
+}
diff --git a/cmd/podmanV2/pods/unpause.go b/cmd/podmanV2/pods/unpause.go
new file mode 100644
index 000000000..2de7b964f
--- /dev/null
+++ b/cmd/podmanV2/pods/unpause.go
@@ -0,0 +1,66 @@
+package pods
+
+import (
+ "context"
+ "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 (
+ podUnpauseDescription = `The podman unpause command will unpause all "paused" containers assigned to the pod.
+
+ The pod name or ID can be used.`
+ unpauseCommand = &cobra.Command{
+ Use: "unpause [flags] POD [POD...]",
+ Short: "Unpause one or more pods",
+ Long: podUnpauseDescription,
+ RunE: unpause,
+ Args: func(cmd *cobra.Command, args []string) error {
+ return parse.CheckAllLatestAndCIDFile(cmd, args, false, false)
+ },
+ Example: `podman pod unpause podID1 podID2
+ podman pod unpause --all
+ podman pod unpause --latest`,
+ }
+)
+
+var (
+ unpauseOptions entities.PodunpauseOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: unpauseCommand,
+ Parent: podCmd,
+ })
+ flags := unpauseCommand.Flags()
+ flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Pause all running pods")
+ flags.BoolVarP(&unpauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+func unpause(cmd *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ responses, err := registry.ContainerEngine().PodUnpause(context.Background(), args, unpauseOptions)
+ if err != nil {
+ return err
+ }
+ // in the cli, first we print out all the successful attempts
+ for _, r := range responses {
+ if len(r.Errs) == 0 {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Errs...)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
index f0650a7cf..401f82718 100644
--- a/cmd/podmanV2/registry/registry.go
+++ b/cmd/podmanV2/registry/registry.go
@@ -3,37 +3,38 @@ package registry
import (
"context"
- "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/infra"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
+type CobraFuncs func(cmd *cobra.Command, args []string) error
+
type CliCommand struct {
Mode []entities.EngineMode
Command *cobra.Command
Parent *cobra.Command
}
-var (
- Commands []CliCommand
+const ExecErrorCodeGeneric = 125
- imageEngine entities.ImageEngine
- containerEngine entities.ContainerEngine
+var (
cliCtx context.Context
+ containerEngine entities.ContainerEngine
+ exitCode = ExecErrorCodeGeneric
+ imageEngine entities.ImageEngine
+ Commands []CliCommand
EngineOptions entities.EngineOptions
-
- ExitCode = define.ExecErrorCodeGeneric
)
func SetExitCode(code int) {
- ExitCode = code
+ exitCode = code
}
func GetExitCode() int {
- return ExitCode
+ return exitCode
}
// HelpTemplate returns the help template for podman commands
diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go
index f3bc06405..e46048e97 100644
--- a/cmd/podmanV2/report/templates.go
+++ b/cmd/podmanV2/report/templates.go
@@ -19,6 +19,9 @@ var defaultFuncMap = template.FuncMap{
"humanDuration": func(t int64) string {
return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago"
},
+ "humanDurationFromTime": func(t time.Time) string {
+ return units.HumanDuration(time.Since(t)) + " ago"
+ },
"humanSize": func(sz int64) string {
s := units.HumanSizeWithPrecision(float64(sz), 3)
i := strings.LastIndexFunc(s, unicode.IsNumber)
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
index 68e8b4531..6fc12f57e 100644
--- a/cmd/podmanV2/root.go
+++ b/cmd/podmanV2/root.go
@@ -2,24 +2,34 @@ package main
import (
"fmt"
+ "log/syslog"
"os"
"path"
"github.com/containers/libpod/cmd/podmanV2/registry"
- "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/version"
+ "github.com/sirupsen/logrus"
+ logrusSyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/spf13/cobra"
)
-var rootCmd = &cobra.Command{
- Use: path.Base(os.Args[0]),
- Long: "Manage pods, containers and images",
- SilenceUsage: true,
- SilenceErrors: true,
- TraverseChildren: true,
- RunE: registry.SubCommandExists,
- Version: version.Version,
-}
+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,
+ }
+
+ logLevels = entities.NewStringSet("debug", "info", "warn", "error", "fatal", "panic")
+ logLevel = "error"
+ useSyslog bool
+)
func init() {
// Override default --help information of `--version` global flag}
@@ -28,14 +38,57 @@ func init() {
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,
+ syslogHook,
+ )
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+ return nil
+}
+
+func logging() {
+ if !logLevels.Contains(logLevel) {
+ fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, logLevels.String())
+ os.Exit(1)
+ }
+
+ level, err := logrus.ParseLevel(logLevel)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ logrus.SetLevel(level)
+
+ if logrus.IsLevelEnabled(logrus.InfoLevel) {
+ logrus.Infof("%s filtering at log level %s", os.Args[0], logrus.GetLevel())
+ }
+}
+
+func syslogHook() {
+ if useSyslog {
+ hook, err := logrusSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+ if err != nil {
+ logrus.WithError(err).Error("Failed to initialize syslog hook")
+ }
+ if err == nil {
+ logrus.AddHook(hook)
+ }
+ }
}
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() == define.ExecErrorCodeGeneric {
- // The exitCode modified from define.ExecErrorCodeGeneric,
+ } 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
diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go
index 30ed328e8..4e805c7bd 100644
--- a/cmd/podmanV2/system/system.go
+++ b/cmd/podmanV2/system/system.go
@@ -1,4 +1,4 @@
-package images
+package system
import (
"github.com/containers/libpod/cmd/podmanV2/registry"
diff --git a/cmd/podmanV2/system/version.go b/cmd/podmanV2/system/version.go
new file mode 100644
index 000000000..e8002056b
--- /dev/null
+++ b/cmd/podmanV2/system/version.go
@@ -0,0 +1,119 @@
+package system
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "text/tabwriter"
+ "time"
+
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ versionCommand = &cobra.Command{
+ Use: "version",
+ Args: cobra.NoArgs,
+ Short: "Display the Podman Version Information",
+ RunE: version,
+ PersistentPreRunE: preRunE,
+ }
+ format string
+)
+
+type versionStruct struct {
+ Client define.Version
+ Server define.Version
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: versionCommand,
+ })
+ flags := versionCommand.Flags()
+ flags.StringVarP(&format, "format", "f", "", "Change the output format to JSON or a Go template")
+}
+
+func version(cmd *cobra.Command, args []string) error {
+ var (
+ v versionStruct
+ err error
+ )
+ v.Client, err = define.GetVersion()
+ if err != nil {
+ return errors.Wrapf(err, "unable to determine version")
+ }
+ // TODO we need to discuss how to implement
+ // this more. current endpoints dont have a
+ // version endpoint. maybe we use info?
+ //if remote {
+ // v.Server, err = getRemoteVersion(c)
+ // if err != nil {
+ // return err
+ // }
+ //} else {
+ v.Server = v.Client
+ //}
+
+ versionOutputFormat := format
+ if versionOutputFormat != "" {
+ if strings.Join(strings.Fields(versionOutputFormat), "") == "{{json.}}" {
+ versionOutputFormat = formats.JSONString
+ }
+ var out formats.Writer
+ switch versionOutputFormat {
+ case formats.JSONString:
+ out = formats.JSONStruct{Output: v}
+ return out.Out()
+ default:
+ out = formats.StdoutTemplate{Output: v, Template: versionOutputFormat}
+ err := out.Out()
+ if err != nil {
+ // On Failure, assume user is using older version of podman version --format and check client
+ out = formats.StdoutTemplate{Output: v.Client, Template: versionOutputFormat}
+ if err1 := out.Out(); err1 != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ }
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+ defer w.Flush()
+
+ if registry.IsRemote() {
+ if _, err := fmt.Fprintf(w, "Client:\n"); err != nil {
+ return err
+ }
+ formatVersion(w, v.Client)
+ if _, err := fmt.Fprintf(w, "\nServer:\n"); err != nil {
+ return err
+ }
+ formatVersion(w, v.Server)
+ } else {
+ formatVersion(w, v.Client)
+ }
+ return nil
+}
+
+func formatVersion(writer io.Writer, version define.Version) {
+ fmt.Fprintf(writer, "Version:\t%s\n", version.Version)
+ fmt.Fprintf(writer, "RemoteAPI Version:\t%d\n", version.RemoteAPIVersion)
+ fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion)
+ if version.GitCommit != "" {
+ fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit)
+ }
+ // Prints out the build time in readable format
+ if version.Built != 0 {
+ fmt.Fprintf(writer, "Built:\t%s\n", time.Unix(version.Built, 0).Format(time.ANSIC))
+ }
+
+ fmt.Fprintf(writer, "OS/Arch:\t%s\n", version.OsArch)
+}
diff --git a/cmd/podmanV2/utils/alias.go b/cmd/podmanV2/utils/alias.go
new file mode 100644
index 000000000..54b3c5e89
--- /dev/null
+++ b/cmd/podmanV2/utils/alias.go
@@ -0,0 +1,24 @@
+package utils
+
+import "github.com/spf13/pflag"
+
+// AliasFlags is a function to handle backwards compatability with old flags
+func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
+ switch name {
+ case "healthcheck-command":
+ name = "health-cmd"
+ case "healthcheck-interval":
+ name = "health-interval"
+ case "healthcheck-retries":
+ name = "health-retries"
+ case "healthcheck-start-period":
+ name = "health-start-period"
+ case "healthcheck-timeout":
+ name = "health-timeout"
+ case "net":
+ name = "network"
+ case "timeout":
+ name = "time"
+ }
+ return pflag.NormalizedName(name)
+}