Implement linux factory and container with readonly interface

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2014-10-22 20:45:23 +00:00 committed by Victor Marmol
parent 6bf1e4ddfc
commit 6310a958e6
7 changed files with 226 additions and 31 deletions

View File

@ -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
}

View File

@ -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.

46
generic_error.go Normal file
View File

@ -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)
}

66
linux_container.go Normal file
View File

@ -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
}

89
linux_factory.go Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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)