mirror of
https://github.com/containers/podman.git
synced 2025-05-22 09:36:57 +08:00
podman-remote ps
add the ability to run ps on containers using the remote client. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
@ -16,7 +16,6 @@ func getMainCommands() []*cobra.Command {
|
|||||||
_execCommand,
|
_execCommand,
|
||||||
_generateCommand,
|
_generateCommand,
|
||||||
_playCommand,
|
_playCommand,
|
||||||
&_psCommand,
|
|
||||||
_loginCommand,
|
_loginCommand,
|
||||||
_logoutCommand,
|
_logoutCommand,
|
||||||
_mountCommand,
|
_mountCommand,
|
||||||
|
@ -52,6 +52,7 @@ var mainCommands = []*cobra.Command{
|
|||||||
_loadCommand,
|
_loadCommand,
|
||||||
_logsCommand,
|
_logsCommand,
|
||||||
podCommand.Command,
|
podCommand.Command,
|
||||||
|
&_psCommand,
|
||||||
_pullCommand,
|
_pullCommand,
|
||||||
_pushCommand,
|
_pushCommand,
|
||||||
&_rmiCommand,
|
&_rmiCommand,
|
||||||
|
181
cmd/podman/ps.go
181
cmd/podman/ps.go
@ -6,7 +6,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
@ -14,15 +13,12 @@ import (
|
|||||||
tm "github.com/buger/goterm"
|
tm "github.com/buger/goterm"
|
||||||
"github.com/containers/buildah/pkg/formats"
|
"github.com/containers/buildah/pkg/formats"
|
||||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/pkg/adapter"
|
||||||
"github.com/containers/libpod/pkg/util"
|
|
||||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
)
|
)
|
||||||
@ -224,7 +220,7 @@ func psCmd(c *cliconfig.PsValues) error {
|
|||||||
return errors.Wrapf(err, "error with flags passed")
|
return errors.Wrapf(err, "error with flags passed")
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
|
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating libpod runtime")
|
return errors.Wrapf(err, "error creating libpod runtime")
|
||||||
}
|
}
|
||||||
@ -279,128 +275,6 @@ func checkFlagsPassed(c *cliconfig.PsValues) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateContainerFilterFuncs(filter, filterValue string, runtime *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 []string = strings.SplitN(filterValue, "=", 2)
|
|
||||||
var filterKey string = 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 {
|
|
||||||
return strings.Contains(c.Name(), filterValue)
|
|
||||||
}, 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 == true {
|
|
||||||
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 == libpod.ContainerStateConfigured {
|
|
||||||
state = "created"
|
|
||||||
} else if status == libpod.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 := runtime.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 := runtime.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
|
|
||||||
}
|
|
||||||
return nil, errors.Errorf("%s is an invalid filter", filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate the accurate header based on template given
|
// generate the accurate header based on template given
|
||||||
func (p *psTemplateParams) headerMap() map[string]string {
|
func (p *psTemplateParams) headerMap() map[string]string {
|
||||||
v := reflect.Indirect(reflect.ValueOf(p))
|
v := reflect.Indirect(reflect.ValueOf(p))
|
||||||
@ -550,10 +424,8 @@ func dumpJSON(containers []shared.PsContainerOutput) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error {
|
func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error {
|
||||||
var (
|
var (
|
||||||
filterFuncs []libpod.ContainerFilter
|
|
||||||
outputContainers []*libpod.Container
|
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
opts := shared.PsOptions{
|
opts := shared.PsOptions{
|
||||||
@ -570,51 +442,8 @@ func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error {
|
|||||||
Sync: c.Sync,
|
Sync: c.Sync,
|
||||||
}
|
}
|
||||||
|
|
||||||
maxWorkers := shared.Parallelize("ps")
|
pss, err := runtime.Ps(c, opts)
|
||||||
if c.GlobalIsSet("max-workers") {
|
// Here and down
|
||||||
maxWorkers = c.GlobalFlags.MaxWorks
|
|
||||||
}
|
|
||||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
|
||||||
|
|
||||||
filters := c.Filter
|
|
||||||
if len(filters) > 0 {
|
|
||||||
for _, f := range filters {
|
|
||||||
filterSplit := strings.SplitN(f, "=", 2)
|
|
||||||
if len(filterSplit) < 2 {
|
|
||||||
return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
|
||||||
}
|
|
||||||
generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], runtime)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "invalid filter")
|
|
||||||
}
|
|
||||||
filterFuncs = append(filterFuncs, generatedFunc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !opts.Latest {
|
|
||||||
// Get all containers
|
|
||||||
containers, err := runtime.GetContainers(filterFuncs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only want the last few containers
|
|
||||||
if opts.Last > 0 && opts.Last <= len(containers) {
|
|
||||||
return errors.Errorf("--last not yet supported")
|
|
||||||
} else {
|
|
||||||
outputContainers = containers
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Get just the latest container
|
|
||||||
// Ignore filters
|
|
||||||
latestCtr, err := runtime.GetLatestContainer()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
outputContainers = []*libpod.Container{latestCtr}
|
|
||||||
}
|
|
||||||
|
|
||||||
pss := shared.PBatch(outputContainers, maxWorkers, opts)
|
|
||||||
if opts.Sort != "" {
|
if opts.Sort != "" {
|
||||||
pss, err = sortPsOutput(opts.Sort, pss)
|
pss, err = sortPsOutput(opts.Sort, pss)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,7 +44,6 @@ type PsOptions struct {
|
|||||||
Quiet bool
|
Quiet bool
|
||||||
Size bool
|
Size bool
|
||||||
Sort string
|
Sort string
|
||||||
Label string
|
|
||||||
Namespace bool
|
Namespace bool
|
||||||
Sync bool
|
Sync bool
|
||||||
}
|
}
|
||||||
@ -274,6 +273,176 @@ func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 []string = strings.SplitN(filterValue, "=", 2)
|
||||||
|
var filterKey string = 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 {
|
||||||
|
return strings.Contains(c.Name(), filterValue)
|
||||||
|
}, 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 == true {
|
||||||
|
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 == libpod.ContainerStateConfigured {
|
||||||
|
state = "created"
|
||||||
|
} else if status == libpod.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
|
||||||
|
}
|
||||||
|
return nil, errors.Errorf("%s is an invalid filter", filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPsContainerOutput returns a slice of containers specifically for ps output
|
||||||
|
func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, maxWorkers int) ([]PsContainerOutput, error) {
|
||||||
|
var (
|
||||||
|
filterFuncs []libpod.ContainerFilter
|
||||||
|
outputContainers []*libpod.Container
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(filters) > 0 {
|
||||||
|
for _, f := range filters {
|
||||||
|
filterSplit := strings.SplitN(f, "=", 2)
|
||||||
|
if len(filterSplit) < 2 {
|
||||||
|
return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
||||||
|
}
|
||||||
|
generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "invalid filter")
|
||||||
|
}
|
||||||
|
filterFuncs = append(filterFuncs, generatedFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !opts.Latest {
|
||||||
|
// Get all containers
|
||||||
|
containers, err := r.GetContainers(filterFuncs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only want the last few containers
|
||||||
|
if opts.Last > 0 && opts.Last <= len(containers) {
|
||||||
|
return nil, errors.Errorf("--last not yet supported")
|
||||||
|
} else {
|
||||||
|
outputContainers = containers
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get just the latest container
|
||||||
|
// Ignore filters
|
||||||
|
latestCtr, err := r.GetLatestContainer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
outputContainers = []*libpod.Container{latestCtr}
|
||||||
|
}
|
||||||
|
|
||||||
|
pss := PBatch(outputContainers, maxWorkers, opts)
|
||||||
|
return pss, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PBatch is performs batch operations on a container in parallel. It spawns the number of workers
|
// PBatch is performs batch operations on a container in parallel. It spawns the number of workers
|
||||||
// relative to the the number of parallel operations desired.
|
// relative to the the number of parallel operations desired.
|
||||||
func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput {
|
func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput {
|
||||||
|
@ -133,6 +133,47 @@ type ContainerStats (
|
|||||||
pids: int
|
pids: int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PsOpts (
|
||||||
|
all: bool,
|
||||||
|
filters: ?[]string,
|
||||||
|
last: ?int,
|
||||||
|
latest: ?bool,
|
||||||
|
noTrunc: ?bool,
|
||||||
|
pod: ?bool,
|
||||||
|
quiet: ?bool,
|
||||||
|
sort: ?string,
|
||||||
|
sync: ?bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type PsContainer (
|
||||||
|
id: string,
|
||||||
|
image: string,
|
||||||
|
command: string,
|
||||||
|
created: string,
|
||||||
|
ports: string,
|
||||||
|
names: string,
|
||||||
|
isInfra: bool,
|
||||||
|
status: string,
|
||||||
|
state: string,
|
||||||
|
pidNum: int,
|
||||||
|
rootFsSize: int,
|
||||||
|
rwSize: int,
|
||||||
|
pod: string,
|
||||||
|
createdAt: string,
|
||||||
|
exitedAt: string,
|
||||||
|
startedAt: string,
|
||||||
|
labels: [string]string,
|
||||||
|
nsPid: string,
|
||||||
|
cgroup: string,
|
||||||
|
ipc: string,
|
||||||
|
mnt: string,
|
||||||
|
net: string,
|
||||||
|
pidNs: string,
|
||||||
|
user: string,
|
||||||
|
uts: string,
|
||||||
|
mounts: string
|
||||||
|
)
|
||||||
|
|
||||||
# ContainerMount describes the struct for mounts in a container
|
# ContainerMount describes the struct for mounts in a container
|
||||||
type ContainerMount (
|
type ContainerMount (
|
||||||
destination: string,
|
destination: string,
|
||||||
@ -474,6 +515,8 @@ method GetInfo() -> (info: PodmanInfo)
|
|||||||
# See also [GetContainer](#GetContainer).
|
# See also [GetContainer](#GetContainer).
|
||||||
method ListContainers() -> (containers: []Container)
|
method ListContainers() -> (containers: []Container)
|
||||||
|
|
||||||
|
method Ps(opts: PsOpts) -> (containers: []PsContainer)
|
||||||
|
|
||||||
# GetContainer returns information about a single container. If a container
|
# GetContainer returns information about a single container. If a container
|
||||||
# with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound)
|
# with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound)
|
||||||
# error will be returned. See also [ListContainers](ListContainers) and
|
# error will be returned. See also [ListContainers](ListContainers) and
|
||||||
|
@ -401,6 +401,29 @@ func (t ContainerStatus) String() string {
|
|||||||
return "bad state"
|
return "bad state"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToContainerStatus converts a string representation of a containers
|
||||||
|
// status into an actual container status type
|
||||||
|
func StringToContainerStatus(status string) (ContainerStatus, error) {
|
||||||
|
switch status {
|
||||||
|
case ContainerStateUnknown.String():
|
||||||
|
return ContainerStateUnknown, nil
|
||||||
|
case ContainerStateConfigured.String():
|
||||||
|
return ContainerStateConfigured, nil
|
||||||
|
case ContainerStateCreated.String():
|
||||||
|
return ContainerStateCreated, nil
|
||||||
|
case ContainerStateRunning.String():
|
||||||
|
return ContainerStateRunning, nil
|
||||||
|
case ContainerStateStopped.String():
|
||||||
|
return ContainerStateStopped, nil
|
||||||
|
case ContainerStatePaused.String():
|
||||||
|
return ContainerStatePaused, nil
|
||||||
|
case ContainerStateExited.String():
|
||||||
|
return ContainerStateExited, nil
|
||||||
|
default:
|
||||||
|
return ContainerStateUnknown, errors.Wrapf(ErrInvalidArg, "unknown container state: %s", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Config accessors
|
// Config accessors
|
||||||
// Unlocked
|
// Unlocked
|
||||||
|
|
||||||
|
@ -304,3 +304,13 @@ func ReadExitFile(runtimeTmp, ctrID string) (int, error) {
|
|||||||
|
|
||||||
return exitCode, nil
|
return exitCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ps ...
|
||||||
|
func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]shared.PsContainerOutput, error) {
|
||||||
|
maxWorkers := shared.Parallelize("ps")
|
||||||
|
if c.GlobalIsSet("max-workers") {
|
||||||
|
maxWorkers = c.GlobalFlags.MaxWorks
|
||||||
|
}
|
||||||
|
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||||
|
return shared.GetPsContainerOutput(r.Runtime, opts, c.Filter, maxWorkers)
|
||||||
|
}
|
||||||
|
@ -292,3 +292,75 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
|
|||||||
func ReadExitFile(runtimeTmp, ctrID string) (int, error) {
|
func ReadExitFile(runtimeTmp, ctrID string) (int, error) {
|
||||||
return 0, libpod.ErrNotImplemented
|
return 0, libpod.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ps ...
|
||||||
|
func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]shared.PsContainerOutput, error) {
|
||||||
|
var psContainers []shared.PsContainerOutput
|
||||||
|
last := int64(c.Last)
|
||||||
|
PsOpts := iopodman.PsOpts{
|
||||||
|
All: c.All,
|
||||||
|
Filters: &c.Filter,
|
||||||
|
Last: &last,
|
||||||
|
Latest: &c.Latest,
|
||||||
|
NoTrunc: &c.NoTrunct,
|
||||||
|
Pod: &c.Pod,
|
||||||
|
Quiet: &c.Quiet,
|
||||||
|
Sort: &c.Sort,
|
||||||
|
Sync: &c.Sync,
|
||||||
|
}
|
||||||
|
containers, err := iopodman.Ps().Call(r.Conn, PsOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ctr := range containers {
|
||||||
|
createdAt, err := time.Parse(time.RFC3339Nano, ctr.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exitedAt, err := time.Parse(time.RFC3339Nano, ctr.ExitedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
startedAt, err := time.Parse(time.RFC3339Nano, ctr.StartedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
containerSize := shared.ContainerSize{
|
||||||
|
RootFsSize: ctr.RootFsSize,
|
||||||
|
RwSize: ctr.RwSize,
|
||||||
|
}
|
||||||
|
state, err := libpod.StringToContainerStatus(ctr.State)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
psc := shared.PsContainerOutput{
|
||||||
|
ID: ctr.Id,
|
||||||
|
Image: ctr.Image,
|
||||||
|
Command: ctr.Command,
|
||||||
|
Created: ctr.Created,
|
||||||
|
Ports: ctr.Ports,
|
||||||
|
Names: ctr.Names,
|
||||||
|
IsInfra: ctr.IsInfra,
|
||||||
|
Status: ctr.Status,
|
||||||
|
State: state,
|
||||||
|
Pid: int(ctr.PidNum),
|
||||||
|
Size: &containerSize,
|
||||||
|
Pod: ctr.Pod,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
ExitedAt: exitedAt,
|
||||||
|
StartedAt: startedAt,
|
||||||
|
Labels: ctr.Labels,
|
||||||
|
PID: ctr.NsPid,
|
||||||
|
Cgroup: ctr.Cgroup,
|
||||||
|
IPC: ctr.Ipc,
|
||||||
|
MNT: ctr.Mnt,
|
||||||
|
NET: ctr.Net,
|
||||||
|
PIDNS: ctr.PidNs,
|
||||||
|
User: ctr.User,
|
||||||
|
UTS: ctr.Uts,
|
||||||
|
Mounts: ctr.Mounts,
|
||||||
|
}
|
||||||
|
psContainers = append(psContainers, psc)
|
||||||
|
}
|
||||||
|
return psContainers, nil
|
||||||
|
}
|
||||||
|
@ -47,6 +47,55 @@ func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error {
|
|||||||
return call.ReplyListContainers(listContainers)
|
return call.ReplyListContainers(listContainers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *LibpodAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error {
|
||||||
|
var (
|
||||||
|
containers []iopodman.PsContainer
|
||||||
|
)
|
||||||
|
maxWorkers := shared.Parallelize("ps")
|
||||||
|
psOpts := makePsOpts(opts)
|
||||||
|
filters := []string{}
|
||||||
|
if opts.Filters != nil {
|
||||||
|
filters = *opts.Filters
|
||||||
|
}
|
||||||
|
psContainerOutputs, err := shared.GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers)
|
||||||
|
if err != nil {
|
||||||
|
return call.ReplyErrorOccurred(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ctr := range psContainerOutputs {
|
||||||
|
container := iopodman.PsContainer{
|
||||||
|
Id: ctr.ID,
|
||||||
|
Image: ctr.Image,
|
||||||
|
Command: ctr.Command,
|
||||||
|
Created: ctr.Created,
|
||||||
|
Ports: ctr.Ports,
|
||||||
|
Names: ctr.Names,
|
||||||
|
IsInfra: ctr.IsInfra,
|
||||||
|
Status: ctr.Status,
|
||||||
|
State: ctr.State.String(),
|
||||||
|
PidNum: int64(ctr.Pid),
|
||||||
|
RootFsSize: ctr.Size.RootFsSize,
|
||||||
|
RwSize: ctr.Size.RwSize,
|
||||||
|
Pod: ctr.Pod,
|
||||||
|
CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano),
|
||||||
|
ExitedAt: ctr.ExitedAt.Format(time.RFC3339Nano),
|
||||||
|
StartedAt: ctr.StartedAt.Format(time.RFC3339Nano),
|
||||||
|
Labels: ctr.Labels,
|
||||||
|
NsPid: ctr.PID,
|
||||||
|
Cgroup: ctr.Cgroup,
|
||||||
|
Ipc: ctr.Cgroup,
|
||||||
|
Mnt: ctr.MNT,
|
||||||
|
Net: ctr.NET,
|
||||||
|
PidNs: ctr.PIDNS,
|
||||||
|
User: ctr.User,
|
||||||
|
Uts: ctr.UTS,
|
||||||
|
Mounts: ctr.Mounts,
|
||||||
|
}
|
||||||
|
containers = append(containers, container)
|
||||||
|
}
|
||||||
|
return call.ReplyPs(containers)
|
||||||
|
}
|
||||||
|
|
||||||
// GetContainer ...
|
// GetContainer ...
|
||||||
func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
|
func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
|
||||||
ctr, err := i.Runtime.LookupContainer(id)
|
ctr, err := i.Runtime.LookupContainer(id)
|
||||||
|
@ -162,3 +162,36 @@ func stringPullPolicyToType(s string) buildah.PullPolicy {
|
|||||||
}
|
}
|
||||||
return buildah.PullIfMissing
|
return buildah.PullIfMissing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func derefBool(inBool *bool) bool {
|
||||||
|
if inBool == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *inBool
|
||||||
|
}
|
||||||
|
|
||||||
|
func derefString(in *string) string {
|
||||||
|
if in == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return *in
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePsOpts(inOpts iopodman.PsOpts) shared.PsOptions {
|
||||||
|
last := 0
|
||||||
|
if inOpts.Last != nil {
|
||||||
|
lastT := *inOpts.Last
|
||||||
|
last = int(lastT)
|
||||||
|
}
|
||||||
|
return shared.PsOptions{
|
||||||
|
All: inOpts.All,
|
||||||
|
Last: last,
|
||||||
|
Latest: derefBool(inOpts.Latest),
|
||||||
|
NoTrunc: derefBool(inOpts.NoTrunc),
|
||||||
|
Pod: derefBool(inOpts.Pod),
|
||||||
|
Size: true,
|
||||||
|
Sort: derefString(inOpts.Sort),
|
||||||
|
Namespace: true,
|
||||||
|
Sync: derefBool(inOpts.Sync),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user