Add state method to return container's runtime state
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
9f0cca11d0
commit
7fff13632e
|
@ -16,15 +16,3 @@ const (
|
|||
// The container does not exist.
|
||||
Destroyed
|
||||
)
|
||||
|
||||
// State represents a running container's state
|
||||
type State struct {
|
||||
// InitPid is the init process id in the parent namespace
|
||||
InitPid int `json:"init_pid,omitempty"`
|
||||
|
||||
// InitStartTime is the init process start time
|
||||
InitStartTime string `json:"init_start_time,omitempty"`
|
||||
|
||||
// Path to all the cgroups setup for a container. Key is cgroup subsystem name.
|
||||
CgroupPaths map[string]string `json:"cgroup_paths,omitempty"`
|
||||
}
|
||||
|
|
24
container.go
24
container.go
|
@ -9,6 +9,23 @@ import (
|
|||
"github.com/docker/libcontainer/configs"
|
||||
)
|
||||
|
||||
// State represents a running container's state
|
||||
type State struct {
|
||||
// InitProcessPid is the init process id in the parent namespace.
|
||||
InitProcessPid int
|
||||
|
||||
// InitProcessStartTime is the init process start time.
|
||||
InitProcessStartTime string
|
||||
|
||||
// Path to all the cgroups setup for a container. Key is cgroup subsystem name
|
||||
// with the value as the path.
|
||||
CgroupPaths map[string]string
|
||||
|
||||
// NamespacePaths are filepaths to the container's namespaces. Key is the namespace name
|
||||
// with the value as the path.
|
||||
NamespacePaths map[string]string
|
||||
}
|
||||
|
||||
// A libcontainer container object.
|
||||
//
|
||||
// Each container is thread-safe within the same process. Since a container can
|
||||
|
@ -21,9 +38,16 @@ type Container interface {
|
|||
// Returns the current status of the container.
|
||||
//
|
||||
// errors:
|
||||
// ContainerDestroyed - Container no longer exists,
|
||||
// Systemerror - System error.
|
||||
Status() (configs.Status, error)
|
||||
|
||||
// State returns the current container's state information.
|
||||
//
|
||||
// errors:
|
||||
// Systemerror - System erroor.
|
||||
State() (*State, error)
|
||||
|
||||
// Returns the current config of the container.
|
||||
Config() configs.Config
|
||||
|
||||
|
|
|
@ -43,13 +43,55 @@ func (c *linuxContainer) Status() (configs.Status, error) {
|
|||
}
|
||||
return 0, err
|
||||
}
|
||||
if c.config.Cgroups != nil &&
|
||||
c.config.Cgroups.Freezer == configs.Frozen {
|
||||
if c.config.Cgroups != nil && c.config.Cgroups.Freezer == configs.Frozen {
|
||||
return configs.Paused, nil
|
||||
}
|
||||
return configs.Running, nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) State() (*State, error) {
|
||||
status, err := c.Status()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if status == configs.Destroyed {
|
||||
return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists)
|
||||
}
|
||||
startTime, err := c.initProcess.startTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state := &State{
|
||||
InitProcessPid: c.initProcess.pid(),
|
||||
InitProcessStartTime: startTime,
|
||||
CgroupPaths: c.cgroupManager.GetPaths(),
|
||||
NamespacePaths: make(map[string]string),
|
||||
}
|
||||
for _, ns := range c.config.Namespaces {
|
||||
if ns.Path != "" {
|
||||
state.NamespacePaths[string(ns.Type)] = ns.Path
|
||||
continue
|
||||
}
|
||||
file := ""
|
||||
switch ns.Type {
|
||||
case configs.NEWNET:
|
||||
file = "net"
|
||||
case configs.NEWNS:
|
||||
file = "mnt"
|
||||
case configs.NEWPID:
|
||||
file = "pid"
|
||||
case configs.NEWIPC:
|
||||
file = "ipc"
|
||||
case configs.NEWUSER:
|
||||
file = "user"
|
||||
case configs.NEWUTS:
|
||||
file = "uts"
|
||||
}
|
||||
state.NamespacePaths[string(ns.Type)] = fmt.Sprintf("/proc/%d/ns/%s", c.initProcess.pid(), file)
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) Processes() ([]int, error) {
|
||||
glog.Info("fetch container processes")
|
||||
pids, err := c.cgroupManager.GetPids()
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
package libcontainer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
|
@ -12,6 +14,7 @@ import (
|
|||
type mockCgroupManager struct {
|
||||
pids []int
|
||||
stats *cgroups.Stats
|
||||
paths map[string]string
|
||||
}
|
||||
|
||||
func (m *mockCgroupManager) GetPids() ([]int, error) {
|
||||
|
@ -31,25 +34,52 @@ func (m *mockCgroupManager) Destroy() error {
|
|||
}
|
||||
|
||||
func (m *mockCgroupManager) GetPaths() map[string]string {
|
||||
return nil
|
||||
return m.paths
|
||||
}
|
||||
|
||||
func (m *mockCgroupManager) Freeze(state configs.FreezerState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockProcess struct {
|
||||
_pid int
|
||||
started string
|
||||
}
|
||||
|
||||
func (m *mockProcess) terminate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockProcess) pid() int {
|
||||
return m._pid
|
||||
}
|
||||
|
||||
func (m *mockProcess) startTime() (string, error) {
|
||||
return m.started, nil
|
||||
}
|
||||
|
||||
func (m *mockProcess) start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockProcess) wait() (*os.ProcessState, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockProcess) signal(_ os.Signal) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestGetContainerPids(t *testing.T) {
|
||||
container := &linuxContainer{
|
||||
id: "myid",
|
||||
config: &configs.Config{},
|
||||
cgroupManager: &mockCgroupManager{pids: []int{1, 2, 3}},
|
||||
}
|
||||
|
||||
pids, err := container.Processes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, expected := range []int{1, 2, 3} {
|
||||
if pids[i] != expected {
|
||||
t.Fatalf("expected pid %d but received %d", expected, pids[i])
|
||||
|
@ -70,7 +100,6 @@ func TestGetContainerStats(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
stats, err := container.Stats()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -82,3 +111,86 @@ func TestGetContainerStats(t *testing.T) {
|
|||
t.Fatalf("expected memory usage 1024 but recevied %d", stats.CgroupStats.MemoryStats.Usage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetContainerState(t *testing.T) {
|
||||
var (
|
||||
pid = os.Getpid()
|
||||
expectedMemoryPath = "/sys/fs/cgroup/memory/myid"
|
||||
expectedNetworkPath = "/networks/fd"
|
||||
)
|
||||
container := &linuxContainer{
|
||||
id: "myid",
|
||||
config: &configs.Config{
|
||||
Namespaces: configs.Namespaces{
|
||||
{Type: configs.NEWPID},
|
||||
{Type: configs.NEWNS},
|
||||
{Type: configs.NEWNET, Path: expectedNetworkPath},
|
||||
{Type: configs.NEWUTS},
|
||||
{Type: configs.NEWIPC},
|
||||
},
|
||||
},
|
||||
initProcess: &mockProcess{
|
||||
_pid: pid,
|
||||
started: "010",
|
||||
},
|
||||
cgroupManager: &mockCgroupManager{
|
||||
pids: []int{1, 2, 3},
|
||||
stats: &cgroups.Stats{
|
||||
MemoryStats: cgroups.MemoryStats{
|
||||
Usage: 1024,
|
||||
},
|
||||
},
|
||||
paths: map[string]string{
|
||||
"memory": expectedMemoryPath,
|
||||
},
|
||||
},
|
||||
}
|
||||
state, err := container.State()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state.InitProcessPid != pid {
|
||||
t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
|
||||
}
|
||||
if state.InitProcessStartTime != "010" {
|
||||
t.Fatalf("expected process start time 010 but received %s", state.InitProcessStartTime)
|
||||
}
|
||||
paths := state.CgroupPaths
|
||||
if paths == nil {
|
||||
t.Fatal("cgroup paths should not be nil")
|
||||
}
|
||||
if memPath := paths["memory"]; memPath != expectedMemoryPath {
|
||||
t.Fatalf("expected memory path %q but received %q", expectedMemoryPath, memPath)
|
||||
}
|
||||
for _, ns := range container.config.Namespaces {
|
||||
path := state.NamespacePaths[string(ns.Type)]
|
||||
if path == "" {
|
||||
t.Fatalf("expected non nil namespace path for %s", ns.Type)
|
||||
}
|
||||
if ns.Type == configs.NEWNET {
|
||||
if path != expectedNetworkPath {
|
||||
t.Fatalf("expected path %q but received %q", expectedNetworkPath, path)
|
||||
}
|
||||
} else {
|
||||
file := ""
|
||||
switch ns.Type {
|
||||
case configs.NEWNET:
|
||||
file = "net"
|
||||
case configs.NEWNS:
|
||||
file = "mnt"
|
||||
case configs.NEWPID:
|
||||
file = "pid"
|
||||
case configs.NEWIPC:
|
||||
file = "ipc"
|
||||
case configs.NEWUSER:
|
||||
file = "user"
|
||||
case configs.NEWUTS:
|
||||
file = "uts"
|
||||
}
|
||||
expected := fmt.Sprintf("/proc/%d/ns/%s", pid, file)
|
||||
if expected != path {
|
||||
t.Fatalf("expected path %q but received %q", expected, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,8 @@ func (l *linuxFactory) Load(id string) (Container, error) {
|
|||
return nil, err
|
||||
}
|
||||
r := &restoredProcess{
|
||||
processPid: state.InitPid,
|
||||
processStartTime: state.InitStartTime,
|
||||
processPid: state.InitProcessPid,
|
||||
processStartTime: state.InitProcessStartTime,
|
||||
}
|
||||
cgroupManager := cgroups.LoadCgroupManager(config.Cgroups, state.CgroupPaths)
|
||||
glog.Infof("using %s as cgroup manager", cgroupManager)
|
||||
|
@ -171,7 +171,7 @@ func (l *linuxFactory) loadContainerConfig(root string) (*configs.Config, error)
|
|||
return config, nil
|
||||
}
|
||||
|
||||
func (l *linuxFactory) loadContainerState(root string) (*configs.State, error) {
|
||||
func (l *linuxFactory) loadContainerState(root string) (*State, error) {
|
||||
f, err := os.Open(filepath.Join(root, stateFilename))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
@ -180,7 +180,7 @@ func (l *linuxFactory) loadContainerState(root string) (*configs.State, error) {
|
|||
return nil, newGenericError(err, SystemError)
|
||||
}
|
||||
defer f.Close()
|
||||
var state *configs.State
|
||||
var state *State
|
||||
if err := json.NewDecoder(f).Decode(&state); err != nil {
|
||||
return nil, newGenericError(err, SystemError)
|
||||
}
|
||||
|
|
|
@ -80,8 +80,8 @@ func TestFactoryLoadContainer(t *testing.T) {
|
|||
expectedConfig = &configs.Config{
|
||||
Rootfs: "/mycontainer/root",
|
||||
}
|
||||
expectedState = &configs.State{
|
||||
InitPid: 1024,
|
||||
expectedState = &State{
|
||||
InitProcessPid: 1024,
|
||||
}
|
||||
)
|
||||
if err := os.Mkdir(filepath.Join(root, id), 0700); err != nil {
|
||||
|
@ -112,8 +112,8 @@ func TestFactoryLoadContainer(t *testing.T) {
|
|||
if !ok {
|
||||
t.Fatal("expected linux container on linux based systems")
|
||||
}
|
||||
if lcontainer.initProcess.pid() != expectedState.InitPid {
|
||||
t.Fatalf("expected init pid %d but received %d", expectedState.InitPid, lcontainer.initProcess.pid())
|
||||
if lcontainer.initProcess.pid() != expectedState.InitProcessPid {
|
||||
t.Fatalf("expected init pid %d but received %d", expectedState.InitProcessPid, lcontainer.initProcess.pid())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue