diff --git a/libcontainer/container.go b/libcontainer/container.go index 051c8cf6..1d848877 100644 --- a/libcontainer/container.go +++ b/libcontainer/container.go @@ -6,6 +6,7 @@ package libcontainer import ( "os" + "time" "github.com/opencontainers/runc/libcontainer/configs" ) @@ -56,9 +57,12 @@ type BaseState struct { // InitProcessPid is the init process id in the parent namespace. InitProcessPid int `json:"init_process_pid"` - // InitProcessStartTime is the init process start time. + // InitProcessStartTime is the init process start time in clock cycles since boot time. InitProcessStartTime string `json:"init_process_start"` + // CreatedTime is the unix timestamp for the creation time of the container in UTC + CreatedTime time.Time `json:"created_time"` + // Config is the container's configuration. Config configs.Config `json:"config"` } diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 19b8f3a7..edd7d57e 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -15,6 +15,7 @@ import ( "strings" "sync" "syscall" + "time" "github.com/Sirupsen/logrus" "github.com/golang/protobuf/proto" @@ -38,6 +39,7 @@ type linuxContainer struct { m sync.Mutex criuVersion int state containerState + createdTime time.Time } // State represents a running container's state @@ -117,6 +119,11 @@ func (c *linuxContainer) ID() string { return c.id } +// CreatedTime returns the timestamp from when the container was CREATED +func (c *linuxContainer) CreatedTime() time.Time { + return c.createdTime +} + // Config returns the container's configuration func (c *linuxContainer) Config() configs.Config { return *c.config @@ -189,6 +196,9 @@ func (c *linuxContainer) Start(process *Process) error { } return newSystemError(err) } + // generate a timestamp indicating when the container was started + c.createdTime = time.Now().UTC() + c.state = &runningState{ c: c, } @@ -1042,6 +1052,7 @@ func (c *linuxContainer) currentState() (*State, error) { Config: *c.config, InitProcessPid: pid, InitProcessStartTime: startTime, + CreatedTime: c.createdTime, }, CgroupPaths: c.cgroupManager.GetPaths(), NamespacePaths: make(map[configs.NamespaceType]string), diff --git a/libcontainer/factory_linux.go b/libcontainer/factory_linux.go index eb2bb5fb..ea4e864a 100644 --- a/libcontainer/factory_linux.go +++ b/libcontainer/factory_linux.go @@ -201,6 +201,7 @@ func (l *LinuxFactory) Load(id string) (Container, error) { criuPath: l.CriuPath, cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths), root: containerRoot, + createdTime: state.CreatedTime, } c.state = &nullState{c: c} if err := c.refreshState(); err != nil { diff --git a/list.go b/list.go new file mode 100644 index 00000000..96fa67e7 --- /dev/null +++ b/list.go @@ -0,0 +1,83 @@ +// +build linux + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "text/tabwriter" + "time" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" +) + +var listCommand = cli.Command{ + Name: "list", + Usage: "lists containers started by runc with the given root", + Action: func(context *cli.Context) { + + // preload the container factory + if factory == nil { + err := factoryPreload(context) + if err != nil { + logrus.Fatal(err) + return + } + } + + // get the list of containers + root := context.GlobalString("root") + absRoot, err := filepath.Abs(root) + if err != nil { + logrus.Fatal(err) + return + } + list, err := ioutil.ReadDir(absRoot) + + w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0) + fmt.Fprint(w, "ID\tPID\tSTATUS\tCREATED\n") + + // output containers + for _, item := range list { + switch { + case !item.IsDir(): + // do nothing with misc files in the containers directory + case item.IsDir(): + outputListInfo(item.Name(), w) + } + } + + if err := w.Flush(); err != nil { + logrus.Fatal(err) + } + }, +} + +func outputListInfo(id string, w *tabwriter.Writer) { + container, err := factory.Load(id) + if err != nil { + logrus.Fatal(err) + return + } + + containerStatus, err := container.Status() + if err != nil { + logrus.Fatal(err) + return + } + + state, err := container.State() + if err != nil { + logrus.Fatal(err) + return + } + + fmt.Fprintf(w, "%s\t%d\t%s\t%s\n", container.ID(), + state.BaseState.InitProcessPid, + containerStatus.String(), + state.BaseState.CreatedTime.Format(time.RFC3339Nano)) + +} diff --git a/main.go b/main.go index 85c68c2d..574cf89a 100644 --- a/main.go +++ b/main.go @@ -79,6 +79,7 @@ func main() { pauseCommand, resumeCommand, execCommand, + listCommand, } app.Before = func(context *cli.Context) error { if context.GlobalBool("debug") {