mirror of
https://github.com/containers/podman.git
synced 2025-12-05 12:52:12 +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,
|
||||
_generateCommand,
|
||||
_playCommand,
|
||||
&_psCommand,
|
||||
_loginCommand,
|
||||
_logoutCommand,
|
||||
_mountCommand,
|
||||
|
||||
@@ -52,6 +52,7 @@ var mainCommands = []*cobra.Command{
|
||||
_loadCommand,
|
||||
_logsCommand,
|
||||
podCommand.Command,
|
||||
&_psCommand,
|
||||
_pullCommand,
|
||||
_pushCommand,
|
||||
&_rmiCommand,
|
||||
|
||||
183
cmd/podman/ps.go
183
cmd/podman/ps.go
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
@@ -14,15 +13,12 @@ import (
|
||||
tm "github.com/buger/goterm"
|
||||
"github.com/containers/buildah/pkg/formats"
|
||||
"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/libpod"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
)
|
||||
@@ -224,7 +220,7 @@ func psCmd(c *cliconfig.PsValues) error {
|
||||
return errors.Wrapf(err, "error with flags passed")
|
||||
}
|
||||
|
||||
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
|
||||
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating libpod runtime")
|
||||
}
|
||||
@@ -279,128 +275,6 @@ func checkFlagsPassed(c *cliconfig.PsValues) error {
|
||||
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
|
||||
func (p *psTemplateParams) headerMap() map[string]string {
|
||||
v := reflect.Indirect(reflect.ValueOf(p))
|
||||
@@ -550,11 +424,9 @@ func dumpJSON(containers []shared.PsContainerOutput) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error {
|
||||
func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error {
|
||||
var (
|
||||
filterFuncs []libpod.ContainerFilter
|
||||
outputContainers []*libpod.Container
|
||||
err error
|
||||
err error
|
||||
)
|
||||
opts := shared.PsOptions{
|
||||
All: c.All,
|
||||
@@ -570,51 +442,8 @@ func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error {
|
||||
Sync: c.Sync,
|
||||
}
|
||||
|
||||
maxWorkers := shared.Parallelize("ps")
|
||||
if c.GlobalIsSet("max-workers") {
|
||||
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)
|
||||
pss, err := runtime.Ps(c, opts)
|
||||
// Here and down
|
||||
if opts.Sort != "" {
|
||||
pss, err = sortPsOutput(opts.Sort, pss)
|
||||
if err != nil {
|
||||
|
||||
@@ -44,7 +44,6 @@ type PsOptions struct {
|
||||
Quiet bool
|
||||
Size bool
|
||||
Sort string
|
||||
Label string
|
||||
Namespace 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
|
||||
// relative to the the number of parallel operations desired.
|
||||
func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput {
|
||||
|
||||
@@ -133,6 +133,47 @@ type ContainerStats (
|
||||
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
|
||||
type ContainerMount (
|
||||
destination: string,
|
||||
@@ -474,6 +515,8 @@ method GetInfo() -> (info: PodmanInfo)
|
||||
# See also [GetContainer](#GetContainer).
|
||||
method ListContainers() -> (containers: []Container)
|
||||
|
||||
method Ps(opts: PsOpts) -> (containers: []PsContainer)
|
||||
|
||||
# GetContainer returns information about a single container. If a container
|
||||
# with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound)
|
||||
# error will be returned. See also [ListContainers](ListContainers) and
|
||||
|
||||
Reference in New Issue
Block a user