diff --git a/factory.go b/factory.go index 69014c14..4959ff1e 100644 --- a/factory.go +++ b/factory.go @@ -28,13 +28,13 @@ type Factory interface { // System error Load(id string) (Container, error) - // StartInitialization is an internal API to libcontainer used during the rexec of the - // container. pipefd is the fd to the child end of the pipe used to syncronize the - // parent and child process providing state and configuration to the child process and - // returning any errors during the init of the container - // - // Errors: - // pipe connection error - // system error - StartInitialization(pipefd uintptr) error + // StartInitialization is an internal API to libcontainer used during the rexec of the + // container. pipefd is the fd to the child end of the pipe used to syncronize the + // parent and child process providing state and configuration to the child process and + // returning any errors during the init of the container + // + // Errors: + // pipe connection error + // system error + StartInitialization(pipefd uintptr) error } diff --git a/linux_container.go b/linux_container.go index 63135cb8..10d66bfe 100644 --- a/linux_container.go +++ b/linux_container.go @@ -3,6 +3,13 @@ package libcontainer import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "syscall" + "github.com/docker/libcontainer/network" "github.com/golang/glog" ) @@ -13,6 +20,7 @@ type linuxContainer struct { config *Config state *State cgroupManager CgroupManager + initArgs []string } func (c *linuxContainer) ID() string { @@ -24,7 +32,7 @@ func (c *linuxContainer) Config() *Config { } func (c *linuxContainer) RunState() (RunState, error) { - panic("not implemented") + return Destroyed, nil // FIXME return a real state } func (c *linuxContainer) Processes() ([]int, error) { @@ -53,8 +61,77 @@ func (c *linuxContainer) Stats() (*ContainerStats, error) { } func (c *linuxContainer) StartProcess(config *ProcessConfig) (int, error) { - glog.Info("start new container process") - panic("not implemented") + state, err := c.RunState() + if err != nil { + return -1, err + } + + if state != Destroyed { + glog.Info("start new container process") + panic("not implemented") + } + + if err := c.startInitProcess(config); err != nil { + return -1, err + } + + return c.state.InitPid, nil +} + +func (c *linuxContainer) updateStateFile() error { + data, err := json.MarshalIndent(c.state, "", "\t") + if err != nil { + return newGenericError(err, SystemError) + } + + fnew := filepath.Join(c.root, fmt.Sprintf("%s.new", stateFilename)) + f, err := os.Create(fnew) + if err != nil { + return newGenericError(err, SystemError) + } + + _, err = f.Write(data) + if err != nil { + f.Close() + return newGenericError(err, SystemError) + } + f.Close() + + fname := filepath.Join(c.root, stateFilename) + if err := os.Rename(fnew, fname); err != nil { + return newGenericError(err, SystemError) + } + + return nil +} + +func (c *linuxContainer) startInitProcess(config *ProcessConfig) error { + cmd := exec.Command(c.initArgs[0], append(c.initArgs[1:], config.Args...)...) + cmd.Stdin = config.Stdin + cmd.Stdout = config.Stdout + cmd.Stderr = config.Stderr + + cmd.Env = config.Env + cmd.Dir = c.config.RootFs + + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + + cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL + + //FIXME call namespaces.Exec() + if err := cmd.Start(); err != nil { + return err + } + + c.state.InitPid = cmd.Process.Pid + err := c.updateStateFile() + if err != nil { + return err + } + + return nil } func (c *linuxContainer) Destroy() error { @@ -67,8 +144,8 @@ func (c *linuxContainer) Destroy() error { return newGenericError(nil, ContainerNotStopped) } - glog.Info("destroy container") - panic("not implemented") + os.RemoveAll(c.root) + return nil } func (c *linuxContainer) Pause() error { diff --git a/linux_factory.go b/linux_factory.go index 772c89ef..30c63566 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -23,23 +23,30 @@ var ( ) // 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) +func New(root string, initArgs []string) (Factory, error) { + if root != "" { + if err := os.MkdirAll(root, 0700); err != nil { + return nil, newGenericError(err, SystemError) + } } return &linuxFactory{ - root: root, + root: root, + initArgs: initArgs, }, nil } // linuxFactory implements the default factory interface for linux based systems. type linuxFactory struct { // root is the root directory - root string + root string + initArgs []string } func (l *linuxFactory) Create(id string, config *Config) (Container, error) { + if l.root == "" { + return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) + } if !idRegex.MatchString(id) { return nil, newGenericError(fmt.Errorf("Invalid id format: %v", id), InvalidIdFormat) } @@ -56,10 +63,43 @@ func (l *linuxFactory) Create(id string, config *Config) (Container, error) { return nil, newGenericError(err, SystemError) } - panic("not implemented") + data, err := json.MarshalIndent(config, "", "\t") + if err != nil { + return nil, newGenericError(err, SystemError) + } + + if err := os.MkdirAll(containerRoot, 0700); err != nil { + return nil, newGenericError(err, SystemError) + } + + f, err := os.Create(filepath.Join(containerRoot, configFilename)) + if err != nil { + os.RemoveAll(containerRoot) + return nil, newGenericError(err, SystemError) + } + defer f.Close() + + _, err = f.Write(data) + if err != nil { + os.RemoveAll(containerRoot) + return nil, newGenericError(err, SystemError) + } + + cgroupManager := NewCgroupManager() + return &linuxContainer{ + id: id, + root: containerRoot, + config: config, + initArgs: l.initArgs, + state: &State{}, + cgroupManager: cgroupManager, + }, nil } func (l *linuxFactory) Load(id string) (Container, error) { + if l.root == "" { + return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) + } containerRoot := filepath.Join(l.root, id) glog.Infof("loading container config from %s", containerRoot) config, err := l.loadContainerConfig(containerRoot) @@ -81,6 +121,7 @@ func (l *linuxFactory) Load(id string) (Container, error) { config: config, state: state, cgroupManager: cgroupManager, + initArgs: l.initArgs, }, nil } @@ -117,3 +158,11 @@ func (l *linuxFactory) loadContainerState(root string) (*State, error) { } return state, nil } + +// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state +// This is a low level implementation detail of the reexec and should not be consumed externally +func (f *linuxFactory) StartInitialization(pipefd uintptr) (err error) { + + /* FIXME call namespaces.Init() */ + return nil +} diff --git a/linux_factory_test.go b/linux_factory_test.go index aa9a8848..168be1be 100644 --- a/linux_factory_test.go +++ b/linux_factory_test.go @@ -28,7 +28,7 @@ func TestFactoryNew(t *testing.T) { } defer os.RemoveAll(root) - factory, err := New(root) + factory, err := New(root, nil) if err != nil { t.Fatal(err) } @@ -54,7 +54,7 @@ func TestFactoryLoadNotExists(t *testing.T) { } defer os.RemoveAll(root) - factory, err := New(root) + factory, err := New(root, nil) if err != nil { t.Fatal(err) } @@ -101,7 +101,7 @@ func TestFactoryLoadContainer(t *testing.T) { t.Fatal(err) } - factory, err := New(root) + factory, err := New(root, nil) if err != nil { t.Fatal(err) }