mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +08:00
377
cmd/podman/ps.go
377
cmd/podman/ps.go
@ -1,11 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/formats"
|
"github.com/containers/libpod/cmd/podman/formats"
|
||||||
@ -16,12 +20,31 @@ import (
|
|||||||
"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/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
)
|
)
|
||||||
|
|
||||||
const mountTruncLength = 12
|
const (
|
||||||
|
mountTruncLength = 12
|
||||||
|
hid = "CONTAINER ID"
|
||||||
|
himage = "IMAGE"
|
||||||
|
hcommand = "COMMAND"
|
||||||
|
hcreated = "CREATED"
|
||||||
|
hstatus = "STATUS"
|
||||||
|
hports = "PORTS"
|
||||||
|
hnames = "NAMES"
|
||||||
|
hsize = "SIZE"
|
||||||
|
hinfra = "IS INFRA"
|
||||||
|
hpod = "POD"
|
||||||
|
nspid = "PID"
|
||||||
|
nscgroup = "CGROUPNS"
|
||||||
|
nsipc = "IPC"
|
||||||
|
nsmnt = "MNT"
|
||||||
|
nsnet = "NET"
|
||||||
|
nspidns = "PIDNS"
|
||||||
|
nsuserns = "USERNS"
|
||||||
|
nsuts = "UTS"
|
||||||
|
)
|
||||||
|
|
||||||
type psTemplateParams struct {
|
type psTemplateParams struct {
|
||||||
ID string
|
ID string
|
||||||
@ -76,7 +99,7 @@ type psJSONParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Type declaration and functions for sorting the PS output
|
// Type declaration and functions for sorting the PS output
|
||||||
type psSorted []psJSONParams
|
type psSorted []shared.PsContainerOutput
|
||||||
|
|
||||||
func (a psSorted) Len() int { return len(a) }
|
func (a psSorted) Len() int { return len(a) }
|
||||||
func (a psSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
func (a psSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
@ -84,7 +107,7 @@ func (a psSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|||||||
type psSortedCommand struct{ psSorted }
|
type psSortedCommand struct{ psSorted }
|
||||||
|
|
||||||
func (a psSortedCommand) Less(i, j int) bool {
|
func (a psSortedCommand) Less(i, j int) bool {
|
||||||
return strings.Join(a.psSorted[i].Command, " ") < strings.Join(a.psSorted[j].Command, " ")
|
return a.psSorted[i].Command < a.psSorted[j].Command
|
||||||
}
|
}
|
||||||
|
|
||||||
type psSortedCreated struct{ psSorted }
|
type psSortedCreated struct{ psSorted }
|
||||||
@ -201,6 +224,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func psCmd(c *cli.Context) error {
|
func psCmd(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
filterFuncs []libpod.ContainerFilter
|
||||||
|
outputContainers []*libpod.Container
|
||||||
|
)
|
||||||
|
|
||||||
if err := validateFlags(c, psFlags); err != nil {
|
if err := validateFlags(c, psFlags); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -220,11 +248,9 @@ func psCmd(c *cli.Context) error {
|
|||||||
return errors.Errorf("too many arguments, ps takes no arguments")
|
return errors.Errorf("too many arguments, ps takes no arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
format := genPsFormat(c.String("format"), c.Bool("quiet"), c.Bool("size"), c.Bool("namespace"), c.Bool("pod"), c.Bool("all"))
|
|
||||||
|
|
||||||
opts := shared.PsOptions{
|
opts := shared.PsOptions{
|
||||||
All: c.Bool("all"),
|
All: c.Bool("all"),
|
||||||
Format: format,
|
Format: c.String("format"),
|
||||||
Last: c.Int("last"),
|
Last: c.Int("last"),
|
||||||
Latest: c.Bool("latest"),
|
Latest: c.Bool("latest"),
|
||||||
NoTrunc: c.Bool("no-trunc"),
|
NoTrunc: c.Bool("no-trunc"),
|
||||||
@ -235,18 +261,6 @@ func psCmd(c *cli.Context) error {
|
|||||||
Sort: c.String("sort"),
|
Sort: c.String("sort"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var filterFuncs []libpod.ContainerFilter
|
|
||||||
// When we are dealing with latest or last=n, we need to
|
|
||||||
// get all containers.
|
|
||||||
if !opts.All && !opts.Latest && opts.Last < 1 {
|
|
||||||
// only get running containers
|
|
||||||
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
|
|
||||||
state, _ := c.State()
|
|
||||||
// Don't return infra containers
|
|
||||||
return state == libpod.ContainerStateRunning && !c.IsInfra()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
filters := c.StringSlice("filter")
|
filters := c.StringSlice("filter")
|
||||||
if len(filters) > 0 {
|
if len(filters) > 0 {
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
@ -262,8 +276,6 @@ func psCmd(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputContainers []*libpod.Container
|
|
||||||
|
|
||||||
if !opts.Latest {
|
if !opts.Latest {
|
||||||
// Get all containers
|
// Get all containers
|
||||||
containers, err := runtime.GetContainers(filterFuncs...)
|
containers, err := runtime.GetContainers(filterFuncs...)
|
||||||
@ -288,7 +300,92 @@ func psCmd(c *cli.Context) error {
|
|||||||
outputContainers = []*libpod.Container{latestCtr}
|
outputContainers = []*libpod.Container{latestCtr}
|
||||||
}
|
}
|
||||||
|
|
||||||
return generatePsOutput(outputContainers, opts)
|
pss := shared.PBatch(outputContainers, 8, opts)
|
||||||
|
if opts.Sort != "" {
|
||||||
|
pss, err = sortPsOutput(opts.Sort, pss)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If quiet, print only cids and return
|
||||||
|
if opts.Quiet {
|
||||||
|
return printQuiet(pss)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user wants their own GO template format
|
||||||
|
if opts.Format != "" {
|
||||||
|
if opts.Format == "json" {
|
||||||
|
return dumpJSON(pss)
|
||||||
|
}
|
||||||
|
return printFormat(opts.Format, pss)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a tab writer with stdout as the output
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||||
|
defer w.Flush()
|
||||||
|
|
||||||
|
// Output standard PS headers
|
||||||
|
if !opts.Namespace {
|
||||||
|
fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, himage, hcommand, hcreated, hstatus, hports, hnames)
|
||||||
|
// If the user does not want size OR pod info, we print the isInfra bool
|
||||||
|
if !opts.Size && !opts.Pod {
|
||||||
|
fmt.Fprintf(w, "\t%s", hinfra)
|
||||||
|
}
|
||||||
|
// User wants pod info
|
||||||
|
if opts.Pod {
|
||||||
|
fmt.Fprintf(w, "\t%s", hpod)
|
||||||
|
}
|
||||||
|
//User wants size info
|
||||||
|
if opts.Size {
|
||||||
|
fmt.Fprintf(w, "\t%s", hsize)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Output Namespace headers
|
||||||
|
fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, hnames, nspid, nscgroup, nsipc, nsmnt, nsnet, nspidns, nsuserns, nsuts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now iterate each container and output its information
|
||||||
|
for _, container := range pss {
|
||||||
|
|
||||||
|
// Standard PS output
|
||||||
|
if !opts.Namespace {
|
||||||
|
fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Image, container.Command, container.Created, container.Status, container.Ports, container.Names)
|
||||||
|
|
||||||
|
// If not size and not pod info, do isInfra
|
||||||
|
if !opts.Size && !opts.Pod {
|
||||||
|
fmt.Fprintf(w, "\t%t", container.IsInfra)
|
||||||
|
}
|
||||||
|
// User wants pod info
|
||||||
|
if opts.Pod {
|
||||||
|
fmt.Fprintf(w, "\t%s", container.Pod)
|
||||||
|
}
|
||||||
|
//User wants size info
|
||||||
|
if opts.Size {
|
||||||
|
var size string
|
||||||
|
if container.Size == nil {
|
||||||
|
size = units.HumanSizeWithPrecision(0, 0)
|
||||||
|
} else {
|
||||||
|
size = units.HumanSizeWithPrecision(float64(container.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(container.Size.RootFsSize), 3) + ")"
|
||||||
|
fmt.Fprintf(w, "\t%s", size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Print namespace information
|
||||||
|
ns := shared.GetNamespaces(container.Pid)
|
||||||
|
fmt.Fprintf(w, "\n%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Names, container.Pid, ns.Cgroup, ns.IPC, ns.MNT, ns.NET, ns.PIDNS, ns.User, ns.UTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printQuiet(containers []shared.PsContainerOutput) error {
|
||||||
|
for _, c := range containers {
|
||||||
|
fmt.Println(c.ID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkFlagsPassed checks if mutually exclusive flags are passed together
|
// checkFlagsPassed checks if mutually exclusive flags are passed together
|
||||||
@ -420,47 +517,6 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru
|
|||||||
return nil, errors.Errorf("%s is an invalid filter", filter)
|
return nil, errors.Errorf("%s is an invalid filter", filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate the template based on conditions given
|
|
||||||
func genPsFormat(format string, quiet, size, namespace, pod, infra bool) string {
|
|
||||||
if format != "" {
|
|
||||||
// "\t" from the command line is not being recognized as a tab
|
|
||||||
// replacing the string "\t" to a tab character if the user passes in "\t"
|
|
||||||
return strings.Replace(format, `\t`, "\t", -1)
|
|
||||||
}
|
|
||||||
if quiet {
|
|
||||||
return formats.IDString
|
|
||||||
}
|
|
||||||
podappend := ""
|
|
||||||
if pod {
|
|
||||||
podappend = "{{.Pod}}\t"
|
|
||||||
}
|
|
||||||
if namespace {
|
|
||||||
return fmt.Sprintf("table {{.ID}}\t{{.Names}}\t%s{{.PID}}\t{{.CGROUPNS}}\t{{.IPC}}\t{{.MNT}}\t{{.NET}}\t{{.PIDNS}}\t{{.USERNS}}\t{{.UTS}}", podappend)
|
|
||||||
}
|
|
||||||
format = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.Created}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}\t"
|
|
||||||
format += podappend
|
|
||||||
if size {
|
|
||||||
format += "{{.Size}}\t"
|
|
||||||
}
|
|
||||||
if infra {
|
|
||||||
format += "{{.IsInfra}}\t"
|
|
||||||
}
|
|
||||||
return format
|
|
||||||
}
|
|
||||||
|
|
||||||
func psToGeneric(templParams []psTemplateParams, JSONParams []psJSONParams) (genericParams []interface{}) {
|
|
||||||
if len(templParams) > 0 {
|
|
||||||
for _, v := range templParams {
|
|
||||||
genericParams = append(genericParams, interface{}(v))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range JSONParams {
|
|
||||||
genericParams = append(genericParams, interface{}(v))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate the accurate header based on template given
|
// 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))
|
||||||
@ -503,176 +559,6 @@ func sortPsOutput(sortBy string, psOutput psSorted) (psSorted, error) {
|
|||||||
return psOutput, nil
|
return psOutput, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTemplateOutput returns the modified container information
|
|
||||||
func getTemplateOutput(psParams []psJSONParams, opts shared.PsOptions) ([]psTemplateParams, error) {
|
|
||||||
var (
|
|
||||||
psOutput []psTemplateParams
|
|
||||||
pod, status, size string
|
|
||||||
ns *shared.Namespace
|
|
||||||
)
|
|
||||||
// If the user is trying to filter based on size, or opted to sort on size
|
|
||||||
// the size bool must be set.
|
|
||||||
if strings.Contains(opts.Format, ".Size") || opts.Sort == "size" {
|
|
||||||
opts.Size = true
|
|
||||||
}
|
|
||||||
if strings.Contains(opts.Format, ".Pod") || opts.Sort == "pod" {
|
|
||||||
opts.Pod = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, psParam := range psParams {
|
|
||||||
// do we need this?
|
|
||||||
imageName := psParam.Image
|
|
||||||
ctrID := psParam.ID
|
|
||||||
|
|
||||||
if opts.Namespace {
|
|
||||||
ns = psParam.Namespaces
|
|
||||||
}
|
|
||||||
if opts.Size {
|
|
||||||
if psParam.Size == nil {
|
|
||||||
size = units.HumanSizeWithPrecision(0, 0)
|
|
||||||
} else {
|
|
||||||
size = units.HumanSizeWithPrecision(float64(psParam.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(psParam.Size.RootFsSize), 3) + ")"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if opts.Pod {
|
|
||||||
pod = psParam.Pod
|
|
||||||
}
|
|
||||||
|
|
||||||
command := strings.Join(psParam.Command, " ")
|
|
||||||
if !opts.NoTrunc {
|
|
||||||
if len(command) > 20 {
|
|
||||||
command = command[:19] + "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ports := portsToString(psParam.Ports)
|
|
||||||
labels := formatLabels(psParam.Labels)
|
|
||||||
|
|
||||||
switch psParam.Status {
|
|
||||||
case libpod.ContainerStateExited.String():
|
|
||||||
fallthrough
|
|
||||||
case libpod.ContainerStateStopped.String():
|
|
||||||
exitedSince := units.HumanDuration(time.Since(psParam.ExitedAt))
|
|
||||||
status = fmt.Sprintf("Exited (%d) %s ago", psParam.ExitCode, exitedSince)
|
|
||||||
case libpod.ContainerStateRunning.String():
|
|
||||||
status = "Up " + units.HumanDuration(time.Since(psParam.StartedAt)) + " ago"
|
|
||||||
case libpod.ContainerStatePaused.String():
|
|
||||||
status = "Paused"
|
|
||||||
case libpod.ContainerStateCreated.String(), libpod.ContainerStateConfigured.String():
|
|
||||||
status = "Created"
|
|
||||||
default:
|
|
||||||
status = "Error"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !opts.NoTrunc {
|
|
||||||
ctrID = shortID(psParam.ID)
|
|
||||||
pod = shortID(psParam.Pod)
|
|
||||||
}
|
|
||||||
params := psTemplateParams{
|
|
||||||
ID: ctrID,
|
|
||||||
Image: imageName,
|
|
||||||
Command: command,
|
|
||||||
CreatedAtTime: psParam.CreatedAt,
|
|
||||||
Created: units.HumanDuration(time.Since(psParam.CreatedAt)) + " ago",
|
|
||||||
Status: status,
|
|
||||||
Ports: ports,
|
|
||||||
Size: size,
|
|
||||||
Names: psParam.Names,
|
|
||||||
Labels: labels,
|
|
||||||
Mounts: getMounts(psParam.Mounts, opts.NoTrunc),
|
|
||||||
PID: psParam.PID,
|
|
||||||
Pod: pod,
|
|
||||||
IsInfra: psParam.IsInfra,
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Namespace {
|
|
||||||
params.CGROUPNS = ns.Cgroup
|
|
||||||
params.IPC = ns.IPC
|
|
||||||
params.MNT = ns.MNT
|
|
||||||
params.NET = ns.NET
|
|
||||||
params.PIDNS = ns.PIDNS
|
|
||||||
params.USERNS = ns.User
|
|
||||||
params.UTS = ns.UTS
|
|
||||||
}
|
|
||||||
psOutput = append(psOutput, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
return psOutput, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAndSortJSONOutput returns the container info in its raw, sorted form
|
|
||||||
func getAndSortJSONParams(containers []*libpod.Container, opts shared.PsOptions) ([]psJSONParams, error) {
|
|
||||||
var (
|
|
||||||
psOutput psSorted
|
|
||||||
ns *shared.Namespace
|
|
||||||
)
|
|
||||||
for _, ctr := range containers {
|
|
||||||
batchInfo, err := shared.BatchContainerOp(ctr, opts)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Cause(err) == libpod.ErrNoSuchCtr {
|
|
||||||
logrus.Warn(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Namespace {
|
|
||||||
ns = shared.GetNamespaces(batchInfo.Pid)
|
|
||||||
}
|
|
||||||
params := psJSONParams{
|
|
||||||
ID: ctr.ID(),
|
|
||||||
Image: batchInfo.ConConfig.RootfsImageName,
|
|
||||||
ImageID: batchInfo.ConConfig.RootfsImageID,
|
|
||||||
Command: batchInfo.ConConfig.Spec.Process.Args,
|
|
||||||
ExitCode: batchInfo.ExitCode,
|
|
||||||
Exited: batchInfo.Exited,
|
|
||||||
CreatedAt: batchInfo.ConConfig.CreatedTime,
|
|
||||||
StartedAt: batchInfo.StartedTime,
|
|
||||||
ExitedAt: batchInfo.ExitedTime,
|
|
||||||
Status: batchInfo.ConState.String(),
|
|
||||||
PID: batchInfo.Pid,
|
|
||||||
Ports: batchInfo.ConConfig.PortMappings,
|
|
||||||
Size: batchInfo.Size,
|
|
||||||
Names: batchInfo.ConConfig.Name,
|
|
||||||
Labels: batchInfo.ConConfig.Labels,
|
|
||||||
Mounts: batchInfo.ConConfig.UserVolumes,
|
|
||||||
ContainerRunning: batchInfo.ConState == libpod.ContainerStateRunning,
|
|
||||||
Namespaces: ns,
|
|
||||||
Pod: ctr.PodID(),
|
|
||||||
IsInfra: ctr.IsInfra(),
|
|
||||||
}
|
|
||||||
|
|
||||||
psOutput = append(psOutput, params)
|
|
||||||
}
|
|
||||||
return sortPsOutput(opts.Sort, psOutput)
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePsOutput(containers []*libpod.Container, opts shared.PsOptions) error {
|
|
||||||
if len(containers) == 0 && opts.Format != formats.JSONString {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
psOutput, err := getAndSortJSONParams(containers, opts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var out formats.Writer
|
|
||||||
|
|
||||||
switch opts.Format {
|
|
||||||
case formats.JSONString:
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to create JSON for output")
|
|
||||||
}
|
|
||||||
out = formats.JSONStructArray{Output: psToGeneric([]psTemplateParams{}, psOutput)}
|
|
||||||
default:
|
|
||||||
psOutput, err := getTemplateOutput(psOutput, opts)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to create output")
|
|
||||||
}
|
|
||||||
out = formats.StdoutTemplateArray{Output: psToGeneric(psOutput, []psJSONParams{}), Template: opts.Format, Fields: psOutput[0].headerMap()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return formats.Writer(out).Out()
|
|
||||||
}
|
|
||||||
|
|
||||||
// getLabels converts the labels to a string of the form "key=value, key2=value2"
|
// getLabels converts the labels to a string of the form "key=value, key2=value2"
|
||||||
func formatLabels(labels map[string]string) string {
|
func formatLabels(labels map[string]string) string {
|
||||||
var arr []string
|
var arr []string
|
||||||
@ -723,3 +609,28 @@ func portsToString(ports []ocicni.PortMapping) string {
|
|||||||
}
|
}
|
||||||
return strings.Join(portDisplay, ", ")
|
return strings.Join(portDisplay, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printFormat(format string, containers []shared.PsContainerOutput) error {
|
||||||
|
out := template.New("output")
|
||||||
|
out, err := out.Parse(format + "\n")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, container := range containers {
|
||||||
|
if err := out.Execute(os.Stdout, container); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpJSON(containers []shared.PsContainerOutput) error {
|
||||||
|
b, err := json.MarshalIndent(containers, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
os.Stdout.Write(b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,11 +2,15 @@ package shared
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||||
|
"github.com/docker/go-units"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
@ -17,6 +21,11 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cidTruncLength = 12
|
||||||
|
podTruncLength = 12
|
||||||
|
)
|
||||||
|
|
||||||
// PsOptions describes the struct being formed for ps
|
// PsOptions describes the struct being formed for ps
|
||||||
type PsOptions struct {
|
type PsOptions struct {
|
||||||
All bool
|
All bool
|
||||||
@ -45,6 +54,35 @@ type BatchContainerStruct struct {
|
|||||||
Size *ContainerSize
|
Size *ContainerSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PsContainerOutput is the struct being returned from a parallel
|
||||||
|
// Batch operation
|
||||||
|
type PsContainerOutput struct {
|
||||||
|
ID string
|
||||||
|
Image string
|
||||||
|
Command string
|
||||||
|
Created string
|
||||||
|
Ports string
|
||||||
|
Names string
|
||||||
|
IsInfra bool
|
||||||
|
Status string
|
||||||
|
State libpod.ContainerStatus
|
||||||
|
Pid int
|
||||||
|
Size *ContainerSize
|
||||||
|
Pod string
|
||||||
|
CreatedAt time.Time
|
||||||
|
ExitedAt time.Time
|
||||||
|
StartedAt time.Time
|
||||||
|
Labels map[string]string
|
||||||
|
PID string
|
||||||
|
Cgroup string
|
||||||
|
IPC string
|
||||||
|
MNT string
|
||||||
|
NET string
|
||||||
|
PIDNS string
|
||||||
|
User string
|
||||||
|
UTS string
|
||||||
|
}
|
||||||
|
|
||||||
// Namespace describes output for ps namespace
|
// Namespace describes output for ps namespace
|
||||||
type Namespace struct {
|
type Namespace struct {
|
||||||
PID string `json:"pid,omitempty"`
|
PID string `json:"pid,omitempty"`
|
||||||
@ -64,6 +102,212 @@ type ContainerSize struct {
|
|||||||
RwSize int64 `json:"rwSize"`
|
RwSize int64 `json:"rwSize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBatchContainer runs a batch process under one lock to get container information and only
|
||||||
|
// be called in PBatch
|
||||||
|
func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput, error) {
|
||||||
|
var (
|
||||||
|
conState libpod.ContainerStatus
|
||||||
|
command string
|
||||||
|
created string
|
||||||
|
status string
|
||||||
|
exitedAt time.Time
|
||||||
|
startedAt time.Time
|
||||||
|
exitCode int32
|
||||||
|
err error
|
||||||
|
pid int
|
||||||
|
size *ContainerSize
|
||||||
|
ns *Namespace
|
||||||
|
pso PsContainerOutput
|
||||||
|
)
|
||||||
|
batchErr := ctr.Batch(func(c *libpod.Container) error {
|
||||||
|
conState, err = c.State()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain container state")
|
||||||
|
}
|
||||||
|
command = strings.Join(c.Command(), " ")
|
||||||
|
created = units.HumanDuration(time.Since(c.CreatedTime())) + " ago"
|
||||||
|
|
||||||
|
exitCode, _, err = c.ExitCode()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain container exit code")
|
||||||
|
}
|
||||||
|
startedAt, err = c.StartedTime()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
exitedAt, err = c.FinishedTime()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
if opts.Namespace {
|
||||||
|
pid, err = c.PID()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain container pid")
|
||||||
|
}
|
||||||
|
ns = GetNamespaces(pid)
|
||||||
|
}
|
||||||
|
if opts.Size {
|
||||||
|
size = new(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 pso, batchErr
|
||||||
|
}
|
||||||
|
|
||||||
|
switch conState.String() {
|
||||||
|
case libpod.ContainerStateExited.String():
|
||||||
|
fallthrough
|
||||||
|
case libpod.ContainerStateStopped.String():
|
||||||
|
exitedSince := units.HumanDuration(time.Since(exitedAt))
|
||||||
|
status = fmt.Sprintf("Exited (%d) %s ago", exitCode, exitedSince)
|
||||||
|
case libpod.ContainerStateRunning.String():
|
||||||
|
status = "Up " + units.HumanDuration(time.Since(startedAt)) + " ago"
|
||||||
|
case libpod.ContainerStatePaused.String():
|
||||||
|
status = "Paused"
|
||||||
|
case libpod.ContainerStateCreated.String(), libpod.ContainerStateConfigured.String():
|
||||||
|
status = "Created"
|
||||||
|
default:
|
||||||
|
status = "Error"
|
||||||
|
}
|
||||||
|
|
||||||
|
_, imageName := ctr.Image()
|
||||||
|
cid := ctr.ID()
|
||||||
|
pod := ctr.PodID()
|
||||||
|
if !opts.NoTrunc {
|
||||||
|
cid = cid[0:cidTruncLength]
|
||||||
|
if len(pod) > 12 {
|
||||||
|
pod = pod[0:podTruncLength]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pso.ID = cid
|
||||||
|
pso.Image = imageName
|
||||||
|
pso.Command = command
|
||||||
|
pso.Created = created
|
||||||
|
pso.Ports = portsToString(ctr.PortMappings())
|
||||||
|
pso.Names = ctr.Name()
|
||||||
|
pso.IsInfra = ctr.IsInfra()
|
||||||
|
pso.Status = status
|
||||||
|
pso.State = conState
|
||||||
|
pso.Pid = pid
|
||||||
|
pso.Size = size
|
||||||
|
pso.Pod = pod
|
||||||
|
pso.ExitedAt = exitedAt
|
||||||
|
pso.CreatedAt = ctr.CreatedTime()
|
||||||
|
pso.StartedAt = startedAt
|
||||||
|
pso.Labels = ctr.Labels()
|
||||||
|
|
||||||
|
if opts.Namespace {
|
||||||
|
pso.Cgroup = ns.Cgroup
|
||||||
|
pso.IPC = ns.IPC
|
||||||
|
pso.MNT = ns.MNT
|
||||||
|
pso.NET = ns.NET
|
||||||
|
pso.User = ns.User
|
||||||
|
pso.UTS = ns.UTS
|
||||||
|
pso.PIDNS = ns.PIDNS
|
||||||
|
}
|
||||||
|
|
||||||
|
return pso, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pFunc func() (PsContainerOutput, error)
|
||||||
|
|
||||||
|
type workerInput struct {
|
||||||
|
parallelFunc pFunc
|
||||||
|
opts PsOptions
|
||||||
|
cid string
|
||||||
|
job int
|
||||||
|
}
|
||||||
|
|
||||||
|
// worker is a "threaded" worker that takes jobs from the channel "queue"
|
||||||
|
func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContainerOutput, errors chan<- error) {
|
||||||
|
for j := range jobs {
|
||||||
|
r, err := j.parallelFunc()
|
||||||
|
// If we find an error, we return just the error
|
||||||
|
if err != nil {
|
||||||
|
errors <- err
|
||||||
|
} else {
|
||||||
|
// Return the result
|
||||||
|
results <- r
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
psResults []PsContainerOutput
|
||||||
|
)
|
||||||
|
|
||||||
|
// If the number of containers in question is less than the number of
|
||||||
|
// proposed parallel operations, we shouldnt spawn so many workers
|
||||||
|
if workers > len(containers) {
|
||||||
|
workers = len(containers)
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs := make(chan workerInput, len(containers))
|
||||||
|
results := make(chan PsContainerOutput, len(containers))
|
||||||
|
batchErrors := make(chan error, len(containers))
|
||||||
|
|
||||||
|
// Create the workers
|
||||||
|
for w := 1; w <= workers; w++ {
|
||||||
|
go worker(&wg, jobs, results, batchErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add jobs to the workers
|
||||||
|
for i, j := range containers {
|
||||||
|
j := j
|
||||||
|
wg.Add(1)
|
||||||
|
f := func() (PsContainerOutput, error) {
|
||||||
|
return NewBatchContainer(j, opts)
|
||||||
|
}
|
||||||
|
jobs <- workerInput{
|
||||||
|
parallelFunc: f,
|
||||||
|
opts: opts,
|
||||||
|
cid: j.ID(),
|
||||||
|
job: i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(jobs)
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
close(batchErrors)
|
||||||
|
for err := range batchErrors {
|
||||||
|
logrus.Errorf("unable to get container info: %q", err)
|
||||||
|
}
|
||||||
|
for res := range results {
|
||||||
|
// We sort out running vs non-running here to save lots of copying
|
||||||
|
// later.
|
||||||
|
if !opts.All && !opts.Latest && opts.Last < 1 {
|
||||||
|
if !res.IsInfra && res.State == libpod.ContainerStateRunning {
|
||||||
|
psResults = append(psResults, res)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
psResults = append(psResults, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return psResults
|
||||||
|
}
|
||||||
|
|
||||||
// BatchContainer is used in ps to reduce performance hits by "batching"
|
// BatchContainer is used in ps to reduce performance hits by "batching"
|
||||||
// locks.
|
// locks.
|
||||||
func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
|
func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
|
||||||
@ -325,3 +569,19 @@ func getCgroup(spec *specs.Spec) string {
|
|||||||
}
|
}
|
||||||
return cgroup
|
return cgroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// portsToString converts the ports used to a string of the from "port1, port2"
|
||||||
|
func portsToString(ports []ocicni.PortMapping) string {
|
||||||
|
var portDisplay []string
|
||||||
|
if len(ports) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for _, v := range ports {
|
||||||
|
hostIP := v.HostIP
|
||||||
|
if hostIP == "" {
|
||||||
|
hostIP = "0.0.0.0"
|
||||||
|
}
|
||||||
|
portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
|
||||||
|
}
|
||||||
|
return strings.Join(portDisplay, ", ")
|
||||||
|
}
|
||||||
|
@ -666,14 +666,10 @@ func (c *Container) Batch(batchFunc func(*Container) error) error {
|
|||||||
newCtr.valid = true
|
newCtr.valid = true
|
||||||
|
|
||||||
newCtr.batched = true
|
newCtr.batched = true
|
||||||
|
err := batchFunc(newCtr)
|
||||||
if err := batchFunc(newCtr); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
newCtr.batched = false
|
newCtr.batched = false
|
||||||
|
|
||||||
return c.save()
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync updates the current state of the container, checking whether its state
|
// Sync updates the current state of the container, checking whether its state
|
||||||
|
@ -311,8 +311,14 @@ func (s *PodmanSession) OutputToString() string {
|
|||||||
// OutputToStringArray returns the output as a []string
|
// OutputToStringArray returns the output as a []string
|
||||||
// where each array item is a line split by newline
|
// where each array item is a line split by newline
|
||||||
func (s *PodmanSession) OutputToStringArray() []string {
|
func (s *PodmanSession) OutputToStringArray() []string {
|
||||||
|
var results []string
|
||||||
output := fmt.Sprintf("%s", s.Out.Contents())
|
output := fmt.Sprintf("%s", s.Out.Contents())
|
||||||
return strings.Split(output, "\n")
|
for _, line := range strings.Split(output, "\n") {
|
||||||
|
if line != "" {
|
||||||
|
results = append(results, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorGrepString takes session stderr output and behaves like grep. it returns a bool
|
// ErrorGrepString takes session stderr output and behaves like grep. it returns a bool
|
||||||
|
@ -42,7 +42,7 @@ var _ = Describe("Podman logs", func() {
|
|||||||
results := podmanTest.Podman([]string{"logs", cid})
|
results := podmanTest.Podman([]string{"logs", cid})
|
||||||
results.WaitWithDefaultTimeout()
|
results.WaitWithDefaultTimeout()
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
Expect(results.ExitCode()).To(Equal(0))
|
||||||
Expect(len(results.OutputToStringArray())).To(Equal(4))
|
Expect(len(results.OutputToStringArray())).To(Equal(3))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman logs tail two lines", func() {
|
It("podman logs tail two lines", func() {
|
||||||
@ -55,7 +55,7 @@ var _ = Describe("Podman logs", func() {
|
|||||||
results := podmanTest.Podman([]string{"logs", "--tail", "2", cid})
|
results := podmanTest.Podman([]string{"logs", "--tail", "2", cid})
|
||||||
results.WaitWithDefaultTimeout()
|
results.WaitWithDefaultTimeout()
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
Expect(results.ExitCode()).To(Equal(0))
|
||||||
Expect(len(results.OutputToStringArray())).To(Equal(3))
|
Expect(len(results.OutputToStringArray())).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman logs tail 99 lines", func() {
|
It("podman logs tail 99 lines", func() {
|
||||||
@ -68,7 +68,7 @@ var _ = Describe("Podman logs", func() {
|
|||||||
results := podmanTest.Podman([]string{"logs", "--tail", "99", cid})
|
results := podmanTest.Podman([]string{"logs", "--tail", "99", cid})
|
||||||
results.WaitWithDefaultTimeout()
|
results.WaitWithDefaultTimeout()
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
Expect(results.ExitCode()).To(Equal(0))
|
||||||
Expect(len(results.OutputToStringArray())).To(Equal(4))
|
Expect(len(results.OutputToStringArray())).To(Equal(3))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman logs tail 2 lines with timestamps", func() {
|
It("podman logs tail 2 lines with timestamps", func() {
|
||||||
@ -81,7 +81,7 @@ var _ = Describe("Podman logs", func() {
|
|||||||
results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid})
|
results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid})
|
||||||
results.WaitWithDefaultTimeout()
|
results.WaitWithDefaultTimeout()
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
Expect(results.ExitCode()).To(Equal(0))
|
||||||
Expect(len(results.OutputToStringArray())).To(Equal(3))
|
Expect(len(results.OutputToStringArray())).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman logs latest with since time", func() {
|
It("podman logs latest with since time", func() {
|
||||||
@ -94,7 +94,7 @@ var _ = Describe("Podman logs", func() {
|
|||||||
results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid})
|
results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid})
|
||||||
results.WaitWithDefaultTimeout()
|
results.WaitWithDefaultTimeout()
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
Expect(results.ExitCode()).To(Equal(0))
|
||||||
Expect(len(results.OutputToStringArray())).To(Equal(4))
|
Expect(len(results.OutputToStringArray())).To(Equal(3))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman logs latest with since duration", func() {
|
It("podman logs latest with since duration", func() {
|
||||||
@ -107,6 +107,6 @@ var _ = Describe("Podman logs", func() {
|
|||||||
results := podmanTest.Podman([]string{"logs", "--since", "10m", cid})
|
results := podmanTest.Podman([]string{"logs", "--since", "10m", cid})
|
||||||
results.WaitWithDefaultTimeout()
|
results.WaitWithDefaultTimeout()
|
||||||
Expect(results.ExitCode()).To(Equal(0))
|
Expect(results.ExitCode()).To(Equal(0))
|
||||||
Expect(len(results.OutputToStringArray())).To(Equal(4))
|
Expect(len(results.OutputToStringArray())).To(Equal(3))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -77,6 +77,6 @@ var _ = Describe("Podman pod create", func() {
|
|||||||
|
|
||||||
check := podmanTest.Podman([]string{"pod", "ps", "-q"})
|
check := podmanTest.Podman([]string{"pod", "ps", "-q"})
|
||||||
check.WaitWithDefaultTimeout()
|
check.WaitWithDefaultTimeout()
|
||||||
Expect(len(check.OutputToStringArray())).To(Equal(1))
|
Expect(len(check.OutputToStringArray())).To(Equal(0))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -155,7 +155,7 @@ var _ = Describe("Podman pod create", func() {
|
|||||||
check.WaitWithDefaultTimeout()
|
check.WaitWithDefaultTimeout()
|
||||||
Expect(check.ExitCode()).To(Equal(0))
|
Expect(check.ExitCode()).To(Equal(0))
|
||||||
PIDs := check.OutputToStringArray()
|
PIDs := check.OutputToStringArray()
|
||||||
Expect(len(PIDs)).To(Equal(4))
|
Expect(len(PIDs)).To(Equal(3))
|
||||||
|
|
||||||
ctrPID, _ := strconv.Atoi(PIDs[1])
|
ctrPID, _ := strconv.Atoi(PIDs[1])
|
||||||
infraPID, _ := strconv.Atoi(PIDs[2])
|
infraPID, _ := strconv.Atoi(PIDs[2])
|
||||||
|
@ -109,7 +109,7 @@ var _ = Describe("Podman top", func() {
|
|||||||
result := podmanTest.Podman([]string{"pod", "top", podid})
|
result := podmanTest.Podman([]string{"pod", "top", podid})
|
||||||
result.WaitWithDefaultTimeout()
|
result.WaitWithDefaultTimeout()
|
||||||
Expect(result.ExitCode()).To(Equal(0))
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
Expect(len(result.OutputToStringArray())).To(Equal(4))
|
Expect(len(result.OutputToStringArray())).To(Equal(3))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman pod top on pod with containers in different namespace", func() {
|
It("podman pod top on pod with containers in different namespace", func() {
|
||||||
@ -127,6 +127,6 @@ var _ = Describe("Podman top", func() {
|
|||||||
result := podmanTest.Podman([]string{"pod", "top", podid})
|
result := podmanTest.Podman([]string{"pod", "top", podid})
|
||||||
result.WaitWithDefaultTimeout()
|
result.WaitWithDefaultTimeout()
|
||||||
Expect(result.ExitCode()).To(Equal(0))
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
Expect(len(result.OutputToStringArray())).To(Equal(4))
|
Expect(len(result.OutputToStringArray())).To(Equal(3))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -50,7 +50,7 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
|
|||||||
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"})
|
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(len(session.OutputToStringArray())).To(Equal(3))
|
Expect(len(session.OutputToStringArray())).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman run entrypoint with cmd", func() {
|
It("podman run entrypoint with cmd", func() {
|
||||||
@ -62,7 +62,7 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
|
|||||||
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"})
|
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(len(session.OutputToStringArray())).To(Equal(5))
|
Expect(len(session.OutputToStringArray())).To(Equal(4))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman run entrypoint with user cmd overrides image cmd", func() {
|
It("podman run entrypoint with user cmd overrides image cmd", func() {
|
||||||
@ -74,7 +74,7 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
|
|||||||
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "-i"})
|
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "-i"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(len(session.OutputToStringArray())).To(Equal(6))
|
Expect(len(session.OutputToStringArray())).To(Equal(5))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman run entrypoint with user cmd no image cmd", func() {
|
It("podman run entrypoint with user cmd no image cmd", func() {
|
||||||
@ -85,7 +85,7 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
|
|||||||
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "-i"})
|
session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "-i"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(len(session.OutputToStringArray())).To(Equal(6))
|
Expect(len(session.OutputToStringArray())).To(Equal(5))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman run user entrypoint overrides image entrypoint and image cmd", func() {
|
It("podman run user entrypoint overrides image entrypoint and image cmd", func() {
|
||||||
|
@ -75,7 +75,7 @@ var _ = Describe("Podman privileged container tests", func() {
|
|||||||
session := podmanTest.Podman([]string{"run", "-t", "busybox", "ls", "-l", "/dev"})
|
session := podmanTest.Podman([]string{"run", "-t", "busybox", "ls", "-l", "/dev"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(len(session.OutputToStringArray())).To(Equal(18))
|
Expect(len(session.OutputToStringArray())).To(Equal(17))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman privileged should inherit host devices", func() {
|
It("podman privileged should inherit host devices", func() {
|
||||||
|
@ -34,6 +34,6 @@ var _ = Describe("Podman version", func() {
|
|||||||
session := podmanTest.Podman([]string{"version"})
|
session := podmanTest.Podman([]string{"version"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 3))
|
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user