mirror of
https://github.com/containers/podman.git
synced 2025-06-23 10:38:20 +08:00
Merge pull request #5159 from baude/apiv2cockpit1
Rewire ListContainers for APIv2 libpod
This commit is contained in:
@ -640,6 +640,11 @@ func GetNamespaces(pid int) *Namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// GetNamespaceInfo is an exported wrapper for getNamespaceInfo
|
||||
func GetNamespaceInfo(path string) (string, error) {
|
||||
return getNamespaceInfo(path)
|
||||
}
|
||||
|
||||
func getNamespaceInfo(path string) (string, error) {
|
||||
val, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
|
@ -9,4 +9,4 @@ validate: ${SWAGGER_OUT}
|
||||
${SWAGGER_OUT}:
|
||||
# generate doesn't remove file on error
|
||||
rm -f ${SWAGGER_OUT}
|
||||
swagger generate spec -o ${SWAGGER_OUT} -i tags.yaml -w ./
|
||||
swagger generate spec -o ${SWAGGER_OUT} -i tags.yaml -w ./ -m
|
||||
|
@ -1,16 +1,20 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func StopContainer(w http.ResponseWriter, r *http.Request) {
|
||||
@ -46,7 +50,8 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
func ListContainers(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
filters []string
|
||||
filterFuncs []libpod.ContainerFilter
|
||||
pss []ListContainer
|
||||
)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
@ -73,20 +78,55 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
|
||||
Size: query.Size,
|
||||
Sort: "",
|
||||
Namespace: query.Namespace,
|
||||
NoTrunc: true,
|
||||
Pod: query.Pod,
|
||||
Sync: query.Sync,
|
||||
}
|
||||
if len(query.Filter) > 0 {
|
||||
for k, v := range query.Filter {
|
||||
for _, val := range v {
|
||||
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
|
||||
generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
filterFuncs = append(filterFuncs, generatedFunc)
|
||||
}
|
||||
}
|
||||
}
|
||||
pss, err := shared.GetPsContainerOutput(runtime, opts, filters, 2)
|
||||
|
||||
if !query.All {
|
||||
// The default is get only running containers. Do this with a filterfunc
|
||||
runningOnly, err := shared.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
filterFuncs = append(filterFuncs, runningOnly)
|
||||
}
|
||||
|
||||
cons, err := runtime.GetContainers(filterFuncs...)
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, pss)
|
||||
}
|
||||
|
||||
@ -194,3 +234,122 @@ 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
|
||||
}
|
||||
|
8
pkg/api/handlers/libpod/swagger.go
Normal file
8
pkg/api/handlers/libpod/swagger.go
Normal file
@ -0,0 +1,8 @@
|
||||
package libpod
|
||||
|
||||
// List Containers
|
||||
// swagger:response ListContainers
|
||||
type swagInspectPodResponse struct {
|
||||
// in:body
|
||||
Body []ListContainer
|
||||
}
|
82
pkg/api/handlers/libpod/types.go
Normal file
82
pkg/api/handlers/libpod/types.go
Normal file
@ -0,0 +1,82 @@
|
||||
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())
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/inspect"
|
||||
@ -104,13 +103,6 @@ type swagDockerTopResponse struct {
|
||||
}
|
||||
}
|
||||
|
||||
// List containers
|
||||
// swagger:response LibpodListContainersResponse
|
||||
type swagLibpodListContainersResponse struct {
|
||||
// in:body
|
||||
Body []shared.PsContainerOutput
|
||||
}
|
||||
|
||||
// Inspect container
|
||||
// swagger:response LibpodInspectContainerResponse
|
||||
type swagLibpodInspectContainerResponse struct {
|
||||
|
@ -610,7 +610,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
|
||||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/LibpodListContainersResponse"
|
||||
// $ref: "#/responses/ListContainers"
|
||||
// 400:
|
||||
// $ref: "#/responses/BadParamError"
|
||||
// 500:
|
||||
|
Reference in New Issue
Block a user