mirror of
https://github.com/containers/podman.git
synced 2025-05-22 01:27:07 +08:00
@ -1,29 +1,379 @@
|
|||||||
package containers
|
package containers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tm "github.com/buger/goterm"
|
||||||
|
"github.com/containers/buildah/pkg/formats"
|
||||||
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
"github.com/containers/libpod/cmd/podmanV2/registry"
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/report"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// podman _ps_
|
psDescription = "Prints out information about the containers"
|
||||||
psCmd = &cobra.Command{
|
psCommand = &cobra.Command{
|
||||||
Use: "ps",
|
Use: "ps",
|
||||||
Args: cobra.NoArgs,
|
Args: checkFlags,
|
||||||
Short: listCmd.Short,
|
Short: "List containers",
|
||||||
Long: listCmd.Long,
|
Long: psDescription,
|
||||||
PersistentPreRunE: preRunE,
|
RunE: ps,
|
||||||
RunE: containers,
|
PreRunE: preRunE,
|
||||||
Example: strings.Replace(listCmd.Example, "container list", "ps", -1),
|
Example: `podman ps -a
|
||||||
|
podman ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
|
||||||
|
podman ps --size --sort names`,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
var (
|
||||||
|
listOpts = entities.ContainerListOptions{
|
||||||
|
Filters: make(map[string][]string),
|
||||||
|
}
|
||||||
|
filters []string
|
||||||
|
noTrunc bool
|
||||||
|
defaultHeaders string = "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES"
|
||||||
|
|
||||||
|
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
Command: psCmd,
|
Command: psCommand,
|
||||||
})
|
})
|
||||||
|
flags := psCommand.Flags()
|
||||||
|
flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers")
|
||||||
|
flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given")
|
||||||
|
flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template")
|
||||||
|
flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)")
|
||||||
|
flags.BoolVarP(&listOpts.Latest, "latest", "l", false, "Show the latest container created (all states)")
|
||||||
|
flags.BoolVar(&listOpts.Namespace, "namespace", false, "Display namespace information")
|
||||||
|
flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information")
|
||||||
|
flags.BoolVar(&noTrunc, "no-trunc", false, "Display the extended information")
|
||||||
|
flags.BoolVarP(&listOpts.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with")
|
||||||
|
flags.BoolVarP(&listOpts.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only")
|
||||||
|
flags.BoolVarP(&listOpts.Size, "size", "s", false, "Display the total file sizes")
|
||||||
|
flags.StringVar(&listOpts.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status")
|
||||||
|
flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime")
|
||||||
|
flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds")
|
||||||
|
if registry.IsRemote() {
|
||||||
|
_ = flags.MarkHidden("latest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func checkFlags(c *cobra.Command, args []string) error {
|
||||||
|
// latest, and last are mutually exclusive.
|
||||||
|
if listOpts.Last >= 0 && listOpts.Latest {
|
||||||
|
return errors.Errorf("last and latest are mutually exclusive")
|
||||||
|
}
|
||||||
|
// Filter on status forces all
|
||||||
|
for _, filter := range filters {
|
||||||
|
splitFilter := strings.SplitN(filter, "=", 2)
|
||||||
|
if strings.ToLower(splitFilter[0]) == "status" {
|
||||||
|
listOpts.All = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Quiet conflicts with size and namespace and is overridden by a Go
|
||||||
|
// template.
|
||||||
|
if listOpts.Quiet {
|
||||||
|
if listOpts.Size || listOpts.Namespace {
|
||||||
|
return errors.Errorf("quiet conflicts with size and namespace")
|
||||||
|
}
|
||||||
|
if c.Flag("format").Changed && listOpts.Format != formats.JSONString {
|
||||||
|
// Quiet is overridden by Go template output.
|
||||||
|
listOpts.Quiet = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Size and namespace conflict with each other
|
||||||
|
if listOpts.Size && listOpts.Namespace {
|
||||||
|
return errors.Errorf("size and namespace options conflict")
|
||||||
|
}
|
||||||
|
|
||||||
|
if listOpts.Watch > 0 && listOpts.Latest {
|
||||||
|
return errors.New("the watch and latest flags cannot be used together")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonOut(responses []entities.ListContainer) error {
|
||||||
|
b, err := json.MarshalIndent(responses, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func quietOut(responses []entities.ListContainer) error {
|
||||||
|
for _, r := range responses {
|
||||||
|
id := r.ID
|
||||||
|
if !noTrunc {
|
||||||
|
id = id[0:12]
|
||||||
|
}
|
||||||
|
fmt.Println(id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResponses() ([]entities.ListContainer, error) {
|
||||||
|
responses, err := registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(listOpts.Sort) > 0 {
|
||||||
|
responses, err = entities.SortPsOutput(listOpts.Sort, responses)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ps(cmd *cobra.Command, args []string) error {
|
||||||
|
// []string to map[string][]string
|
||||||
|
for _, f := range filters {
|
||||||
|
split := strings.SplitN(f, "=", 2)
|
||||||
|
if len(split) == 1 {
|
||||||
|
return errors.Errorf("invalid filter %q", f)
|
||||||
|
}
|
||||||
|
listOpts.Filters[split[0]] = append(listOpts.Filters[split[0]], split[1])
|
||||||
|
}
|
||||||
|
responses, err := getResponses()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(listOpts.Sort) > 0 {
|
||||||
|
responses, err = entities.SortPsOutput(listOpts.Sort, responses)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if listOpts.Format == "json" {
|
||||||
|
return jsonOut(responses)
|
||||||
|
}
|
||||||
|
if listOpts.Quiet {
|
||||||
|
return quietOut(responses)
|
||||||
|
}
|
||||||
|
headers, row := createPsOut()
|
||||||
|
if cmd.Flag("format").Changed {
|
||||||
|
row = listOpts.Format
|
||||||
|
if !strings.HasPrefix(row, "\n") {
|
||||||
|
row += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format := "{{range . }}" + row + "{{end}}"
|
||||||
|
if !listOpts.Quiet && !cmd.Flag("format").Changed {
|
||||||
|
format = headers + format
|
||||||
|
}
|
||||||
|
funcs := report.AppendFuncMap(psFuncMap)
|
||||||
|
tmpl, err := template.New("listPods").Funcs(funcs).Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
|
||||||
|
if listOpts.Watch > 0 {
|
||||||
|
for {
|
||||||
|
tm.Clear()
|
||||||
|
tm.MoveCursor(1, 1)
|
||||||
|
tm.Flush()
|
||||||
|
responses, err := getResponses()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tmpl.Execute(w, responses); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(listOpts.Watch) * time.Second)
|
||||||
|
tm.Clear()
|
||||||
|
tm.MoveCursor(1, 1)
|
||||||
|
tm.Flush()
|
||||||
|
}
|
||||||
|
} else if listOpts.Watch < 1 {
|
||||||
|
if err := tmpl.Execute(w, responses); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPsOut() (string, string) {
|
||||||
|
var row string
|
||||||
|
if listOpts.Namespace {
|
||||||
|
headers := "CONTAINER ID\tNAMES\tPID\tCGROUPNS\tIPC\tMNT\tNET\tPIDN\tUSERNS\tUTS\n"
|
||||||
|
row := "{{.ID}}\t{{names .Names}}\t{{.Pid}}\t{{.Namespaces.Cgroup}}\t{{.Namespaces.IPC}}\t{{.Namespaces.MNT}}\t{{.Namespaces.NET}}\t{{.Namespaces.PIDNS}}\t{{.Namespaces.User}}\t{{.Namespaces.UTS}}\n"
|
||||||
|
return headers, row
|
||||||
|
}
|
||||||
|
headers := defaultHeaders
|
||||||
|
if noTrunc {
|
||||||
|
row += "{{.ID}}"
|
||||||
|
} else {
|
||||||
|
row += "{{slice .ID 0 12}}"
|
||||||
|
}
|
||||||
|
row += "\t{{.Image}}\t{{cmd .Command}}\t{{humanDuration .Created}}\t{{state .}}\t{{ports .Ports}}\t{{names .Names}}"
|
||||||
|
|
||||||
|
if listOpts.Pod {
|
||||||
|
headers += "\tPOD ID\tPODNAME"
|
||||||
|
if noTrunc {
|
||||||
|
row += "\t{{.Pod}}"
|
||||||
|
} else {
|
||||||
|
row += "\t{{slice .Pod 0 12}}"
|
||||||
|
}
|
||||||
|
row += "\t{{.PodName}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if listOpts.Size {
|
||||||
|
headers += "\tSIZE"
|
||||||
|
row += "\t{{consize .Size}}"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(headers, "\n") {
|
||||||
|
headers += "\n"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(row, "\n") {
|
||||||
|
row += "\n"
|
||||||
|
}
|
||||||
|
return headers, row
|
||||||
|
}
|
||||||
|
|
||||||
|
var psFuncMap = template.FuncMap{
|
||||||
|
"cmd": func(conCommand []string) string {
|
||||||
|
return strings.Join(conCommand, " ")
|
||||||
|
},
|
||||||
|
"state": func(con entities.ListContainer) string {
|
||||||
|
var state string
|
||||||
|
switch con.State {
|
||||||
|
case "running":
|
||||||
|
t := units.HumanDuration(time.Since(time.Unix(con.StartedAt, 0)))
|
||||||
|
state = "Up " + t + " ago"
|
||||||
|
case "configured":
|
||||||
|
state = "Created"
|
||||||
|
case "exited":
|
||||||
|
t := units.HumanDuration(time.Since(time.Unix(con.ExitedAt, 0)))
|
||||||
|
state = fmt.Sprintf("Exited (%d) %s ago", con.ExitCode, t)
|
||||||
|
default:
|
||||||
|
state = con.State
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
},
|
||||||
|
"ports": func(ports []ocicni.PortMapping) string {
|
||||||
|
if len(ports) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return portsToString(ports)
|
||||||
|
},
|
||||||
|
"names": func(names []string) string {
|
||||||
|
return names[0]
|
||||||
|
},
|
||||||
|
"consize": func(csize shared.ContainerSize) string {
|
||||||
|
virt := units.HumanSizeWithPrecision(float64(csize.RootFsSize), 3)
|
||||||
|
s := units.HumanSizeWithPrecision(float64(csize.RwSize), 3)
|
||||||
|
return fmt.Sprintf("%s (virtual %s)", s, virt)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// portsToString converts the ports used to a string of the from "port1, port2"
|
||||||
|
// and also groups a continuous list of ports into a readable format.
|
||||||
|
func portsToString(ports []ocicni.PortMapping) string {
|
||||||
|
type portGroup struct {
|
||||||
|
first int32
|
||||||
|
last int32
|
||||||
|
}
|
||||||
|
var portDisplay []string
|
||||||
|
if len(ports) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//Sort the ports, so grouping continuous ports become easy.
|
||||||
|
sort.Slice(ports, func(i, j int) bool {
|
||||||
|
return comparePorts(ports[i], ports[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
// portGroupMap is used for grouping continuous ports.
|
||||||
|
portGroupMap := make(map[string]*portGroup)
|
||||||
|
var groupKeyList []string
|
||||||
|
|
||||||
|
for _, v := range ports {
|
||||||
|
|
||||||
|
hostIP := v.HostIP
|
||||||
|
if hostIP == "" {
|
||||||
|
hostIP = "0.0.0.0"
|
||||||
|
}
|
||||||
|
// If hostPort and containerPort are not same, consider as individual port.
|
||||||
|
if v.ContainerPort != v.HostPort {
|
||||||
|
portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
|
||||||
|
|
||||||
|
portgroup, ok := portGroupMap[portMapKey]
|
||||||
|
if !ok {
|
||||||
|
portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
|
||||||
|
// This list is required to traverse portGroupMap.
|
||||||
|
groupKeyList = append(groupKeyList, portMapKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if portgroup.last == (v.ContainerPort - 1) {
|
||||||
|
portgroup.last = v.ContainerPort
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For each portMapKey, format group list and appned to output string.
|
||||||
|
for _, portKey := range groupKeyList {
|
||||||
|
group := portGroupMap[portKey]
|
||||||
|
portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
|
||||||
|
}
|
||||||
|
return strings.Join(portDisplay, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePorts(i, j ocicni.PortMapping) bool {
|
||||||
|
if i.ContainerPort != j.ContainerPort {
|
||||||
|
return i.ContainerPort < j.ContainerPort
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.HostIP != j.HostIP {
|
||||||
|
return i.HostIP < j.HostIP
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.HostPort != j.HostPort {
|
||||||
|
return i.HostPort < j.HostPort
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.Protocol < j.Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
|
||||||
|
// e.g 0.0.0.0:1000-1006->1000-1006/tcp.
|
||||||
|
func formatGroup(key string, start, last int32) string {
|
||||||
|
parts := strings.Split(key, "/")
|
||||||
|
groupType := parts[0]
|
||||||
|
var ip string
|
||||||
|
if len(parts) > 1 {
|
||||||
|
ip = parts[0]
|
||||||
|
groupType = parts[1]
|
||||||
|
}
|
||||||
|
group := strconv.Itoa(int(start))
|
||||||
|
if start != last {
|
||||||
|
group = fmt.Sprintf("%s-%d", group, last)
|
||||||
|
}
|
||||||
|
if ip != "" {
|
||||||
|
group = fmt.Sprintf("%s:%s->%s", ip, group, group)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s", group, groupType)
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func pods(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Println(string(b))
|
fmt.Println(string(b))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
headers, row := createPodPsOut(cmd)
|
headers, row := createPodPsOut()
|
||||||
if psInput.Quiet {
|
if psInput.Quiet {
|
||||||
if noTrunc {
|
if noTrunc {
|
||||||
row = "{{.Id}}\n"
|
row = "{{.Id}}\n"
|
||||||
@ -123,7 +123,7 @@ func pods(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPodPsOut(cmd *cobra.Command) (string, string) {
|
func createPodPsOut() (string, string) {
|
||||||
var row string
|
var row string
|
||||||
headers := defaultHeaders
|
headers := defaultHeaders
|
||||||
if noTrunc {
|
if noTrunc {
|
||||||
|
157
libpod/filters/containers.go
Normal file
157
libpod/filters/containers.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package lpfilters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
"github.com/containers/libpod/pkg/timetype"
|
||||||
|
"github.com/containers/libpod/pkg/util"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
|
||||||
|
func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) {
|
||||||
|
switch filter {
|
||||||
|
case "id":
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
return strings.Contains(c.ID(), filterValue)
|
||||||
|
}, nil
|
||||||
|
case "label":
|
||||||
|
var filterArray = strings.SplitN(filterValue, "=", 2)
|
||||||
|
var filterKey = filterArray[0]
|
||||||
|
if len(filterArray) > 1 {
|
||||||
|
filterValue = filterArray[1]
|
||||||
|
} else {
|
||||||
|
filterValue = ""
|
||||||
|
}
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
for labelKey, labelValue := range c.Labels() {
|
||||||
|
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, nil
|
||||||
|
case "name":
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
match, err := regexp.MatchString(filterValue, c.Name())
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}, nil
|
||||||
|
case "exited":
|
||||||
|
exitCode, err := strconv.ParseInt(filterValue, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "exited code out of range %q", filterValue)
|
||||||
|
}
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
ec, exited, err := c.ExitCode()
|
||||||
|
if ec == int32(exitCode) && err == nil && exited {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, nil
|
||||||
|
case "status":
|
||||||
|
if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) {
|
||||||
|
return nil, errors.Errorf("%s is not a valid status", filterValue)
|
||||||
|
}
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
status, err := c.State()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if filterValue == "stopped" {
|
||||||
|
filterValue = "exited"
|
||||||
|
}
|
||||||
|
state := status.String()
|
||||||
|
if status == define.ContainerStateConfigured {
|
||||||
|
state = "created"
|
||||||
|
} else if status == define.ContainerStateStopped {
|
||||||
|
state = "exited"
|
||||||
|
}
|
||||||
|
return state == filterValue
|
||||||
|
}, nil
|
||||||
|
case "ancestor":
|
||||||
|
// This needs to refine to match docker
|
||||||
|
// - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant.
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
containerConfig := c.Config()
|
||||||
|
if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, nil
|
||||||
|
case "before":
|
||||||
|
ctr, err := r.LookupContainer(filterValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
|
||||||
|
}
|
||||||
|
containerConfig := ctr.Config()
|
||||||
|
createTime := containerConfig.CreatedTime
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
cc := c.Config()
|
||||||
|
return createTime.After(cc.CreatedTime)
|
||||||
|
}, nil
|
||||||
|
case "since":
|
||||||
|
ctr, err := r.LookupContainer(filterValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
|
||||||
|
}
|
||||||
|
containerConfig := ctr.Config()
|
||||||
|
createTime := containerConfig.CreatedTime
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
cc := c.Config()
|
||||||
|
return createTime.Before(cc.CreatedTime)
|
||||||
|
}, nil
|
||||||
|
case "volume":
|
||||||
|
//- volume=(<volume-name>|<mount-point-destination>)
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
containerConfig := c.Config()
|
||||||
|
var dest string
|
||||||
|
arr := strings.Split(filterValue, ":")
|
||||||
|
source := arr[0]
|
||||||
|
if len(arr) == 2 {
|
||||||
|
dest = arr[1]
|
||||||
|
}
|
||||||
|
for _, mount := range containerConfig.Spec.Mounts {
|
||||||
|
if dest != "" && (mount.Source == source && mount.Destination == dest) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if dest == "" && mount.Source == source {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, nil
|
||||||
|
case "health":
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
hcStatus, err := c.HealthCheckStatus()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return hcStatus == filterValue
|
||||||
|
}, nil
|
||||||
|
case "until":
|
||||||
|
ts, err := timetype.GetTimestamp(filterValue, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
until := time.Unix(seconds, nanoseconds)
|
||||||
|
return func(c *libpod.Container) bool {
|
||||||
|
if !until.IsZero() && c.CreatedTime().After((until)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, errors.Errorf("%s is an invalid filter", filter)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package podfilters
|
package lpfilters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
@ -4,21 +4,16 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containers/libpod/pkg/api/handlers/compat"
|
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
"github.com/containers/libpod/pkg/api/handlers/compat"
|
||||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/ps"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ContainerExists(w http.ResponseWriter, r *http.Request) {
|
func ContainerExists(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -38,8 +33,8 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func ListContainers(w http.ResponseWriter, r *http.Request) {
|
func ListContainers(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
filterFuncs []libpod.ContainerFilter
|
//filterFuncs []libpod.ContainerFilter
|
||||||
pss []ListContainer
|
//pss []entities.ListContainer
|
||||||
)
|
)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
query := struct {
|
query := struct {
|
||||||
@ -61,66 +56,19 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
opts := shared.PsOptions{
|
opts := entities.ContainerListOptions{
|
||||||
All: query.All,
|
All: query.All,
|
||||||
Last: query.Last,
|
Last: query.Last,
|
||||||
Size: query.Size,
|
Size: query.Size,
|
||||||
Sort: "",
|
Sort: "",
|
||||||
Namespace: query.Namespace,
|
Namespace: query.Namespace,
|
||||||
NoTrunc: true,
|
|
||||||
Pod: query.Pod,
|
Pod: query.Pod,
|
||||||
Sync: query.Sync,
|
Sync: query.Sync,
|
||||||
}
|
}
|
||||||
|
pss, err := ps.GetContainerLists(runtime, opts)
|
||||||
all := query.All
|
|
||||||
if len(query.Filters) > 0 {
|
|
||||||
for k, v := range query.Filters {
|
|
||||||
for _, val := range v {
|
|
||||||
generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime)
|
|
||||||
if err != nil {
|
|
||||||
utils.InternalServerError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filterFuncs = append(filterFuncs, generatedFunc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Docker thinks that if status is given as an input, then we should override
|
|
||||||
// the all setting and always deal with all containers.
|
|
||||||
if len(query.Filters["status"]) > 0 {
|
|
||||||
all = true
|
|
||||||
}
|
|
||||||
if !all {
|
|
||||||
runningOnly, err := shared.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime)
|
|
||||||
if err != nil {
|
|
||||||
utils.InternalServerError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filterFuncs = append(filterFuncs, runningOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
cons, err := runtime.GetContainers(filterFuncs...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
}
|
return
|
||||||
if query.Last > 0 {
|
|
||||||
// Sort the containers we got
|
|
||||||
sort.Sort(psSortCreateTime{cons})
|
|
||||||
// we should perform the lopping before we start getting
|
|
||||||
// the expensive information on containers
|
|
||||||
if query.Last < len(cons) {
|
|
||||||
cons = cons[len(cons)-query.Last:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, con := range cons {
|
|
||||||
listCon, err := ListContainerBatch(runtime, con, opts)
|
|
||||||
if err != nil {
|
|
||||||
utils.InternalServerError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pss = append(pss, listCon)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusOK, pss)
|
utils.WriteResponse(w, http.StatusOK, pss)
|
||||||
}
|
}
|
||||||
@ -212,125 +160,6 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.WriteResponse(w, http.StatusOK, response)
|
utils.WriteResponse(w, http.StatusOK, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchContainerOp is used in ps to reduce performance hits by "batching"
|
|
||||||
// locks.
|
|
||||||
func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts shared.PsOptions) (ListContainer, error) {
|
|
||||||
var (
|
|
||||||
conConfig *libpod.ContainerConfig
|
|
||||||
conState define.ContainerStatus
|
|
||||||
err error
|
|
||||||
exitCode int32
|
|
||||||
exited bool
|
|
||||||
pid int
|
|
||||||
size *shared.ContainerSize
|
|
||||||
startedTime time.Time
|
|
||||||
exitedTime time.Time
|
|
||||||
cgroup, ipc, mnt, net, pidns, user, uts string
|
|
||||||
)
|
|
||||||
|
|
||||||
batchErr := ctr.Batch(func(c *libpod.Container) error {
|
|
||||||
conConfig = c.Config()
|
|
||||||
conState, err = c.State()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to obtain container state")
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCode, exited, err = c.ExitCode()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to obtain container exit code")
|
|
||||||
}
|
|
||||||
startedTime, err = c.StartedTime()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
|
|
||||||
}
|
|
||||||
exitedTime, err = c.FinishedTime()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !opts.Size && !opts.Namespace {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Namespace {
|
|
||||||
pid, err = c.PID()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to obtain container pid")
|
|
||||||
}
|
|
||||||
ctrPID := strconv.Itoa(pid)
|
|
||||||
cgroup, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
|
|
||||||
ipc, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
|
|
||||||
mnt, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
|
|
||||||
net, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
|
|
||||||
pidns, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
|
|
||||||
user, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
|
|
||||||
uts, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
|
|
||||||
}
|
|
||||||
if opts.Size {
|
|
||||||
size = new(shared.ContainerSize)
|
|
||||||
|
|
||||||
rootFsSize, err := c.RootFsSize()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rwSize, err := c.RWSize()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
size.RootFsSize = rootFsSize
|
|
||||||
size.RwSize = rwSize
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if batchErr != nil {
|
|
||||||
return ListContainer{}, batchErr
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := ListContainer{
|
|
||||||
Command: conConfig.Command,
|
|
||||||
Created: conConfig.CreatedTime.Unix(),
|
|
||||||
Exited: exited,
|
|
||||||
ExitCode: exitCode,
|
|
||||||
ExitedAt: exitedTime.Unix(),
|
|
||||||
ID: conConfig.ID,
|
|
||||||
Image: conConfig.RootfsImageName,
|
|
||||||
IsInfra: conConfig.IsInfra,
|
|
||||||
Labels: conConfig.Labels,
|
|
||||||
Mounts: ctr.UserVolumes(),
|
|
||||||
Names: []string{conConfig.Name},
|
|
||||||
Pid: pid,
|
|
||||||
Pod: conConfig.Pod,
|
|
||||||
Ports: conConfig.PortMappings,
|
|
||||||
Size: size,
|
|
||||||
StartedAt: startedTime.Unix(),
|
|
||||||
State: conState.String(),
|
|
||||||
}
|
|
||||||
if opts.Pod && len(conConfig.Pod) > 0 {
|
|
||||||
pod, err := rt.GetPod(conConfig.Pod)
|
|
||||||
if err != nil {
|
|
||||||
return ListContainer{}, err
|
|
||||||
}
|
|
||||||
ps.PodName = pod.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Namespace {
|
|
||||||
ns := ListContainerNamespaces{
|
|
||||||
Cgroup: cgroup,
|
|
||||||
IPC: ipc,
|
|
||||||
MNT: mnt,
|
|
||||||
NET: net,
|
|
||||||
PIDNS: pidns,
|
|
||||||
User: user,
|
|
||||||
UTS: uts,
|
|
||||||
}
|
|
||||||
ps.Namespaces = ns
|
|
||||||
}
|
|
||||||
return ps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Checkpoint(w http.ResponseWriter, r *http.Request) {
|
func Checkpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
var targetFile string
|
var targetFile string
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
|
@ -18,7 +18,7 @@ const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
|
|||||||
// swagger:response ListContainers
|
// swagger:response ListContainers
|
||||||
type swagInspectPodResponse struct {
|
type swagInspectPodResponse struct {
|
||||||
// in:body
|
// in:body
|
||||||
Body []ListContainer
|
Body []entities.ListContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inspect Manifest
|
// Inspect Manifest
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
package libpod
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
|
||||||
"github.com/containers/libpod/libpod"
|
|
||||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Listcontainer describes a container suitable for listing
|
|
||||||
type ListContainer struct {
|
|
||||||
// Container command
|
|
||||||
Command []string
|
|
||||||
// Container creation time
|
|
||||||
Created int64
|
|
||||||
// If container has exited/stopped
|
|
||||||
Exited bool
|
|
||||||
// Time container exited
|
|
||||||
ExitedAt int64
|
|
||||||
// If container has exited, the return code from the command
|
|
||||||
ExitCode int32
|
|
||||||
// The unique identifier for the container
|
|
||||||
ID string `json:"Id"`
|
|
||||||
// Container image
|
|
||||||
Image string
|
|
||||||
// If this container is a Pod infra container
|
|
||||||
IsInfra bool
|
|
||||||
// Labels for container
|
|
||||||
Labels map[string]string
|
|
||||||
// User volume mounts
|
|
||||||
Mounts []string
|
|
||||||
// The names assigned to the container
|
|
||||||
Names []string
|
|
||||||
// Namespaces the container belongs to. Requires the
|
|
||||||
// namespace boolean to be true
|
|
||||||
Namespaces ListContainerNamespaces
|
|
||||||
// The process id of the container
|
|
||||||
Pid int
|
|
||||||
// If the container is part of Pod, the Pod ID. Requires the pod
|
|
||||||
// boolean to be set
|
|
||||||
Pod string
|
|
||||||
// If the container is part of Pod, the Pod name. Requires the pod
|
|
||||||
// boolean to be set
|
|
||||||
PodName string
|
|
||||||
// Port mappings
|
|
||||||
Ports []ocicni.PortMapping
|
|
||||||
// Size of the container rootfs. Requires the size boolean to be true
|
|
||||||
Size *shared.ContainerSize
|
|
||||||
// Time when container started
|
|
||||||
StartedAt int64
|
|
||||||
// State of container
|
|
||||||
State string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListContainer Namespaces contains the identifiers of the container's Linux namespaces
|
|
||||||
type ListContainerNamespaces struct {
|
|
||||||
// Mount namespace
|
|
||||||
MNT string `json:"Mnt,omitempty"`
|
|
||||||
// Cgroup namespace
|
|
||||||
Cgroup string `json:"Cgroup,omitempty"`
|
|
||||||
// IPC namespace
|
|
||||||
IPC string `json:"Ipc,omitempty"`
|
|
||||||
// Network namespace
|
|
||||||
NET string `json:"Net,omitempty"`
|
|
||||||
// PID namespace
|
|
||||||
PIDNS string `json:"Pidns,omitempty"`
|
|
||||||
// UTS namespace
|
|
||||||
UTS string `json:"Uts,omitempty"`
|
|
||||||
// User namespace
|
|
||||||
User string `json:"User,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// sortContainers helps us set-up ability to sort by createTime
|
|
||||||
type sortContainers []*libpod.Container
|
|
||||||
|
|
||||||
func (a sortContainers) Len() int { return len(a) }
|
|
||||||
func (a sortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
|
|
||||||
type psSortCreateTime struct{ sortContainers }
|
|
||||||
|
|
||||||
func (a psSortCreateTime) Less(i, j int) bool {
|
|
||||||
return a.sortContainers[i].CreatedTime().Before(a.sortContainers[j].CreatedTime())
|
|
||||||
}
|
|
@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/api/handlers"
|
"github.com/containers/libpod/pkg/api/handlers"
|
||||||
lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod"
|
|
||||||
"github.com/containers/libpod/pkg/bindings"
|
"github.com/containers/libpod/pkg/bindings"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
)
|
)
|
||||||
|
|
||||||
// List obtains a list of containers in local storage. All parameters to this method are optional.
|
// List obtains a list of containers in local storage. All parameters to this method are optional.
|
||||||
@ -19,12 +19,12 @@ import (
|
|||||||
// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
|
// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
|
||||||
// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
|
// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
|
||||||
// container state.
|
// container state.
|
||||||
func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]lpapiv2.ListContainer, error) { // nolint:typecheck
|
func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck
|
||||||
conn, err := bindings.GetClient(ctx)
|
conn, err := bindings.GetClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var containers []lpapiv2.ListContainer
|
var containers []entities.ListContainer
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
if all != nil {
|
if all != nil {
|
||||||
params.Set("all", strconv.FormatBool(*all))
|
params.Set("all", strconv.FormatBool(*all))
|
||||||
|
174
pkg/domain/entities/container_ps.go
Normal file
174
pkg/domain/entities/container_ps.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listcontainer describes a container suitable for listing
|
||||||
|
type ListContainer struct {
|
||||||
|
// Container command
|
||||||
|
Command []string
|
||||||
|
// Container creation time
|
||||||
|
Created int64
|
||||||
|
// If container has exited/stopped
|
||||||
|
Exited bool
|
||||||
|
// Time container exited
|
||||||
|
ExitedAt int64
|
||||||
|
// If container has exited, the return code from the command
|
||||||
|
ExitCode int32
|
||||||
|
// The unique identifier for the container
|
||||||
|
ID string `json:"Id"`
|
||||||
|
// Container image
|
||||||
|
Image string
|
||||||
|
// If this container is a Pod infra container
|
||||||
|
IsInfra bool
|
||||||
|
// Labels for container
|
||||||
|
Labels map[string]string
|
||||||
|
// User volume mounts
|
||||||
|
Mounts []string
|
||||||
|
// The names assigned to the container
|
||||||
|
Names []string
|
||||||
|
// Namespaces the container belongs to. Requires the
|
||||||
|
// namespace boolean to be true
|
||||||
|
Namespaces ListContainerNamespaces
|
||||||
|
// The process id of the container
|
||||||
|
Pid int
|
||||||
|
// If the container is part of Pod, the Pod ID. Requires the pod
|
||||||
|
// boolean to be set
|
||||||
|
Pod string
|
||||||
|
// If the container is part of Pod, the Pod name. Requires the pod
|
||||||
|
// boolean to be set
|
||||||
|
PodName string
|
||||||
|
// Port mappings
|
||||||
|
Ports []ocicni.PortMapping
|
||||||
|
// Size of the container rootfs. Requires the size boolean to be true
|
||||||
|
Size *shared.ContainerSize
|
||||||
|
// Time when container started
|
||||||
|
StartedAt int64
|
||||||
|
// State of container
|
||||||
|
State string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListContainer Namespaces contains the identifiers of the container's Linux namespaces
|
||||||
|
type ListContainerNamespaces struct {
|
||||||
|
// Mount namespace
|
||||||
|
MNT string `json:"Mnt,omitempty"`
|
||||||
|
// Cgroup namespace
|
||||||
|
Cgroup string `json:"Cgroup,omitempty"`
|
||||||
|
// IPC namespace
|
||||||
|
IPC string `json:"Ipc,omitempty"`
|
||||||
|
// Network namespace
|
||||||
|
NET string `json:"Net,omitempty"`
|
||||||
|
// PID namespace
|
||||||
|
PIDNS string `json:"Pidns,omitempty"`
|
||||||
|
// UTS namespace
|
||||||
|
UTS string `json:"Uts,omitempty"`
|
||||||
|
// User namespace
|
||||||
|
User string `json:"User,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortContainers helps us set-up ability to sort by createTime
|
||||||
|
type SortContainers []*libpod.Container
|
||||||
|
|
||||||
|
func (a SortContainers) Len() int { return len(a) }
|
||||||
|
func (a SortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
type SortCreateTime struct{ SortContainers }
|
||||||
|
|
||||||
|
func (a SortCreateTime) Less(i, j int) bool {
|
||||||
|
return a.SortContainers[i].CreatedTime().Before(a.SortContainers[j].CreatedTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortListContainers []ListContainer
|
||||||
|
|
||||||
|
func (a SortListContainers) Len() int { return len(a) }
|
||||||
|
func (a SortListContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
type psSortedCommand struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedCommand) Less(i, j int) bool {
|
||||||
|
return strings.Join(a.SortListContainers[i].Command, " ") < strings.Join(a.SortListContainers[j].Command, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedId struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedId) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].ID < a.SortListContainers[j].ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedImage struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedImage) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].Image < a.SortListContainers[j].Image
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedNames struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedNames) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].Names[0] < a.SortListContainers[j].Names[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedPod struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedPod) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].Pod < a.SortListContainers[j].Pod
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedRunningFor struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedRunningFor) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].StartedAt < a.SortListContainers[j].StartedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedStatus struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedStatus) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].State < a.SortListContainers[j].State
|
||||||
|
}
|
||||||
|
|
||||||
|
type psSortedSize struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a psSortedSize) Less(i, j int) bool {
|
||||||
|
if a.SortListContainers[i].Size == nil || a.SortListContainers[j].Size == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return a.SortListContainers[i].Size.RootFsSize < a.SortListContainers[j].Size.RootFsSize
|
||||||
|
}
|
||||||
|
|
||||||
|
type PsSortedCreateTime struct{ SortListContainers }
|
||||||
|
|
||||||
|
func (a PsSortedCreateTime) Less(i, j int) bool {
|
||||||
|
return a.SortListContainers[i].Created < a.SortListContainers[j].Created
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainers, error) {
|
||||||
|
switch sortBy {
|
||||||
|
case "id":
|
||||||
|
sort.Sort(psSortedId{psOutput})
|
||||||
|
case "image":
|
||||||
|
sort.Sort(psSortedImage{psOutput})
|
||||||
|
case "command":
|
||||||
|
sort.Sort(psSortedCommand{psOutput})
|
||||||
|
case "runningfor":
|
||||||
|
sort.Sort(psSortedRunningFor{psOutput})
|
||||||
|
case "status":
|
||||||
|
sort.Sort(psSortedStatus{psOutput})
|
||||||
|
case "size":
|
||||||
|
sort.Sort(psSortedSize{psOutput})
|
||||||
|
case "names":
|
||||||
|
sort.Sort(psSortedNames{psOutput})
|
||||||
|
case "created":
|
||||||
|
sort.Sort(PsSortedCreateTime{psOutput})
|
||||||
|
case "pod":
|
||||||
|
sort.Sort(psSortedPod{psOutput})
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("invalid option for --sort, options are: command, created, id, image, names, runningfor, size, or status")
|
||||||
|
}
|
||||||
|
return psOutput, nil
|
||||||
|
}
|
@ -207,3 +207,20 @@ type ContainerStartReport struct {
|
|||||||
Err error
|
Err error
|
||||||
ExitCode int
|
ExitCode int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerListOptions describes the CLI options
|
||||||
|
// for listing containers
|
||||||
|
type ContainerListOptions struct {
|
||||||
|
All bool
|
||||||
|
Filters map[string][]string
|
||||||
|
Format string
|
||||||
|
Last int
|
||||||
|
Latest bool
|
||||||
|
Namespace bool
|
||||||
|
Pod bool
|
||||||
|
Quiet bool
|
||||||
|
Size bool
|
||||||
|
Sort string
|
||||||
|
Sync bool
|
||||||
|
Watch uint
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ type ContainerEngine interface {
|
|||||||
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
|
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
|
||||||
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
|
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
|
||||||
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
|
||||||
|
ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error)
|
||||||
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
|
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
|
||||||
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
|
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
|
||||||
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
|
ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/containers/libpod/pkg/checkpoint"
|
"github.com/containers/libpod/pkg/checkpoint"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
||||||
|
"github.com/containers/libpod/pkg/ps"
|
||||||
"github.com/containers/libpod/pkg/signal"
|
"github.com/containers/libpod/pkg/signal"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
"github.com/containers/libpod/pkg/specgen/generate"
|
"github.com/containers/libpod/pkg/specgen/generate"
|
||||||
@ -617,3 +618,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
|
|||||||
}
|
}
|
||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
|
||||||
|
return ps.GetContainerLists(ic.Libpod, options)
|
||||||
|
}
|
||||||
|
@ -5,9 +5,10 @@ package abi
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
lpfilters "github.com/containers/libpod/libpod/filters"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/libpod/podfilters"
|
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/signal"
|
"github.com/containers/libpod/pkg/signal"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
@ -281,7 +282,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti
|
|||||||
)
|
)
|
||||||
for k, v := range options.Filters {
|
for k, v := range options.Filters {
|
||||||
for _, filter := range v {
|
for _, filter := range v {
|
||||||
f, err := podfilters.GeneratePodFilterFunc(k, filter)
|
f, err := lpfilters.GeneratePodFilterFunc(k, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/image/v5/docker/reference"
|
"github.com/containers/image/v5/docker/reference"
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
|
||||||
"github.com/containers/libpod/pkg/bindings/containers"
|
"github.com/containers/libpod/pkg/bindings/containers"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
@ -233,7 +232,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
|
|||||||
var (
|
var (
|
||||||
reports []*entities.CheckpointReport
|
reports []*entities.CheckpointReport
|
||||||
err error
|
err error
|
||||||
ctrs []libpod.ListContainer
|
ctrs []entities.ListContainer
|
||||||
)
|
)
|
||||||
|
|
||||||
if options.All {
|
if options.All {
|
||||||
@ -268,7 +267,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
|
|||||||
var (
|
var (
|
||||||
reports []*entities.RestoreReport
|
reports []*entities.RestoreReport
|
||||||
err error
|
err error
|
||||||
ctrs []libpod.ListContainer
|
ctrs []entities.ListContainer
|
||||||
)
|
)
|
||||||
if options.All {
|
if options.All {
|
||||||
allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
|
allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
|
||||||
@ -317,3 +316,7 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o
|
|||||||
func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
|
func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
|
||||||
|
return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Pod, &options.Size, &options.Sync)
|
||||||
|
}
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
|
||||||
"github.com/containers/libpod/pkg/bindings"
|
"github.com/containers/libpod/pkg/bindings"
|
||||||
"github.com/containers/libpod/pkg/bindings/containers"
|
"github.com/containers/libpod/pkg/bindings/containers"
|
||||||
"github.com/containers/libpod/pkg/bindings/pods"
|
"github.com/containers/libpod/pkg/bindings/pods"
|
||||||
@ -14,9 +13,9 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]libpod.ListContainer, error) {
|
func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]entities.ListContainer, error) {
|
||||||
var (
|
var (
|
||||||
cons []libpod.ListContainer
|
cons []entities.ListContainer
|
||||||
)
|
)
|
||||||
if all && len(namesOrIds) > 0 {
|
if all && len(namesOrIds) > 0 {
|
||||||
return nil, errors.New("cannot lookup containers and all")
|
return nil, errors.New("cannot lookup containers and all")
|
||||||
|
189
pkg/ps/ps.go
Normal file
189
pkg/ps/ps.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
package ps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
lpfilters "github.com/containers/libpod/libpod/filters"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
|
||||||
|
var (
|
||||||
|
filterFuncs []libpod.ContainerFilter
|
||||||
|
pss []entities.ListContainer
|
||||||
|
)
|
||||||
|
all := options.All
|
||||||
|
if len(options.Filters) > 0 {
|
||||||
|
for k, v := range options.Filters {
|
||||||
|
for _, val := range v {
|
||||||
|
generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, val, runtime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
filterFuncs = append(filterFuncs, generatedFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docker thinks that if status is given as an input, then we should override
|
||||||
|
// the all setting and always deal with all containers.
|
||||||
|
if len(options.Filters["status"]) > 0 {
|
||||||
|
all = true
|
||||||
|
}
|
||||||
|
if !all {
|
||||||
|
runningOnly, err := lpfilters.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
filterFuncs = append(filterFuncs, runningOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
cons, err := runtime.GetContainers(filterFuncs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if options.Last > 0 {
|
||||||
|
// Sort the containers we got
|
||||||
|
sort.Sort(entities.SortCreateTime{SortContainers: cons})
|
||||||
|
// we should perform the lopping before we start getting
|
||||||
|
// the expensive information on containers
|
||||||
|
if options.Last < len(cons) {
|
||||||
|
cons = cons[len(cons)-options.Last:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, con := range cons {
|
||||||
|
listCon, err := ListContainerBatch(runtime, con, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pss = append(pss, listCon)
|
||||||
|
|
||||||
|
}
|
||||||
|
return pss, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchContainerOp is used in ps to reduce performance hits by "batching"
|
||||||
|
// locks.
|
||||||
|
func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities.ContainerListOptions) (entities.ListContainer, error) {
|
||||||
|
var (
|
||||||
|
conConfig *libpod.ContainerConfig
|
||||||
|
conState define.ContainerStatus
|
||||||
|
err error
|
||||||
|
exitCode int32
|
||||||
|
exited bool
|
||||||
|
pid int
|
||||||
|
size *shared.ContainerSize
|
||||||
|
startedTime time.Time
|
||||||
|
exitedTime time.Time
|
||||||
|
cgroup, ipc, mnt, net, pidns, user, uts string
|
||||||
|
)
|
||||||
|
|
||||||
|
batchErr := ctr.Batch(func(c *libpod.Container) error {
|
||||||
|
conConfig = c.Config()
|
||||||
|
conState, err = c.State()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain container state")
|
||||||
|
}
|
||||||
|
|
||||||
|
exitCode, exited, err = c.ExitCode()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain container exit code")
|
||||||
|
}
|
||||||
|
startedTime, err = c.StartedTime()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
exitedTime, err = c.FinishedTime()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.Size && !opts.Namespace {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace {
|
||||||
|
pid, err = c.PID()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain container pid")
|
||||||
|
}
|
||||||
|
ctrPID := strconv.Itoa(pid)
|
||||||
|
cgroup, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
|
||||||
|
ipc, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
|
||||||
|
mnt, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
|
||||||
|
net, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
|
||||||
|
pidns, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
|
||||||
|
user, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
|
||||||
|
uts, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
|
||||||
|
}
|
||||||
|
if opts.Size {
|
||||||
|
size = new(shared.ContainerSize)
|
||||||
|
|
||||||
|
rootFsSize, err := c.RootFsSize()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rwSize, err := c.RWSize()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
size.RootFsSize = rootFsSize
|
||||||
|
size.RwSize = rwSize
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if batchErr != nil {
|
||||||
|
return entities.ListContainer{}, batchErr
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := entities.ListContainer{
|
||||||
|
Command: conConfig.Command,
|
||||||
|
Created: conConfig.CreatedTime.Unix(),
|
||||||
|
Exited: exited,
|
||||||
|
ExitCode: exitCode,
|
||||||
|
ExitedAt: exitedTime.Unix(),
|
||||||
|
ID: conConfig.ID,
|
||||||
|
Image: conConfig.RootfsImageName,
|
||||||
|
IsInfra: conConfig.IsInfra,
|
||||||
|
Labels: conConfig.Labels,
|
||||||
|
Mounts: ctr.UserVolumes(),
|
||||||
|
Names: []string{conConfig.Name},
|
||||||
|
Pid: pid,
|
||||||
|
Pod: conConfig.Pod,
|
||||||
|
Ports: conConfig.PortMappings,
|
||||||
|
Size: size,
|
||||||
|
StartedAt: startedTime.Unix(),
|
||||||
|
State: conState.String(),
|
||||||
|
}
|
||||||
|
if opts.Pod && len(conConfig.Pod) > 0 {
|
||||||
|
pod, err := rt.GetPod(conConfig.Pod)
|
||||||
|
if err != nil {
|
||||||
|
return entities.ListContainer{}, err
|
||||||
|
}
|
||||||
|
ps.PodName = pod.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace {
|
||||||
|
ps.Namespaces = entities.ListContainerNamespaces{
|
||||||
|
Cgroup: cgroup,
|
||||||
|
IPC: ipc,
|
||||||
|
MNT: mnt,
|
||||||
|
NET: net,
|
||||||
|
PIDNS: pidns,
|
||||||
|
User: user,
|
||||||
|
UTS: uts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ps, nil
|
||||||
|
}
|
Reference in New Issue
Block a user