aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/shared/container.go5
-rw-r--r--pkg/api/Makefile2
-rw-r--r--pkg/api/handlers/libpod/containers.go167
-rw-r--r--pkg/api/handlers/libpod/swagger.go8
-rw-r--r--pkg/api/handlers/libpod/types.go82
-rw-r--r--pkg/api/handlers/swagger.go8
-rw-r--r--pkg/api/server/register_containers.go2
7 files changed, 260 insertions, 14 deletions
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index 9459247ed..ff3846e70 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -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 {
diff --git a/pkg/api/Makefile b/pkg/api/Makefile
index c68e50011..6b24bfd83 100644
--- a/pkg/api/Makefile
+++ b/pkg/api/Makefile
@@ -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
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 54acdbd36..a64ed446c 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -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
+}
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
new file mode 100644
index 000000000..aec30ef56
--- /dev/null
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -0,0 +1,8 @@
+package libpod
+
+// List Containers
+// swagger:response ListContainers
+type swagInspectPodResponse struct {
+ // in:body
+ Body []ListContainer
+}
diff --git a/pkg/api/handlers/libpod/types.go b/pkg/api/handlers/libpod/types.go
new file mode 100644
index 000000000..0949b2a72
--- /dev/null
+++ b/pkg/api/handlers/libpod/types.go
@@ -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())
+}
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index bc75777aa..10525bfc7 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -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 {
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index abae81c38..ed30bc14a 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -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: