1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Implement lxc driver

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
Michael Crosby 2014-01-09 15:04:45 -08:00
parent 1ceb049118
commit 203b69c8c9
2 changed files with 228 additions and 66 deletions

View file

@ -1,10 +1,26 @@
package execdriver
import (
"errors"
"io"
"net"
"os/exec"
"sync"
"syscall"
"time"
)
var (
ErrCommandIsNil = errors.New("Process's cmd is nil")
)
type Driver interface {
Start(c *Process) error
Stop(c *Process) error
Kill(c *Process, sig int) error
Wait(c *Process, duration time.Duration) error
}
// Network settings of the container
type Network struct {
Gateway string
@ -13,12 +29,44 @@ type Network struct {
Mtu int
}
type State struct {
sync.RWMutex
running bool
pid int
exitCode int
startedAt time.Time
finishedAt time.Time
}
func (s *State) IsRunning() bool {
s.RLock()
defer s.RUnlock()
return s.running
}
func (s *State) SetRunning() error {
s.Lock()
defer s.Unlock()
s.running = true
return nil
}
func (s *State) SetStopped(exitCode int) error {
s.Lock()
defer s.Unlock()
s.exitCode = exitCode
s.running = false
return nil
}
// Container / Process / Whatever, we can redefine the conatiner here
// to be what it should be and not have to carry the baggage of the
// container type in the core with backward compat. This is what a
// driver needs to execute a process inside of a conatiner. This is what
// a container is at it's core.
type Container struct {
type Process struct {
State State
Name string // unique name for the conatienr
Privileged bool
User string
@ -28,19 +76,45 @@ type Container struct {
Args []string
Environment map[string]string
WorkingDir string
ConfigPath string
Network *Network // if network is nil then networking is disabled
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Context interface{}
cmd *exec.Cmd
}
// State can be handled internally in the drivers
type Driver interface {
Start(c *Container) error
Stop(c *Container) error
Kill(c *Container, sig int) error
Running(c *Container) (bool, error)
Wait(c *Container, seconds int) error
func (c *Process) SetCmd(cmd *exec.Cmd) error {
c.cmd = cmd
cmd.Stdout = c.Stdout
cmd.Stderr = c.Stderr
cmd.Stdin = c.Stdin
return nil
}
func (c *Process) StdinPipe() (io.WriteCloser, error) {
return c.cmd.StdinPipe()
}
func (c *Process) StderrPipe() (io.ReadCloser, error) {
return c.cmd.StderrPipe()
}
func (c *Process) StdoutPipe() (io.ReadCloser, error) {
return c.cmd.StdoutPipe()
}
func (c *Process) GetExitCode() int {
if c.cmd != nil {
return c.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
}
return -1
}
func (c *Process) Wait() error {
if c.cmd != nil {
return c.cmd.Wait()
}
return ErrCommandIsNil
}

View file

@ -1,47 +1,58 @@
package lxc
import (
"errors"
"fmt"
"github.com/dotcloud/docker/execdriver"
"os/exec"
"strconv"
"sync"
"strings"
"syscall"
"time"
)
const (
startPath = "lxc-start"
)
var (
ErrNotRunning = errors.New("Process could not be started")
ErrWaitTimeoutReached = errors.New("Wait timeout reached")
)
func init() {
// Register driver
}
type driver struct {
containerLock map[string]*sync.Mutex
root string // root path for the driver to use
containers map[string]*execdriver.Process
}
func NewDriver() (execdriver.Driver, error) {
func NewDriver(root string) (execdriver.Driver, error) {
// setup unconfined symlink
return &driver{
root: root,
containers: make(map[string]*execdriver.Process),
}, nil
}
func (d *driver) Start(c *execdriver.Container) error {
l := d.getLock(c)
l.Lock()
defer l.Unlock()
running, err := d.running(c)
if err != nil {
return err
}
if running {
func (d *driver) Start(c *execdriver.Process) error {
if c.State.IsRunning() {
return nil
}
configPath, err := d.generateConfig(c)
if err != nil {
return err
}
/*
configPath, err := d.generateConfig(c)
if err != nil {
return err
}
*/
params := []string{
startPath,
"-n", c.Name,
"-f", configPath,
"-f", c.ConfigPath,
"--",
c.InitPath,
}
@ -49,8 +60,8 @@ func (d *driver) Start(c *execdriver.Container) error {
if c.Network != nil {
params = append(params,
"-g", c.Network.Gateway,
"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network, IPPrefixLen),
"-mtu", c.Network.Mtu,
"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
"-mtu", strconv.Itoa(c.Network.Mtu),
)
}
@ -72,9 +83,9 @@ func (d *driver) Start(c *execdriver.Container) error {
cmd := exec.Command(params[0], params[1:]...)
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
cmd.Stdout = c.Stdout
cmd.Stderr = c.Stderr
cmd.Stdin = c.Stdin
if err := c.SetCmd(cmd); err != nil {
return err
}
if err := cmd.Start(); err != nil {
return err
@ -87,58 +98,135 @@ func (d *driver) Start(c *execdriver.Container) error {
return nil
}
func (d *driver) Stop(c *execdriver.Container) error {
l := d.getLock(c)
l.Lock()
defer l.Unlock()
func (d *driver) Stop(c *execdriver.Process) error {
if err := d.kill(c, 15); err != nil {
if err := d.kill(c, 9); err != nil {
return err
}
}
if err := d.wait(c, 10); err != nil {
return d.kill(c, 9)
if err := d.wait(c, 10*time.Second); err != nil {
if err := d.kill(c, 9); err != nil {
return err
}
}
exitCode := c.GetExitCode()
if err := c.State.SetStopped(exitCode); err != nil {
return err
}
return nil
}
func (d *driver) Wait(c *execdriver.Container, seconds int) error {
l := d.getLock(c)
l.Lock()
defer l.Unlock()
func (d *driver) Kill(c *execdriver.Process, sig int) error {
c.State.Lock()
defer c.State.Unlock()
return d.kill(c, sig)
}
return d.wait(c, seconds)
func (d *driver) Wait(c *execdriver.Process, duration time.Duration) error {
return d.wait(c, duration)
}
// If seconds < 0 then wait forever
func (d *driver) wait(c *execdriver.Container, seconds int) error {
func (d *driver) wait(c *execdriver.Process, duration time.Duration) error {
begin:
var (
killer bool
done = d.waitCmd(c)
)
if duration > 0 {
select {
case err := <-done:
if err != nil && err == execdriver.ErrCommandIsNil {
done = d.waitLxc(c, &killer)
goto begin
}
return err
case <-time.After(duration):
killer = true
return ErrWaitTimeoutReached
}
} else {
if err := <-done; err != nil {
if err == execdriver.ErrCommandIsNil {
done = d.waitLxc(c, &killer)
goto begin
}
return err
}
}
return nil
}
func (d *driver) kill(c *execdriver.Container, sig int) error {
func (d *driver) kill(c *execdriver.Process, sig int) error {
return exec.Command("lxc-kill", "-n", c.Name, strconv.Itoa(sig)).Run()
}
func (d *driver) running(c *execdriver.Container) (bool, error) {
}
// Generate the lxc configuration and return the path to the file
func (d *driver) generateConfig(c *execdriver.Container) (string, error) {
}
func (d *driver) waitForStart(cmd *exec.Cmd, c *execdriver.Container) error {
}
func (d *driver) getLock(c *execdriver.Container) *sync.Mutex {
l, ok := d.containerLock[c.Name]
if !ok {
l = &sync.Mutex{}
d.containerLock[c.Name] = l
/* Generate the lxc configuration and return the path to the file
func (d *driver) generateConfig(c *execdriver.Process) (string, error) {
p := path.Join(d.root, c.Name)
f, err := os.Create(p)
if err != nil {
return "", nil
}
return l
defer f.Close()
if err := LxcTemplateCompiled.Execute(f, c.Context); err != nil {
return "", err
}
return p, nil
}
*/
func (d *driver) waitForStart(cmd *exec.Cmd, c *execdriver.Process) error {
// We wait for the container to be fully running.
// Timeout after 5 seconds. In case of broken pipe, just retry.
// Note: The container can run and finish correctly before
// the end of this loop
for now := time.Now(); time.Since(now) < 5*time.Second; {
// If the container dies while waiting for it, just return
if !c.State.IsRunning() {
return nil
}
output, err := exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput()
if err != nil {
output, err = exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput()
if err != nil {
return err
}
}
if strings.Contains(string(output), "RUNNING") {
return nil
}
time.Sleep(50 * time.Millisecond)
}
return ErrNotRunning
}
func (d *driver) waitCmd(c *execdriver.Process) <-chan error {
done := make(chan error)
go func() {
done <- c.Wait()
}()
return done
}
func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error {
done := make(chan error)
go func() {
for *kill {
output, err := exec.Command("lxc-info", "-n", c.Name).CombinedOutput()
if err != nil {
done <- err
return
}
if !strings.Contains(string(output), "RUNNING") {
done <- err
return
}
time.Sleep(500 * time.Millisecond)
}
}()
return done
}