summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/autoupdate.go56
-rw-r--r--cmd/podman/build.go2
-rw-r--r--cmd/podman/cliconfig/config.go14
-rw-r--r--cmd/podman/commands.go1
-rw-r--r--cmd/podman/commit.go7
-rw-r--r--cmd/podman/images.go40
-rw-r--r--cmd/podman/main_local.go5
-rw-r--r--cmd/podman/main_local_unsupported.go44
-rw-r--r--cmd/podman/pod_ps.go126
-rw-r--r--cmd/podman/pod_top.go2
-rw-r--r--cmd/podman/runlabel.go2
-rw-r--r--cmd/podman/shared/create.go61
-rw-r--r--cmd/podman/shared/funcs_linux_test.go119
-rw-r--r--cmd/podman/shared/funcs_test.go112
-rw-r--r--cmd/podman/shared/pod.go126
-rw-r--r--cmd/podmanV2/README.md113
-rw-r--r--cmd/podmanV2/containers/container.go33
-rw-r--r--cmd/podmanV2/containers/exists.go43
-rw-r--r--cmd/podmanV2/containers/inspect.go42
-rw-r--r--cmd/podmanV2/containers/list.go34
-rw-r--r--cmd/podmanV2/containers/ps.go29
-rw-r--r--cmd/podmanV2/containers/wait.go82
-rw-r--r--cmd/podmanV2/images/history.go79
-rw-r--r--cmd/podmanV2/images/image.go33
-rw-r--r--cmd/podmanV2/images/images.go46
-rw-r--r--cmd/podmanV2/images/inspect.go124
-rw-r--r--cmd/podmanV2/images/list.go33
-rw-r--r--cmd/podmanV2/main.go87
-rw-r--r--cmd/podmanV2/networks/network.go33
-rw-r--r--cmd/podmanV2/parse/parse.go188
-rw-r--r--cmd/podmanV2/parse/parse_test.go152
-rw-r--r--cmd/podmanV2/pods/exists.go43
-rw-r--r--cmd/podmanV2/pods/pod.go33
-rw-r--r--cmd/podmanV2/pods/ps.go32
-rw-r--r--cmd/podmanV2/registry/registry.go100
-rw-r--r--cmd/podmanV2/report/templates.go61
-rw-r--r--cmd/podmanV2/root.go36
-rw-r--r--cmd/podmanV2/system/system.go33
-rw-r--r--cmd/podmanV2/volumes/create.go72
-rw-r--r--cmd/podmanV2/volumes/volume.go33
40 files changed, 2042 insertions, 269 deletions
diff --git a/cmd/podman/autoupdate.go b/cmd/podman/autoupdate.go
new file mode 100644
index 000000000..2cc1ae72e
--- /dev/null
+++ b/cmd/podman/autoupdate.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ autoUpdateCommand cliconfig.AutoUpdateValues
+ autoUpdateDescription = `Auto update containers according to their auto-update policy.
+
+Auto-update policies are specified with the "io.containers.autoupdate" label.`
+ _autoUpdateCommand = &cobra.Command{
+ Use: "auto-update [flags]",
+ Short: "Auto update containers according to their auto-update policy",
+ Args: noSubArgs,
+ Long: autoUpdateDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ restartCommand.InputArgs = args
+ restartCommand.GlobalFlags = MainGlobalOpts
+ return autoUpdateCmd(&restartCommand)
+ },
+ Example: `podman auto-update`,
+ }
+)
+
+func init() {
+ autoUpdateCommand.Command = _autoUpdateCommand
+ autoUpdateCommand.SetHelpTemplate(HelpTemplate())
+ autoUpdateCommand.SetUsageTemplate(UsageTemplate())
+}
+
+func autoUpdateCmd(c *cliconfig.RestartValues) error {
+ runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.DeferredShutdown(false)
+
+ units, failures := runtime.AutoUpdate()
+ for _, unit := range units {
+ fmt.Println(unit)
+ }
+ var finalErr error
+ if len(failures) > 0 {
+ finalErr = failures[0]
+ for _, e := range failures[1:] {
+ finalErr = errors.Errorf("%v\n%v", finalErr, e)
+ }
+ }
+ return finalErr
+}
diff --git a/cmd/podman/build.go b/cmd/podman/build.go
index b8b315c68..acd402fdd 100644
--- a/cmd/podman/build.go
+++ b/cmd/podman/build.go
@@ -342,6 +342,7 @@ func buildCmd(c *cliconfig.BuildValues) error {
}
options := imagebuildah.BuildOptions{
+ Architecture: c.Arch,
CommonBuildOpts: &buildOpts,
AdditionalTags: tags,
Annotations: c.Annotation,
@@ -359,6 +360,7 @@ func buildCmd(c *cliconfig.BuildValues) error {
Layers: layers,
NamespaceOptions: nsValues,
NoCache: c.NoCache,
+ OS: c.OS,
Out: stdout,
Output: output,
OutputFormat: format,
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 79917946a..3428746a9 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -54,6 +54,10 @@ type AttachValues struct {
SigProxy bool
}
+type AutoUpdateValues struct {
+ PodmanCommand
+}
+
type ImagesValues struct {
PodmanCommand
All bool
@@ -111,6 +115,7 @@ type CommitValues struct {
Pause bool
Quiet bool
IncludeVolumes bool
+ ImageIDFile string
}
type ContainersPrune struct {
@@ -470,10 +475,11 @@ type RefreshValues struct {
type RestartValues struct {
PodmanCommand
- All bool
- Latest bool
- Running bool
- Timeout uint
+ All bool
+ AutoUpdate bool
+ Latest bool
+ Running bool
+ Timeout uint
}
type RestoreValues struct {
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index d6018a6f4..dfa04315e 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -11,6 +11,7 @@ const remoteclient = false
// Commands that the local client implements
func getMainCommands() []*cobra.Command {
rootCommands := []*cobra.Command{
+ _autoUpdateCommand,
_cpCommand,
_playCommand,
_loginCommand,
diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go
index 7c35a4832..3ad3bd275 100644
--- a/cmd/podman/commit.go
+++ b/cmd/podman/commit.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+ "io/ioutil"
"strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
@@ -41,6 +42,7 @@ func init() {
flags := commitCommand.Flags()
flags.StringArrayVarP(&commitCommand.Change, "change", "c", []string{}, fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(ChangeCmds, " | ")))
flags.StringVarP(&commitCommand.Format, "format", "f", "oci", "`Format` of the image manifest and metadata")
+ flags.StringVarP(&commitCommand.ImageIDFile, "iidfile", "", "", "`file` to write the image ID to")
flags.StringVarP(&commitCommand.Message, "message", "m", "", "Set commit message for imported image")
flags.StringVarP(&commitCommand.Author, "author", "a", "", "Set the author for the image committed")
flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit")
@@ -70,6 +72,11 @@ func commitCmd(c *cliconfig.CommitValues) error {
if err != nil {
return err
}
+ if c.ImageIDFile != "" {
+ if err = ioutil.WriteFile(c.ImageIDFile, []byte(iid), 0644); err != nil {
+ return errors.Wrapf(err, "failed to write image ID to file %q", c.ImageIDFile)
+ }
+ }
fmt.Println(iid)
return nil
}
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index de61690ae..ed33402ab 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -13,8 +13,8 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/adapter"
- "github.com/docker/go-units"
- "github.com/opencontainers/go-digest"
+ units "github.com/docker/go-units"
+ digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -34,14 +34,15 @@ type imagesTemplateParams struct {
}
type imagesJSONParams struct {
- ID string `json:"id"`
- Name []string `json:"names"`
- Digest digest.Digest `json:"digest"`
- Digests []digest.Digest `json:"digests"`
- Created time.Time `json:"created"`
- Size *uint64 `json:"size"`
- ReadOnly bool `json:"readonly"`
- History []string `json:"history"`
+ ID string `json:"ID"`
+ Name []string `json:"Names"`
+ Created string `json:"Created"`
+ Digest digest.Digest `json:"Digest"`
+ Digests []digest.Digest `json:"Digests"`
+ CreatedAt time.Time `json:"CreatedAt"`
+ Size *uint64 `json:"Size"`
+ ReadOnly bool `json:"ReadOnly"`
+ History []string `json:"History"`
}
type imagesOptions struct {
@@ -155,7 +156,7 @@ func imagesCmd(c *cliconfig.ImagesValues) error {
return errors.New("can not specify an image and a filter")
}
filters := c.Filter
- if len(filters) < 1 {
+ if len(filters) < 1 && len(image) > 0 {
filters = append(filters, fmt.Sprintf("reference=%s", image))
}
@@ -344,14 +345,15 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage)
size = nil
}
params := imagesJSONParams{
- ID: img.ID(),
- Name: img.Names(),
- Digest: img.Digest(),
- Digests: img.Digests(),
- Created: img.Created(),
- Size: size,
- ReadOnly: img.IsReadOnly(),
- History: img.NamesHistory(),
+ ID: img.ID(),
+ Name: img.Names(),
+ Digest: img.Digest(),
+ Digests: img.Digests(),
+ Created: units.HumanDuration(time.Since(img.Created())) + " ago",
+ CreatedAt: img.Created(),
+ Size: size,
+ ReadOnly: img.IsReadOnly(),
+ History: img.NamesHistory(),
}
imagesOutput = append(imagesOutput, params)
}
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go
index e5b87754b..79b5e5af7 100644
--- a/cmd/podman/main_local.go
+++ b/cmd/podman/main_local.go
@@ -67,10 +67,11 @@ func init() {
// -s is deprecated due to conflict with -s on subcommands
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
rootCmd.PersistentFlags().StringArrayVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
- rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console")
+ rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
- rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output")
+ rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output (default false)")
+ markFlagHidden(rootCmd.PersistentFlags(), "trace")
}
func setSyslog() error {
diff --git a/cmd/podman/main_local_unsupported.go b/cmd/podman/main_local_unsupported.go
new file mode 100644
index 000000000..75728627e
--- /dev/null
+++ b/cmd/podman/main_local_unsupported.go
@@ -0,0 +1,44 @@
+// +build !remoteclient,!linux
+
+package main
+
+// The ONLY purpose of this file is to allow the subpackage to compile. Don’t expect anything
+// to work.
+
+import (
+ "syscall"
+
+ "github.com/spf13/cobra"
+)
+
+const remote = false
+
+func setSyslog() error {
+ return nil
+}
+
+func profileOn(cmd *cobra.Command) error {
+ return nil
+}
+
+func profileOff(cmd *cobra.Command) error {
+ return nil
+}
+
+func setupRootless(cmd *cobra.Command, args []string) error {
+ return nil
+}
+
+func setRLimits() error {
+ return nil
+}
+
+func setUMask() {
+ // Be sure we can create directories with 0755 mode.
+ syscall.Umask(0022)
+}
+
+// checkInput can be used to verify any of the globalopt values
+func checkInput() error {
+ return nil
+}
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
index d7731e983..7acbd6888 100644
--- a/cmd/podman/pod_ps.go
+++ b/cmd/podman/pod_ps.go
@@ -4,7 +4,6 @@ import (
"fmt"
"reflect"
"sort"
- "strconv"
"strings"
"time"
@@ -13,7 +12,6 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/adapter"
- "github.com/containers/libpod/pkg/util"
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -29,8 +27,6 @@ const (
NUM_CTR_INFO = 10
)
-type PodFilter func(*adapter.Pod) bool
-
var (
bc_opts shared.PsOptions
)
@@ -174,29 +170,23 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
opts.Format = genPodPsFormat(c)
- var filterFuncs []PodFilter
- if c.Filter != "" {
- filters := strings.Split(c.Filter, ",")
- for _, f := range filters {
- filterSplit := strings.Split(f, "=")
- if len(filterSplit) < 2 {
- return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
- }
- generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
- if err != nil {
- return errors.Wrapf(err, "invalid filter")
- }
- filterFuncs = append(filterFuncs, generatedFunc)
- }
- }
-
var pods []*adapter.Pod
+
+ // If latest is set true filters are ignored.
if c.Latest {
pod, err := runtime.GetLatestPod()
if err != nil {
return err
}
pods = append(pods, pod)
+ return generatePodPsOutput(pods, opts)
+ }
+
+ if c.Filter != "" {
+ pods, err = runtime.GetPodsWithFilters(c.Filter)
+ if err != nil {
+ return err
+ }
} else {
pods, err = runtime.GetAllPods()
if err != nil {
@@ -204,19 +194,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- podsFiltered := make([]*adapter.Pod, 0, len(pods))
- for _, pod := range pods {
- include := true
- for _, filter := range filterFuncs {
- include = include && filter(pod)
- }
-
- if include {
- podsFiltered = append(podsFiltered, pod)
- }
- }
-
- return generatePodPsOutput(podsFiltered, opts)
+ return generatePodPsOutput(pods, opts)
}
// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
@@ -235,88 +213,6 @@ func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error {
return nil
}
-func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) bool, error) {
- switch filter {
- case "ctr-ids":
- return func(p *adapter.Pod) bool {
- ctrIds, err := p.AllContainersByID()
- if err != nil {
- return false
- }
- return util.StringInSlice(filterValue, ctrIds)
- }, nil
- case "ctr-names":
- return func(p *adapter.Pod) bool {
- ctrs, err := p.AllContainers()
- if err != nil {
- return false
- }
- for _, ctr := range ctrs {
- if filterValue == ctr.Name() {
- return true
- }
- }
- return false
- }, nil
- case "ctr-number":
- return func(p *adapter.Pod) bool {
- ctrIds, err := p.AllContainersByID()
- if err != nil {
- return false
- }
-
- fVint, err2 := strconv.Atoi(filterValue)
- if err2 != nil {
- return false
- }
- return len(ctrIds) == fVint
- }, nil
- case "ctr-status":
- if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) {
- return nil, errors.Errorf("%s is not a valid status", filterValue)
- }
- return func(p *adapter.Pod) bool {
- ctr_statuses, err := p.Status()
- if err != nil {
- return false
- }
- for _, ctr_status := range ctr_statuses {
- state := ctr_status.String()
- if ctr_status == define.ContainerStateConfigured {
- state = "created"
- }
- if state == filterValue {
- return true
- }
- }
- return false
- }, nil
- case "id":
- return func(p *adapter.Pod) bool {
- return strings.Contains(p.ID(), filterValue)
- }, nil
- case "name":
- return func(p *adapter.Pod) bool {
- return strings.Contains(p.Name(), filterValue)
- }, nil
- case "status":
- if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
- return nil, errors.Errorf("%s is not a valid pod status", filterValue)
- }
- return func(p *adapter.Pod) bool {
- status, err := p.GetPodStatus()
- if err != nil {
- return false
- }
- if strings.ToLower(status) == filterValue {
- return true
- }
- return false
- }, nil
- }
- return nil, errors.Errorf("%s is an invalid filter", filter)
-}
-
// generate the template based on conditions given
func genPodPsFormat(c *cliconfig.PodPsValues) string {
format := ""
diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go
index fcd9c4f3c..734472817 100644
--- a/cmd/podman/pod_top.go
+++ b/cmd/podman/pod_top.go
@@ -42,7 +42,7 @@ func init() {
podTopCommand.SetHelpTemplate(HelpTemplate())
podTopCommand.SetUsageTemplate(UsageTemplate())
flags := podTopCommand.Flags()
- flags.BoolVarP(&podTopCommand.Latest, "latest,", "l", false, "Act on the latest pod podman is aware of")
+ flags.BoolVarP(&podTopCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
flags.BoolVar(&podTopCommand.ListDescriptors, "list-descriptors", false, "")
markFlagHidden(flags, "list-descriptors")
}
diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go
index 358538155..1ec4da650 100644
--- a/cmd/podman/runlabel.go
+++ b/cmd/podman/runlabel.go
@@ -49,7 +49,7 @@ func init() {
flags.StringVar(&runlabelCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.BoolVar(&runlabelCommand.Display, "display", false, "Preview the command that the label would run")
flags.BoolVar(&runlabelCommand.Replace, "replace", false, "Replace existing container with a new one from the image")
- flags.StringVar(&runlabelCommand.Name, "name", "", "Assign a name to the container")
+ flags.StringVarP(&runlabelCommand.Name, "name", "n", "", "Assign a name to the container")
flags.StringVar(&runlabelCommand.Opt1, "opt1", "", "Optional parameter to pass for install")
flags.StringVar(&runlabelCommand.Opt2, "opt2", "", "Optional parameter to pass for install")
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 11b2bd027..cec837af6 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -18,6 +18,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations"
+ "github.com/containers/libpod/pkg/autoupdate"
envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/inspect"
@@ -25,6 +26,7 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/seccomp"
cc "github.com/containers/libpod/pkg/spec"
+ systemdGen "github.com/containers/libpod/pkg/systemd/generate"
"github.com/containers/libpod/pkg/util"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
@@ -69,6 +71,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
imageName := ""
+ rawImageName := ""
var imageData *inspect.ImageData = nil
// Set the storage if there is no rootfs specified
@@ -78,9 +81,8 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
writer = os.Stderr
}
- name := ""
if len(c.InputArgs) != 0 {
- name = c.InputArgs[0]
+ rawImageName = c.InputArgs[0]
} else {
return nil, nil, errors.Errorf("error, image name not provided")
}
@@ -97,11 +99,11 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
ArchitectureChoice: overrideArch,
}
- newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
+ newImage, err := runtime.ImageRuntime().New(ctx, rawImageName, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType)
if err != nil {
return nil, nil, err
}
- imageData, err = newImage.Inspect(ctx)
+ imageData, err = newImage.InspectNoSize(ctx)
if err != nil {
return nil, nil, err
}
@@ -174,11 +176,32 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
- createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, imageData)
+ createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, rawImageName, imageData)
if err != nil {
return nil, nil, err
}
+ // (VR): Ideally we perform the checks _before_ pulling the image but that
+ // would require some bigger code refactoring of `ParseCreateOpts` and the
+ // logic here. But as the creation code will be consolidated in the future
+ // and given auto updates are experimental, we can live with that for now.
+ // In the end, the user may only need to correct the policy or the raw image
+ // name.
+ autoUpdatePolicy, autoUpdatePolicySpecified := createConfig.Labels[autoupdate.Label]
+ if autoUpdatePolicySpecified {
+ if _, err := autoupdate.LookupPolicy(autoUpdatePolicy); err != nil {
+ return nil, nil, err
+ }
+ // Now we need to make sure we're having a fully-qualified image reference.
+ if rootfs != "" {
+ return nil, nil, errors.Errorf("auto updates do not work with --rootfs")
+ }
+ // Make sure the input image is a docker.
+ if err := autoupdate.ValidateImageReference(rawImageName); err != nil {
+ return nil, nil, err
+ }
+ }
+
// Because parseCreateOpts does derive anything from the image, we add health check
// at this point. The rest is done by WithOptions.
createConfig.HealthCheck = healthCheck
@@ -270,7 +293,7 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[
// Parses CLI options related to container creation into a config which can be
// parsed into an OCI runtime spec
-func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
+func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, rawImageName string, data *inspect.ImageData) (*cc.CreateConfig, error) {
var (
inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64
@@ -481,12 +504,15 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
"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 nil, errors.Wrap(err, "error parsing host environment variables")
+ }
+
// Start with env-host
if c.Bool("env-host") {
- osEnv, err := envLib.ParseSlice(os.Environ())
- if err != nil {
- return nil, errors.Wrap(err, "error parsing host environment variables")
- }
env = envLib.Join(env, osEnv)
}
@@ -534,6 +560,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
}
}
+ if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists {
+ labels[systemdGen.EnvVariable] = systemdUnit
+ }
+
// ANNOTATIONS
annotations := make(map[string]string)
@@ -764,11 +794,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
Entrypoint: entrypoint,
Env: env,
// ExposedPorts: ports,
- Init: c.Bool("init"),
- InitPath: c.String("init-path"),
- Image: imageName,
- ImageID: imageID,
- Interactive: c.Bool("interactive"),
+ Init: c.Bool("init"),
+ InitPath: c.String("init-path"),
+ Image: imageName,
+ RawImageName: rawImageName,
+ ImageID: imageID,
+ Interactive: c.Bool("interactive"),
// IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6
Labels: labels,
// LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet
diff --git a/cmd/podman/shared/funcs_linux_test.go b/cmd/podman/shared/funcs_linux_test.go
new file mode 100644
index 000000000..88571153f
--- /dev/null
+++ b/cmd/podman/shared/funcs_linux_test.go
@@ -0,0 +1,119 @@
+package shared
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenerateCommand(t *testing.T) {
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo \"hello world\""
+ correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
+ assert.Nil(t, err)
+ assert.Equal(t, "hello world", newCommand[11])
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandCheckSubstitution(t *testing.T) {
+ type subsTest struct {
+ input string
+ expected string
+ shouldFail bool
+ }
+
+ absTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestAbsolutePath")
+ assert.Nil(t, err, "error creating tempfile")
+ defer os.Remove(absTmpFile.Name())
+
+ relTmpFile, err := ioutil.TempFile("./", "podmanRunlabelTestRelativePath")
+ assert.Nil(t, err, "error creating tempfile")
+ defer os.Remove(relTmpFile.Name())
+ relTmpCmd, err := filepath.Abs(relTmpFile.Name())
+ assert.Nil(t, err, "error getting absolute path for relative tmpfile")
+
+ // this has a (low) potential of race conditions but no other way
+ removedTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestRemove")
+ assert.Nil(t, err, "error creating tempfile")
+ os.Remove(removedTmpFile.Name())
+
+ absTmpCmd := fmt.Sprintf("%s --flag1 --flag2 --args=foo", absTmpFile.Name())
+ tests := []subsTest{
+ {
+ input: "docker run -it alpine:latest",
+ expected: "/proc/self/exe run -it alpine:latest",
+ shouldFail: false,
+ },
+ {
+ input: "podman run -it alpine:latest",
+ expected: "/proc/self/exe run -it alpine:latest",
+ shouldFail: false,
+ },
+ {
+ input: absTmpCmd,
+ expected: absTmpCmd,
+ shouldFail: false,
+ },
+ {
+ input: "./" + relTmpFile.Name(),
+ expected: relTmpCmd,
+ shouldFail: false,
+ },
+ {
+ input: "ls -la",
+ expected: "ls -la",
+ shouldFail: false,
+ },
+ {
+ input: removedTmpFile.Name(),
+ expected: "",
+ shouldFail: true,
+ },
+ }
+
+ for _, test := range tests {
+ newCommand, err := GenerateCommand(test.input, "foo", "bar", "")
+ if test.shouldFail {
+ assert.NotNil(t, err)
+ } else {
+ assert.Nil(t, err)
+ }
+ assert.Equal(t, test.expected, strings.Join(newCommand, " "))
+ }
+}
+
+func TestGenerateCommandPath(t *testing.T) {
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
+ newCommand, _ := GenerateCommand(inputCommand, "foo", "bar", "")
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandNoSetName(t *testing.T) {
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
+ assert.Nil(t, err)
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandNoName(t *testing.T) {
+ inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
+ assert.Nil(t, err)
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
+
+func TestGenerateCommandAlreadyPodman(t *testing.T) {
+ inputCommand := "podman run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
+ newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
+ assert.Nil(t, err)
+ assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
+}
diff --git a/cmd/podman/shared/funcs_test.go b/cmd/podman/shared/funcs_test.go
index c05348242..dd856166e 100644
--- a/cmd/podman/shared/funcs_test.go
+++ b/cmd/podman/shared/funcs_test.go
@@ -1,11 +1,6 @@
package shared
import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
"testing"
"github.com/containers/libpod/pkg/util"
@@ -17,113 +12,6 @@ var (
imageName = "bar"
)
-func TestGenerateCommand(t *testing.T) {
- inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo \"hello world\""
- correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
- newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
- assert.Nil(t, err)
- assert.Equal(t, "hello world", newCommand[11])
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandCheckSubstitution(t *testing.T) {
- type subsTest struct {
- input string
- expected string
- shouldFail bool
- }
-
- absTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestAbsolutePath")
- assert.Nil(t, err, "error creating tempfile")
- defer os.Remove(absTmpFile.Name())
-
- relTmpFile, err := ioutil.TempFile("./", "podmanRunlabelTestRelativePath")
- assert.Nil(t, err, "error creating tempfile")
- defer os.Remove(relTmpFile.Name())
- relTmpCmd, err := filepath.Abs(relTmpFile.Name())
- assert.Nil(t, err, "error getting absolute path for relative tmpfile")
-
- // this has a (low) potential of race conditions but no other way
- removedTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestRemove")
- assert.Nil(t, err, "error creating tempfile")
- os.Remove(removedTmpFile.Name())
-
- absTmpCmd := fmt.Sprintf("%s --flag1 --flag2 --args=foo", absTmpFile.Name())
- tests := []subsTest{
- {
- input: "docker run -it alpine:latest",
- expected: "/proc/self/exe run -it alpine:latest",
- shouldFail: false,
- },
- {
- input: "podman run -it alpine:latest",
- expected: "/proc/self/exe run -it alpine:latest",
- shouldFail: false,
- },
- {
- input: absTmpCmd,
- expected: absTmpCmd,
- shouldFail: false,
- },
- {
- input: "./" + relTmpFile.Name(),
- expected: relTmpCmd,
- shouldFail: false,
- },
- {
- input: "ls -la",
- expected: "ls -la",
- shouldFail: false,
- },
- {
- input: removedTmpFile.Name(),
- expected: "",
- shouldFail: true,
- },
- }
-
- for _, test := range tests {
- newCommand, err := GenerateCommand(test.input, "foo", "bar", "")
- if test.shouldFail {
- assert.NotNil(t, err)
- } else {
- assert.Nil(t, err)
- }
- assert.Equal(t, test.expected, strings.Join(newCommand, " "))
- }
-}
-
-func TestGenerateCommandPath(t *testing.T) {
- inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand, _ := GenerateCommand(inputCommand, "foo", "bar", "")
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandNoSetName(t *testing.T) {
- inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install"
- newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
- assert.Nil(t, err)
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandNoName(t *testing.T) {
- inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
- newCommand, err := GenerateCommand(inputCommand, "foo", "", "")
- assert.Nil(t, err)
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
-func TestGenerateCommandAlreadyPodman(t *testing.T) {
- inputCommand := "podman run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
- correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand, err := GenerateCommand(inputCommand, "foo", "bar", "")
- assert.Nil(t, err)
- assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
-}
-
func TestGenerateRunEnvironment(t *testing.T) {
opts := make(map[string]string)
opts["opt1"] = "one"
diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index 7b0b497fc..3046953b5 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -2,9 +2,11 @@ package shared
import (
"strconv"
+ "strings"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
@@ -134,4 +136,128 @@ func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
return portBindings, nil
}
+// GetPodsWithFilters uses the cliconfig to categorize if the latest pod is required.
+func GetPodsWithFilters(r *libpod.Runtime, filters string) ([]*libpod.Pod, error) {
+ filterFuncs, err := GenerateFilterFunction(r, strings.Split(filters, ","))
+ if err != nil {
+ return nil, err
+ }
+ return FilterAllPodsWithFilterFunc(r, filterFuncs...)
+}
+
+// FilterAllPodsWithFilterFunc retrieves all pods
+// Filters can be provided which will determine which pods are included in the
+// output. Multiple filters are handled by ANDing their output, so only pods
+// matching all filters are returned
+func FilterAllPodsWithFilterFunc(r *libpod.Runtime, filters ...libpod.PodFilter) ([]*libpod.Pod, error) {
+ pods, err := r.Pods(filters...)
+ if err != nil {
+ return nil, err
+ }
+ return pods, nil
+}
+
+// GenerateFilterFunction basically gets the filters based on the input by the user
+// and filter the pod list based on the criteria.
+func GenerateFilterFunction(r *libpod.Runtime, filters []string) ([]libpod.PodFilter, error) {
+ var filterFuncs []libpod.PodFilter
+ for _, f := range filters {
+ filterSplit := strings.Split(f, "=")
+ if len(filterSplit) < 2 {
+ return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+ generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+
+ return filterFuncs, nil
+}
+func generatePodFilterFuncs(filter, filterValue string) (
+ func(pod *libpod.Pod) bool, error) {
+ switch filter {
+ case "ctr-ids":
+ return func(p *libpod.Pod) bool {
+ ctrIds, err := p.AllContainersByID()
+ if err != nil {
+ return false
+ }
+ return util.StringInSlice(filterValue, ctrIds)
+ }, nil
+ case "ctr-names":
+ return func(p *libpod.Pod) bool {
+ ctrs, err := p.AllContainers()
+ if err != nil {
+ return false
+ }
+ for _, ctr := range ctrs {
+ if filterValue == ctr.Name() {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "ctr-number":
+ return func(p *libpod.Pod) bool {
+ ctrIds, err := p.AllContainersByID()
+ if err != nil {
+ return false
+ }
+
+ fVint, err2 := strconv.Atoi(filterValue)
+ if err2 != nil {
+ return false
+ }
+ return len(ctrIds) == fVint
+ }, nil
+ case "ctr-status":
+ if !util.StringInSlice(filterValue,
+ []string{"created", "restarting", "running", "paused",
+ "exited", "unknown"}) {
+ return nil, errors.Errorf("%s is not a valid status", filterValue)
+ }
+ return func(p *libpod.Pod) bool {
+ ctr_statuses, err := p.Status()
+ if err != nil {
+ return false
+ }
+ for _, ctr_status := range ctr_statuses {
+ state := ctr_status.String()
+ if ctr_status == define.ContainerStateConfigured {
+ state = "created"
+ }
+ if state == filterValue {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "id":
+ return func(p *libpod.Pod) bool {
+ return strings.Contains(p.ID(), filterValue)
+ }, nil
+ case "name":
+ return func(p *libpod.Pod) bool {
+ return strings.Contains(p.Name(), filterValue)
+ }, nil
+ case "status":
+ if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
+ return nil, errors.Errorf("%s is not a valid pod status", filterValue)
+ }
+ return func(p *libpod.Pod) bool {
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return false
+ }
+ if strings.ToLower(status) == filterValue {
+ return true
+ }
+ return false
+ }, nil
+ }
+ return nil, errors.Errorf("%s is an invalid filter", filter)
+}
+
var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
diff --git a/cmd/podmanV2/README.md b/cmd/podmanV2/README.md
new file mode 100644
index 000000000..a17e6f850
--- /dev/null
+++ b/cmd/podmanV2/README.md
@@ -0,0 +1,113 @@
+# Adding a podman V2 commands
+
+## Build podman V2
+
+```shell script
+$ cd $GOPATH/src/github.com/containers/libpod/cmd/podmanV2
+```
+If you wish to include the libpod library in your program,
+```shell script
+$ go build -tags 'ABISupport' .
+```
+The `--remote` flag may be used to connect to the Podman service using the API.
+Otherwise, direct calls will be made to the Libpod library.
+```shell script
+$ go build -tags '!ABISupport' .
+```
+The Libpod library is not linked into the executable.
+All calls are made via the API and `--remote=False` is an error condition.
+
+## Adding a new command `podman manifests`
+```shell script
+$ mkdir -p $GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests
+```
+Create the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests/manifest.go```
+```go
+package manifests
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman _manifests_
+ manifestCmd = &cobra.Command{
+ Use: "manifest",
+ Short: "Manage manifests",
+ Long: "Manage manifests",
+ Example: "podman manifests IMAGE",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists, // Report error if there is no sub command given
+ }
+)
+func init() {
+ // Subscribe command to podman
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ // _podman manifest_ will support both ABIMode and TunnelMode
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ // The definition for this command
+ Command: manifestCmd,
+ })
+ // Setup cobra templates, sub commands will inherit
+ manifestCmd.SetHelpTemplate(registry.HelpTemplate())
+ manifestCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+// preRunE populates the image engine for sub commands
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewImageEngine(cmd, args)
+ return err
+}
+```
+To "wire" in the `manifest` command, edit the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/main.go``` to add:
+```go
+package main
+
+import _ "github.com/containers/libpod/cmd/podmanV2/manifests"
+```
+
+## Adding a new sub command `podman manifests list`
+Create the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests/inspect.go```
+```go
+package manifests
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman manifests _inspect_
+ inspectCmd = &cobra.Command{
+ Use: "inspect IMAGE",
+ Short: "Display manifest from image",
+ Long: "Displays the low-level information on a manifest identified by image name or ID",
+ RunE: inspect,
+ Example: "podman manifest DEADBEEF",
+ }
+)
+
+func init() {
+ // Subscribe inspect sub command to manifest command
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ // _podman manifest inspect_ will support both ABIMode and TunnelMode
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ // The definition for this command
+ Command: inspectCmd,
+ Parent: manifestCmd,
+ })
+
+ // This is where you would configure the cobra flags using inspectCmd.Flags()
+}
+
+// Business logic: cmd is inspectCmd, args is the positional arguments from os.Args
+func inspect(cmd *cobra.Command, args []string) error {
+ // Business logic using registry.ImageEngine
+ // Do not pull from libpod directly use the domain objects and types
+ return nil
+}
+```
diff --git a/cmd/podmanV2/containers/container.go b/cmd/podmanV2/containers/container.go
new file mode 100644
index 000000000..6b44f2a3e
--- /dev/null
+++ b/cmd/podmanV2/containers/container.go
@@ -0,0 +1,33 @@
+package containers
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _container_
+ containerCmd = &cobra.Command{
+ Use: "container",
+ Short: "Manage containers",
+ Long: "Manage containers",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: containerCmd,
+ })
+ containerCmd.SetHelpTemplate(registry.HelpTemplate())
+ containerCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/containers/exists.go b/cmd/podmanV2/containers/exists.go
new file mode 100644
index 000000000..22c798fcd
--- /dev/null
+++ b/cmd/podmanV2/containers/exists.go
@@ -0,0 +1,43 @@
+package containers
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.`
+
+ existsCommand = &cobra.Command{
+ Use: "exists CONTAINER",
+ Short: "Check if a container exists in local storage",
+ Long: containerExistsDescription,
+ Example: `podman container exists containerID
+ podman container exists myctr || podman run --name myctr [etc...]`,
+ RunE: exists,
+ Args: cobra.ExactArgs(1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: existsCommand,
+ Parent: containerCmd,
+ })
+}
+
+func exists(cmd *cobra.Command, args []string) error {
+ response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0])
+ if err != nil {
+ return err
+ }
+ if !response.Value {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go
new file mode 100644
index 000000000..635be4789
--- /dev/null
+++ b/cmd/podmanV2/containers/inspect.go
@@ -0,0 +1,42 @@
+package containers
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman container _inspect_
+ inspectCmd = &cobra.Command{
+ 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,
+ RunE: inspect,
+ Example: `podman container inspect myCtr
+ podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: inspectCmd,
+ Parent: containerCmd,
+ })
+}
+
+func inspectPreRunE(cmd *cobra.Command, args []string) (err error) {
+ err = preRunE(cmd, args)
+ if err != nil {
+ return
+ }
+
+ _, err = registry.NewImageEngine(cmd, args)
+ return err
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/containers/list.go b/cmd/podmanV2/containers/list.go
new file mode 100644
index 000000000..630d9bbc7
--- /dev/null
+++ b/cmd/podmanV2/containers/list.go
@@ -0,0 +1,34 @@
+package containers
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman container _list_
+ listCmd = &cobra.Command{
+ Use: "list",
+ Aliases: []string{"ls"},
+ Args: cobra.NoArgs,
+ Short: "List containers",
+ Long: "Prints out information about the containers",
+ RunE: containers,
+ Example: `podman container list -a
+ podman container list -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
+ podman container list --size --sort names`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: listCmd,
+ Parent: containerCmd,
+ })
+}
+
+func containers(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/containers/ps.go b/cmd/podmanV2/containers/ps.go
new file mode 100644
index 000000000..ce3d66c51
--- /dev/null
+++ b/cmd/podmanV2/containers/ps.go
@@ -0,0 +1,29 @@
+package containers
+
+import (
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman _ps_
+ psCmd = &cobra.Command{
+ Use: "ps",
+ Args: cobra.NoArgs,
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ PersistentPreRunE: preRunE,
+ RunE: containers,
+ Example: strings.Replace(listCmd.Example, "container list", "ps", -1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: psCmd,
+ })
+}
diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go
new file mode 100644
index 000000000..27acb3348
--- /dev/null
+++ b/cmd/podmanV2/containers/wait.go
@@ -0,0 +1,82 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "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 (
+ waitDescription = `Block until one or more containers stop and then print their exit codes.
+`
+ waitCommand = &cobra.Command{
+ Use: "wait [flags] CONTAINER [CONTAINER...]",
+ Short: "Block on one or more containers",
+ Long: waitDescription,
+ RunE: wait,
+ Example: `podman wait --latest
+ podman wait --interval 5000 ctrID
+ podman wait ctrID1 ctrID2`,
+ }
+)
+
+var (
+ waitFlags = entities.WaitOptions{}
+ waitCondition string
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: waitCommand,
+ Parent: containerCmd,
+ })
+
+ flags := waitCommand.Flags()
+ flags.DurationVarP(&waitFlags.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion")
+ flags.BoolVarP(&waitFlags.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on")
+ if registry.EngineOpts.EngineMode == entities.ABIMode {
+ // TODO: This is the same as V1. We could skip creating the flag altogether in V2...
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func wait(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ )
+ if waitFlags.Latest && len(args) > 0 {
+ return errors.New("cannot combine latest flag and arguments")
+ }
+ if waitFlags.Interval == 0 {
+ return errors.New("interval must be greater then 0")
+ }
+
+ waitFlags.Condition, err = define.StringToContainerStatus(waitCondition)
+ if err != nil {
+ return err
+ }
+
+ responses, err := registry.ContainerEngine().ContainerWait(context.Background(), args, waitFlags)
+ if err != nil {
+ return err
+ }
+ for _, r := range responses {
+ if r.Error == nil {
+ fmt.Println(r.Id)
+ }
+ }
+ for _, r := range responses {
+ if r.Error != nil {
+ fmt.Println(err)
+ }
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/images/history.go b/cmd/podmanV2/images/history.go
new file mode 100644
index 000000000..c75ae6ddc
--- /dev/null
+++ b/cmd/podmanV2/images/history.go
@@ -0,0 +1,79 @@
+package images
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "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"
+)
+
+var (
+ long = `Displays the history of an image.
+
+ The information can be printed out in an easy to read, or user specified format, and can be truncated.`
+
+ // podman _history_
+ historyCmd = &cobra.Command{
+ Use: "history [flags] IMAGE",
+ Short: "Show history of a specified image",
+ Long: long,
+ Example: "podman history quay.io/fedora/fedora",
+ Args: cobra.ExactArgs(1),
+ PersistentPreRunE: preRunE,
+ RunE: history,
+ }
+)
+
+var cmdFlags = struct {
+ Human bool
+ NoTrunc bool
+ Quiet bool
+ Format string
+}{}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: historyCmd,
+ })
+
+ historyCmd.SetHelpTemplate(registry.HelpTemplate())
+ historyCmd.SetUsageTemplate(registry.UsageTemplate())
+ flags := historyCmd.Flags()
+ flags.StringVar(&cmdFlags.Format, "format", "", "Change the output to JSON or a Go template")
+ flags.BoolVarP(&cmdFlags.Human, "human", "H", true, "Display sizes and dates in human readable format")
+ flags.BoolVar(&cmdFlags.NoTrunc, "no-trunc", false, "Do not truncate the output")
+ flags.BoolVar(&cmdFlags.NoTrunc, "notruncate", false, "Do not truncate the output")
+ flags.BoolVarP(&cmdFlags.Quiet, "quiet", "q", false, "Display the numeric IDs only")
+}
+
+func history(cmd *cobra.Command, args []string) error {
+ results, err := registry.ImageEngine().History(context.Background(), args[0], entities.ImageHistoryOptions{})
+ if err != nil {
+ return err
+ }
+
+ row := "{{slice $x.ID 0 12}}\t{{toRFC3339 $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{$x.Size}}\t{{$x.Comment}}\n"
+ if cmdFlags.Human {
+ row = "{{slice $x.ID 0 12}}\t{{toHumanDuration $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{toHumanSize $x.Size}}\t{{$x.Comment}}\n"
+ }
+ format := "{{range $y, $x := . }}" + row + "{{end}}"
+
+ tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format))
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+
+ _, _ = w.Write(report.ReportHeader("id", "created", "created by", "size", "comment"))
+ err = tmpl.Execute(w, results.Layers)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Failed to print report"))
+ }
+ w.Flush()
+ return nil
+}
diff --git a/cmd/podmanV2/images/image.go b/cmd/podmanV2/images/image.go
new file mode 100644
index 000000000..a15c3e826
--- /dev/null
+++ b/cmd/podmanV2/images/image.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 (
+ // Command: podman _image_
+ imageCmd = &cobra.Command{
+ Use: "image",
+ Short: "Manage images",
+ Long: "Manage images",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imageCmd,
+ })
+ imageCmd.SetHelpTemplate(registry.HelpTemplate())
+ imageCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewImageEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go
new file mode 100644
index 000000000..a1e56396a
--- /dev/null
+++ b/cmd/podmanV2/images/images.go
@@ -0,0 +1,46 @@
+package images
+
+import (
+ "strings"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // podman _images_
+ imagesCmd = &cobra.Command{
+ Use: strings.Replace(listCmd.Use, "list", "images", 1),
+ Short: listCmd.Short,
+ Long: listCmd.Long,
+ PersistentPreRunE: preRunE,
+ RunE: images,
+ Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
+ }
+
+ imagesOpts = entities.ImageListOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imagesCmd,
+ })
+ imagesCmd.SetHelpTemplate(registry.HelpTemplate())
+ imagesCmd.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := imagesCmd.Flags()
+ flags.BoolVarP(&imagesOpts.All, "all", "a", false, "Show all images (default hides intermediate images)")
+ flags.BoolVar(&imagesOpts.Digests, "digests", false, "Show digests")
+ flags.StringSliceVarP(&imagesOpts.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
+ flags.StringVar(&imagesOpts.Format, "format", "", "Change the output format to JSON or a Go template")
+ flags.BoolVarP(&imagesOpts.Noheading, "noheading", "n", false, "Do not print column headings")
+ // TODO Need to learn how to deal with second name being a string instead of a char.
+ // This needs to be "no-trunc, notruncate"
+ flags.BoolVar(&imagesOpts.NoTrunc, "no-trunc", false, "Do not truncate output")
+ flags.BoolVar(&imagesOpts.NoTrunc, "notruncate", false, "Do not truncate output")
+ flags.BoolVarP(&imagesOpts.Quiet, "quiet", "q", false, "Display only image IDs")
+ flags.StringVar(&imagesOpts.Sort, "sort", "created", "Sort by created, id, repository, size, or tag")
+ flags.BoolVarP(&imagesOpts.History, "history", "", false, "Display the image name history")
+}
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
new file mode 100644
index 000000000..2ecbbb201
--- /dev/null
+++ b/cmd/podmanV2/images/inspect.go
@@ -0,0 +1,124 @@
+package images
+
+import (
+ "strings"
+
+ "github.com/containers/buildah/pkg/formats"
+ "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,
+ Example: `podman image inspect alpine`,
+ }
+
+ containerEngine entities.ContainerEngine
+)
+
+// 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.EngineOpts.EngineMode == entities.ABIMode {
+ // TODO: This is the same as V1. We could skip creating the flag altogether in V2...
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+const (
+ inspectTypeContainer = "container"
+ inspectTypeImage = "image"
+ inspectAll = "all"
+)
+
+func imageInspect(cmd *cobra.Command, args []string) error {
+ inspectType := inspectTypeImage
+ latestContainer := inspectOpts.Latest
+
+ if len(args) == 0 && !latestContainer {
+ return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name")
+ }
+
+ if len(args) > 0 && latestContainer {
+ 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)
+ }
+
+ 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 strings.Contains(outputFormat, ".Dst") {
+ outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1)
+ }
+ if strings.Contains(outputFormat, ".ImageID") {
+ outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1)
+ }
+ _ = 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
+}
diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go
new file mode 100644
index 000000000..cfdfaaed2
--- /dev/null
+++ b/cmd/podmanV2/images/list.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 (
+ // Command: podman image _list_
+ listCmd = &cobra.Command{
+ Use: "list [flag] [IMAGE]",
+ Aliases: []string{"ls"},
+ Short: "List images in local storage",
+ Long: "Lists images previously pulled to the system or created on the system.",
+ RunE: images,
+ Example: `podman image list --format json
+ podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}"
+ podman image list --filter dangling=true`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: listCmd,
+ Parent: imageCmd,
+ })
+}
+
+func images(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
new file mode 100644
index 000000000..24f21d804
--- /dev/null
+++ b/cmd/podmanV2/main.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+ "runtime"
+ "strings"
+
+ _ "github.com/containers/libpod/cmd/podmanV2/containers"
+ _ "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/volumes"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ if err := libpod.SetXdgDirs(); err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ initCobra()
+}
+
+func initCobra() {
+ switch runtime.GOOS {
+ case "darwin":
+ fallthrough
+ case "windows":
+ registry.EngineOpts.EngineMode = entities.TunnelMode
+ case "linux":
+ registry.EngineOpts.EngineMode = entities.ABIMode
+ default:
+ logrus.Errorf("%s is not a supported OS", runtime.GOOS)
+ os.Exit(1)
+ }
+
+ // TODO: Is there a Cobra way to "peek" at os.Args?
+ for _, v := range os.Args {
+ if strings.HasPrefix(v, "--remote") {
+ registry.EngineOpts.EngineMode = entities.TunnelMode
+ }
+ }
+
+ cobra.OnInitialize(func() {})
+}
+
+func main() {
+ fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands))
+ for _, c := range registry.Commands {
+ if Contains(registry.EngineOpts.EngineMode, c.Mode) {
+ parent := rootCmd
+ if c.Parent != nil {
+ parent = c.Parent
+ }
+ parent.AddCommand(c.Command)
+ }
+ }
+
+ Execute()
+ os.Exit(0)
+}
+
+func Contains(item interface{}, slice interface{}) bool {
+ s := reflect.ValueOf(slice)
+
+ switch s.Kind() {
+ case reflect.Array:
+ fallthrough
+ case reflect.Slice:
+ break
+ default:
+ return false
+ }
+
+ for i := 0; i < s.Len(); i++ {
+ if s.Index(i).Interface() == item {
+ return true
+ }
+ }
+ return false
+}
diff --git a/cmd/podmanV2/networks/network.go b/cmd/podmanV2/networks/network.go
new file mode 100644
index 000000000..fc92d2321
--- /dev/null
+++ b/cmd/podmanV2/networks/network.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 (
+ // Command: podman _network_
+ cmd = &cobra.Command{
+ Use: "network",
+ Short: "Manage networks",
+ Long: "Manage networks",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: cmd,
+ })
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/parse/parse.go b/cmd/podmanV2/parse/parse.go
new file mode 100644
index 000000000..03cda268c
--- /dev/null
+++ b/cmd/podmanV2/parse/parse.go
@@ -0,0 +1,188 @@
+//nolint
+// most of these validate and parse functions have been taken from projectatomic/docker
+// and modified for cri-o
+package parse
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+ "net/url"
+ "os"
+ "regexp"
+ "strings"
+
+ "github.com/pkg/errors"
+)
+
+const (
+ Protocol_TCP Protocol = 0
+ Protocol_UDP Protocol = 1
+)
+
+type Protocol int32
+
+// PortMapping specifies the port mapping configurations of a sandbox.
+type PortMapping struct {
+ // Protocol of the port mapping.
+ Protocol Protocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=runtime.Protocol" json:"protocol,omitempty"`
+ // Port number within the container. Default: 0 (not specified).
+ ContainerPort int32 `protobuf:"varint,2,opt,name=container_port,json=containerPort,proto3" json:"container_port,omitempty"`
+ // Port number on the host. Default: 0 (not specified).
+ HostPort int32 `protobuf:"varint,3,opt,name=host_port,json=hostPort,proto3" json:"host_port,omitempty"`
+ // Host IP.
+ HostIp string `protobuf:"bytes,4,opt,name=host_ip,json=hostIp,proto3" json:"host_ip,omitempty"`
+}
+
+// Note: for flags that are in the form <number><unit>, use the RAMInBytes function
+// from the units package in docker/go-units/size.go
+
+var (
+ whiteSpaces = " \t"
+ alphaRegexp = regexp.MustCompile(`[a-zA-Z]`)
+ domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
+)
+
+// validateExtraHost validates that the specified string is a valid extrahost and returns it.
+// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
+// for add-host flag
+func ValidateExtraHost(val string) (string, error) { //nolint
+ // allow for IPv6 addresses in extra hosts by only splitting on first ":"
+ arr := strings.SplitN(val, ":", 2)
+ if len(arr) != 2 || len(arr[0]) == 0 {
+ return "", fmt.Errorf("bad format for add-host: %q", val)
+ }
+ if _, err := validateIPAddress(arr[1]); err != nil {
+ return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
+ }
+ return val, nil
+}
+
+// validateIPAddress validates an Ip address.
+// for dns, ip, and ip6 flags also
+func validateIPAddress(val string) (string, error) {
+ var ip = net.ParseIP(strings.TrimSpace(val))
+ if ip != nil {
+ return ip.String(), nil
+ }
+ return "", fmt.Errorf("%s is not an ip address", val)
+}
+
+func ValidateDomain(val string) (string, error) {
+ if alphaRegexp.FindString(val) == "" {
+ return "", fmt.Errorf("%s is not a valid domain", val)
+ }
+ ns := domainRegexp.FindSubmatch([]byte(val))
+ if len(ns) > 0 && len(ns[1]) < 255 {
+ return string(ns[1]), nil
+ }
+ return "", fmt.Errorf("%s is not a valid domain", val)
+}
+
+// GetAllLabels retrieves all labels given a potential label file and a number
+// of labels provided from the command line.
+func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
+ labels := make(map[string]string)
+ for _, file := range labelFile {
+ // Use of parseEnvFile still seems safe, as it's missing the
+ // extra parsing logic of parseEnv.
+ // There's an argument that we SHOULD be doing that parsing for
+ // all environment variables, even those sourced from files, but
+ // that would require a substantial rework.
+ if err := parseEnvFile(labels, file); err != nil {
+ // FIXME: parseEnvFile is using parseEnv, so we need to add extra
+ // logic for labels.
+ return nil, err
+ }
+ }
+ for _, label := range inputLabels {
+ split := strings.SplitN(label, "=", 2)
+ if split[0] == "" {
+ return nil, errors.Errorf("invalid label format: %q", label)
+ }
+ value := ""
+ if len(split) > 1 {
+ value = split[1]
+ }
+ labels[split[0]] = value
+ }
+ return labels, nil
+}
+
+func parseEnv(env map[string]string, line string) error {
+ data := strings.SplitN(line, "=", 2)
+
+ // catch invalid variables such as "=" or "=A"
+ if data[0] == "" {
+ return errors.Errorf("invalid environment variable: %q", line)
+ }
+
+ // trim the front of a variable, but nothing else
+ name := strings.TrimLeft(data[0], whiteSpaces)
+ if strings.ContainsAny(name, whiteSpaces) {
+ return errors.Errorf("name %q has white spaces, poorly formatted name", name)
+ }
+
+ if len(data) > 1 {
+ env[name] = data[1]
+ } else {
+ if strings.HasSuffix(name, "*") {
+ name = strings.TrimSuffix(name, "*")
+ for _, e := range os.Environ() {
+ part := strings.SplitN(e, "=", 2)
+ if len(part) < 2 {
+ continue
+ }
+ if strings.HasPrefix(part[0], name) {
+ env[part[0]] = part[1]
+ }
+ }
+ } else {
+ // if only a pass-through variable is given, clean it up.
+ if val, ok := os.LookupEnv(name); ok {
+ env[name] = val
+ }
+ }
+ }
+ return nil
+}
+
+// parseEnvFile reads a file with environment variables enumerated by lines
+func parseEnvFile(env map[string]string, filename string) error {
+ fh, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer fh.Close()
+
+ scanner := bufio.NewScanner(fh)
+ for scanner.Scan() {
+ // trim the line from all leading whitespace first
+ line := strings.TrimLeft(scanner.Text(), whiteSpaces)
+ // line is not empty, and not starting with '#'
+ if len(line) > 0 && !strings.HasPrefix(line, "#") {
+ if err := parseEnv(env, line); err != nil {
+ return err
+ }
+ }
+ }
+ return scanner.Err()
+}
+
+// ValidateFileName returns an error if filename contains ":"
+// as it is currently not supported
+func ValidateFileName(filename string) error {
+ if strings.Contains(filename, ":") {
+ return errors.Errorf("invalid filename (should not contain ':') %q", filename)
+ }
+ return nil
+}
+
+// ValidURL checks a string urlStr is a url or not
+func ValidURL(urlStr string) error {
+ _, err := url.ParseRequestURI(urlStr)
+ if err != nil {
+ return errors.Wrapf(err, "invalid url path: %q", urlStr)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/parse/parse_test.go b/cmd/podmanV2/parse/parse_test.go
new file mode 100644
index 000000000..a6ddc2be9
--- /dev/null
+++ b/cmd/podmanV2/parse/parse_test.go
@@ -0,0 +1,152 @@
+//nolint
+// most of these validate and parse functions have been taken from projectatomic/docker
+// and modified for cri-o
+package parse
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+var (
+ Var1 = []string{"ONE=1", "TWO=2"}
+)
+
+func createTmpFile(content []byte) (string, error) {
+ tmpfile, err := ioutil.TempFile(os.TempDir(), "unittest")
+ if err != nil {
+ return "", err
+ }
+
+ if _, err := tmpfile.Write(content); err != nil {
+ return "", err
+
+ }
+ if err := tmpfile.Close(); err != nil {
+ return "", err
+ }
+ return tmpfile.Name(), nil
+}
+
+func TestValidateExtraHost(t *testing.T) {
+ type args struct {
+ val string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ //2001:0db8:85a3:0000:0000:8a2e:0370:7334
+ {name: "good-ipv4", args: args{val: "foobar:192.168.1.1"}, want: "foobar:192.168.1.1", wantErr: false},
+ {name: "bad-ipv4", args: args{val: "foobar:999.999.999.99"}, want: "", wantErr: true},
+ {name: "bad-ipv4", args: args{val: "foobar:999.999.999"}, want: "", wantErr: true},
+ {name: "noname-ipv4", args: args{val: "192.168.1.1"}, want: "", wantErr: true},
+ {name: "noname-ipv4", args: args{val: ":192.168.1.1"}, want: "", wantErr: true},
+ {name: "noip", args: args{val: "foobar:"}, want: "", wantErr: true},
+ {name: "noip", args: args{val: "foobar"}, want: "", wantErr: true},
+ {name: "good-ipv6", args: args{val: "foobar:2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "foobar:2001:0db8:85a3:0000:0000:8a2e:0370:7334", wantErr: false},
+ {name: "bad-ipv6", args: args{val: "foobar:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "", wantErr: true},
+ {name: "bad-ipv6", args: args{val: "foobar:0db8:85a3:0000:0000:8a2e:0370:7334.0000.0000.000"}, want: "", wantErr: true},
+ {name: "noname-ipv6", args: args{val: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "", wantErr: true},
+ {name: "noname-ipv6", args: args{val: ":2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, want: "", wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ValidateExtraHost(tt.args.val)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ValidateExtraHost() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("ValidateExtraHost() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_validateIPAddress(t *testing.T) {
+ type args struct {
+ val string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ {name: "ipv4-good", args: args{val: "192.168.1.1"}, want: "192.168.1.1", wantErr: false},
+ {name: "ipv4-bad", args: args{val: "192.168.1.1.1"}, want: "", wantErr: true},
+ {name: "ipv4-bad", args: args{val: "192."}, want: "", wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := validateIPAddress(tt.args.val)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("validateIPAddress() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("validateIPAddress() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestValidateFileName(t *testing.T) {
+ type args struct {
+ filename string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {name: "good", args: args{filename: "/some/rand/path"}, wantErr: false},
+ {name: "good", args: args{filename: "some/rand/path"}, wantErr: false},
+ {name: "good", args: args{filename: "/"}, wantErr: false},
+ {name: "bad", args: args{filename: "/:"}, wantErr: true},
+ {name: "bad", args: args{filename: ":/"}, wantErr: true},
+ {name: "bad", args: args{filename: "/some/rand:/path"}, wantErr: true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := ValidateFileName(tt.args.filename); (err != nil) != tt.wantErr {
+ t.Errorf("ValidateFileName() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestGetAllLabels(t *testing.T) {
+ fileLabels := []string{}
+ labels, _ := GetAllLabels(fileLabels, Var1)
+ assert.Equal(t, len(labels), 2)
+}
+
+func TestGetAllLabelsBadKeyValue(t *testing.T) {
+ inLabels := []string{"=badValue", "="}
+ fileLabels := []string{}
+ _, err := GetAllLabels(fileLabels, inLabels)
+ assert.Error(t, err, assert.AnError)
+}
+
+func TestGetAllLabelsBadLabelFile(t *testing.T) {
+ fileLabels := []string{"/foobar5001/be"}
+ _, err := GetAllLabels(fileLabels, Var1)
+ assert.Error(t, err, assert.AnError)
+}
+
+func TestGetAllLabelsFile(t *testing.T) {
+ content := []byte("THREE=3")
+ tFile, err := createTmpFile(content)
+ defer os.Remove(tFile)
+ assert.NoError(t, err)
+ fileLabels := []string{tFile}
+ result, _ := GetAllLabels(fileLabels, Var1)
+ assert.Equal(t, len(result), 3)
+}
diff --git a/cmd/podmanV2/pods/exists.go b/cmd/podmanV2/pods/exists.go
new file mode 100644
index 000000000..e37f2ebd7
--- /dev/null
+++ b/cmd/podmanV2/pods/exists.go
@@ -0,0 +1,43 @@
+package pods
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ podExistsDescription = `If the named pod exists in local storage, podman pod exists exits with 0, otherwise the exit code will be 1.`
+
+ existsCommand = &cobra.Command{
+ Use: "exists POD",
+ Short: "Check if a pod exists in local storage",
+ Long: podExistsDescription,
+ RunE: exists,
+ Args: cobra.ExactArgs(1),
+ Example: `podman pod exists podID
+ podman pod exists mypod || podman pod create --name mypod`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: existsCommand,
+ Parent: podCmd,
+ })
+}
+
+func exists(cmd *cobra.Command, args []string) error {
+ response, err := registry.ContainerEngine().PodExists(context.Background(), args[0])
+ if err != nil {
+ return err
+ }
+ if !response.Value {
+ os.Exit(1)
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/pods/pod.go b/cmd/podmanV2/pods/pod.go
new file mode 100644
index 000000000..81c0d33e1
--- /dev/null
+++ b/cmd/podmanV2/pods/pod.go
@@ -0,0 +1,33 @@
+package pods
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _pod_
+ podCmd = &cobra.Command{
+ Use: "pod",
+ Short: "Manage pods",
+ Long: "Manage pods",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: podCmd,
+ })
+ podCmd.SetHelpTemplate(registry.HelpTemplate())
+ podCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go
new file mode 100644
index 000000000..d4c625b2e
--- /dev/null
+++ b/cmd/podmanV2/pods/ps.go
@@ -0,0 +1,32 @@
+package pods
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ psDescription = "List all pods on system including their names, ids and current state."
+
+ // Command: podman pod _ps_
+ psCmd = &cobra.Command{
+ Use: "ps",
+ Aliases: []string{"ls", "list"},
+ Short: "list pods",
+ Long: psDescription,
+ RunE: pods,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: psCmd,
+ Parent: podCmd,
+ })
+}
+
+func pods(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
new file mode 100644
index 000000000..793d520a8
--- /dev/null
+++ b/cmd/podmanV2/registry/registry.go
@@ -0,0 +1,100 @@
+package registry
+
+import (
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+type CliCommand struct {
+ Mode []entities.EngineMode
+ Command *cobra.Command
+ Parent *cobra.Command
+}
+
+var (
+ Commands []CliCommand
+
+ imageEngine entities.ImageEngine
+ containerEngine entities.ContainerEngine
+
+ EngineOpts entities.EngineOptions
+ GlobalFlags entities.EngineFlags
+)
+
+// HelpTemplate returns the help template for podman commands
+// This uses the short and long options.
+// command should not use this.
+func HelpTemplate() string {
+ return `{{.Short}}
+
+Description:
+ {{.Long}}
+
+{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
+}
+
+// UsageTemplate returns the usage template for podman commands
+// This blocks the displaying of the global options. The main podman
+// command should not use this.
+func UsageTemplate() string {
+ return `Usage(v2):{{if (and .Runnable (not .HasAvailableSubCommands))}}
+ {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+ {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
+
+Aliases:
+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Examples:
+ {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
+
+Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
+
+Flags:
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
+{{end}}
+`
+}
+
+func ImageEngine() entities.ImageEngine {
+ return imageEngine
+}
+
+// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions
+func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) {
+ if imageEngine == nil {
+ EngineOpts.FlagSet = cmd.Flags()
+ engine, err := infra.NewImageEngine(EngineOpts)
+ if err != nil {
+ return nil, err
+ }
+ imageEngine = engine
+ }
+ return imageEngine, nil
+}
+
+func ContainerEngine() entities.ContainerEngine {
+ return containerEngine
+}
+
+// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions
+func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) {
+ if containerEngine == nil {
+ EngineOpts.FlagSet = cmd.Flags()
+ engine, err := infra.NewContainerEngine(EngineOpts)
+ if err != nil {
+ return nil, err
+ }
+ containerEngine = engine
+ }
+ return containerEngine, nil
+}
+
+func SubCommandExists(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 {
+ return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0])
+ }
+ return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath())
+}
diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go
new file mode 100644
index 000000000..dc43d4f9b
--- /dev/null
+++ b/cmd/podmanV2/report/templates.go
@@ -0,0 +1,61 @@
+package report
+
+import (
+ "strings"
+ "text/template"
+ "time"
+
+ "github.com/docker/go-units"
+)
+
+var defaultFuncMap = template.FuncMap{
+ "ellipsis": func(s string, length int) string {
+ if len(s) > length {
+ return s[:length-3] + "..."
+ }
+ return s
+ },
+ // TODO: Remove on Go 1.14 port
+ "slice": func(s string, i, j int) string {
+ if i > j || len(s) < i {
+ return s
+ }
+ if len(s) < j {
+ return s[i:]
+ }
+ return s[i:j]
+ },
+ "toRFC3339": func(t int64) string {
+ return time.Unix(t, 0).Format(time.RFC3339)
+ },
+ "toHumanDuration": func(t int64) string {
+ return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago"
+ },
+ "toHumanSize": func(sz int64) string {
+ return units.HumanSize(float64(sz))
+ },
+}
+
+func ReportHeader(columns ...string) []byte {
+ hdr := make([]string, len(columns))
+ for i, h := range columns {
+ hdr[i] = strings.ToUpper(h)
+ }
+ return []byte(strings.Join(hdr, "\t") + "\n")
+}
+
+func AppendFuncMap(funcMap template.FuncMap) template.FuncMap {
+ merged := PodmanTemplateFuncs()
+ for k, v := range funcMap {
+ merged[k] = v
+ }
+ return merged
+}
+
+func PodmanTemplateFuncs() template.FuncMap {
+ merged := make(template.FuncMap)
+ for k, v := range defaultFuncMap {
+ merged[k] = v
+ }
+ return merged
+}
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
new file mode 100644
index 000000000..24b083b9f
--- /dev/null
+++ b/cmd/podmanV2/root.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/version"
+ "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,
+}
+
+func init() {
+ // Override default --help information of `--version` global flag}
+ var dummyVersion bool
+ rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman")
+ rootCmd.PersistentFlags().StringVarP(&registry.EngineOpts.Uri, "remote", "r", "", "URL to access podman service")
+ rootCmd.PersistentFlags().StringSliceVar(&registry.EngineOpts.Identities, "identity", []string{}, "path to SSH identity file")
+}
+
+func Execute() {
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go
new file mode 100644
index 000000000..30ed328e8
--- /dev/null
+++ b/cmd/podmanV2/system/system.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 (
+ // Command: podman _system_
+ cmd = &cobra.Command{
+ Use: "system",
+ Short: "Manage podman",
+ Long: "Manage podman",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: cmd,
+ })
+ cmd.SetHelpTemplate(registry.HelpTemplate())
+ cmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/volumes/create.go b/cmd/podmanV2/volumes/create.go
new file mode 100644
index 000000000..91181dd03
--- /dev/null
+++ b/cmd/podmanV2/volumes/create.go
@@ -0,0 +1,72 @@
+package volumes
+
+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/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ createDescription = `If using the default driver, "local", the volume will be created on the host in the volumes directory under container storage.`
+
+ createCommand = &cobra.Command{
+ Use: "create [flags] [NAME]",
+ Short: "Create a new volume",
+ Long: createDescription,
+ RunE: create,
+ Example: `podman volume create myvol
+ podman volume create
+ podman volume create --label foo=bar myvol`,
+ }
+)
+
+var (
+ createOpts = entities.VolumeCreateOptions{}
+ opts = struct {
+ Label []string
+ Opts []string
+ }{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: createCommand,
+ Parent: volumeCmd,
+ })
+ flags := createCommand.Flags()
+ flags.StringVar(&createOpts.Driver, "driver", "", "Specify volume driver name (default local)")
+ flags.StringSliceVarP(&opts.Label, "label", "l", []string{}, "Set metadata for a volume (default [])")
+ flags.StringArrayVarP(&opts.Opts, "opt", "o", []string{}, "Set driver specific options (default [])")
+}
+
+func create(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ )
+ if len(args) > 1 {
+ return errors.Errorf("too many arguments, create takes at most 1 argument")
+ }
+ if len(args) > 0 {
+ createOpts.Name = args[0]
+ }
+ createOpts.Label, err = parse.GetAllLabels([]string{}, opts.Label)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+ createOpts.Options, err = parse.GetAllLabels([]string{}, opts.Opts)
+ if err != nil {
+ return errors.Wrapf(err, "unable to process options")
+ }
+ response, err := registry.ContainerEngine().VolumeCreate(context.Background(), createOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println(response.IdOrName)
+ return nil
+}
diff --git a/cmd/podmanV2/volumes/volume.go b/cmd/podmanV2/volumes/volume.go
new file mode 100644
index 000000000..84abe3d24
--- /dev/null
+++ b/cmd/podmanV2/volumes/volume.go
@@ -0,0 +1,33 @@
+package volumes
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: podman _volume_
+ volumeCmd = &cobra.Command{
+ Use: "volume",
+ Short: "Manage volumes",
+ Long: "Volumes are created in and can be shared between containers",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: volumeCmd,
+ })
+ volumeCmd.SetHelpTemplate(registry.HelpTemplate())
+ volumeCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}