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:
parent
1ceb049118
commit
203b69c8c9
2 changed files with 228 additions and 66 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue