summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/create.go52
-rw-r--r--cmd/podman/images.go28
-rw-r--r--cmd/podman/main.go13
-rw-r--r--cmd/podman/pod.go25
-rw-r--r--cmd/podman/pod_create.go109
-rw-r--r--cmd/podman/pod_ps.go600
-rw-r--r--cmd/podman/pod_rm.go103
-rw-r--r--cmd/podman/ps.go2
-rw-r--r--cmd/podman/run.go14
-rw-r--r--cmd/podman/sigproxy.go2
-rw-r--r--cmd/podman/start.go2
11 files changed, 920 insertions, 30 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index d61f85442..6a70e3f43 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -19,9 +19,11 @@ import (
"github.com/projectatomic/libpod/libpod"
"github.com/projectatomic/libpod/libpod/image"
ann "github.com/projectatomic/libpod/pkg/annotations"
+ "github.com/projectatomic/libpod/pkg/apparmor"
"github.com/projectatomic/libpod/pkg/inspect"
cc "github.com/projectatomic/libpod/pkg/spec"
"github.com/projectatomic/libpod/pkg/util"
+ libpodVersion "github.com/projectatomic/libpod/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -194,6 +196,56 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
}
}
+ if config.ApparmorProfile == "" {
+ // Unless specified otherwise, make sure that the default AppArmor
+ // profile is installed. To avoid redundantly loading the profile
+ // on each invocation, check if it's loaded before installing it.
+ // Suffix the profile with the current libpod version to allow
+ // loading the new, potentially updated profile after an update.
+ profile := fmt.Sprintf("%s-%s", apparmor.DefaultLibpodProfile, libpodVersion.Version)
+
+ loadProfile := func() error {
+ isLoaded, err := apparmor.IsLoaded(profile)
+ if err != nil {
+ return err
+ }
+ if !isLoaded {
+ err = apparmor.InstallDefault(profile)
+ if err != nil {
+ return err
+ }
+
+ }
+ return nil
+ }
+
+ if err := loadProfile(); err != nil {
+ switch err {
+ case apparmor.ErrApparmorUnsupported:
+ // do not set the profile when AppArmor isn't supported
+ logrus.Debugf("AppArmor is not supported: setting empty profile")
+ default:
+ return err
+ }
+ } else {
+ logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile)
+ config.ApparmorProfile = profile
+ }
+ } else {
+ isLoaded, err := apparmor.IsLoaded(config.ApparmorProfile)
+ if err != nil {
+ switch err {
+ case apparmor.ErrApparmorUnsupported:
+ return fmt.Errorf("profile specified but AppArmor is not supported")
+ default:
+ return fmt.Errorf("error checking if AppArmor profile is loaded: %v", err)
+ }
+ }
+ if !isLoaded {
+ return fmt.Errorf("specified AppArmor profile '%s' is not loaded", config.ApparmorProfile)
+ }
+ }
+
if config.SeccompProfilePath == "" {
if _, err := os.Stat(libpod.SeccompOverridePath); err == nil {
config.SeccompProfilePath = libpod.SeccompOverridePath
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index fdf2eb02c..092326b1f 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -7,7 +7,8 @@ import (
"strings"
"time"
- "github.com/containers/storage"
+ "github.com/sirupsen/logrus"
+
"github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -259,12 +260,16 @@ func sortImagesOutput(sortBy string, imagesOutput imagesSorted) imagesSorted {
}
// getImagesTemplateOutput returns the images information to be printed in human readable format
-func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image, opts imagesOptions, storageLayers []storage.Layer) (imagesOutput imagesSorted) {
+func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) (imagesOutput imagesSorted) {
for _, img := range images {
// If all is false and the image doesn't have a name, check to see if the top layer of the image is a parent
// to another image's top layer. If it is, then it is an intermediate image so don't print out if the --all flag
// is not set.
- if !opts.all && len(img.Names()) == 0 && !layerIsLeaf(storageLayers, img.TopLayer()) {
+ isParent, err := img.IsParent()
+ if err != nil {
+ logrus.Errorf("error checking if image is a parent %q: %v", img.ID(), err)
+ }
+ if !opts.all && len(img.Names()) == 0 && isParent {
continue
}
createdTime := img.Created()
@@ -325,17 +330,13 @@ func generateImagesOutput(ctx context.Context, runtime *libpod.Runtime, images [
return nil
}
var out formats.Writer
- storageLayers, err := runtime.GetLayers()
- if err != nil {
- return errors.Wrap(err, "error getting layers from store")
- }
switch opts.format {
case formats.JSONString:
imagesOutput := getImagesJSONOutput(ctx, runtime, images)
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
default:
- imagesOutput := getImagesTemplateOutput(ctx, runtime, images, opts, storageLayers)
+ imagesOutput := getImagesTemplateOutput(ctx, runtime, images, opts)
out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.outputformat, Fields: imagesOutput[0].HeaderMap()}
}
return formats.Writer(out).Out()
@@ -391,14 +392,3 @@ func CreateFilterFuncs(ctx context.Context, r *libpod.Runtime, c *cli.Context, i
}
return filterFuncs, nil
}
-
-// layerIsLeaf goes through the layers in the store and checks if "layer" is
-// the parent of any other layer in store.
-func layerIsLeaf(storageLayers []storage.Layer, layer string) bool {
- for _, storeLayer := range storageLayers {
- if storeLayer.Parent == layer {
- return false
- }
- }
- return true
-}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 4d841e0f2..3dbf196c2 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -14,7 +14,9 @@ import (
"github.com/projectatomic/libpod/pkg/rootless"
"github.com/projectatomic/libpod/version"
"github.com/sirupsen/logrus"
+ lsyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/urfave/cli"
+ "log/syslog"
)
// This is populated by the Makefile from the VERSION file
@@ -69,6 +71,7 @@ func main() {
mountCommand,
pauseCommand,
psCommand,
+ podCommand,
portCommand,
pullCommand,
pushCommand,
@@ -94,6 +97,12 @@ func main() {
}
app.Before = func(c *cli.Context) error {
+ if c.GlobalBool("syslog") {
+ hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+ if err == nil {
+ logrus.AddHook(hook)
+ }
+ }
logLevel := c.GlobalString("log-level")
if logLevel != "" {
level, err := logrus.ParseLevel(logLevel)
@@ -187,6 +196,10 @@ func main() {
Name: "storage-opt",
Usage: "used to pass an option to the storage driver",
},
+ cli.BoolFlag{
+ Name: "syslog",
+ Usage: "output logging information to syslog as well as the console",
+ },
}
if _, err := os.Stat("/etc/containers/registries.conf"); err != nil {
if os.IsNotExist(err) {
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
new file mode 100644
index 000000000..6cf2920a5
--- /dev/null
+++ b/cmd/podman/pod.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+ "github.com/urfave/cli"
+)
+
+var (
+ podDescription = `
+ podman pod
+
+ Manage container pods.
+ Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.
+`
+ 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..f86faa409
--- /dev/null
+++ b/cmd/podman/pod_create.go
@@ -0,0 +1,109 @@
+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.StringFlag{
+ Name: "cgroup-parent",
+ Usage: "Set 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 a new empty 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"))
+ }
+ }
+
+ if c.IsSet("cgroup-parent") {
+ options = append(options, libpod.WithPodCgroupParent(c.String("cgroup-parent")))
+ }
+
+ 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")))
+ }
+
+ // always have containers use pod cgroups
+ options = append(options, libpod.WithPodCgroups())
+
+ 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..470810901
--- /dev/null
+++ b/cmd/podman/pod_ps.go
@@ -0,0 +1,600 @@
+package main
+
+import (
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/docker/go-units"
+ "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/projectatomic/libpod/pkg/util"
+ "github.com/urfave/cli"
+)
+
+const (
+ STOPPED = "Stopped"
+ RUNNING = "Running"
+ PAUSED = "Paused"
+ EXITED = "Exited"
+ ERROR = "Error"
+ CREATED = "Created"
+ NUM_CTR_INFO = 10
+)
+
+var (
+ bc_opts batchcontainer.PsOptions
+)
+
+type podPsCtrInfo struct {
+ Name string `"json:name,omitempty"`
+ Id string `"json:id,omitempty"`
+ Status string `"json:status,omitempty"`
+}
+
+type podPsOptions struct {
+ NoTrunc bool
+ Format string
+ Sort string
+ Quiet bool
+ NumberOfContainers bool
+ Cgroup bool
+ NamesOfContainers bool
+ IdsOfContainers bool
+ StatusOfContainers bool
+}
+
+type podPsTemplateParams struct {
+ Created string
+ ID string
+ Name string
+ NumberOfContainers int
+ Status string
+ Cgroup string
+ UsePodCgroup bool
+ ContainerInfo string
+}
+
+// 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 {
+ CreatedAt time.Time `json:"createdAt"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ NumberOfContainers int `json:"numberofcontainers"`
+ Status string `json:"status"`
+ CtrsInfo []podPsCtrInfo `json:"containerinfo,omitempty"`
+ Cgroup string `json:"cgroup,omitempty"`
+ UsePodCgroup bool `json:"podcgroup,omitempty"`
+}
+
+// Type declaration and functions for sorting the pod PS output
+type podPsSorted []podPsJSONParams
+
+func (a podPsSorted) Len() int { return len(a) }
+func (a podPsSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type podPsSortedCreated struct{ podPsSorted }
+
+func (a podPsSortedCreated) Less(i, j int) bool {
+ return a.podPsSorted[i].CreatedAt.After(a.podPsSorted[j].CreatedAt)
+}
+
+type podPsSortedId struct{ podPsSorted }
+
+func (a podPsSortedId) Less(i, j int) bool { return a.podPsSorted[i].ID < a.podPsSorted[j].ID }
+
+type podPsSortedNumber struct{ podPsSorted }
+
+func (a podPsSortedNumber) Less(i, j int) bool {
+ return len(a.podPsSorted[i].CtrsInfo) < len(a.podPsSorted[j].CtrsInfo)
+}
+
+type podPsSortedName struct{ podPsSorted }
+
+func (a podPsSortedName) Less(i, j int) bool { return a.podPsSorted[i].Name < a.podPsSorted[j].Name }
+
+type podPsSortedStatus struct{ podPsSorted }
+
+func (a podPsSortedStatus) Less(i, j int) bool {
+ return a.podPsSorted[i].Status < a.podPsSorted[j].Status
+}
+
+var (
+ podPsFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "cgroup",
+ Usage: "Print the Cgroup information of the pod",
+ },
+ cli.BoolFlag{
+ Name: "ctr-names",
+ Usage: "Display the container names",
+ },
+ cli.BoolFlag{
+ Name: "ctr-ids",
+ Usage: "Display the container UUIDs. If no-trunc is not set they will be truncated",
+ },
+ cli.BoolFlag{
+ Name: "ctr-status",
+ Usage: "Display the container status",
+ },
+ cli.StringFlag{
+ Name: "filter, f",
+ Usage: "Filter output based on conditions given",
+ },
+ cli.StringFlag{
+ Name: "format",
+ Usage: "Pretty-print pods to JSON or using a Go template",
+ },
+ cli.BoolFlag{
+ Name: "latest, l",
+ Usage: "Show the latest pod created",
+ },
+ cli.BoolFlag{
+ Name: "no-trunc",
+ Usage: "Do not truncate pod and container IDs",
+ },
+ cli.BoolFlag{
+ Name: "quiet, q",
+ Usage: "Print the numeric IDs of the pods only",
+ },
+ cli.StringFlag{
+ Name: "sort",
+ Usage: "Sort output by created, id, name, or number",
+ Value: "created",
+ },
+ }
+ podPsDescription = "List all pods on system including their names, ids and current state."
+ 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 := podPsCheckFlagsPassed(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")
+ }
+
+ opts := podPsOptions{
+ NoTrunc: c.Bool("no-trunc"),
+ Quiet: c.Bool("quiet"),
+ Sort: c.String("sort"),
+ IdsOfContainers: c.Bool("ctr-ids"),
+ NamesOfContainers: c.Bool("ctr-names"),
+ StatusOfContainers: c.Bool("ctr-status"),
+ }
+
+ opts.Format = genPodPsFormat(c)
+
+ var filterFuncs []libpod.PodFilter
+ if c.String("filter") != "" {
+ filters := strings.Split(c.String("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], runtime)
+ if err != nil {
+ return errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+ }
+
+ var pods []*libpod.Pod
+ if c.IsSet("latest") {
+ pod, err := runtime.GetLatestPod()
+ if err != nil {
+ return err
+ }
+ pods = append(pods, pod)
+ } else {
+ pods, err = runtime.GetAllPods()
+ if err != nil {
+ return err
+ }
+ }
+
+ podsFiltered := make([]*libpod.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, runtime)
+}
+
+// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
+func podPsCheckFlagsPassed(c *cli.Context) error {
+ // quiet, and format with Go template are mutually exclusive
+ flags := 0
+ if c.Bool("quiet") {
+ flags++
+ }
+ if c.IsSet("format") && c.String("format") != formats.JSONString {
+ flags++
+ }
+ if flags > 1 {
+ return errors.Errorf("quiet and format with Go template are mutually exclusive")
+ }
+ return nil
+}
+
+func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (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 {
+ ctrs, err := p.AllContainers()
+ if err != nil {
+ return false
+ }
+ for _, ctr := range ctrs {
+ status, err := ctr.State()
+ if err != nil {
+ return false
+ }
+ state := status.String()
+ if status == libpod.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 {
+ ctrs, err := p.AllContainers()
+ if err != nil {
+ return false
+ }
+ status, err := getPodStatus(ctrs)
+ 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 *cli.Context) string {
+ format := ""
+ if c.String("format") != "" {
+ // "\t" from the command line is not being recognized as a tab
+ // replacing the string "\t" to a tab character if the user passes in "\t"
+ format = strings.Replace(c.String("format"), `\t`, "\t", -1)
+ } else if c.Bool("quiet") {
+ format = formats.IDString
+ } else {
+ format = "table {{.ID}}\t{{.Name}}\t{{.Status}}\t{{.Created}}"
+ if c.Bool("cgroup") {
+ format += "\t{{.Cgroup}}\t{{.UsePodCgroup}}"
+ }
+ if c.Bool("ctr-names") || c.Bool("ctr-ids") || c.Bool("ctr-status") {
+ format += "\t{{.ContainerInfo}}"
+ } else {
+ format += "\t{{.NumberOfContainers}}"
+ }
+ }
+ 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
+}
+
+func sortPodPsOutput(sortBy string, psOutput podPsSorted) (podPsSorted, error) {
+ switch sortBy {
+ case "created":
+ sort.Sort(podPsSortedCreated{psOutput})
+ case "id":
+ sort.Sort(podPsSortedId{psOutput})
+ case "name":
+ sort.Sort(podPsSortedName{psOutput})
+ case "number":
+ sort.Sort(podPsSortedNumber{psOutput})
+ case "status":
+ sort.Sort(podPsSortedStatus{psOutput})
+ default:
+ return nil, errors.Errorf("invalid option for --sort, options are: id, names, or number")
+ }
+ return psOutput, nil
+}
+
+// getPodTemplateOutput returns the modified container information
+func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podPsTemplateParams, error) {
+ var (
+ psOutput []podPsTemplateParams
+ )
+
+ for _, psParam := range psParams {
+ podID := psParam.ID
+ var ctrStr string
+
+ truncated := ""
+ if !opts.NoTrunc {
+ podID = shortID(podID)
+ if len(psParam.CtrsInfo) > NUM_CTR_INFO {
+ psParam.CtrsInfo = psParam.CtrsInfo[:NUM_CTR_INFO]
+ truncated = "..."
+ }
+ }
+ for _, ctrInfo := range psParam.CtrsInfo {
+ ctrStr += "[ "
+ if opts.IdsOfContainers {
+ if opts.NoTrunc {
+ ctrStr += ctrInfo.Id
+ } else {
+ ctrStr += shortID(ctrInfo.Id)
+ }
+ }
+ if opts.NamesOfContainers {
+ ctrStr += ctrInfo.Name + " "
+ }
+ if opts.StatusOfContainers {
+ ctrStr += ctrInfo.Status + " "
+ }
+ ctrStr += "] "
+ }
+ ctrStr += truncated
+ params := podPsTemplateParams{
+ Created: units.HumanDuration(time.Since(psParam.CreatedAt)) + " ago",
+ ID: podID,
+ Name: psParam.Name,
+ Status: psParam.Status,
+ NumberOfContainers: psParam.NumberOfContainers,
+ UsePodCgroup: psParam.UsePodCgroup,
+ Cgroup: psParam.Cgroup,
+ ContainerInfo: ctrStr,
+ }
+
+ psOutput = append(psOutput, params)
+ }
+
+ return psOutput, nil
+}
+
+func getPodStatus(ctrs []*libpod.Container) (string, error) {
+ ctrNum := len(ctrs)
+ if ctrNum == 0 {
+ return CREATED, nil
+ }
+ statuses := map[string]int{
+ STOPPED: 0,
+ RUNNING: 0,
+ PAUSED: 0,
+ CREATED: 0,
+ ERROR: 0,
+ }
+ for _, ctr := range ctrs {
+ state, err := ctr.State()
+ if err != nil {
+ return "", err
+ }
+ switch state {
+ case libpod.ContainerStateStopped:
+ statuses[STOPPED]++
+ case libpod.ContainerStateRunning:
+ statuses[RUNNING]++
+ case libpod.ContainerStatePaused:
+ statuses[PAUSED]++
+ case libpod.ContainerStateCreated, libpod.ContainerStateConfigured:
+ statuses[CREATED]++
+ default:
+ statuses[ERROR]++
+ }
+ }
+
+ if statuses[RUNNING] > 0 {
+ return RUNNING, nil
+ } else if statuses[PAUSED] == ctrNum {
+ return PAUSED, nil
+ } else if statuses[STOPPED] == ctrNum {
+ return EXITED, nil
+ } else if statuses[STOPPED] > 0 {
+ return STOPPED, nil
+ } else if statuses[ERROR] > 0 {
+ return ERROR, nil
+ } else {
+ return CREATED, 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 := pod.AllContainers()
+ ctrsInfo := make([]podPsCtrInfo, 0)
+ if err != nil {
+ return nil, err
+ }
+ ctrNum := len(ctrs)
+ status, err := getPodStatus(ctrs)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, ctr := range ctrs {
+ batchInfo, err := batchcontainer.BatchContainerOp(ctr, bc_opts)
+ if err != nil {
+ return nil, err
+ }
+ var status string
+ switch batchInfo.ConState {
+ case libpod.ContainerStateStopped:
+ status = EXITED
+ case libpod.ContainerStateRunning:
+ status = RUNNING
+ case libpod.ContainerStatePaused:
+ status = PAUSED
+ case libpod.ContainerStateCreated, libpod.ContainerStateConfigured:
+ status = CREATED
+ default:
+ status = ERROR
+ }
+ ctrsInfo = append(ctrsInfo, podPsCtrInfo{
+ Name: batchInfo.ConConfig.Name,
+ Id: ctr.ID(),
+ Status: status,
+ })
+ }
+ params := podPsJSONParams{
+ CreatedAt: pod.CreatedTime(),
+ ID: pod.ID(),
+ Name: pod.Name(),
+ Status: status,
+ Cgroup: pod.CgroupParent(),
+ UsePodCgroup: pod.UsePodCgroup(),
+ NumberOfContainers: ctrNum,
+ CtrsInfo: ctrsInfo,
+ }
+
+ psOutput = append(psOutput, params)
+ }
+ return sortPodPsOutput(opts.Sort, psOutput)
+}
+
+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..8cc46761e
--- /dev/null
+++ b/cmd/podman/pod_rm.go
@@ -0,0 +1,103 @@
+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",
+ },
+ LatestFlag,
+ }
+ 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 containers will not be removed without --force.
+ If --force is specified, all containers will be stopped, then removed.`),
+ Description: podRmDescription,
+ Flags: podRmFlags,
+ Action: podRmCmd,
+ ArgsUsage: "[POD ...]",
+ 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
+ }
+
+ if c.Bool("latest") && c.Bool("all") {
+ return errors.Errorf("--all and --latest cannot be used together")
+ }
+
+ 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") && !c.Bool("latest") {
+ return errors.Errorf("specify one or more pods to remove")
+ }
+
+ var delPods []*libpod.Pod
+ var lastError error
+ if c.IsSet("all") {
+ delPods, err = runtime.GetAllPods()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get pod list")
+ }
+ } else if c.IsSet("latest") {
+ delPod, err := runtime.GetLatestPod()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get latest pod")
+ }
+ delPods = append(delPods, delPod)
+ } else {
+ for _, i := range args {
+ pod, err := runtime.LookupPod(i)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ if lastError != nil {
+ fmt.Fprintln(os.Stderr, lastError)
+ }
+ 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
+}
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index a89523e83..49e43ffac 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -519,7 +519,7 @@ func getTemplateOutput(psParams []psJSONParams, opts batchcontainer.PsOptions) (
case libpod.ContainerStateCreated.String(), libpod.ContainerStateConfigured.String():
status = "Created"
default:
- status = "Dead"
+ status = "Error"
}
if !opts.NoTrunc {
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 2964605f6..b126c9e73 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -172,6 +172,10 @@ func runCmd(c *cli.Context) error {
if c.IsSet("attach") || c.IsSet("a") {
outputStream = nil
errorStream = nil
+ if !c.Bool("interactive") {
+ inputStream = nil
+ }
+
inputStream = nil
attachTo := c.StringSlice("attach")
@@ -187,13 +191,7 @@ func runCmd(c *cli.Context) error {
return errors.Wrapf(libpod.ErrInvalidArg, "invalid stream %q for --attach - must be one of stdin, stdout, or stderr", stream)
}
}
-
- // If --interactive is set, restore stdin
- if c.Bool("interactive") {
- inputStream = os.Stdin
- }
}
-
if err := startAttachCtr(ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.BoolT("sig-proxy"), true); err != nil {
// This means the command did not exist
exitCode = 127
@@ -203,7 +201,7 @@ func runCmd(c *cli.Context) error {
return err
}
- if ecode, err := ctr.ExitCode(); err != nil {
+ if ecode, err := ctr.Wait(); err != nil {
if errors.Cause(err) == libpod.ErrNoSuchCtr {
// The container may have been removed
// Go looking for an exit file
@@ -213,8 +211,6 @@ func runCmd(c *cli.Context) error {
} else {
exitCode = ctrExitCode
}
- } else {
- logrus.Errorf("Unable to get exit code of container %s: %q", ctr.ID(), err)
}
} else {
exitCode = int(ecode)
diff --git a/cmd/podman/sigproxy.go b/cmd/podman/sigproxy.go
index fd1415dc6..388e23439 100644
--- a/cmd/podman/sigproxy.go
+++ b/cmd/podman/sigproxy.go
@@ -25,6 +25,8 @@ func ProxySignals(ctr *libpod.Container) {
if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil {
logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err)
+ signal.StopCatch(sigBuffer)
+ syscall.Kill(syscall.Getpid(), s.(syscall.Signal))
}
}
}()
diff --git a/cmd/podman/start.go b/cmd/podman/start.go
index e917d9198..3dde306d7 100644
--- a/cmd/podman/start.go
+++ b/cmd/podman/start.go
@@ -114,7 +114,7 @@ func startCmd(c *cli.Context) error {
return errors.Wrapf(err, "unable to start container %s", ctr.ID())
}
- if ecode, err := ctr.ExitCode(); err != nil {
+ if ecode, err := ctr.Wait(); err != nil {
logrus.Errorf("unable to get exit code of container %s: %q", ctr.ID(), err)
} else {
exitCode = int(ecode)