summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
authorhaircommander <pehunt@redhat.com>2018-07-09 13:04:29 -0400
committerhaircommander <pehunt@redhat.com>2018-07-13 09:05:03 -0400
commit1aad3fd96b61705243e8f6ae35f65946916aa8a5 (patch)
treef4dfc5822357e04f556fd64ab8128a36619f1f17 /cmd/podman
parenta2dde5a50d21f8857a57d412a8a1c4c8f731a8d1 (diff)
downloadpodman-1aad3fd96b61705243e8f6ae35f65946916aa8a5.tar.gz
podman-1aad3fd96b61705243e8f6ae35f65946916aa8a5.tar.bz2
podman-1aad3fd96b61705243e8f6ae35f65946916aa8a5.zip
Podman pod create/rm commands with man page and tests.
Includes a very stripped down version of podman pod ps, just for testing Signed-off-by: haircommander <pehunt@redhat.com>
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/pod.go24
-rw-r--r--cmd/podman/pod_create.go117
-rw-r--r--cmd/podman/pod_ps.go227
-rw-r--r--cmd/podman/pod_rm.go89
5 files changed, 458 insertions, 0 deletions
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index f533a8b13..a83dc5fb4 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -71,6 +71,7 @@ func main() {
mountCommand,
pauseCommand,
psCommand,
+ podCommand,
portCommand,
pullCommand,
pushCommand,
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
new file mode 100644
index 000000000..f32ae4626
--- /dev/null
+++ b/cmd/podman/pod.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "github.com/urfave/cli"
+)
+
+var (
+ podDescription = `
+ podman pod
+
+ manage pods
+`
+ podCommand = cli.Command{
+ Name: "pod",
+ Usage: "Manage pods",
+ Description: podDescription,
+ UseShortOptionHandling: true,
+ Subcommands: []cli.Command{
+ podCreateCommand,
+ podPsCommand,
+ podRmCommand,
+ },
+ }
+)
diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go
new file mode 100644
index 000000000..9bbc60d4d
--- /dev/null
+++ b/cmd/podman/pod_create.go
@@ -0,0 +1,117 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/projectatomic/libpod/libpod"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var podCreateDescription = "Creates a new empty pod. The pod ID is then" +
+ " 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'."
+
+var podCreateFlags = []cli.Flag{
+ cli.BoolTFlag{
+ Name: "cgroup-to-ctr",
+ Usage: "Tells containers in this pod to use the cgroup created for the pod",
+ },
+ cli.StringFlag{
+ Name: "cgroup-parent",
+ Usage: "Optional parent cgroup for the pod",
+ },
+ cli.StringSliceFlag{
+ Name: "label-file",
+ Usage: "Read in a line delimited file of labels (default [])",
+ },
+ cli.StringSliceFlag{
+ Name: "label, l",
+ Usage: "Set metadata on pod (default [])",
+ },
+ cli.StringFlag{
+ Name: "name, n",
+ Usage: "Assign a name to the pod",
+ },
+ cli.StringFlag{
+ Name: "pod-id-file",
+ Usage: "Write the pod ID to the file",
+ },
+}
+
+var podCreateCommand = cli.Command{
+ Name: "create",
+ Usage: "create but do not start a pod",
+ Description: podCreateDescription,
+ Flags: podCreateFlags,
+ Action: podCreateCmd,
+ SkipArgReorder: true,
+ UseShortOptionHandling: true,
+}
+
+func podCreateCmd(c *cli.Context) error {
+ var options []libpod.PodCreateOption
+ var err error
+
+ if err = validateFlags(c, createFlags); err != nil {
+ return err
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ if c.IsSet("pod-id-file") {
+ if _, err = os.Stat(c.String("pod-id-file")); err == nil {
+ return errors.Errorf("pod id file exists. ensure another pod is not using it or delete %s", c.String("pod-id-file"))
+ }
+ if err = libpod.WriteFile("", c.String("pod-id-file")); err != nil {
+ return errors.Wrapf(err, "unable to write pod id file %s", c.String("pod-id-file"))
+ }
+ }
+ // BEGIN GetPodCreateOptions
+
+ // TODO make sure this is correct usage
+ if c.IsSet("cgroup-parent") {
+ options = append(options, libpod.WithPodCgroupParent(c.String("cgroup-parent")))
+ }
+
+ if c.Bool("cgroup-to-ctr") {
+ options = append(options, libpod.WithPodCgroups())
+ }
+ // LABEL VARIABLES
+ // TODO make sure this works as expected
+ labels, err := getAllLabels(c.StringSlice("label-file"), c.StringSlice("label"))
+ if err != nil {
+ return errors.Wrapf(err, "unable to process labels")
+ }
+ if len(labels) != 0 {
+ options = append(options, libpod.WithPodLabels(labels))
+ }
+
+ if c.IsSet("name") {
+ options = append(options, libpod.WithPodName(c.String("name")))
+ }
+
+ pod, err := runtime.NewPod(options...)
+ if err != nil {
+ return err
+ }
+
+ if c.IsSet("pod-id-file") {
+ err = libpod.WriteFile(pod.ID(), c.String("pod-id-file"))
+ if err != nil {
+ logrus.Error(err)
+ }
+ }
+
+ fmt.Printf("%s\n", pod.ID())
+
+ return nil
+}
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
new file mode 100644
index 000000000..f4e2d0ae5
--- /dev/null
+++ b/cmd/podman/pod_ps.go
@@ -0,0 +1,227 @@
+package main
+
+import (
+ "reflect"
+ "strings"
+
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/batchcontainer"
+ "github.com/projectatomic/libpod/cmd/podman/formats"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/projectatomic/libpod/libpod"
+ "github.com/urfave/cli"
+)
+
+var (
+ opts batchcontainer.PsOptions
+)
+
+type podPsOptions struct {
+ NoTrunc bool
+ Format string
+ Quiet bool
+ NumberOfContainers bool
+}
+
+type podPsTemplateParams struct {
+ ID string
+ Name string
+ NumberOfContainers int
+}
+
+// podPsJSONParams is used as a base structure for the psParams
+// If template output is requested, podPsJSONParams will be converted to
+// podPsTemplateParams.
+// podPsJSONParams will be populated by data from libpod.Container,
+// the members of the struct are the sama data types as their sources.
+type podPsJSONParams struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ NumberOfContainers int `json:"numberofcontainers"`
+}
+
+var (
+ podPsFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "no-trunc",
+ Usage: "Display the extended information",
+ },
+ cli.BoolFlag{
+ Name: "quiet, q",
+ Usage: "Print the numeric IDs of the pods only",
+ },
+ }
+ podPsDescription = "Prints out information about pods"
+ podPsCommand = cli.Command{
+ Name: "ps",
+ Aliases: []string{"ls", "list"},
+ Usage: "List pods",
+ Description: podPsDescription,
+ Flags: podPsFlags,
+ Action: podPsCmd,
+ UseShortOptionHandling: true,
+ }
+)
+
+func podPsCmd(c *cli.Context) error {
+ if err := validateFlags(c, podPsFlags); err != nil {
+ return err
+ }
+
+ if err := podCheckFlagsPassed(c); err != nil {
+ return errors.Wrapf(err, "error with flags passed")
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+
+ defer runtime.Shutdown(false)
+
+ if len(c.Args()) > 0 {
+ return errors.Errorf("too many arguments, ps takes no arguments")
+ }
+
+ format := genPodPsFormat(c.Bool("quiet"))
+
+ opts := podPsOptions{
+ Format: format,
+ NoTrunc: c.Bool("no-trunc"),
+ Quiet: c.Bool("quiet"),
+ }
+
+ var filterFuncs []libpod.PodFilter
+
+ pods, err := runtime.Pods(filterFuncs...)
+ if err != nil {
+ return err
+ }
+
+ return generatePodPsOutput(pods, opts, runtime)
+}
+
+// podCheckFlagsPassed checks if mutually exclusive flags are passed together
+func podCheckFlagsPassed(c *cli.Context) error {
+ // quiet, and format with Go template are mutually exclusive
+ flags := 0
+ if c.Bool("quiet") {
+ flags++
+ }
+ if flags > 1 {
+ return errors.Errorf("quiet, and format with Go template are mutually exclusive")
+ }
+ return nil
+}
+
+// generate the template based on conditions given
+func genPodPsFormat(quiet bool) string {
+ if quiet {
+ return formats.IDString
+ }
+ format := "table {{.ID}}\t{{.Name}}"
+ return format
+}
+
+func podPsToGeneric(templParams []podPsTemplateParams, JSONParams []podPsJSONParams) (genericParams []interface{}) {
+ if len(templParams) > 0 {
+ for _, v := range templParams {
+ genericParams = append(genericParams, interface{}(v))
+ }
+ return
+ }
+ for _, v := range JSONParams {
+ genericParams = append(genericParams, interface{}(v))
+ }
+ return
+}
+
+// generate the accurate header based on template given
+func (p *podPsTemplateParams) podHeaderMap() map[string]string {
+ v := reflect.Indirect(reflect.ValueOf(p))
+ values := make(map[string]string)
+
+ for i := 0; i < v.NumField(); i++ {
+ key := v.Type().Field(i).Name
+ value := key
+ if value == "ID" {
+ value = "Pod" + value
+ }
+ values[key] = strings.ToUpper(splitCamelCase(value))
+ }
+ return values
+}
+
+// getPodTemplateOutput returns the modified container information
+func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podPsTemplateParams, error) {
+ var (
+ psOutput []podPsTemplateParams
+ )
+
+ for _, psParam := range psParams {
+ podID := psParam.ID
+
+ if !opts.NoTrunc {
+ podID = shortID(psParam.ID)
+ }
+ params := podPsTemplateParams{
+ ID: podID,
+ Name: psParam.Name,
+ }
+
+ psOutput = append(psOutput, params)
+ }
+
+ return psOutput, nil
+}
+
+// getAndSortPodJSONOutput returns the container info in its raw, sorted form
+func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) ([]podPsJSONParams, error) {
+ var (
+ psOutput []podPsJSONParams
+ )
+
+ for _, pod := range pods {
+ ctrs, err := runtime.ContainersInPod(pod)
+ if err != nil {
+ return nil, err
+ }
+ ctrNum := len(ctrs)
+
+ params := podPsJSONParams{
+ ID: pod.ID(),
+ Name: pod.Name(),
+ NumberOfContainers: ctrNum,
+ }
+
+ psOutput = append(psOutput, params)
+ }
+ return psOutput, nil
+}
+
+func generatePodPsOutput(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) error {
+ if len(pods) == 0 && opts.Format != formats.JSONString {
+ return nil
+ }
+ psOutput, err := getAndSortPodJSONParams(pods, opts, runtime)
+ if err != nil {
+ return err
+ }
+ var out formats.Writer
+
+ switch opts.Format {
+ case formats.JSONString:
+ if err != nil {
+ return errors.Wrapf(err, "unable to create JSON for output")
+ }
+ out = formats.JSONStructArray{Output: podPsToGeneric([]podPsTemplateParams{}, psOutput)}
+ default:
+ psOutput, err := getPodTemplateOutput(psOutput, opts)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create output")
+ }
+ out = formats.StdoutTemplateArray{Output: podPsToGeneric(psOutput, []podPsJSONParams{}), Template: opts.Format, Fields: psOutput[0].podHeaderMap()}
+ }
+
+ return formats.Writer(out).Out()
+}
diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go
new file mode 100644
index 000000000..db2b1ee51
--- /dev/null
+++ b/cmd/podman/pod_rm.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/projectatomic/libpod/libpod"
+ "github.com/urfave/cli"
+)
+
+var (
+ podRmFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "force, f",
+ Usage: "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false",
+ },
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "Remove all pods",
+ },
+ }
+ podRmDescription = "Remove one or more pods"
+ podRmCommand = cli.Command{
+ Name: "rm",
+ Usage: fmt.Sprintf(`podman rm will remove one or more pods from the host. The pod name or ID can be used.
+ A pod with running or attached containrs will not be removed.
+ If --force, -f is specified, all containers will be stopped, then removed.`),
+ Description: podRmDescription,
+ Flags: podRmFlags,
+ Action: podRmCmd,
+ ArgsUsage: "",
+ UseShortOptionHandling: true,
+ }
+)
+
+// saveCmd saves the image to either docker-archive or oci
+func podRmCmd(c *cli.Context) error {
+ ctx := getContext()
+ if err := validateFlags(c, rmFlags); err != nil {
+ return err
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ args := c.Args()
+
+ if len(args) == 0 && !c.Bool("all") {
+ return errors.Errorf("specify one or more pods to remove")
+ }
+
+ var delPods []*libpod.Pod
+ var lastError error
+ if c.Bool("all") || c.Bool("a") {
+ delPods, err = runtime.Pods()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get pod list")
+ }
+ } else {
+ for _, i := range args {
+ pod, err := runtime.LookupPod(i)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ lastError = errors.Wrapf(err, "unable to find pods %s", i)
+ continue
+ }
+ delPods = append(delPods, pod)
+ }
+ }
+ force := c.IsSet("force")
+
+ for _, pod := range delPods {
+ err = runtime.RemovePod(ctx, pod, force, force)
+ if err != nil {
+ if lastError != nil {
+ fmt.Fprintln(os.Stderr, lastError)
+ }
+ lastError = errors.Wrapf(err, "failed to delete pod %v", pod.ID())
+ } else {
+ fmt.Println(pod.ID())
+ }
+ }
+ return lastError
+}