mirror of
https://github.com/containers/podman.git
synced 2025-06-23 18:59:30 +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) {
|
func getNamespaceInfo(path string) (string, error) {
|
||||||
val, err := os.Readlink(path)
|
val, err := os.Readlink(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -9,4 +9,4 @@ validate: ${SWAGGER_OUT}
|
|||||||
${SWAGGER_OUT}:
|
${SWAGGER_OUT}:
|
||||||
# generate doesn't remove file on error
|
# generate doesn't remove file on error
|
||||||
rm -f ${SWAGGER_OUT}
|
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
|
package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
"github.com/containers/libpod/libpod"
|
"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"
|
||||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StopContainer(w http.ResponseWriter, r *http.Request) {
|
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) {
|
func ListContainers(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
filters []string
|
filterFuncs []libpod.ContainerFilter
|
||||||
|
pss []ListContainer
|
||||||
)
|
)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
query := struct {
|
query := struct {
|
||||||
@ -73,19 +78,54 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
|
|||||||
Size: query.Size,
|
Size: query.Size,
|
||||||
Sort: "",
|
Sort: "",
|
||||||
Namespace: query.Namespace,
|
Namespace: query.Namespace,
|
||||||
|
NoTrunc: true,
|
||||||
Pod: query.Pod,
|
Pod: query.Pod,
|
||||||
Sync: query.Sync,
|
Sync: query.Sync,
|
||||||
}
|
}
|
||||||
if len(query.Filter) > 0 {
|
if len(query.Filter) > 0 {
|
||||||
for k, v := range query.Filter {
|
for k, v := range query.Filter {
|
||||||
for _, val := range v {
|
for _, val := range v {
|
||||||
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
|
generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pss, err := shared.GetPsContainerOutput(runtime, opts, filters, 2)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filterFuncs = append(filterFuncs, generatedFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
utils.WriteResponse(w, http.StatusOK, pss)
|
||||||
}
|
}
|
||||||
@ -194,3 +234,122 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusOK, response)
|
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
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containers/libpod/cmd/podman/shared"
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/pkg/inspect"
|
"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
|
// Inspect container
|
||||||
// swagger:response LibpodInspectContainerResponse
|
// swagger:response LibpodInspectContainerResponse
|
||||||
type swagLibpodInspectContainerResponse struct {
|
type swagLibpodInspectContainerResponse struct {
|
||||||
|
@ -610,7 +610,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
|
|||||||
// - application/json
|
// - application/json
|
||||||
// responses:
|
// responses:
|
||||||
// 200:
|
// 200:
|
||||||
// $ref: "#/responses/LibpodListContainersResponse"
|
// $ref: "#/responses/ListContainers"
|
||||||
// 400:
|
// 400:
|
||||||
// $ref: "#/responses/BadParamError"
|
// $ref: "#/responses/BadParamError"
|
||||||
// 500:
|
// 500:
|
||||||
|
Reference in New Issue
Block a user