Merge pull request #5159 from baude/apiv2cockpit1

Rewire ListContainers for APIv2 libpod
This commit is contained in:
OpenShift Merge Robot
2020-02-11 17:13:33 +01:00
committed by GitHub
7 changed files with 260 additions and 14 deletions

View File

@ -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 {

View File

@ -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

View File

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

View File

@ -0,0 +1,8 @@
package libpod
// List Containers
// swagger:response ListContainers
type swagInspectPodResponse struct {
// in:body
Body []ListContainer
}

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

View File

@ -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 {

View File

@ -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: