podmanv2 ps

add the ability to list containers

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2020-04-04 13:59:55 -05:00
parent e318b09b68
commit b22254dc8b
16 changed files with 928 additions and 285 deletions

View File

@ -4,21 +4,16 @@ import (
"io/ioutil"
"net/http"
"os"
"path/filepath"
"sort"
"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/define"
"github.com/containers/libpod/pkg/api/handlers/compat"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/ps"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
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) {
var (
filterFuncs []libpod.ContainerFilter
pss []ListContainer
//filterFuncs []libpod.ContainerFilter
//pss []entities.ListContainer
)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
@ -61,66 +56,19 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
opts := shared.PsOptions{
opts := entities.ContainerListOptions{
All: query.All,
Last: query.Last,
Size: query.Size,
Sort: "",
Namespace: query.Namespace,
NoTrunc: true,
Pod: query.Pod,
Sync: query.Sync,
}
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...)
pss, err := ps.GetContainerLists(runtime, opts)
if err != nil {
utils.InternalServerError(w, err)
}
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)
return
}
utils.WriteResponse(w, http.StatusOK, pss)
}
@ -212,125 +160,6 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
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) {
var targetFile string
decoder := r.Context().Value("decoder").(*schema.Decoder)

View File

@ -17,7 +17,7 @@ const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
// swagger:response ListContainers
type swagInspectPodResponse struct {
// in:body
Body []ListContainer
Body []entities.ListContainer
}
// Inspect Manifest

View File

@ -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())
}