Files
runc/list.go
T
Aleksa Sarai d2f49696b0 runc: add support for rootless containers
This enables the support for the rootless container mode. There are many
restrictions on what rootless containers can do, so many different runC
commands have been disabled:

* runc checkpoint
* runc events
* runc pause
* runc ps
* runc restore
* runc resume
* runc update

The following commands work:

* runc create
* runc delete
* runc exec
* runc kill
* runc list
* runc run
* runc spec
* runc state

In addition, any specification options that imply joining cgroups have
also been disabled. This is due to support for unprivileged subtree
management not being available from Linux upstream.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
2017-03-23 20:45:24 +11:00

176 lines
4.4 KiB
Go

// +build linux
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"text/tabwriter"
"time"
"encoding/json"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/urfave/cli"
)
const formatOptions = `table or json`
// containerState represents the platform agnostic pieces relating to a
// running container's status and state
type containerState struct {
// Version is the OCI version for the container
Version string `json:"ociVersion"`
// ID is the container ID
ID string `json:"id"`
// InitProcessPid is the init process id in the parent namespace
InitProcessPid int `json:"pid"`
// Status is the current status of the container, running, paused, ...
Status string `json:"status"`
// Bundle is the path on the filesystem to the bundle
Bundle string `json:"bundle"`
// Rootfs is a path to a directory containing the container's root filesystem.
Rootfs string `json:"rootfs"`
// Created is the unix timestamp for the creation time of the container in UTC
Created time.Time `json:"created"`
// Annotations is the user defined annotations added to the config.
Annotations map[string]string `json:"annotations,omitempty"`
// The owner of the state directory (the owner of the container).
Owner string `json:"owner"`
}
var listCommand = cli.Command{
Name: "list",
Usage: "lists containers started by runc with the given root",
ArgsUsage: `
Where the given root is specified via the global option "--root"
(default: "/run/runc").
EXAMPLE 1:
To list containers created via the default "--root":
# runc list
EXAMPLE 2:
To list containers created using a non-default value for "--root":
# runc --root value list`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: `select one of: ` + formatOptions,
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "display only container IDs",
},
},
Action: func(context *cli.Context) error {
if err := checkArgs(context, 0, exactArgs); err != nil {
return err
}
s, err := getContainers(context)
if err != nil {
return err
}
if context.Bool("quiet") {
for _, item := range s {
fmt.Println(item.ID)
}
return nil
}
switch context.String("format") {
case "table":
w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)
fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\tOWNER\n")
for _, item := range s {
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\n",
item.ID,
item.InitProcessPid,
item.Status,
item.Bundle,
item.Created.Format(time.RFC3339Nano),
item.Owner)
}
if err := w.Flush(); err != nil {
return err
}
case "json":
if err := json.NewEncoder(os.Stdout).Encode(s); err != nil {
return err
}
default:
return fmt.Errorf("invalid format option")
}
return nil
},
}
func getContainers(context *cli.Context) ([]containerState, error) {
factory, err := loadFactory(context)
if err != nil {
return nil, err
}
root := context.GlobalString("root")
absRoot, err := filepath.Abs(root)
if err != nil {
return nil, err
}
list, err := ioutil.ReadDir(absRoot)
if err != nil {
fatal(err)
}
var s []containerState
for _, item := range list {
if item.IsDir() {
// This cast is safe on Linux.
stat := item.Sys().(*syscall.Stat_t)
owner, err := user.LookupUid(int(stat.Uid))
if err != nil {
owner.Name = string(stat.Uid)
}
container, err := factory.Load(item.Name())
if err != nil {
fmt.Fprintf(os.Stderr, "load container %s: %v\n", item.Name(), err)
continue
}
containerStatus, err := container.Status()
if err != nil {
fmt.Fprintf(os.Stderr, "status for %s: %v\n", item.Name(), err)
continue
}
state, err := container.State()
if err != nil {
fmt.Fprintf(os.Stderr, "state for %s: %v\n", item.Name(), err)
continue
}
pid := state.BaseState.InitProcessPid
if containerStatus == libcontainer.Stopped {
pid = 0
}
bundle, annotations := utils.Annotations(state.Config.Labels)
s = append(s, containerState{
Version: state.BaseState.Config.Version,
ID: state.BaseState.ID,
InitProcessPid: pid,
Status: containerStatus.String(),
Bundle: bundle,
Rootfs: state.BaseState.Config.Rootfs,
Created: state.BaseState.Created,
Annotations: annotations,
Owner: owner.Name,
})
}
}
return s, nil
}