From 6310a958e61914ebccd6d7f64c429f40343f0716 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 22 Oct 2014 20:45:23 +0000 Subject: [PATCH] Implement linux factory and container with readonly interface Signed-off-by: Michael Crosby --- api_temp.go | 21 ----------- error.go | 24 ++++++++++--- generic_error.go | 46 ++++++++++++++++++++++++ linux_container.go | 66 ++++++++++++++++++++++++++++++++++ linux_factory.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++ nsinit/main.go | 1 + nsinit/stats.go | 10 +++--- 7 files changed, 226 insertions(+), 31 deletions(-) delete mode 100644 api_temp.go create mode 100644 generic_error.go create mode 100644 linux_container.go create mode 100644 linux_factory.go diff --git a/api_temp.go b/api_temp.go deleted file mode 100644 index 5c682ee3..00000000 --- a/api_temp.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Temporary API endpoint for libcontainer while the full API is finalized (api.go). -*/ -package libcontainer - -import ( - "github.com/docker/libcontainer/cgroups/fs" - "github.com/docker/libcontainer/network" -) - -// TODO(vmarmol): Complete Stats() in final libcontainer API and move users to that. -// DEPRECATED: The below portions are only to be used during the transition to the official API. -// Returns all available stats for the given container. -func GetStats(container *Config, state *State) (stats *ContainerStats, err error) { - stats = &ContainerStats{} - if stats.CgroupStats, err = fs.GetStats(state.CgroupPaths); err != nil { - return stats, err - } - stats.NetworkStats, err = network.GetStats(&state.NetworkState) - return stats, err -} diff --git a/error.go b/error.go index 5ff56d80..5b96a3d2 100644 --- a/error.go +++ b/error.go @@ -8,7 +8,6 @@ const ( // Factory errors IdInUse ErrorCode = iota InvalidIdFormat - // TODO: add Load errors // Container errors ContainerDestroyed @@ -19,14 +18,29 @@ const ( SystemError ) +func (c ErrorCode) String() string { + switch c { + case IdInUse: + return "Id already in use" + case InvalidIdFormat: + return "Invalid format" + case ContainerDestroyed: + return "Container destroyed" + case ContainerPaused: + return "Container paused" + case ConfigInvalid: + return "Invalid configuration" + case SystemError: + return "System Error" + default: + return "Unknown error" + } +} + // API Error type. type Error interface { error - // Returns the stack trace, if any, which identifies the - // point at which the error occurred. - Stack() []byte - // Returns a verbose string including the error message // and a representation of the stack trace suitable for // printing. diff --git a/generic_error.go b/generic_error.go new file mode 100644 index 00000000..7e97af7c --- /dev/null +++ b/generic_error.go @@ -0,0 +1,46 @@ +package libcontainer + +import ( + "bytes" + "fmt" + "runtime" + "time" +) + +var newLine = []byte("\n") + +func newGenericError(err error, c ErrorCode) Error { + return &GenericError{ + timestamp: time.Now(), + err: err, + code: c, + stack: captureStackTrace(2), + } +} + +func captureStackTrace(skip int) string { + buf := make([]byte, 4096) + buf = buf[:runtime.Stack(buf, true)] + + lines := bytes.Split(buf, newLine) + return string(bytes.Join(lines[skip:], newLine)) +} + +type GenericError struct { + timestamp time.Time + code ErrorCode + err error + stack string +} + +func (e *GenericError) Error() string { + return fmt.Sprintf("[%d] %s: %s", e.code, e.code, e.err) +} + +func (e *GenericError) Code() ErrorCode { + return e.code +} + +func (e *GenericError) Detail() string { + return fmt.Sprintf("[%d] %s\n%s", e.code, e.err, e.stack) +} diff --git a/linux_container.go b/linux_container.go new file mode 100644 index 00000000..9c16c489 --- /dev/null +++ b/linux_container.go @@ -0,0 +1,66 @@ +// +build linux + +package libcontainer + +import ( + "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/cgroups/systemd" + "github.com/docker/libcontainer/network" +) + +type linuxContainer struct { + id string + root string + config *Config + state *State +} + +func (c *linuxContainer) ID() string { + return c.id +} + +func (c *linuxContainer) Config() *Config { + return c.config +} + +func (c *linuxContainer) RunState() (*RunState, Error) { + panic("not implemented") +} + +func (c *linuxContainer) Processes() ([]int, Error) { + var ( + err error + pids []int + ) + + if systemd.UseSystemd() { + pids, err = systemd.GetPids(c.config.Cgroups) + } else { + pids, err = fs.GetPids(c.config.Cgroups) + } + if err != nil { + return nil, newGenericError(err, SystemError) + } + return pids, nil +} + +func (c *linuxContainer) Stats() (*ContainerStats, Error) { + var ( + err error + stats = &ContainerStats{} + ) + + if systemd.UseSystemd() { + stats.CgroupStats, err = systemd.GetStats(c.config.Cgroups) + } else { + stats.CgroupStats, err = fs.GetStats(c.config.Cgroups) + } + if err != nil { + return stats, newGenericError(err, SystemError) + } + + if stats.NetworkStats, err = network.GetStats(&c.state.NetworkState); err != nil { + return stats, newGenericError(err, SystemError) + } + return stats, nil +} diff --git a/linux_factory.go b/linux_factory.go new file mode 100644 index 00000000..73782e3a --- /dev/null +++ b/linux_factory.go @@ -0,0 +1,89 @@ +// +build linux + +package libcontainer + +import ( + "encoding/json" + "os" + "path/filepath" +) + +const ( + configFilename = "config.json" + stateFilename = "state.json" +) + +// New returns a linux based container factory based in the root directory. +func New(root string) (Factory, Error) { + if err := os.MkdirAll(root, 0700); err != nil { + return nil, newGenericError(err, SystemError) + } + + return &linuxFactory{ + root: root, + }, nil +} + +// linuxFactory implements the default factory interface for linux based systems. +type linuxFactory struct { + // root is the root directory + root string +} + +func (l *linuxFactory) Create(id string, config *Config) (Container, Error) { + panic("not implemented") +} + +func (l *linuxFactory) Load(id string) (ContainerInfo, Error) { + containerRoot := filepath.Join(l.root, id) + config, err := l.loadContainerConfig(containerRoot) + if err != nil { + return nil, err + } + + state, err := l.loadContainerState(containerRoot) + if err != nil { + return nil, err + } + + return &linuxContainer{ + id: id, + root: containerRoot, + config: config, + state: state, + }, nil +} + +func (l *linuxFactory) loadContainerConfig(root string) (*Config, Error) { + f, err := os.Open(filepath.Join(root, configFilename)) + if err != nil { + if os.IsNotExist(err) { + return nil, newGenericError(err, ContainerDestroyed) + } + return nil, newGenericError(err, SystemError) + } + defer f.Close() + + var config *Config + if err := json.NewDecoder(f).Decode(&config); err != nil { + return nil, newGenericError(err, ConfigInvalid) + } + return config, nil +} + +func (l *linuxFactory) loadContainerState(root string) (*State, Error) { + f, err := os.Open(filepath.Join(root, stateFilename)) + if err != nil { + if os.IsNotExist(err) { + return nil, newGenericError(err, ContainerDestroyed) + } + return nil, newGenericError(err, SystemError) + } + defer f.Close() + + var state *State + if err := json.NewDecoder(f).Decode(&state); err != nil { + return nil, newGenericError(err, SystemError) + } + return state, nil +} diff --git a/nsinit/main.go b/nsinit/main.go index d65c0140..561ce3a9 100644 --- a/nsinit/main.go +++ b/nsinit/main.go @@ -48,6 +48,7 @@ func main() { app.Flags = []cli.Flag{ cli.StringFlag{Name: "nspid"}, cli.StringFlag{Name: "console"}, + cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"}, } app.Before = preload diff --git a/nsinit/stats.go b/nsinit/stats.go index 612b4a4b..62fc1d4b 100644 --- a/nsinit/stats.go +++ b/nsinit/stats.go @@ -16,23 +16,23 @@ var statsCommand = cli.Command{ } func statsAction(context *cli.Context) { - container, err := loadConfig() + factory, err := libcontainer.New(context.GlobalString("root")) if err != nil { log.Fatal(err) } - state, err := libcontainer.GetState(dataPath) + container, err := factory.Load(context.Args().First()) if err != nil { log.Fatal(err) } - stats, err := libcontainer.GetStats(container, state) + stats, err := container.Stats() if err != nil { log.Fatal(err) } - data, err := json.MarshalIndent(stats, "", "\t") + data, jerr := json.MarshalIndent(stats, "", "\t") if err != nil { - log.Fatal(err) + log.Fatal(jerr) } fmt.Printf("%s", data)