From 1ceb049118e16367c2f4015e61657f03d0eb8e29 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 8 Jan 2014 18:47:57 -0800 Subject: [PATCH 01/34] Initial driver changes proposal Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/driver.go | 46 +++++++++++++ execdriver/lxc/driver.go | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 execdriver/driver.go create mode 100644 execdriver/lxc/driver.go diff --git a/execdriver/driver.go b/execdriver/driver.go new file mode 100644 index 0000000000..225f23cf26 --- /dev/null +++ b/execdriver/driver.go @@ -0,0 +1,46 @@ +package execdriver + +import ( + "io" + "net" +) + +// Network settings of the container +type Network struct { + Gateway string + IPAddress net.IPAddr + IPPrefixLen int + Mtu int +} + +// 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 { + Name string // unique name for the conatienr + Privileged bool + User string + Dir string // root fs of the container + InitPath string // dockerinit + Entrypoint string + Args []string + Environment map[string]string + WorkingDir string + Network *Network // if network is nil then networking is disabled + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer + + Context interface{} +} + +// 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 +} diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go new file mode 100644 index 0000000000..c146571970 --- /dev/null +++ b/execdriver/lxc/driver.go @@ -0,0 +1,144 @@ +package lxc + +import ( + "github.com/dotcloud/docker/execdriver" + "os/exec" + "strconv" + "sync" + "syscall" +) + +const ( + startPath = "lxc-start" +) + +type driver struct { + containerLock map[string]*sync.Mutex +} + +func NewDriver() (execdriver.Driver, error) { + // setup unconfined symlink +} + +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 { + return nil + } + + configPath, err := d.generateConfig(c) + if err != nil { + return err + } + + params := []string{ + startPath, + "-n", c.Name, + "-f", configPath, + "--", + c.InitPath, + } + + 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, + ) + } + + if c.User != "" { + params = append(params, "-u", c.User) + } + + if c.Privileged { + params = append(params, "-privileged") + } + + if c.WorkingDir != "" { + params = append(params, "-w", c.WorkingDir) + } + + params = append(params, "--", c.Entrypoint) + params = append(params, c.Args...) + + 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 := cmd.Start(); err != nil { + return err + } + + // Poll for running + if err := d.waitForStart(cmd, c); err != nil { + return err + } + return nil +} + +func (d *driver) Stop(c *execdriver.Container) error { + l := d.getLock(c) + l.Lock() + defer l.Unlock() + + 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) + } + return nil +} + +func (d *driver) Wait(c *execdriver.Container, seconds int) error { + l := d.getLock(c) + l.Lock() + defer l.Unlock() + + return d.wait(c, seconds) +} + +// If seconds < 0 then wait forever +func (d *driver) wait(c *execdriver.Container, seconds int) error { + +} + +func (d *driver) kill(c *execdriver.Container, 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 + } + return l +} From 203b69c8c907b9cf57c887774b6f492d405a0621 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 9 Jan 2014 15:04:45 -0800 Subject: [PATCH 02/34] Implement lxc driver Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/driver.go | 92 ++++++++++++++++-- execdriver/lxc/driver.go | 202 ++++++++++++++++++++++++++++----------- 2 files changed, 228 insertions(+), 66 deletions(-) diff --git a/execdriver/driver.go b/execdriver/driver.go index 225f23cf26..d5b2073669 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -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 } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index c146571970..61223ce8b7 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -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 } From f2680e5a5b1fc2393dfe319fe4dd197672985411 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 9 Jan 2014 16:03:22 -0800 Subject: [PATCH 03/34] Integrate process changes in container.go Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 156 ++++++++++++--------------------------- execdriver/driver.go | 32 ++++---- execdriver/lxc/driver.go | 16 +--- runtime.go | 9 +++ 4 files changed, 78 insertions(+), 135 deletions(-) diff --git a/container.go b/container.go index 9e4495890a..0a0715890a 100644 --- a/container.go +++ b/container.go @@ -1,11 +1,11 @@ package docker import ( - "bytes" "encoding/json" "errors" "fmt" "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/mount" "github.com/dotcloud/docker/pkg/term" @@ -16,7 +16,6 @@ import ( "log" "net" "os" - "os/exec" "path" "strconv" "strings" @@ -55,7 +54,7 @@ type Container struct { Name string Driver string - cmd *exec.Cmd + process *execdriver.Process stdout *utils.WriteBroadcaster stderr *utils.WriteBroadcaster stdin io.ReadCloser @@ -235,10 +234,6 @@ func (container *Container) Inject(file io.Reader, pth string) error { return nil } -func (container *Container) Cmd() *exec.Cmd { - return container.cmd -} - func (container *Container) When() time.Time { return container.Created } @@ -320,8 +315,8 @@ func (container *Container) startPty() error { return err } container.ptyMaster = ptyMaster - container.cmd.Stdout = ptySlave - container.cmd.Stderr = ptySlave + container.process.Stdout = ptySlave + container.process.Stderr = ptySlave // Copy the PTYs to our broadcasters go func() { @@ -333,8 +328,7 @@ func (container *Container) startPty() error { // stdin if container.Config.OpenStdin { - container.cmd.Stdin = ptySlave - container.cmd.SysProcAttr.Setctty = true + container.process.Stdin = ptySlave go func() { defer container.stdin.Close() utils.Debugf("startPty: begin of stdin pipe") @@ -342,7 +336,7 @@ func (container *Container) startPty() error { utils.Debugf("startPty: end of stdin pipe") }() } - if err := container.cmd.Start(); err != nil { + if err := container.runtime.execDriver.Start(container.process); err != nil { return err } ptySlave.Close() @@ -350,10 +344,10 @@ func (container *Container) startPty() error { } func (container *Container) start() error { - container.cmd.Stdout = container.stdout - container.cmd.Stderr = container.stderr + container.process.Stdout = container.stdout + container.process.Stderr = container.stderr if container.Config.OpenStdin { - stdin, err := container.cmd.StdinPipe() + stdin, err := container.process.StdinPipe() if err != nil { return err } @@ -364,7 +358,7 @@ func (container *Container) start() error { utils.Debugf("start: end of stdin pipe") }() } - return container.cmd.Start() + return container.runtime.execDriver.Start(container.process) } func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { @@ -653,8 +647,9 @@ func (container *Container) Start() (err error) { return err } + var workingDir string if container.Config.WorkingDir != "" { - workingDir := path.Clean(container.Config.WorkingDir) + workingDir = path.Clean(container.Config.WorkingDir) utils.Debugf("[working dir] working dir is %s", workingDir) if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil { @@ -713,7 +708,6 @@ func (container *Container) Start() (err error) { } // Mount user specified volumes - for r, v := range container.Volumes { mountAs := "ro" if container.VolumesRW[r] { @@ -725,7 +719,30 @@ func (container *Container) Start() (err error) { } } - container.cmd = exec.Command(params[0], params[1:]...) + var en *execdriver.Network + if !container.runtime.networkManager.disabled { + network := container.NetworkSettings + en = &execdriver.Network{ + Gateway: network.Gateway, + IPAddress: network.IPAddress, + IPPrefixLen: network.IPPrefixLen, + Mtu: container.runtime.config.Mtu, + } + } + + container.process = &execdriver.Process{ + Name: container.ID, + Privileged: container.hostConfig.Privileged, + Dir: root, + InitPath: "/.dockerinit", + Entrypoint: container.Path, + Args: container.Args, + WorkingDir: workingDir, + ConfigPath: container.lxcConfigPath(), + Network: en, + Tty: container.Config.Tty, + User: container.Config.User, + } // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { @@ -735,8 +752,6 @@ func (container *Container) Start() (err error) { return err } - container.cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - if container.Config.Tty { err = container.startPty() } else { @@ -745,48 +760,13 @@ func (container *Container) Start() (err error) { if err != nil { return err } - // FIXME: save state on disk *first*, then converge - // this way disk state is used as a journal, eg. we can restore after crash etc. - container.State.SetRunning(container.cmd.Process.Pid) // Init the lock container.waitLock = make(chan struct{}) container.ToDisk() go container.monitor() - - defer utils.Debugf("Container running: %v", container.State.IsRunning()) - // 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 !container.State.IsRunning() { - return nil - } - output, err := exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput() - if err != nil { - utils.Debugf("Error with lxc-info: %s (%s)", err, output) - - output, err = exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput() - if err != nil { - utils.Debugf("Second Error with lxc-info: %s (%s)", err, output) - return err - } - - } - if strings.Contains(string(output), "RUNNING") { - return nil - } - utils.Debugf("Waiting for the container to start (running: %v): %s", container.State.IsRunning(), bytes.TrimSpace(output)) - time.Sleep(50 * time.Millisecond) - } - - if container.State.IsRunning() { - return ErrContainerStartTimeout - } - return ErrContainerStart + return nil } func (container *Container) getBindMap() (map[string]BindMap, error) { @@ -1159,47 +1139,18 @@ func (container *Container) releaseNetwork() { container.NetworkSettings = &NetworkSettings{} } -// FIXME: replace this with a control socket within dockerinit -func (container *Container) waitLxc() error { - for { - output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput() - if err != nil { - return err - } - if !strings.Contains(string(output), "RUNNING") { - return nil - } - time.Sleep(500 * time.Millisecond) - } -} - func (container *Container) monitor() { // Wait for the program to exit - - // If the command does not exist, try to wait via lxc - // (This probably happens only for ghost containers, i.e. containers that were running when Docker started) - if container.cmd == nil { - utils.Debugf("monitor: waiting for container %s using waitLxc", container.ID) - if err := container.waitLxc(); err != nil { - utils.Errorf("monitor: while waiting for container %s, waitLxc had a problem: %s", container.ID, err) - } - } else { - utils.Debugf("monitor: waiting for container %s using cmd.Wait", container.ID) - if err := container.cmd.Wait(); err != nil { - // Since non-zero exit status and signal terminations will cause err to be non-nil, - // we have to actually discard it. Still, log it anyway, just in case. - utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) - } + if container.process == nil { + panic("Container process is nil") } - utils.Debugf("monitor: container %s finished", container.ID) - - exitCode := -1 - if container.cmd != nil { - exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - } - - if container.runtime != nil && container.runtime.srv != nil { - container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image)) + if err := container.runtime.execDriver.Wait(container.process, time.Duration(0)); err != nil { + // Since non-zero exit status and signal terminations will cause err to be non-nil, + // we have to actually discard it. Still, log it anyway, just in case. + utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) + if container.runtime != nil && container.runtime.srv != nil { + container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image)) + } } // Cleanup @@ -1210,9 +1161,6 @@ func (container *Container) monitor() { container.stdin, container.stdinPipe = io.Pipe() } - // Report status back - container.State.SetStopped(exitCode) - // Release the lock close(container.waitLock) @@ -1267,13 +1215,7 @@ func (container *Container) kill(sig int) error { if !container.State.IsRunning() { return nil } - - if output, err := exec.Command("lxc-kill", "-n", container.ID, strconv.Itoa(sig)).CombinedOutput(); err != nil { - log.Printf("error killing container %s (%s, %s)", utils.TruncateID(container.ID), output, err) - return err - } - - return nil + return container.runtime.execDriver.Kill(container.process, sig) } func (container *Container) Kill() error { @@ -1288,11 +1230,11 @@ func (container *Container) Kill() error { // 2. Wait for the process to die, in last resort, try to kill the process directly if err := container.WaitTimeout(10 * time.Second); err != nil { - if container.cmd == nil { + if container.process == nil { return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID)) } log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID)) - if err := container.cmd.Process.Kill(); err != nil { + if err := container.runtime.execDriver.Kill(container.process, 9); err != nil { return err } } diff --git a/execdriver/driver.go b/execdriver/driver.go index d5b2073669..543c7890d1 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -3,7 +3,6 @@ package execdriver import ( "errors" "io" - "net" "os/exec" "sync" "syscall" @@ -24,7 +23,7 @@ type Driver interface { // Network settings of the container type Network struct { Gateway string - IPAddress net.IPAddr + IPAddress string IPPrefixLen int Mtu int } @@ -67,20 +66,21 @@ func (s *State) SetStopped(exitCode int) error { type Process struct { State State - Name string // unique name for the conatienr - Privileged bool - User string - Dir string // root fs of the container - InitPath string // dockerinit - Entrypoint string - 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 + Name string // unique name for the conatienr + Privileged bool + User string + Dir string // root fs of the container + InitPath string // dockerinit + Entrypoint string + Args []string + // Environment map[string]string // we don't use this right now because we use an env file + WorkingDir string + ConfigPath string + Tty bool + Network *Network // if network is nil then networking is disabled + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer cmd *exec.Cmd } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 61223ce8b7..b36e20986f 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -42,13 +42,6 @@ func (d *driver) Start(c *execdriver.Process) error { return nil } - /* - configPath, err := d.generateConfig(c) - if err != nil { - return err - } - */ - params := []string{ startPath, "-n", c.Name, @@ -82,6 +75,7 @@ func (d *driver) Start(c *execdriver.Process) error { cmd := exec.Command(params[0], params[1:]...) cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + cmd.SysProcAttr.Setctty = true if err := c.SetCmd(cmd); err != nil { return err @@ -111,10 +105,7 @@ func (d *driver) Stop(c *execdriver.Process) error { } } exitCode := c.GetExitCode() - if err := c.State.SetStopped(exitCode); err != nil { - return err - } - return nil + return c.State.SetStopped(exitCode) } func (d *driver) Kill(c *execdriver.Process, sig int) error { @@ -155,7 +146,8 @@ begin: return err } } - return nil + exitCode := c.GetExitCode() + return c.State.SetStopped(exitCode) } func (d *driver) kill(c *execdriver.Process, sig int) error { diff --git a/runtime.go b/runtime.go index 731e3a8784..2686f51df1 100644 --- a/runtime.go +++ b/runtime.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/cgroups" + "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/execdriver/lxc" "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/graphdriver/aufs" _ "github.com/dotcloud/docker/graphdriver/devmapper" @@ -56,6 +58,7 @@ type Runtime struct { config *DaemonConfig containerGraph *graphdb.Database driver graphdriver.Driver + execDriver execdriver.Driver } // List returns an array of all containers registered in the runtime. @@ -735,6 +738,11 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { sysInitPath = localCopy } + ed, err := lxc.NewDriver("") + if err != nil { + return nil, err + } + runtime := &Runtime{ repository: runtimeRepo, containers: list.New(), @@ -748,6 +756,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { containerGraph: graph, driver: driver, sysInitPath: sysInitPath, + execDriver: ed, } if err := runtime.restore(); err != nil { From 25a26977173632b5966838d910550a921ef5a853 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 9 Jan 2014 18:48:34 -0800 Subject: [PATCH 04/34] Keep state in core on container Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 5 +++++ execdriver/driver.go | 42 ++++------------------------------------ execdriver/lxc/driver.go | 20 +++++++------------ 3 files changed, 16 insertions(+), 51 deletions(-) diff --git a/container.go b/container.go index 0a0715890a..92da2c4e99 100644 --- a/container.go +++ b/container.go @@ -761,6 +761,8 @@ func (container *Container) Start() (err error) { return err } + container.State.SetRunning(container.process.Pid()) + // Init the lock container.waitLock = make(chan struct{}) @@ -1161,6 +1163,9 @@ func (container *Container) monitor() { container.stdin, container.stdinPipe = io.Pipe() } + exitCode := container.process.GetExitCode() + container.State.SetStopped(exitCode) + // Release the lock close(container.waitLock) diff --git a/execdriver/driver.go b/execdriver/driver.go index 543c7890d1..61d2b9e90c 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -4,7 +4,6 @@ import ( "errors" "io" "os/exec" - "sync" "syscall" "time" ) @@ -28,44 +27,7 @@ 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 Process struct { - State State - Name string // unique name for the conatienr Privileged bool User string @@ -93,6 +55,10 @@ func (c *Process) SetCmd(cmd *exec.Cmd) error { return nil } +func (c *Process) Pid() int { + return c.cmd.Process.Pid +} + func (c *Process) StdinPipe() (io.WriteCloser, error) { return c.cmd.StdinPipe() } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index b36e20986f..bed11c6809 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -38,10 +38,6 @@ func NewDriver(root string) (execdriver.Driver, error) { } func (d *driver) Start(c *execdriver.Process) error { - if c.State.IsRunning() { - return nil - } - params := []string{ startPath, "-n", c.Name, @@ -104,13 +100,10 @@ func (d *driver) Stop(c *execdriver.Process) error { return err } } - exitCode := c.GetExitCode() - return c.State.SetStopped(exitCode) + return nil } func (d *driver) Kill(c *execdriver.Process, sig int) error { - c.State.Lock() - defer c.State.Unlock() return d.kill(c, sig) } @@ -146,8 +139,7 @@ begin: return err } } - exitCode := c.GetExitCode() - return c.State.SetStopped(exitCode) + return nil } func (d *driver) kill(c *execdriver.Process, sig int) error { @@ -177,9 +169,11 @@ func (d *driver) waitForStart(cmd *exec.Cmd, c *execdriver.Process) error { // 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 - } + /* + 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() From 5573c744e4be16f39e1491188c4d4ff43f61d549 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 11:44:35 -0800 Subject: [PATCH 05/34] Embed exec.Cmd on Process Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 4 ++-- execdriver/driver.go | 46 ++++++---------------------------------- execdriver/lxc/driver.go | 44 +++++++++++++++----------------------- 3 files changed, 25 insertions(+), 69 deletions(-) diff --git a/container.go b/container.go index 92da2c4e99..ab77ea0479 100644 --- a/container.go +++ b/container.go @@ -733,10 +733,10 @@ func (container *Container) Start() (err error) { container.process = &execdriver.Process{ Name: container.ID, Privileged: container.hostConfig.Privileged, - Dir: root, + Rootfs: root, InitPath: "/.dockerinit", Entrypoint: container.Path, - Args: container.Args, + Arguments: container.Args, WorkingDir: workingDir, ConfigPath: container.lxcConfigPath(), Network: en, diff --git a/execdriver/driver.go b/execdriver/driver.go index 61d2b9e90c..503f7f2ae5 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -2,7 +2,6 @@ package execdriver import ( "errors" - "io" "os/exec" "syscall" "time" @@ -28,59 +27,26 @@ type Network struct { } type Process struct { + exec.Cmd + Name string // unique name for the conatienr Privileged bool User string - Dir string // root fs of the container + Rootfs string // root fs of the container InitPath string // dockerinit Entrypoint string - Args []string - // Environment map[string]string // we don't use this right now because we use an env file + Arguments []string WorkingDir string ConfigPath string Tty bool Network *Network // if network is nil then networking is disabled - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer - - cmd *exec.Cmd -} - -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) Pid() int { - return c.cmd.Process.Pid -} - -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() + return c.Process.Pid } func (c *Process) GetExitCode() int { - if c.cmd != nil { - return c.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - } + return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() return -1 } - -func (c *Process) Wait() error { - if c.cmd != nil { - return c.cmd.Wait() - } - return ErrCommandIsNil -} diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index bed11c6809..1af76b4f40 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -67,22 +67,28 @@ func (d *driver) Start(c *execdriver.Process) error { } params = append(params, "--", c.Entrypoint) - params = append(params, c.Args...) + params = append(params, c.Arguments...) - cmd := exec.Command(params[0], params[1:]...) - cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - cmd.SysProcAttr.Setctty = true - - if err := c.SetCmd(cmd); err != nil { - return err + var ( + name = params[0] + arg = params[1:] + ) + aname, err := exec.LookPath(name) + if err != nil { + aname = name } + c.Path = aname + c.Args = append([]string{name}, arg...) - if err := cmd.Start(); err != nil { + c.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + c.SysProcAttr.Setctty = true + + if err := c.Start(); err != nil { return err } // Poll for running - if err := d.waitForStart(cmd, c); err != nil { + if err := d.waitForStart(c); err != nil { return err } return nil @@ -113,11 +119,11 @@ func (d *driver) Wait(c *execdriver.Process, duration time.Duration) error { // If seconds < 0 then wait forever func (d *driver) wait(c *execdriver.Process, duration time.Duration) error { -begin: var ( killer bool done = d.waitCmd(c) ) +begin: if duration > 0 { select { case err := <-done: @@ -146,23 +152,7 @@ func (d *driver) kill(c *execdriver.Process, sig int) error { return exec.Command("lxc-kill", "-n", c.Name, strconv.Itoa(sig)).Run() } -/* 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 - } - 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 { +func (d *driver) waitForStart(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 From 1c1cf54b0aa44553b714b82a1960b4f1199ac33a Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 11:58:34 -0800 Subject: [PATCH 06/34] Fix tty set issue for ioctl error Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 1 + execdriver/lxc/driver.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/container.go b/container.go index ab77ea0479..c1f5577248 100644 --- a/container.go +++ b/container.go @@ -329,6 +329,7 @@ func (container *Container) startPty() error { // stdin if container.Config.OpenStdin { container.process.Stdin = ptySlave + container.process.SysProcAttr.Setctty = true go func() { defer container.stdin.Close() utils.Debugf("startPty: begin of stdin pipe") diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 1af76b4f40..e649263f85 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -81,7 +81,6 @@ func (d *driver) Start(c *execdriver.Process) error { c.Args = append([]string{name}, arg...) c.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - c.SysProcAttr.Setctty = true if err := c.Start(); err != nil { return err From 5a3d9bd432fdaa95f870d4d99fad1d6fafeaad92 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 14:26:29 -0800 Subject: [PATCH 07/34] WIP complete integration test run Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 28 +++++++++--------- execdriver/driver.go | 4 +-- execdriver/lxc/driver.go | 63 ++++++++++++++++++---------------------- runtime.go | 12 ++++++++ 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/container.go b/container.go index c1f5577248..90347a3212 100644 --- a/container.go +++ b/container.go @@ -337,7 +337,7 @@ func (container *Container) startPty() error { utils.Debugf("startPty: end of stdin pipe") }() } - if err := container.runtime.execDriver.Start(container.process); err != nil { + if err := container.runtime.Start(container); err != nil { return err } ptySlave.Close() @@ -359,7 +359,7 @@ func (container *Container) start() error { utils.Debugf("start: end of stdin pipe") }() } - return container.runtime.execDriver.Start(container.process) + return container.runtime.Start(container) } func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { @@ -732,7 +732,7 @@ func (container *Container) Start() (err error) { } container.process = &execdriver.Process{ - Name: container.ID, + ID: container.ID, Privileged: container.hostConfig.Privileged, Rootfs: root, InitPath: "/.dockerinit", @@ -744,6 +744,7 @@ func (container *Container) Start() (err error) { Tty: container.Config.Tty, User: container.Config.User, } + container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { @@ -753,6 +754,10 @@ func (container *Container) Start() (err error) { return err } + // Init the lock + container.waitLock = make(chan struct{}) + go container.monitor() + if container.Config.Tty { err = container.startPty() } else { @@ -763,12 +768,7 @@ func (container *Container) Start() (err error) { } container.State.SetRunning(container.process.Pid()) - - // Init the lock - container.waitLock = make(chan struct{}) - container.ToDisk() - go container.monitor() return nil } @@ -1143,11 +1143,10 @@ func (container *Container) releaseNetwork() { } func (container *Container) monitor() { + time.Sleep(1 * time.Second) // Wait for the program to exit - if container.process == nil { - panic("Container process is nil") - } - if err := container.runtime.execDriver.Wait(container.process, time.Duration(0)); err != nil { + fmt.Printf("--->Before WAIT %s\n", container.ID) + if err := container.runtime.Wait(container, time.Duration(0)); err != nil { // Since non-zero exit status and signal terminations will cause err to be non-nil, // we have to actually discard it. Still, log it anyway, just in case. utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) @@ -1156,6 +1155,7 @@ func (container *Container) monitor() { } } + fmt.Printf("--->After WAIT %s\n", container.ID) // Cleanup container.cleanup() @@ -1221,7 +1221,7 @@ func (container *Container) kill(sig int) error { if !container.State.IsRunning() { return nil } - return container.runtime.execDriver.Kill(container.process, sig) + return container.runtime.Kill(container, sig) } func (container *Container) Kill() error { @@ -1240,7 +1240,7 @@ func (container *Container) Kill() error { return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID)) } log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID)) - if err := container.runtime.execDriver.Kill(container.process, 9); err != nil { + if err := container.runtime.Kill(container, 9); err != nil { return err } } diff --git a/execdriver/driver.go b/execdriver/driver.go index 503f7f2ae5..eb468f3ef6 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -13,7 +13,6 @@ var ( type Driver interface { Start(c *Process) error - Stop(c *Process) error Kill(c *Process, sig int) error Wait(c *Process, duration time.Duration) error } @@ -26,10 +25,11 @@ type Network struct { Mtu int } +// Process wrapps an os/exec.Cmd to add more metadata type Process struct { exec.Cmd - Name string // unique name for the conatienr + ID string Privileged bool User string Rootfs string // root fs of the container diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index e649263f85..f6306b4e16 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/execdriver" + "os" "os/exec" "strconv" "strings" - "syscall" "time" ) @@ -25,22 +25,20 @@ func init() { } type driver struct { - root string // root path for the driver to use - containers map[string]*execdriver.Process + root string // root path for the driver to use } func NewDriver(root string) (execdriver.Driver, error) { // setup unconfined symlink return &driver{ - root: root, - containers: make(map[string]*execdriver.Process), + root: root, }, nil } func (d *driver) Start(c *execdriver.Process) error { params := []string{ startPath, - "-n", c.Name, + "-n", c.ID, "-f", c.ConfigPath, "--", c.InitPath, @@ -80,8 +78,7 @@ func (d *driver) Start(c *execdriver.Process) error { c.Path = aname c.Args = append([]string{name}, arg...) - c.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - + fmt.Printf("-->%s\n-->%v\n", name, arg) if err := c.Start(); err != nil { return err } @@ -93,21 +90,6 @@ func (d *driver) Start(c *execdriver.Process) error { return nil } -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*time.Second); err != nil { - if err := d.kill(c, 9); err != nil { - return err - } - } - return nil -} - func (d *driver) Kill(c *execdriver.Process, sig int) error { return d.kill(c, sig) } @@ -148,40 +130,49 @@ begin: } func (d *driver) kill(c *execdriver.Process, sig int) error { - return exec.Command("lxc-kill", "-n", c.Name, strconv.Itoa(sig)).Run() + return exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).Run() } func (d *driver) waitForStart(c *execdriver.Process) error { + var ( + err error + output []byte + ) // 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 the process dies while waiting for it, just return + if c.ProcessState != nil && c.ProcessState.Exited() { + return nil + } + + output, err = d.getInfo(c) if err != nil { - output, err = exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput() + output, err = d.getInfo(c) if err != nil { return err } - } if strings.Contains(string(output), "RUNNING") { return nil } time.Sleep(50 * time.Millisecond) } + fmt.Printf("-->%s\n", string(output)) + + os.Exit(1) return ErrNotRunning } func (d *driver) waitCmd(c *execdriver.Process) <-chan error { done := make(chan error) go func() { + if c == nil { + done <- execdriver.ErrCommandIsNil + return + } done <- c.Wait() }() return done @@ -191,7 +182,7 @@ 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() + output, err := exec.Command("lxc-info", "-n", c.ID).CombinedOutput() if err != nil { done <- err return @@ -205,3 +196,7 @@ func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error { }() return done } + +func (d *driver) getInfo(c *execdriver.Process) ([]byte, error) { + return exec.Command("lxc-info", "-s", "-n", c.ID).CombinedOutput() +} diff --git a/runtime.go b/runtime.go index 2686f51df1..d302c9c217 100644 --- a/runtime.go +++ b/runtime.go @@ -838,6 +838,18 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) { return archive.ExportChanges(cDir, changes) } +func (runtime *Runtime) Start(c *Container) error { + return runtime.execDriver.Start(c.process) +} + +func (runtime *Runtime) Kill(c *Container, sig int) error { + return runtime.execDriver.Kill(c.process, sig) +} + +func (runtime *Runtime) Wait(c *Container, duration time.Duration) error { + return runtime.execDriver.Wait(c.process, duration) +} + // Nuke kills all containers then removes all content // from the content root, including images, volumes and // container filesystems. From 8e87835968a4c6233406061759e3dca03ab86989 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 15:06:16 -0800 Subject: [PATCH 08/34] Rework monitor and waitlock inside of driver Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 21 ++++++++++------- execdriver/driver.go | 9 +++---- execdriver/lxc/driver.go | 51 ++++++++++------------------------------ runtime.go | 2 +- 4 files changed, 29 insertions(+), 54 deletions(-) diff --git a/container.go b/container.go index 90347a3212..f801302154 100644 --- a/container.go +++ b/container.go @@ -743,6 +743,7 @@ func (container *Container) Start() (err error) { Network: en, Tty: container.Config.Tty, User: container.Config.User, + WaitLock: make(chan struct{}), } container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} @@ -754,8 +755,6 @@ func (container *Container) Start() (err error) { return err } - // Init the lock - container.waitLock = make(chan struct{}) go container.monitor() if container.Config.Tty { @@ -1143,10 +1142,17 @@ func (container *Container) releaseNetwork() { } func (container *Container) monitor() { - time.Sleep(1 * time.Second) // Wait for the program to exit fmt.Printf("--->Before WAIT %s\n", container.ID) - if err := container.runtime.Wait(container, time.Duration(0)); err != nil { + if container.process == nil { + if err := container.runtime.Wait(container, 0); err != nil { + utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) + } + } else { + <-container.process.WaitLock + } + + if err := container.process.WaitError; err != nil { // Since non-zero exit status and signal terminations will cause err to be non-nil, // we have to actually discard it. Still, log it anyway, just in case. utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) @@ -1167,9 +1173,6 @@ func (container *Container) monitor() { exitCode := container.process.GetExitCode() container.State.SetStopped(exitCode) - // Release the lock - close(container.waitLock) - if err := container.ToDisk(); err != nil { // FIXME: there is a race condition here which causes this to fail during the unit tests. // If another goroutine was waiting for Wait() to return before removing the container's root @@ -1283,8 +1286,8 @@ func (container *Container) Restart(seconds int) error { // Wait blocks until the container stops running, then returns its exit code. func (container *Container) Wait() int { - <-container.waitLock - return container.State.GetExitCode() + <-container.process.WaitLock + return container.process.GetExitCode() } func (container *Container) Resize(h, w int) error { diff --git a/execdriver/driver.go b/execdriver/driver.go index eb468f3ef6..4a0f72dd08 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -1,20 +1,15 @@ package execdriver import ( - "errors" "os/exec" "syscall" "time" ) -var ( - ErrCommandIsNil = errors.New("Process's cmd is nil") -) - type Driver interface { Start(c *Process) error Kill(c *Process, sig int) error - Wait(c *Process, duration time.Duration) error + Wait(id string, duration time.Duration) error // Wait on an out of process option - lxc ghosts } // Network settings of the container @@ -40,6 +35,8 @@ type Process struct { ConfigPath string Tty bool Network *Network // if network is nil then networking is disabled + WaitLock chan struct{} + WaitError error } func (c *Process) Pid() int { diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index f6306b4e16..64e89c5c21 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/execdriver" - "os" "os/exec" "strconv" "strings" @@ -78,11 +77,17 @@ func (d *driver) Start(c *execdriver.Process) error { c.Path = aname c.Args = append([]string{name}, arg...) - fmt.Printf("-->%s\n-->%v\n", name, arg) if err := c.Start(); err != nil { return err } + go func() { + if err := c.Wait(); err != nil { + c.WaitError = err + } + close(c.WaitLock) + }() + // Poll for running if err := d.waitForStart(c); err != nil { return err @@ -94,37 +99,22 @@ func (d *driver) Kill(c *execdriver.Process, sig int) error { return d.kill(c, sig) } -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.Process, duration time.Duration) error { +func (d *driver) Wait(id string, duration time.Duration) error { var ( killer bool - done = d.waitCmd(c) + done = d.waitLxc(id, &killer) ) -begin: + 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 <-done } return nil } @@ -160,29 +150,14 @@ func (d *driver) waitForStart(c *execdriver.Process) error { } time.Sleep(50 * time.Millisecond) } - fmt.Printf("-->%s\n", string(output)) - - os.Exit(1) return ErrNotRunning } -func (d *driver) waitCmd(c *execdriver.Process) <-chan error { - done := make(chan error) - go func() { - if c == nil { - done <- execdriver.ErrCommandIsNil - return - } - done <- c.Wait() - }() - return done -} - -func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error { +func (d *driver) waitLxc(id string, kill *bool) <-chan error { done := make(chan error) go func() { for *kill { - output, err := exec.Command("lxc-info", "-n", c.ID).CombinedOutput() + output, err := exec.Command("lxc-info", "-n", id).CombinedOutput() if err != nil { done <- err return diff --git a/runtime.go b/runtime.go index d302c9c217..432e28ea01 100644 --- a/runtime.go +++ b/runtime.go @@ -847,7 +847,7 @@ func (runtime *Runtime) Kill(c *Container, sig int) error { } func (runtime *Runtime) Wait(c *Container, duration time.Duration) error { - return runtime.execDriver.Wait(c.process, duration) + return runtime.execDriver.Wait(c.ID, duration) } // Nuke kills all containers then removes all content From 8b60273f76723897a9831ca125500ede85e05b5d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 15:34:03 -0800 Subject: [PATCH 09/34] Update with container specific waitLock Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 9 ++++----- execdriver/lxc/driver.go | 8 +++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/container.go b/container.go index f801302154..33e39815e5 100644 --- a/container.go +++ b/container.go @@ -754,7 +754,7 @@ func (container *Container) Start() (err error) { if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil { return err } - + container.waitLock = make(chan struct{}) go container.monitor() if container.Config.Tty { @@ -1143,7 +1143,6 @@ func (container *Container) releaseNetwork() { func (container *Container) monitor() { // Wait for the program to exit - fmt.Printf("--->Before WAIT %s\n", container.ID) if container.process == nil { if err := container.runtime.Wait(container, 0); err != nil { utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) @@ -1161,7 +1160,6 @@ func (container *Container) monitor() { } } - fmt.Printf("--->After WAIT %s\n", container.ID) // Cleanup container.cleanup() @@ -1173,6 +1171,7 @@ func (container *Container) monitor() { exitCode := container.process.GetExitCode() container.State.SetStopped(exitCode) + close(container.waitLock) if err := container.ToDisk(); err != nil { // FIXME: there is a race condition here which causes this to fail during the unit tests. // If another goroutine was waiting for Wait() to return before removing the container's root @@ -1286,8 +1285,8 @@ func (container *Container) Restart(seconds int) error { // Wait blocks until the container stops running, then returns its exit code. func (container *Container) Wait() int { - <-container.process.WaitLock - return container.process.GetExitCode() + <-container.waitLock + return container.State.GetExitCode() } func (container *Container) Resize(h, w int) error { diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 64e89c5c21..e9bddf1af5 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -17,6 +17,7 @@ const ( var ( ErrNotRunning = errors.New("Process could not be started") ErrWaitTimeoutReached = errors.New("Wait timeout reached") + Debug bool ) func init() { @@ -120,7 +121,12 @@ func (d *driver) Wait(id string, duration time.Duration) error { } func (d *driver) kill(c *execdriver.Process, sig int) error { - return exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).Run() + output, err := exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).CombinedOutput() + if err != nil { + fmt.Printf("--->%s\n", output) + return fmt.Errorf("Err: %s Output: %s", err, output) + } + return nil } func (d *driver) waitForStart(c *execdriver.Process) error { From ad9710685ca3ae101c84a57c7d68a25cba4bede2 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 16:11:19 -0800 Subject: [PATCH 10/34] Fix race in TestRun Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 2 +- execdriver/driver.go | 4 +++- execdriver/lxc/driver.go | 10 +++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/container.go b/container.go index 33e39815e5..80f9e34ac2 100644 --- a/container.go +++ b/container.go @@ -1171,7 +1171,6 @@ func (container *Container) monitor() { exitCode := container.process.GetExitCode() container.State.SetStopped(exitCode) - close(container.waitLock) if err := container.ToDisk(); err != nil { // FIXME: there is a race condition here which causes this to fail during the unit tests. // If another goroutine was waiting for Wait() to return before removing the container's root @@ -1181,6 +1180,7 @@ func (container *Container) monitor() { // FIXME: why are we serializing running state to disk in the first place? //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err) } + close(container.waitLock) } func (container *Container) cleanup() { diff --git a/execdriver/driver.go b/execdriver/driver.go index 4a0f72dd08..94905a7b0d 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -44,6 +44,8 @@ func (c *Process) Pid() int { } func (c *Process) GetExitCode() int { + if c.ProcessState == nil { + return -1 + } return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - return -1 } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index e9bddf1af5..0f7a4cacf8 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -139,9 +139,13 @@ func (d *driver) waitForStart(c *execdriver.Process) error { // 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 process dies while waiting for it, just return - if c.ProcessState != nil && c.ProcessState.Exited() { - return nil + select { + case <-c.WaitLock: + // If the process dies while waiting for it, just return + if c.ProcessState != nil && c.ProcessState.Exited() { + return nil + } + default: } output, err = d.getInfo(c) From 381d593d04fc46dac5b202d047981e15183c5ed1 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 16:18:15 -0800 Subject: [PATCH 11/34] Fix race in cleanup Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/container.go b/container.go index 80f9e34ac2..856ee352d6 100644 --- a/container.go +++ b/container.go @@ -379,12 +379,14 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s if container.Config.StdinOnce && !container.Config.Tty { defer cStdin.Close() } else { - if cStdout != nil { - defer cStdout.Close() - } - if cStderr != nil { - defer cStderr.Close() - } + defer func() { + if cStdout != nil { + cStdout.Close() + } + if cStderr != nil { + cStderr.Close() + } + }() } if container.Config.Tty { _, err = utils.CopyEscapable(cStdin, stdin) @@ -480,12 +482,15 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s } return utils.Go(func() error { - if cStdout != nil { - defer cStdout.Close() - } - if cStderr != nil { - defer cStderr.Close() - } + defer func() { + if cStdout != nil { + cStdout.Close() + } + if cStderr != nil { + cStderr.Close() + } + }() + // FIXME: how to clean up the stdin goroutine without the unwanted side effect // of closing the passed stdin? Add an intermediary io.Pipe? for i := 0; i < nJobs; i += 1 { From 77936ba1a1ff404b75ae8a34c6d4e280c23d9144 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 17:25:32 -0800 Subject: [PATCH 12/34] Fix race in set running Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 8 ++++++-- execdriver/lxc/driver.go | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/container.go b/container.go index 856ee352d6..924727bd3a 100644 --- a/container.go +++ b/container.go @@ -760,6 +760,7 @@ func (container *Container) Start() (err error) { return err } container.waitLock = make(chan struct{}) + container.State.SetRunning(0) go container.monitor() if container.Config.Tty { @@ -771,7 +772,9 @@ func (container *Container) Start() (err error) { return err } - container.State.SetRunning(container.process.Pid()) + // TODO: @crosbymichael @creack + // find a way to update this + // container.State.SetRunning(container.process.Pid()) container.ToDisk() return nil } @@ -1176,6 +1179,8 @@ func (container *Container) monitor() { exitCode := container.process.GetExitCode() container.State.SetStopped(exitCode) + close(container.waitLock) + if err := container.ToDisk(); err != nil { // FIXME: there is a race condition here which causes this to fail during the unit tests. // If another goroutine was waiting for Wait() to return before removing the container's root @@ -1185,7 +1190,6 @@ func (container *Container) monitor() { // FIXME: why are we serializing running state to disk in the first place? //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err) } - close(container.waitLock) } func (container *Container) cleanup() { diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 0f7a4cacf8..62b60deb61 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -123,7 +123,6 @@ func (d *driver) Wait(id string, duration time.Duration) error { func (d *driver) kill(c *execdriver.Process, sig int) error { output, err := exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).CombinedOutput() if err != nil { - fmt.Printf("--->%s\n", output) return fmt.Errorf("Err: %s Output: %s", err, output) } return nil From 66782730b85db20600ce48fcab203a5eb43bcb02 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 17:41:12 -0800 Subject: [PATCH 13/34] Fix network disabled for container Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.go b/container.go index 924727bd3a..f433418432 100644 --- a/container.go +++ b/container.go @@ -726,7 +726,7 @@ func (container *Container) Start() (err error) { } var en *execdriver.Network - if !container.runtime.networkManager.disabled { + if !container.Config.NetworkDisabled { network := container.NetworkSettings en = &execdriver.Network{ Gateway: network.Gateway, From 93ead2fe789a3a94a8433b12efcca782e0a7d7de Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 18:09:07 -0800 Subject: [PATCH 14/34] Update with lxc unconfined changes Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 69 +++++++++------------------------------ execdriver/driver.go | 1 + execdriver/lxc/driver.go | 50 +++++++++++++++++++++++----- integration/utils_test.go | 1 - runtime.go | 48 ++++++++------------------- server.go | 9 +---- 6 files changed, 73 insertions(+), 105 deletions(-) diff --git a/container.go b/container.go index f433418432..a0b4abea6f 100644 --- a/container.go +++ b/container.go @@ -17,7 +17,6 @@ import ( "net" "os" "path" - "strconv" "strings" "sync" "syscall" @@ -563,34 +562,6 @@ func (container *Container) Start() (err error) { return err } - var lxcStart string = "lxc-start" - if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor { - lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined") - } - - params := []string{ - lxcStart, - "-n", container.ID, - "-f", container.lxcConfigPath(), - "--", - "/.dockerinit", - } - - // Networking - if !container.Config.NetworkDisabled { - network := container.NetworkSettings - params = append(params, - "-g", network.Gateway, - "-i", fmt.Sprintf("%s/%d", network.IPAddress, network.IPPrefixLen), - "-mtu", strconv.Itoa(container.runtime.config.Mtu), - ) - } - - // User - if container.Config.User != "" { - params = append(params, "-u", container.Config.User) - } - // Setup environment env := []string{ "HOME=/", @@ -602,10 +573,6 @@ func (container *Container) Start() (err error) { env = append(env, "TERM=xterm") } - if container.hostConfig.Privileged { - params = append(params, "-privileged") - } - // Init any links between the parent and children runtime := container.runtime @@ -661,31 +628,25 @@ func (container *Container) Start() (err error) { if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil { return nil } - - params = append(params, - "-w", workingDir, - ) } - // Program - params = append(params, "--", container.Path) - params = append(params, container.Args...) + /* + if RootIsShared() { + // lxc-start really needs / to be non-shared, or all kinds of stuff break + // when lxc-start unmount things and those unmounts propagate to the main + // mount namespace. + // What we really want is to clone into a new namespace and then + // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork + // without exec in go we have to do this horrible shell hack... + shellString := + "mount --make-rslave /; exec " + + utils.ShellQuoteArguments(params) - if RootIsShared() { - // lxc-start really needs / to be non-shared, or all kinds of stuff break - // when lxc-start unmount things and those unmounts propagate to the main - // mount namespace. - // What we really want is to clone into a new namespace and then - // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork - // without exec in go we have to do this horrible shell hack... - shellString := - "mount --make-rslave /; exec " + - utils.ShellQuoteArguments(params) - - params = []string{ - "unshare", "-m", "--", "/bin/sh", "-c", shellString, + params = []string{ + "unshare", "-m", "--", "/bin/sh", "-c", shellString, + } } - } + */ root := container.RootfsPath() envPath, err := container.EnvConfigPath() diff --git a/execdriver/driver.go b/execdriver/driver.go index 94905a7b0d..a7d095bda3 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -10,6 +10,7 @@ type Driver interface { Start(c *Process) error Kill(c *Process, sig int) error Wait(id string, duration time.Duration) error // Wait on an out of process option - lxc ghosts + Version() string } // Network settings of the container diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 62b60deb61..8a10c77a4d 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/execdriver" + "os" "os/exec" + "path" "strconv" "strings" "time" @@ -17,21 +19,21 @@ const ( var ( ErrNotRunning = errors.New("Process could not be started") ErrWaitTimeoutReached = errors.New("Wait timeout reached") - Debug bool ) -func init() { - // Register driver -} - type driver struct { - root string // root path for the driver to use + root string // root path for the driver to use + apparmor bool } -func NewDriver(root string) (execdriver.Driver, error) { +func NewDriver(root string, apparmor bool) (execdriver.Driver, error) { // setup unconfined symlink + if err := linkLxcStart(root); err != nil { + return nil, err + } return &driver{ - root: root, + apparmor: apparmor, + root: root, }, nil } @@ -57,6 +59,10 @@ func (d *driver) Start(c *execdriver.Process) error { } if c.Privileged { + if d.apparmor { + params[0] = path.Join(d.root, "lxc-start-unconfined") + + } params = append(params, "-privileged") } @@ -120,6 +126,17 @@ func (d *driver) Wait(id string, duration time.Duration) error { return nil } +func (d *driver) Version() string { + version := "" + if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil { + outputStr := string(output) + if len(strings.SplitN(outputStr, ":", 2)) == 2 { + version = strings.TrimSpace(strings.SplitN(outputStr, ":", 2)[1]) + } + } + return version +} + func (d *driver) kill(c *execdriver.Process, sig int) error { output, err := exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).CombinedOutput() if err != nil { @@ -184,3 +201,20 @@ func (d *driver) waitLxc(id string, kill *bool) <-chan error { func (d *driver) getInfo(c *execdriver.Process) ([]byte, error) { return exec.Command("lxc-info", "-s", "-n", c.ID).CombinedOutput() } + +func linkLxcStart(root string) error { + sourcePath, err := exec.LookPath("lxc-start") + if err != nil { + return err + } + targetPath := path.Join(root, "lxc-start-unconfined") + + if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) { + return err + } else if err == nil { + if err := os.Remove(targetPath); err != nil { + return err + } + } + return os.Symlink(sourcePath, targetPath) +} diff --git a/integration/utils_test.go b/integration/utils_test.go index 4ab1c96cca..63ac3a44b9 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -38,7 +38,6 @@ func mkRuntime(f utils.Fataler) *docker.Runtime { if err != nil { f.Fatal(err) } - r.UpdateCapabilities(true) return r } diff --git a/runtime.go b/runtime.go index 432e28ea01..b1644b985f 100644 --- a/runtime.go +++ b/runtime.go @@ -334,8 +334,8 @@ func (runtime *Runtime) restore() error { return nil } -// FIXME: comment please! -func (runtime *Runtime) UpdateCapabilities(quiet bool) { +func NewRuntimeCapabilities(quiet bool) *Capabilities { + capabilities := &Capabilities{} if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { if !quiet { log.Printf("WARNING: %s\n", err) @@ -343,32 +343,33 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) { } else { _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) - runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil - if !runtime.capabilities.MemoryLimit && !quiet { + capabilities.MemoryLimit = err1 == nil && err2 == nil + if !capabilities.MemoryLimit && !quiet { log.Printf("WARNING: Your kernel does not support cgroup memory limit.") } _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) - runtime.capabilities.SwapLimit = err == nil - if !runtime.capabilities.SwapLimit && !quiet { + capabilities.SwapLimit = err == nil + if !capabilities.SwapLimit && !quiet { log.Printf("WARNING: Your kernel does not support cgroup swap limit.") } } content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward") - runtime.capabilities.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' - if runtime.capabilities.IPv4ForwardingDisabled && !quiet { + capabilities.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' + if capabilities.IPv4ForwardingDisabled && !quiet { log.Printf("WARNING: IPv4 forwarding is disabled.") } // Check if AppArmor seems to be enabled on this system. if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.") - runtime.capabilities.AppArmor = false + capabilities.AppArmor = false } else { utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.") - runtime.capabilities.AppArmor = true + capabilities.AppArmor = true } + return capabilities } // Create creates a new container from the given configuration with a given name. @@ -649,7 +650,6 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) { if err != nil { return nil, err } - runtime.UpdateCapabilities(false) return runtime, nil } @@ -678,10 +678,6 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { } } - utils.Debugf("Escaping AppArmor confinement") - if err := linkLxcStart(config.Root); err != nil { - return nil, err - } utils.Debugf("Creating images graph") g, err := NewGraph(path.Join(config.Root, "graph"), driver) if err != nil { @@ -738,7 +734,8 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { sysInitPath = localCopy } - ed, err := lxc.NewDriver("") + capabilities := NewRuntimeCapabilities(false) + ed, err := lxc.NewDriver(config.Root, capabilities.AppArmor) if err != nil { return nil, err } @@ -750,7 +747,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { graph: g, repositories: repositories, idIndex: utils.NewTruncIndex(), - capabilities: &Capabilities{}, + capabilities: capabilities, volumes: volumes, config: config, containerGraph: graph, @@ -869,23 +866,6 @@ func (runtime *Runtime) Nuke() error { return os.RemoveAll(runtime.config.Root) } -func linkLxcStart(root string) error { - sourcePath, err := exec.LookPath("lxc-start") - if err != nil { - return err - } - targetPath := path.Join(root, "lxc-start-unconfined") - - if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) { - return err - } else if err == nil { - if err := os.Remove(targetPath); err != nil { - return err - } - } - return os.Symlink(sourcePath, targetPath) -} - // FIXME: this is a convenience function for integration tests // which need direct access to runtime.graph. // Once the tests switch to using engine and jobs, this method diff --git a/server.go b/server.go index 3892ba70ea..a9c83575e4 100644 --- a/server.go +++ b/server.go @@ -661,13 +661,6 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { } else { imgcount = len(images) } - lxcVersion := "" - if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil { - outputStr := string(output) - if len(strings.SplitN(outputStr, ":", 2)) == 2 { - lxcVersion = strings.TrimSpace(strings.SplitN(string(output), ":", 2)[1]) - } - } kernelVersion := "" if kv, err := utils.GetKernelVersion(); err == nil { kernelVersion = kv.String() @@ -691,7 +684,7 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { v.SetBool("Debug", os.Getenv("DEBUG") != "") v.SetInt("NFd", utils.GetTotalUsedFds()) v.SetInt("NGoroutines", runtime.NumGoroutine()) - v.Set("LXCVersion", lxcVersion) + v.Set("LXCVersion", srv.runtime.execDriver.Version()) v.SetInt("NEventsListener", len(srv.events)) v.Set("KernelVersion", kernelVersion) v.Set("IndexServerAddress", auth.IndexServerAddress()) From 1d8455e6838f2630221eb3fcafeb382f9f7ce260 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 18:21:41 -0800 Subject: [PATCH 15/34] Move RootIsShared to lxc driver Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 20 ------------------- execdriver/MAINTAINERS | 2 ++ execdriver/lxc/driver.go | 43 ++++++++++++++++++++++++++++++++++++---- utils.go | 15 -------------- 4 files changed, 41 insertions(+), 39 deletions(-) create mode 100644 execdriver/MAINTAINERS diff --git a/container.go b/container.go index a0b4abea6f..f0d687d984 100644 --- a/container.go +++ b/container.go @@ -623,31 +623,11 @@ func (container *Container) Start() (err error) { var workingDir string if container.Config.WorkingDir != "" { workingDir = path.Clean(container.Config.WorkingDir) - utils.Debugf("[working dir] working dir is %s", workingDir) - if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil { return nil } } - /* - if RootIsShared() { - // lxc-start really needs / to be non-shared, or all kinds of stuff break - // when lxc-start unmount things and those unmounts propagate to the main - // mount namespace. - // What we really want is to clone into a new namespace and then - // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork - // without exec in go we have to do this horrible shell hack... - shellString := - "mount --make-rslave /; exec " + - utils.ShellQuoteArguments(params) - - params = []string{ - "unshare", "-m", "--", "/bin/sh", "-c", shellString, - } - } - */ - root := container.RootfsPath() envPath, err := container.EnvConfigPath() if err != nil { diff --git a/execdriver/MAINTAINERS b/execdriver/MAINTAINERS new file mode 100644 index 0000000000..e53d933d47 --- /dev/null +++ b/execdriver/MAINTAINERS @@ -0,0 +1,2 @@ +Michael Crosby (@crosbymichael) +Guillaume Charmes (@creack) diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 8a10c77a4d..359fe4198a 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/utils" + "io/ioutil" "os" "os/exec" "path" @@ -22,8 +24,9 @@ var ( ) type driver struct { - root string // root path for the driver to use - apparmor bool + root string // root path for the driver to use + apparmor bool + sharedRoot bool } func NewDriver(root string, apparmor bool) (execdriver.Driver, error) { @@ -32,8 +35,9 @@ func NewDriver(root string, apparmor bool) (execdriver.Driver, error) { return nil, err } return &driver{ - apparmor: apparmor, - root: root, + apparmor: apparmor, + root: root, + sharedRoot: rootIsShared(), }, nil } @@ -70,6 +74,23 @@ func (d *driver) Start(c *execdriver.Process) error { params = append(params, "-w", c.WorkingDir) } + if d.sharedRoot { + // lxc-start really needs / to be non-shared, or all kinds of stuff break + // when lxc-start unmount things and those unmounts propagate to the main + // mount namespace. + // What we really want is to clone into a new namespace and then + // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork + // without exec in go we have to do this horrible shell hack... + shellString := + "mount --make-rslave /; exec " + + utils.ShellQuoteArguments(params) + + params = []string{ + "unshare", "-m", "--", "/bin/sh", "-c", shellString, + } + + } + params = append(params, "--", c.Entrypoint) params = append(params, c.Arguments...) @@ -218,3 +239,17 @@ func linkLxcStart(root string) error { } return os.Symlink(sourcePath, targetPath) } + +func rootIsShared() bool { + if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil { + for _, line := range strings.Split(string(data), "\n") { + cols := strings.Split(line, " ") + if len(cols) >= 6 && cols[4] == "/" { + return strings.HasPrefix(cols[6], "shared") + } + } + } + + // No idea, probably safe to assume so + return true +} diff --git a/utils.go b/utils.go index 3eb1eac045..f0591158a4 100644 --- a/utils.go +++ b/utils.go @@ -5,7 +5,6 @@ import ( "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/pkg/namesgenerator" "github.com/dotcloud/docker/utils" - "io/ioutil" "strconv" "strings" ) @@ -328,20 +327,6 @@ func parseLink(rawLink string) (map[string]string, error) { return utils.PartParser("name:alias", rawLink) } -func RootIsShared() bool { - if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil { - for _, line := range strings.Split(string(data), "\n") { - cols := strings.Split(line, " ") - if len(cols) >= 6 && cols[4] == "/" { - return strings.HasPrefix(cols[6], "shared") - } - } - } - - // No idea, probably safe to assume so - return true -} - type checker struct { runtime *Runtime } From 8e0741f5e40d107780a5be1f6ae0f4d30ba43d7d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 10 Jan 2014 20:22:39 -0800 Subject: [PATCH 16/34] Add chroot driver for testing Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 25 +++++++------- execdriver/chroot/driver.go | 66 +++++++++++++++++++++++++++++++++++++ execdriver/driver.go | 27 +++++++-------- execdriver/lxc/driver.go | 1 - runtime.go | 8 ++++- sysinit/sysinit.go | 31 ++++++++--------- 6 files changed, 116 insertions(+), 42 deletions(-) create mode 100644 execdriver/chroot/driver.go diff --git a/container.go b/container.go index f0d687d984..d81702fd83 100644 --- a/container.go +++ b/container.go @@ -678,18 +678,19 @@ func (container *Container) Start() (err error) { } container.process = &execdriver.Process{ - ID: container.ID, - Privileged: container.hostConfig.Privileged, - Rootfs: root, - InitPath: "/.dockerinit", - Entrypoint: container.Path, - Arguments: container.Args, - WorkingDir: workingDir, - ConfigPath: container.lxcConfigPath(), - Network: en, - Tty: container.Config.Tty, - User: container.Config.User, - WaitLock: make(chan struct{}), + ID: container.ID, + Privileged: container.hostConfig.Privileged, + Rootfs: root, + InitPath: "/.dockerinit", + Entrypoint: container.Path, + Arguments: container.Args, + WorkingDir: workingDir, + ConfigPath: container.lxcConfigPath(), + Network: en, + Tty: container.Config.Tty, + User: container.Config.User, + WaitLock: make(chan struct{}), + SysInitPath: runtime.sysInitPath, } container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go new file mode 100644 index 0000000000..6a9d4784c2 --- /dev/null +++ b/execdriver/chroot/driver.go @@ -0,0 +1,66 @@ +package chroot + +import ( + "fmt" + "github.com/dotcloud/docker/execdriver" + "io/ioutil" + "os/exec" + "path" + "time" +) + +type driver struct { +} + +func NewDriver() (execdriver.Driver, error) { + return &driver{}, nil +} + +func (d *driver) Start(c *execdriver.Process) error { + data, _ := ioutil.ReadFile(c.SysInitPath) + ioutil.WriteFile(path.Join(c.Rootfs, ".dockerinit"), data, 0644) + params := []string{ + "chroot", + c.Rootfs, + "/.dockerinit", + } + // need to mount proc + params = append(params, c.Entrypoint) + params = append(params, c.Arguments...) + + var ( + name = params[0] + arg = params[1:] + ) + aname, err := exec.LookPath(name) + if err != nil { + aname = name + } + c.Path = aname + c.Args = append([]string{name}, arg...) + + if err := c.Start(); err != nil { + return err + } + + go func() { + if err := c.Wait(); err != nil { + c.WaitError = err + } + close(c.WaitLock) + }() + + return nil +} + +func (d *driver) Kill(p *execdriver.Process, sig int) error { + return p.Process.Kill() +} + +func (d *driver) Wait(id string, duration time.Duration) error { + panic("No Implemented") +} + +func (d *driver) Version() string { + return "0.1" +} diff --git a/execdriver/driver.go b/execdriver/driver.go index a7d095bda3..202d6ccdc7 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -25,19 +25,20 @@ type Network struct { type Process struct { exec.Cmd - ID string - Privileged bool - User string - Rootfs string // root fs of the container - InitPath string // dockerinit - Entrypoint string - Arguments []string - WorkingDir string - ConfigPath string - Tty bool - Network *Network // if network is nil then networking is disabled - WaitLock chan struct{} - WaitError error + ID string + Privileged bool + User string + Rootfs string // root fs of the container + InitPath string // dockerinit + Entrypoint string + Arguments []string + WorkingDir string + ConfigPath string + Tty bool + Network *Network // if network is nil then networking is disabled + SysInitPath string + WaitLock chan struct{} + WaitError error } func (c *Process) Pid() int { diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 359fe4198a..2265185899 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -88,7 +88,6 @@ func (d *driver) Start(c *execdriver.Process) error { params = []string{ "unshare", "-m", "--", "/bin/sh", "-c", shellString, } - } params = append(params, "--", c.Entrypoint) diff --git a/runtime.go b/runtime.go index b1644b985f..0ae23740c3 100644 --- a/runtime.go +++ b/runtime.go @@ -6,6 +6,7 @@ import ( "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/cgroups" "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/execdriver/chroot" "github.com/dotcloud/docker/execdriver/lxc" "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/graphdriver/aufs" @@ -735,7 +736,12 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { } capabilities := NewRuntimeCapabilities(false) - ed, err := lxc.NewDriver(config.Root, capabilities.AppArmor) + var ed execdriver.Driver + if driver := os.Getenv("EXEC_DRIVER"); driver == "lxc" { + ed, err = lxc.NewDriver(config.Root, capabilities.AppArmor) + } else { + ed, err = chroot.NewDriver() + } if err != nil { return nil, err } diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index ce46e06f14..72f5a3ba83 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -182,24 +182,25 @@ func getEnv(args *DockerInitArgs, key string) string { func executeProgram(args *DockerInitArgs) error { setupEnv(args) - if err := setupHostname(args); err != nil { - return err - } + if false { + if err := setupHostname(args); err != nil { + return err + } - if err := setupNetworking(args); err != nil { - return err - } + if err := setupNetworking(args); err != nil { + return err + } - if err := setupCapabilities(args); err != nil { - return err - } + if err := setupCapabilities(args); err != nil { + return err + } + if err := setupWorkingDirectory(args); err != nil { + return err + } - if err := setupWorkingDirectory(args); err != nil { - return err - } - - if err := changeUser(args); err != nil { - return err + if err := changeUser(args); err != nil { + return err + } } path, err := exec.LookPath(args.args[0]) From 92e6db7beba8ad58e425119cc9885c355a5755e7 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 11:13:49 -0800 Subject: [PATCH 17/34] Improve chroot driver by mounting proc Add -driver flag to dockerinit Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/chroot/driver.go | 12 ++++++------ execdriver/driver.go | 1 + execdriver/lxc/driver.go | 6 ++++++ mount/mount.go | 16 +++++++++++++--- sysinit/sysinit.go | 16 +++++++++++++++- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index 6a9d4784c2..b404f180e5 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -1,11 +1,8 @@ package chroot import ( - "fmt" "github.com/dotcloud/docker/execdriver" - "io/ioutil" "os/exec" - "path" "time" ) @@ -16,15 +13,18 @@ func NewDriver() (execdriver.Driver, error) { return &driver{}, nil } +func (d *driver) String() string { + return "chroot" +} + func (d *driver) Start(c *execdriver.Process) error { - data, _ := ioutil.ReadFile(c.SysInitPath) - ioutil.WriteFile(path.Join(c.Rootfs, ".dockerinit"), data, 0644) params := []string{ "chroot", c.Rootfs, "/.dockerinit", + "-driver", + d.String(), } - // need to mount proc params = append(params, c.Entrypoint) params = append(params, c.Arguments...) diff --git a/execdriver/driver.go b/execdriver/driver.go index 202d6ccdc7..04c52161d4 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -11,6 +11,7 @@ type Driver interface { Kill(c *Process, sig int) error Wait(id string, duration time.Duration) error // Wait on an out of process option - lxc ghosts Version() string + String() string } // Network settings of the container diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 2265185899..7a4e754911 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -41,6 +41,10 @@ func NewDriver(root string, apparmor bool) (execdriver.Driver, error) { }, nil } +func (d *driver) String() string { + return "lxc" +} + func (d *driver) Start(c *execdriver.Process) error { params := []string{ startPath, @@ -48,6 +52,8 @@ func (d *driver) Start(c *execdriver.Process) error { "-f", c.ConfigPath, "--", c.InitPath, + "-driver", + d.String(), } if c.Network != nil { diff --git a/mount/mount.go b/mount/mount.go index b087293a9d..3860b975bd 100644 --- a/mount/mount.go +++ b/mount/mount.go @@ -25,27 +25,37 @@ func Mounted(mountpoint string) (bool, error) { return false, nil } -// Mount the specified options at the target path +// Mount the specified options at the target path only if +// the target is not mounted // Options must be specified as fstab style func Mount(device, target, mType, options string) error { if mounted, err := Mounted(target); err != nil || mounted { return err } + return ForceMount(device, target, mType, options) +} +// Mount the specified options at the target path +// reguardless if the target is mounted or not +// Options must be specified as fstab style +func ForceMount(device, target, mType, options string) error { flag, data := parseOptions(options) if err := mount(device, target, mType, uintptr(flag), data); err != nil { return err } return nil - } // Unmount the target only if it is mounted -func Unmount(target string) (err error) { +func Unmount(target string) error { if mounted, err := Mounted(target); err != nil || !mounted { return err } + return ForceUnmount(target) +} +// Unmount the target reguardless if it is mounted or not +func ForceUnmount(target string) (err error) { // Simple retry logic for unmount for i := 0; i < 10; i++ { if err = unmount(target, 0); err == nil { diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index 72f5a3ba83..73349379fb 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "github.com/dotcloud/docker/mount" "github.com/dotcloud/docker/pkg/netlink" "github.com/dotcloud/docker/utils" "github.com/syndtr/gocapability/capability" @@ -26,6 +27,7 @@ type DockerInitArgs struct { env []string args []string mtu int + driver string } func setupHostname(args *DockerInitArgs) error { @@ -92,6 +94,10 @@ func setupWorkingDirectory(args *DockerInitArgs) error { return nil } +func setupMounts(args *DockerInitArgs) error { + return mount.ForceMount("proc", "proc", "proc", "") +} + // Takes care of dropping privileges to the desired user func changeUser(args *DockerInitArgs) error { if args.user == "" { @@ -182,7 +188,7 @@ func getEnv(args *DockerInitArgs, key string) string { func executeProgram(args *DockerInitArgs) error { setupEnv(args) - if false { + if args.driver == "lxc" { if err := setupHostname(args); err != nil { return err } @@ -201,6 +207,12 @@ func executeProgram(args *DockerInitArgs) error { if err := changeUser(args); err != nil { return err } + } else if args.driver == "chroot" { + // TODO: @crosbymichael @creack how do we unmount this after the + // process exists? + if err := setupMounts(args); err != nil { + return err + } } path, err := exec.LookPath(args.args[0]) @@ -233,6 +245,7 @@ func SysInit() { workDir := flag.String("w", "", "workdir") privileged := flag.Bool("privileged", false, "privileged mode") mtu := flag.Int("mtu", 1500, "interface mtu") + driver := flag.String("driver", "", "exec driver") flag.Parse() // Get env @@ -257,6 +270,7 @@ func SysInit() { env: env, args: flag.Args(), mtu: *mtu, + driver: *driver, } if err := executeProgram(args); err != nil { From f846ecdc77bb9ab4d44b41a2e403bcf579d6b6e9 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 15:02:12 -0800 Subject: [PATCH 18/34] Make exec driver run a blocking command Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 69 ++++++++++++++++++++----------------- execdriver/chroot/driver.go | 20 ++++++++--- execdriver/driver.go | 9 ++--- execdriver/lxc/driver.go | 31 +++++++++++------ runtime.go | 6 ++-- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/container.go b/container.go index d81702fd83..9fac0da587 100644 --- a/container.go +++ b/container.go @@ -308,10 +308,10 @@ func (container *Container) generateLXCConfig() error { return LxcTemplateCompiled.Execute(fo, container) } -func (container *Container) startPty() error { +func (container *Container) startPty(startCallback execdriver.StartCallback) (int, error) { ptyMaster, ptySlave, err := pty.Open() if err != nil { - return err + return -1, err } container.ptyMaster = ptyMaster container.process.Stdout = ptySlave @@ -336,20 +336,17 @@ func (container *Container) startPty() error { utils.Debugf("startPty: end of stdin pipe") }() } - if err := container.runtime.Start(container); err != nil { - return err - } - ptySlave.Close() - return nil + + return container.runtime.Run(container, startCallback) } -func (container *Container) start() error { +func (container *Container) start(startCallback execdriver.StartCallback) (int, error) { container.process.Stdout = container.stdout container.process.Stderr = container.stderr if container.Config.OpenStdin { stdin, err := container.process.StdinPipe() if err != nil { - return err + return -1, err } go func() { defer stdin.Close() @@ -358,7 +355,7 @@ func (container *Container) start() error { utils.Debugf("start: end of stdin pipe") }() } - return container.runtime.Start(container) + return container.runtime.Run(container, startCallback) } func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { @@ -689,7 +686,6 @@ func (container *Container) Start() (err error) { Network: en, Tty: container.Config.Tty, User: container.Config.User, - WaitLock: make(chan struct{}), SysInitPath: runtime.sysInitPath, } container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} @@ -703,21 +699,26 @@ func (container *Container) Start() (err error) { } container.waitLock = make(chan struct{}) container.State.SetRunning(0) - go container.monitor() - if container.Config.Tty { - err = container.startPty() - } else { - err = container.start() - } - if err != nil { - return err + waitLock := make(chan struct{}) + f := func(process *execdriver.Process) { + container.State.SetRunning(process.Pid()) + if process.Tty { + if c, ok := process.Stdout.(io.Closer); ok { + c.Close() + } + } + if err := container.ToDisk(); err != nil { + utils.Debugf("%s", err) + } + close(waitLock) } - // TODO: @crosbymichael @creack - // find a way to update this - // container.State.SetRunning(container.process.Pid()) - container.ToDisk() + go container.monitor(f) + + // Start should not return until the process is actually running + <-waitLock + return nil } @@ -1091,17 +1092,24 @@ func (container *Container) releaseNetwork() { container.NetworkSettings = &NetworkSettings{} } -func (container *Container) monitor() { - // Wait for the program to exit +func (container *Container) monitor(f execdriver.StartCallback) { + var ( + err error + exitCode int + ) + if container.process == nil { - if err := container.runtime.Wait(container, 0); err != nil { - utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) - } + // This happends when you have a GHOST container with lxc + err = container.runtime.Wait(container, 0) } else { - <-container.process.WaitLock + if container.Config.Tty { + exitCode, err = container.startPty(f) + } else { + exitCode, err = container.start(f) + } } - if err := container.process.WaitError; err != nil { + if err != nil { //TODO: @crosbymichael @creack report error // Since non-zero exit status and signal terminations will cause err to be non-nil, // we have to actually discard it. Still, log it anyway, just in case. utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) @@ -1118,7 +1126,6 @@ func (container *Container) monitor() { container.stdin, container.stdinPipe = io.Pipe() } - exitCode := container.process.GetExitCode() container.State.SetStopped(exitCode) close(container.waitLock) diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index b404f180e5..eb2525be6d 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -17,7 +17,7 @@ func (d *driver) String() string { return "chroot" } -func (d *driver) Start(c *execdriver.Process) error { +func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { params := []string{ "chroot", c.Rootfs, @@ -40,17 +40,27 @@ func (d *driver) Start(c *execdriver.Process) error { c.Args = append([]string{name}, arg...) if err := c.Start(); err != nil { - return err + return -1, err } + var ( + waitErr error + waitLock = make(chan struct{}) + ) go func() { if err := c.Wait(); err != nil { - c.WaitError = err + waitErr = err } - close(c.WaitLock) + close(waitLock) }() - return nil + if startCallback != nil { + startCallback(c) + } + + <-waitLock + + return c.GetExitCode(), waitErr } func (d *driver) Kill(p *execdriver.Process, sig int) error { diff --git a/execdriver/driver.go b/execdriver/driver.go index 04c52161d4..c38dc96a91 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -6,10 +6,13 @@ import ( "time" ) +type StartCallback func(*Process) + type Driver interface { - Start(c *Process) error + Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Process, sig int) error - Wait(id string, duration time.Duration) error // Wait on an out of process option - lxc ghosts + // TODO: @crosbymichael @creack wait should probably return the exit code + Wait(id string, duration time.Duration) error // Wait on an out of process...process - lxc ghosts Version() string String() string } @@ -38,8 +41,6 @@ type Process struct { Tty bool Network *Network // if network is nil then networking is disabled SysInitPath string - WaitLock chan struct{} - WaitError error } func (c *Process) Pid() int { diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 7a4e754911..5ab6f8b824 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -45,7 +45,7 @@ func (d *driver) String() string { return "lxc" } -func (d *driver) Start(c *execdriver.Process) error { +func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { params := []string{ startPath, "-n", c.ID, @@ -111,21 +111,32 @@ func (d *driver) Start(c *execdriver.Process) error { c.Args = append([]string{name}, arg...) if err := c.Start(); err != nil { - return err + return -1, err } + var ( + waitErr error + waitLock = make(chan struct{}) + ) go func() { if err := c.Wait(); err != nil { - c.WaitError = err + waitErr = err } - close(c.WaitLock) + close(waitLock) }() - // Poll for running - if err := d.waitForStart(c); err != nil { - return err + // Poll lxc for RUNNING status + if err := d.waitForStart(c, waitLock); err != nil { + return -1, err } - return nil + + if startCallback != nil { + startCallback(c) + } + + <-waitLock + + return c.GetExitCode(), waitErr } func (d *driver) Kill(c *execdriver.Process, sig int) error { @@ -171,7 +182,7 @@ func (d *driver) kill(c *execdriver.Process, sig int) error { return nil } -func (d *driver) waitForStart(c *execdriver.Process) error { +func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) error { var ( err error output []byte @@ -182,7 +193,7 @@ func (d *driver) waitForStart(c *execdriver.Process) error { // the end of this loop for now := time.Now(); time.Since(now) < 5*time.Second; { select { - case <-c.WaitLock: + case <-waitLock: // If the process dies while waiting for it, just return if c.ProcessState != nil && c.ProcessState.Exited() { return nil diff --git a/runtime.go b/runtime.go index 0ae23740c3..ec32f75456 100644 --- a/runtime.go +++ b/runtime.go @@ -192,7 +192,7 @@ func (runtime *Runtime) Register(container *Container) error { } container.waitLock = make(chan struct{}) - go container.monitor() + go container.monitor(nil) } } return nil @@ -841,8 +841,8 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) { return archive.ExportChanges(cDir, changes) } -func (runtime *Runtime) Start(c *Container) error { - return runtime.execDriver.Start(c.process) +func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error) { + return runtime.execDriver.Run(c.process, startCallback) } func (runtime *Runtime) Kill(c *Container, sig int) error { From 1e81387edcab600c9b8bc2a502988f7b3a2013e7 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 15:09:41 -0800 Subject: [PATCH 19/34] Remove SysinitPath from process Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 23 +++++++++++------------ execdriver/driver.go | 23 +++++++++++------------ execdriver/lxc/driver.go | 1 + 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/container.go b/container.go index 9fac0da587..424c9d5f1f 100644 --- a/container.go +++ b/container.go @@ -675,18 +675,17 @@ func (container *Container) Start() (err error) { } container.process = &execdriver.Process{ - ID: container.ID, - Privileged: container.hostConfig.Privileged, - Rootfs: root, - InitPath: "/.dockerinit", - Entrypoint: container.Path, - Arguments: container.Args, - WorkingDir: workingDir, - ConfigPath: container.lxcConfigPath(), - Network: en, - Tty: container.Config.Tty, - User: container.Config.User, - SysInitPath: runtime.sysInitPath, + ID: container.ID, + Privileged: container.hostConfig.Privileged, + Rootfs: root, + InitPath: "/.dockerinit", + Entrypoint: container.Path, + Arguments: container.Args, + WorkingDir: workingDir, + ConfigPath: container.lxcConfigPath(), + Network: en, + Tty: container.Config.Tty, + User: container.Config.User, } container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} diff --git a/execdriver/driver.go b/execdriver/driver.go index c38dc96a91..cb9df96e79 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -29,18 +29,17 @@ type Network struct { type Process struct { exec.Cmd - ID string - Privileged bool - User string - Rootfs string // root fs of the container - InitPath string // dockerinit - Entrypoint string - Arguments []string - WorkingDir string - ConfigPath string - Tty bool - Network *Network // if network is nil then networking is disabled - SysInitPath string + ID string + Privileged bool + User string + Rootfs string // root fs of the container + InitPath string // dockerinit + Entrypoint string + Arguments []string + WorkingDir string + ConfigPath string + Tty bool + Network *Network // if network is nil then networking is disabled } func (c *Process) Pid() int { diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 5ab6f8b824..b220ff146e 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -256,6 +256,7 @@ func linkLxcStart(root string) error { return os.Symlink(sourcePath, targetPath) } +// TODO: This can be moved to the mountinfo reader in the mount pkg func rootIsShared() bool { if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil { for _, line := range strings.Split(string(data), "\n") { From 8a38ead21993dd500280ae427895363a2de8be8a Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 15:37:17 -0800 Subject: [PATCH 20/34] Move setup pty and standard pipes to funcs Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/container.go b/container.go index 424c9d5f1f..4e66539c5c 100644 --- a/container.go +++ b/container.go @@ -308,10 +308,10 @@ func (container *Container) generateLXCConfig() error { return LxcTemplateCompiled.Execute(fo, container) } -func (container *Container) startPty(startCallback execdriver.StartCallback) (int, error) { +func (container *Container) setupPty() error { ptyMaster, ptySlave, err := pty.Open() if err != nil { - return -1, err + return err } container.ptyMaster = ptyMaster container.process.Stdout = ptySlave @@ -336,17 +336,16 @@ func (container *Container) startPty(startCallback execdriver.StartCallback) (in utils.Debugf("startPty: end of stdin pipe") }() } - - return container.runtime.Run(container, startCallback) + return nil } -func (container *Container) start(startCallback execdriver.StartCallback) (int, error) { +func (container *Container) setupStd() error { container.process.Stdout = container.stdout container.process.Stderr = container.stderr if container.Config.OpenStdin { stdin, err := container.process.StdinPipe() if err != nil { - return -1, err + return err } go func() { defer stdin.Close() @@ -355,7 +354,7 @@ func (container *Container) start(startCallback execdriver.StartCallback) (int, utils.Debugf("start: end of stdin pipe") }() } - return container.runtime.Run(container, startCallback) + return nil } func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { @@ -697,12 +696,24 @@ func (container *Container) Start() (err error) { return err } container.waitLock = make(chan struct{}) - container.State.SetRunning(0) + + // Setup pipes + if container.Config.Tty { + err = container.setupPty() + } else { + err = container.setupStd() + } + if err != nil { + return err + } waitLock := make(chan struct{}) f := func(process *execdriver.Process) { container.State.SetRunning(process.Pid()) if process.Tty { + // The callback is called after the process Start() + // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace + // which we close here. if c, ok := process.Stdout.(io.Closer); ok { c.Close() } @@ -1091,7 +1102,7 @@ func (container *Container) releaseNetwork() { container.NetworkSettings = &NetworkSettings{} } -func (container *Container) monitor(f execdriver.StartCallback) { +func (container *Container) monitor(callback execdriver.StartCallback) { var ( err error exitCode int @@ -1101,11 +1112,7 @@ func (container *Container) monitor(f execdriver.StartCallback) { // This happends when you have a GHOST container with lxc err = container.runtime.Wait(container, 0) } else { - if container.Config.Tty { - exitCode, err = container.startPty(f) - } else { - exitCode, err = container.start(f) - } + exitCode, err = container.runtime.Run(container, callback) } if err != nil { //TODO: @crosbymichael @creack report error From f3f2456b0451d93a539eac63dac93a748e01317f Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 16:10:23 -0800 Subject: [PATCH 21/34] Simplify chroot wait, address code review issues Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 6 ++++-- execdriver/chroot/driver.go | 22 +++++----------------- execdriver/driver.go | 5 ++++- execdriver/lxc/driver.go | 7 ++++--- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/container.go b/container.go index 4e66539c5c..e8277131cf 100644 --- a/container.go +++ b/container.go @@ -708,7 +708,7 @@ func (container *Container) Start() (err error) { } waitLock := make(chan struct{}) - f := func(process *execdriver.Process) { + callback := func(process *execdriver.Process) { container.State.SetRunning(process.Pid()) if process.Tty { // The callback is called after the process Start() @@ -724,7 +724,9 @@ func (container *Container) Start() (err error) { close(waitLock) } - go container.monitor(f) + // We use a callback here instead of a goroutine and an chan for + // syncronization purposes + go container.monitor(callback) // Start should not return until the process is actually running <-waitLock diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index eb2525be6d..e5dc69131c 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -9,11 +9,11 @@ import ( type driver struct { } -func NewDriver() (execdriver.Driver, error) { +func NewDriver() (*driver, error) { return &driver{}, nil } -func (d *driver) String() string { +func (d *driver) Name() string { return "chroot" } @@ -23,7 +23,7 @@ func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallba c.Rootfs, "/.dockerinit", "-driver", - d.String(), + d.Name(), } params = append(params, c.Entrypoint) params = append(params, c.Arguments...) @@ -43,24 +43,12 @@ func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallba return -1, err } - var ( - waitErr error - waitLock = make(chan struct{}) - ) - go func() { - if err := c.Wait(); err != nil { - waitErr = err - } - close(waitLock) - }() - if startCallback != nil { startCallback(c) } - <-waitLock - - return c.GetExitCode(), waitErr + err = c.Wait() + return c.GetExitCode(), err } func (d *driver) Kill(p *execdriver.Process, sig int) error { diff --git a/execdriver/driver.go b/execdriver/driver.go index cb9df96e79..fac4e27704 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -14,7 +14,7 @@ type Driver interface { // TODO: @crosbymichael @creack wait should probably return the exit code Wait(id string, duration time.Duration) error // Wait on an out of process...process - lxc ghosts Version() string - String() string + Name() string } // Network settings of the container @@ -43,6 +43,9 @@ type Process struct { } func (c *Process) Pid() int { + if c.Process == nil { + return -1 + } return c.Process.Pid } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index b220ff146e..e37e105b5b 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -29,7 +29,7 @@ type driver struct { sharedRoot bool } -func NewDriver(root string, apparmor bool) (execdriver.Driver, error) { +func NewDriver(root string, apparmor bool) (*driver, error) { // setup unconfined symlink if err := linkLxcStart(root); err != nil { return nil, err @@ -41,7 +41,7 @@ func NewDriver(root string, apparmor bool) (execdriver.Driver, error) { }, nil } -func (d *driver) String() string { +func (d *driver) Name() string { return "lxc" } @@ -53,7 +53,7 @@ func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallba "--", c.InitPath, "-driver", - d.String(), + d.Name(), } if c.Network != nil { @@ -195,6 +195,7 @@ func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) err select { case <-waitLock: // If the process dies while waiting for it, just return + return nil if c.ProcessState != nil && c.ProcessState.Exited() { return nil } From e765c67b477308643c8b1b4f84bee3572bf5ec98 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 16:18:46 -0800 Subject: [PATCH 22/34] Add json tags and comments to exedriver types Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/driver.go | 40 +++++++++++++++++++++++++--------------- execdriver/lxc/driver.go | 16 +++------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/execdriver/driver.go b/execdriver/driver.go index fac4e27704..8f566324ae 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -1,11 +1,17 @@ package execdriver import ( + "errors" "os/exec" "syscall" "time" ) +var ( + ErrNotRunning = errors.New("Process could not be started") + ErrWaitTimeoutReached = errors.New("Wait timeout reached") +) + type StartCallback func(*Process) type Driver interface { @@ -19,29 +25,31 @@ type Driver interface { // Network settings of the container type Network struct { - Gateway string - IPAddress string - IPPrefixLen int - Mtu int + Gateway string `json:"gateway"` + IPAddress string `json:"ip"` + IPPrefixLen int `json:"ip_prefix_len"` + Mtu int `json:"mtu"` } // Process wrapps an os/exec.Cmd to add more metadata type Process struct { exec.Cmd - ID string - Privileged bool - User string - Rootfs string // root fs of the container - InitPath string // dockerinit - Entrypoint string - Arguments []string - WorkingDir string - ConfigPath string - Tty bool - Network *Network // if network is nil then networking is disabled + ID string `json:"id"` + Privileged bool `json:"privileged"` + User string `json:"user"` + Rootfs string `json:"rootfs"` // root fs of the container + InitPath string `json:"initpath"` // dockerinit + Entrypoint string `json:"entrypoint"` + Arguments []string `json:"arguments"` + WorkingDir string `json:"working_dir"` + ConfigPath string `json:"config_path"` // This should be able to be removed when the lxc template is moved into the driver + Tty bool `json:"tty"` + Network *Network `json:"network"` // if network is nil then networking is disabled } +// Return the pid of the process +// If the process is nil -1 will be returned func (c *Process) Pid() int { if c.Process == nil { return -1 @@ -49,6 +57,8 @@ func (c *Process) Pid() int { return c.Process.Pid } +// Return the exit code of the process +// if the process has not exited -1 will be returned func (c *Process) GetExitCode() int { if c.ProcessState == nil { return -1 diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index e37e105b5b..3b4d385cac 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -1,7 +1,6 @@ package lxc import ( - "errors" "fmt" "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/utils" @@ -14,15 +13,6 @@ import ( "time" ) -const ( - startPath = "lxc-start" -) - -var ( - ErrNotRunning = errors.New("Process could not be started") - ErrWaitTimeoutReached = errors.New("Wait timeout reached") -) - type driver struct { root string // root path for the driver to use apparmor bool @@ -47,7 +37,7 @@ func (d *driver) Name() string { func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { params := []string{ - startPath, + "lxc-start", "-n", c.ID, "-f", c.ConfigPath, "--", @@ -155,7 +145,7 @@ func (d *driver) Wait(id string, duration time.Duration) error { return err case <-time.After(duration): killer = true - return ErrWaitTimeoutReached + return execdriver.ErrWaitTimeoutReached } } else { return <-done @@ -214,7 +204,7 @@ func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) err } time.Sleep(50 * time.Millisecond) } - return ErrNotRunning + return execdriver.ErrNotRunning } func (d *driver) waitLxc(id string, kill *bool) <-chan error { From c2b602b2ac7b5b3a67dbb7945e97a9f48b1a625e Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 16:41:35 -0800 Subject: [PATCH 23/34] Use utils.Go to get error from monitor Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/container.go b/container.go index e8277131cf..d8ff8488d0 100644 --- a/container.go +++ b/container.go @@ -726,11 +726,14 @@ func (container *Container) Start() (err error) { // We use a callback here instead of a goroutine and an chan for // syncronization purposes - go container.monitor(callback) + cErr := utils.Go(func() error { return container.monitor(callback) }) // Start should not return until the process is actually running - <-waitLock - + select { + case <-waitLock: + case err := <-cErr: + return err + } return nil } @@ -1104,7 +1107,7 @@ func (container *Container) releaseNetwork() { container.NetworkSettings = &NetworkSettings{} } -func (container *Container) monitor(callback execdriver.StartCallback) { +func (container *Container) monitor(callback execdriver.StartCallback) error { var ( err error exitCode int @@ -1117,10 +1120,7 @@ func (container *Container) monitor(callback execdriver.StartCallback) { exitCode, err = container.runtime.Run(container, callback) } - if err != nil { //TODO: @crosbymichael @creack report error - // Since non-zero exit status and signal terminations will cause err to be non-nil, - // we have to actually discard it. Still, log it anyway, just in case. - utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) + if err != nil { if container.runtime != nil && container.runtime.srv != nil { container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image)) } @@ -1138,15 +1138,16 @@ func (container *Container) monitor(callback execdriver.StartCallback) { close(container.waitLock) - if err := container.ToDisk(); err != nil { - // FIXME: there is a race condition here which causes this to fail during the unit tests. - // If another goroutine was waiting for Wait() to return before removing the container's root - // from the filesystem... At this point it may already have done so. - // This is because State.setStopped() has already been called, and has caused Wait() - // to return. - // FIXME: why are we serializing running state to disk in the first place? - //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err) - } + // FIXME: there is a race condition here which causes this to fail during the unit tests. + // If another goroutine was waiting for Wait() to return before removing the container's root + // from the filesystem... At this point it may already have done so. + // This is because State.setStopped() has already been called, and has caused Wait() + // to return. + // FIXME: why are we serializing running state to disk in the first place? + //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err) + container.ToDisk() + + return err } func (container *Container) cleanup() { From 8c9f62d037a1bc82742ea316adaaf658af56b7c3 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 17:17:59 -0800 Subject: [PATCH 24/34] Improve wait for lxc and driver interface Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- container.go | 2 +- execdriver/chroot/driver.go | 5 ++--- execdriver/driver.go | 3 +-- execdriver/lxc/driver.go | 42 +++++++------------------------------ runtime.go | 4 ++-- 5 files changed, 14 insertions(+), 42 deletions(-) diff --git a/container.go b/container.go index d8ff8488d0..702d94e391 100644 --- a/container.go +++ b/container.go @@ -1115,7 +1115,7 @@ func (container *Container) monitor(callback execdriver.StartCallback) error { if container.process == nil { // This happends when you have a GHOST container with lxc - err = container.runtime.Wait(container, 0) + err = container.runtime.WaitGhost(container) } else { exitCode, err = container.runtime.Run(container, callback) } diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index e5dc69131c..6244d2a93f 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -3,7 +3,6 @@ package chroot import ( "github.com/dotcloud/docker/execdriver" "os/exec" - "time" ) type driver struct { @@ -55,8 +54,8 @@ func (d *driver) Kill(p *execdriver.Process, sig int) error { return p.Process.Kill() } -func (d *driver) Wait(id string, duration time.Duration) error { - panic("No Implemented") +func (d *driver) Wait(id string) error { + panic("Not Implemented") } func (d *driver) Version() string { diff --git a/execdriver/driver.go b/execdriver/driver.go index 8f566324ae..d68123d9ba 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -4,7 +4,6 @@ import ( "errors" "os/exec" "syscall" - "time" ) var ( @@ -18,7 +17,7 @@ type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Process, sig int) error // TODO: @crosbymichael @creack wait should probably return the exit code - Wait(id string, duration time.Duration) error // Wait on an out of process...process - lxc ghosts + Wait(id string) error // Wait on an out of process...process - lxc ghosts Version() string Name() string } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 3b4d385cac..3939841320 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -133,24 +133,17 @@ func (d *driver) Kill(c *execdriver.Process, sig int) error { return d.kill(c, sig) } -func (d *driver) Wait(id string, duration time.Duration) error { - var ( - killer bool - done = d.waitLxc(id, &killer) - ) - - if duration > 0 { - select { - case err := <-done: +func (d *driver) Wait(id string) error { + for { + output, err := exec.Command("lxc-info", "-n", id).CombinedOutput() + if err != nil { return err - case <-time.After(duration): - killer = true - return execdriver.ErrWaitTimeoutReached } - } else { - return <-done + if !strings.Contains(string(output), "RUNNING") { + return nil + } + time.Sleep(500 * time.Millisecond) } - return nil } func (d *driver) Version() string { @@ -207,25 +200,6 @@ func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) err return execdriver.ErrNotRunning } -func (d *driver) waitLxc(id string, kill *bool) <-chan error { - done := make(chan error) - go func() { - for *kill { - output, err := exec.Command("lxc-info", "-n", id).CombinedOutput() - if err != nil { - done <- err - return - } - if !strings.Contains(string(output), "RUNNING") { - done <- err - return - } - time.Sleep(500 * time.Millisecond) - } - }() - return done -} - func (d *driver) getInfo(c *execdriver.Process) ([]byte, error) { return exec.Command("lxc-info", "-s", "-n", c.ID).CombinedOutput() } diff --git a/runtime.go b/runtime.go index ec32f75456..8b7d5d3494 100644 --- a/runtime.go +++ b/runtime.go @@ -849,8 +849,8 @@ func (runtime *Runtime) Kill(c *Container, sig int) error { return runtime.execDriver.Kill(c.process, sig) } -func (runtime *Runtime) Wait(c *Container, duration time.Duration) error { - return runtime.execDriver.Wait(c.ID, duration) +func (runtime *Runtime) WaitGhost(c *Container) error { + return runtime.execDriver.Wait(c.ID) } // Nuke kills all containers then removes all content From d3bae131d6fa10dd1c25077204bf2c3274a3b8cc Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 13 Jan 2014 17:55:16 -0800 Subject: [PATCH 25/34] Make sure proc is umounted for chroot driver within init Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- sysinit/sysinit.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index 73349379fb..6701717fe3 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -208,11 +208,10 @@ func executeProgram(args *DockerInitArgs) error { return err } } else if args.driver == "chroot" { - // TODO: @crosbymichael @creack how do we unmount this after the - // process exists? if err := setupMounts(args); err != nil { return err } + defer mount.ForceUnmount("proc") } path, err := exec.LookPath(args.args[0]) @@ -221,11 +220,22 @@ func executeProgram(args *DockerInitArgs) error { os.Exit(127) } - if err := syscall.Exec(path, args.args, os.Environ()); err != nil { - return fmt.Errorf("dockerinit unable to execute %s - %s", path, err) - } + if args.driver == "lxc" { + if err := syscall.Exec(path, args.args, os.Environ()); err != nil { + panic(err) + } + // Will never reach + } else if args.driver == "chroot" { + cmd := exec.Command(path, args.args[1:]...) + + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + + return cmd.Run() + } + panic("Should not be here") - // Will never reach here return nil } From f7684ea7f61c0c69033c27605e9ad9a0a76e74cd Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 13 Jan 2014 18:36:59 -0800 Subject: [PATCH 26/34] Move docker init into drivers functions Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes (github: creack) Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes (github: crosbymichael) --- execdriver/chroot/driver.go | 22 +- execdriver/driver.go | 44 +++- execdriver/lxc/driver.go | 38 ++- execdriver/lxc/init.go | 153 +++++++++++ .../lxc/lxc_init_darwin.go | 2 +- .../lxc/lxc_init_linux.go | 2 +- sysinit/sysinit.go | 238 ++---------------- 7 files changed, 273 insertions(+), 226 deletions(-) create mode 100644 execdriver/lxc/init.go rename sysinit/sysinit_darwin.go => execdriver/lxc/lxc_init_darwin.go (83%) rename sysinit/sysinit_linux.go => execdriver/lxc/lxc_init_linux.go (87%) diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index 6244d2a93f..1f98bba0ac 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -2,9 +2,29 @@ package chroot import ( "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/mount" + "os" "os/exec" ) +const DriverName = "chroot" + +func init() { + execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { + if err := mount.ForceMount("proc", "proc", "proc", ""); err != nil { + return err + } + defer mount.ForceUnmount("proc") + cmd := exec.Command(args.Args[0], args.Args[1:]...) + + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + + return cmd.Run() + }) +} + type driver struct { } @@ -13,7 +33,7 @@ func NewDriver() (*driver, error) { } func (d *driver) Name() string { - return "chroot" + return DriverName } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { diff --git a/execdriver/driver.go b/execdriver/driver.go index d68123d9ba..ca0a2991f5 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -7,11 +7,49 @@ import ( ) var ( - ErrNotRunning = errors.New("Process could not be started") - ErrWaitTimeoutReached = errors.New("Wait timeout reached") + ErrNotRunning = errors.New("Process could not be started") + ErrWaitTimeoutReached = errors.New("Wait timeout reached") + ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function") + ErrDriverNotFound = errors.New("The requested docker init has not been found") ) -type StartCallback func(*Process) +var dockerInitFcts map[string]DockerInitFct + +type ( + StartCallback func(*Process) + DockerInitFct func(i *DockerInitArgs) error +) + +func RegisterDockerInitFct(name string, fct DockerInitFct) error { + if dockerInitFcts == nil { + dockerInitFcts = make(map[string]DockerInitFct) + } + if _, ok := dockerInitFcts[name]; ok { + return ErrDriverAlreadyRegistered + } + dockerInitFcts[name] = fct + return nil +} + +func GetDockerInitFct(name string) (DockerInitFct, error) { + fct, ok := dockerInitFcts[name] + if !ok { + return nil, ErrDriverNotFound + } + return fct, nil +} + +type DockerInitArgs struct { + User string + Gateway string + Ip string + WorkDir string + Privileged bool + Env []string + Args []string + Mtu int + Driver string +} type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 3939841320..61fc99fae0 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -5,14 +5,50 @@ import ( "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/utils" "io/ioutil" + "log" "os" "os/exec" "path" "strconv" "strings" + "syscall" "time" ) +const DriverName = "lxc" + +func init() { + execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { + if err := setupHostname(args); err != nil { + return err + } + + if err := setupNetworking(args); err != nil { + return err + } + + if err := setupCapabilities(args); err != nil { + return err + } + if err := setupWorkingDirectory(args); err != nil { + return err + } + + if err := changeUser(args); err != nil { + return err + } + path, err := exec.LookPath(args.Args[0]) + if err != nil { + log.Printf("Unable to locate %v", args.Args[0]) + os.Exit(127) + } + if err := syscall.Exec(path, args.Args, os.Environ()); err != nil { + panic(err) + } + panic("Unreachable") + }) +} + type driver struct { root string // root path for the driver to use apparmor bool @@ -32,7 +68,7 @@ func NewDriver(root string, apparmor bool) (*driver, error) { } func (d *driver) Name() string { - return "lxc" + return DriverName } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { diff --git a/execdriver/lxc/init.go b/execdriver/lxc/init.go new file mode 100644 index 0000000000..dec33e3ad6 --- /dev/null +++ b/execdriver/lxc/init.go @@ -0,0 +1,153 @@ +package lxc + +import ( + "fmt" + "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/pkg/netlink" + "github.com/dotcloud/docker/utils" + "github.com/syndtr/gocapability/capability" + "net" + "os" + "strconv" + "strings" + "syscall" +) + +func setupHostname(args *execdriver.DockerInitArgs) error { + hostname := getEnv(args, "HOSTNAME") + if hostname == "" { + return nil + } + return setHostname(hostname) +} + +// Setup networking +func setupNetworking(args *execdriver.DockerInitArgs) error { + if args.Ip != "" { + // eth0 + iface, err := net.InterfaceByName("eth0") + if err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + ip, ipNet, err := net.ParseCIDR(args.Ip) + if err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil { + return fmt.Errorf("Unable to set MTU: %v", err) + } + if err := netlink.NetworkLinkUp(iface); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + + // loopback + iface, err = net.InterfaceByName("lo") + if err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + if err := netlink.NetworkLinkUp(iface); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + } + if args.Gateway != "" { + gw := net.ParseIP(args.Gateway) + if gw == nil { + return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway) + } + + if err := netlink.AddDefaultGw(gw); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + } + + return nil +} + +// Setup working directory +func setupWorkingDirectory(args *execdriver.DockerInitArgs) error { + if args.WorkDir == "" { + return nil + } + if err := syscall.Chdir(args.WorkDir); err != nil { + return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err) + } + return nil +} + +// Takes care of dropping privileges to the desired user +func changeUser(args *execdriver.DockerInitArgs) error { + if args.User == "" { + return nil + } + userent, err := utils.UserLookup(args.User) + if err != nil { + return fmt.Errorf("Unable to find user %v: %v", args.User, err) + } + + uid, err := strconv.Atoi(userent.Uid) + if err != nil { + return fmt.Errorf("Invalid uid: %v", userent.Uid) + } + gid, err := strconv.Atoi(userent.Gid) + if err != nil { + return fmt.Errorf("Invalid gid: %v", userent.Gid) + } + + if err := syscall.Setgid(gid); err != nil { + return fmt.Errorf("setgid failed: %v", err) + } + if err := syscall.Setuid(uid); err != nil { + return fmt.Errorf("setuid failed: %v", err) + } + + return nil +} + +func setupCapabilities(args *execdriver.DockerInitArgs) error { + + if args.Privileged { + return nil + } + + drop := []capability.Cap{ + capability.CAP_SETPCAP, + capability.CAP_SYS_MODULE, + capability.CAP_SYS_RAWIO, + capability.CAP_SYS_PACCT, + capability.CAP_SYS_ADMIN, + capability.CAP_SYS_NICE, + capability.CAP_SYS_RESOURCE, + capability.CAP_SYS_TIME, + capability.CAP_SYS_TTY_CONFIG, + capability.CAP_MKNOD, + capability.CAP_AUDIT_WRITE, + capability.CAP_AUDIT_CONTROL, + capability.CAP_MAC_OVERRIDE, + capability.CAP_MAC_ADMIN, + } + + c, err := capability.NewPid(os.Getpid()) + if err != nil { + return err + } + + c.Unset(capability.CAPS|capability.BOUNDS, drop...) + + if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil { + return err + } + return nil +} + +func getEnv(args *execdriver.DockerInitArgs, key string) string { + for _, kv := range args.Env { + parts := strings.SplitN(kv, "=", 2) + if parts[0] == key && len(parts) == 2 { + return parts[1] + } + } + return "" +} diff --git a/sysinit/sysinit_darwin.go b/execdriver/lxc/lxc_init_darwin.go similarity index 83% rename from sysinit/sysinit_darwin.go rename to execdriver/lxc/lxc_init_darwin.go index 64566afb3c..c066fead93 100644 --- a/sysinit/sysinit_darwin.go +++ b/execdriver/lxc/lxc_init_darwin.go @@ -1,4 +1,4 @@ -package sysinit +package lxc func setHostname(hostname string) error { panic("Not supported on darwin") diff --git a/sysinit/sysinit_linux.go b/execdriver/lxc/lxc_init_linux.go similarity index 87% rename from sysinit/sysinit_linux.go rename to execdriver/lxc/lxc_init_linux.go index d18d2fab8b..b0055c3668 100644 --- a/sysinit/sysinit_linux.go +++ b/execdriver/lxc/lxc_init_linux.go @@ -1,4 +1,4 @@ -package sysinit +package lxc import ( "syscall" diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index 6701717fe3..96339018ce 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -4,169 +4,17 @@ import ( "encoding/json" "flag" "fmt" - "github.com/dotcloud/docker/mount" - "github.com/dotcloud/docker/pkg/netlink" - "github.com/dotcloud/docker/utils" - "github.com/syndtr/gocapability/capability" + "github.com/dotcloud/docker/execdriver" "io/ioutil" "log" - "net" "os" - "os/exec" - "strconv" "strings" - "syscall" ) -type DockerInitArgs struct { - user string - gateway string - ip string - workDir string - privileged bool - env []string - args []string - mtu int - driver string -} - -func setupHostname(args *DockerInitArgs) error { - hostname := getEnv(args, "HOSTNAME") - if hostname == "" { - return nil - } - return setHostname(hostname) -} - -// Setup networking -func setupNetworking(args *DockerInitArgs) error { - if args.ip != "" { - // eth0 - iface, err := net.InterfaceByName("eth0") - if err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - ip, ipNet, err := net.ParseCIDR(args.ip) - if err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - if err := netlink.NetworkSetMTU(iface, args.mtu); err != nil { - return fmt.Errorf("Unable to set MTU: %v", err) - } - if err := netlink.NetworkLinkUp(iface); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - - // loopback - iface, err = net.InterfaceByName("lo") - if err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - if err := netlink.NetworkLinkUp(iface); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - } - if args.gateway != "" { - gw := net.ParseIP(args.gateway) - if gw == nil { - return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.gateway) - } - - if err := netlink.AddDefaultGw(gw); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - } - - return nil -} - -// Setup working directory -func setupWorkingDirectory(args *DockerInitArgs) error { - if args.workDir == "" { - return nil - } - if err := syscall.Chdir(args.workDir); err != nil { - return fmt.Errorf("Unable to change dir to %v: %v", args.workDir, err) - } - return nil -} - -func setupMounts(args *DockerInitArgs) error { - return mount.ForceMount("proc", "proc", "proc", "") -} - -// Takes care of dropping privileges to the desired user -func changeUser(args *DockerInitArgs) error { - if args.user == "" { - return nil - } - userent, err := utils.UserLookup(args.user) - if err != nil { - return fmt.Errorf("Unable to find user %v: %v", args.user, err) - } - - uid, err := strconv.Atoi(userent.Uid) - if err != nil { - return fmt.Errorf("Invalid uid: %v", userent.Uid) - } - gid, err := strconv.Atoi(userent.Gid) - if err != nil { - return fmt.Errorf("Invalid gid: %v", userent.Gid) - } - - if err := syscall.Setgid(gid); err != nil { - return fmt.Errorf("setgid failed: %v", err) - } - if err := syscall.Setuid(uid); err != nil { - return fmt.Errorf("setuid failed: %v", err) - } - - return nil -} - -func setupCapabilities(args *DockerInitArgs) error { - - if args.privileged { - return nil - } - - drop := []capability.Cap{ - capability.CAP_SETPCAP, - capability.CAP_SYS_MODULE, - capability.CAP_SYS_RAWIO, - capability.CAP_SYS_PACCT, - capability.CAP_SYS_ADMIN, - capability.CAP_SYS_NICE, - capability.CAP_SYS_RESOURCE, - capability.CAP_SYS_TIME, - capability.CAP_SYS_TTY_CONFIG, - capability.CAP_MKNOD, - capability.CAP_AUDIT_WRITE, - capability.CAP_AUDIT_CONTROL, - capability.CAP_MAC_OVERRIDE, - capability.CAP_MAC_ADMIN, - } - - c, err := capability.NewPid(os.Getpid()) - if err != nil { - return err - } - - c.Unset(capability.CAPS|capability.BOUNDS, drop...) - - if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil { - return err - } - return nil -} - // Clear environment pollution introduced by lxc-start -func setupEnv(args *DockerInitArgs) { +func setupEnv(args *execdriver.DockerInitArgs) { os.Clearenv() - for _, kv := range args.env { + for _, kv := range args.Env { parts := strings.SplitN(kv, "=", 2) if len(parts) == 1 { parts = append(parts, "") @@ -175,66 +23,18 @@ func setupEnv(args *DockerInitArgs) { } } -func getEnv(args *DockerInitArgs, key string) string { - for _, kv := range args.env { - parts := strings.SplitN(kv, "=", 2) - if parts[0] == key && len(parts) == 2 { - return parts[1] - } - } - return "" -} - -func executeProgram(args *DockerInitArgs) error { +func executeProgram(args *execdriver.DockerInitArgs) error { setupEnv(args) - - if args.driver == "lxc" { - if err := setupHostname(args); err != nil { - return err - } - - if err := setupNetworking(args); err != nil { - return err - } - - if err := setupCapabilities(args); err != nil { - return err - } - if err := setupWorkingDirectory(args); err != nil { - return err - } - - if err := changeUser(args); err != nil { - return err - } - } else if args.driver == "chroot" { - if err := setupMounts(args); err != nil { - return err - } - defer mount.ForceUnmount("proc") - } - - path, err := exec.LookPath(args.args[0]) + dockerInitFct, err := execdriver.GetDockerInitFct(args.Driver) if err != nil { - log.Printf("Unable to locate %v", args.args[0]) - os.Exit(127) + panic(err) } + return dockerInitFct(args) - if args.driver == "lxc" { - if err := syscall.Exec(path, args.args, os.Environ()); err != nil { - panic(err) - } + if args.Driver == "lxc" { // Will never reach - } else if args.driver == "chroot" { - cmd := exec.Command(path, args.args[1:]...) - - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - cmd.Stdin = os.Stdin - - return cmd.Run() + } else if args.Driver == "chroot" { } - panic("Should not be here") return nil } @@ -271,16 +71,16 @@ func SysInit() { // Propagate the plugin-specific container env variable env = append(env, "container="+os.Getenv("container")) - args := &DockerInitArgs{ - user: *user, - gateway: *gateway, - ip: *ip, - workDir: *workDir, - privileged: *privileged, - env: env, - args: flag.Args(), - mtu: *mtu, - driver: *driver, + args := &execdriver.DockerInitArgs{ + User: *user, + Gateway: *gateway, + Ip: *ip, + WorkDir: *workDir, + Privileged: *privileged, + Env: env, + Args: flag.Args(), + Mtu: *mtu, + Driver: *driver, } if err := executeProgram(args); err != nil { From 889b4b10ae3ec1d6e7879c30860aafd7674cb576 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 15 Jan 2014 11:46:25 -0800 Subject: [PATCH 27/34] Cleanup + add Info to driver in order to have specific IsRunning() Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes (github: creack) Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes (github: crosbymichael) --- container.go | 15 ++++++++------- execdriver/chroot/driver.go | 21 ++++++++++++++------- execdriver/driver.go | 6 ++++++ execdriver/lxc/driver.go | 34 ++++++++++++++++++++++++++++++---- runtime.go | 28 +++++++++++++++++----------- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/container.go b/container.go index 702d94e391..257925fdda 100644 --- a/container.go +++ b/container.go @@ -697,17 +697,18 @@ func (container *Container) Start() (err error) { } container.waitLock = make(chan struct{}) - // Setup pipes + // Setuping pipes and/or Pty + var setup func() error if container.Config.Tty { - err = container.setupPty() + setup = container.setupPty } else { - err = container.setupStd() + setup = container.setupStd } - if err != nil { + if err := setup(); err != nil { return err } - waitLock := make(chan struct{}) + callbackLock := make(chan struct{}) callback := func(process *execdriver.Process) { container.State.SetRunning(process.Pid()) if process.Tty { @@ -721,7 +722,7 @@ func (container *Container) Start() (err error) { if err := container.ToDisk(); err != nil { utils.Debugf("%s", err) } - close(waitLock) + close(callbackLock) } // We use a callback here instead of a goroutine and an chan for @@ -730,7 +731,7 @@ func (container *Container) Start() (err error) { // Start should not return until the process is actually running select { - case <-waitLock: + case <-callbackLock: case err := <-cErr: return err } diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index 1f98bba0ac..be8eb58a1a 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -7,7 +7,10 @@ import ( "os/exec" ) -const DriverName = "chroot" +const ( + DriverName = "chroot" + Version = "0.1" +) func init() { execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { @@ -32,10 +35,6 @@ func NewDriver() (*driver, error) { return &driver{}, nil } -func (d *driver) Name() string { - return DriverName -} - func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { params := []string{ "chroot", @@ -78,6 +77,14 @@ func (d *driver) Wait(id string) error { panic("Not Implemented") } -func (d *driver) Version() string { - return "0.1" +func (d *driver) Info(id string) execdriver.Info { + panic("Not implemented") +} + +func (d *driver) Name() string { + return DriverName +} + +func (d *driver) Version() string { + return Version } diff --git a/execdriver/driver.go b/execdriver/driver.go index ca0a2991f5..3de3369dab 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -51,6 +51,10 @@ type DockerInitArgs struct { Driver string } +type Info interface { + IsRunning() bool +} + type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Process, sig int) error @@ -58,6 +62,8 @@ type Driver interface { Wait(id string) error // Wait on an out of process...process - lxc ghosts Version() string Name() string + + Info(id string) Info // "temporary" hack (until we move state from core to plugins) } // Network settings of the container diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 61fc99fae0..1ae136b512 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -221,9 +221,9 @@ func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) err default: } - output, err = d.getInfo(c) + output, err = d.getInfo(c.ID) if err != nil { - output, err = d.getInfo(c) + output, err = d.getInfo(c.ID) if err != nil { return err } @@ -236,8 +236,34 @@ func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) err return execdriver.ErrNotRunning } -func (d *driver) getInfo(c *execdriver.Process) ([]byte, error) { - return exec.Command("lxc-info", "-s", "-n", c.ID).CombinedOutput() +func (d *driver) getInfo(id string) ([]byte, error) { + return exec.Command("lxc-info", "-s", "-n", id).CombinedOutput() +} + +type info struct { + ID string + driver *driver +} + +func (i *info) IsRunning() bool { + var running bool + + output, err := i.driver.getInfo(i.ID) + if err != nil { + panic(err) + } + if strings.Contains(string(output), "RUNNING") { + running = true + } + return running +} + +func (d *driver) Info(id string) execdriver.Info { + + return &info{ + ID: id, + driver: d, + } } func linkLxcStart(root string) error { diff --git a/runtime.go b/runtime.go index 8b7d5d3494..5a11faec9f 100644 --- a/runtime.go +++ b/runtime.go @@ -18,7 +18,6 @@ import ( "io/ioutil" "log" "os" - "os/exec" "path" "regexp" "sort" @@ -164,11 +163,9 @@ func (runtime *Runtime) Register(container *Container) error { // if so, then we need to restart monitor and init a new lock // If the container is supposed to be running, make sure of it if container.State.IsRunning() { - output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput() - if err != nil { - return err - } - if !strings.Contains(string(output), "RUNNING") { + info := runtime.execDriver.Info(container.ID) + + if !info.IsRunning() { utils.Debugf("Container %s was supposed to be running but is not.", container.ID) if runtime.config.AutoRestart { utils.Debugf("Restarting") @@ -736,12 +733,21 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { } capabilities := NewRuntimeCapabilities(false) - var ed execdriver.Driver - if driver := os.Getenv("EXEC_DRIVER"); driver == "lxc" { - ed, err = lxc.NewDriver(config.Root, capabilities.AppArmor) - } else { - ed, err = chroot.NewDriver() + + /* + temporarilly disabled. + */ + if false { + var ed execdriver.Driver + if driver := os.Getenv("EXEC_DRIVER"); driver == "lxc" { + ed, err = lxc.NewDriver(config.Root, capabilities.AppArmor) + } else { + ed, err = chroot.NewDriver() + } + if ed != nil { + } } + ed, err := lxc.NewDriver(config.Root, capabilities.AppArmor) if err != nil { return nil, err } From ca8dd73fbfa7aca0768278ff1ff9671f80c48138 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 15 Jan 2014 13:57:07 -0800 Subject: [PATCH 28/34] Small fixes to type names Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/chroot/driver.go | 2 +- execdriver/driver.go | 23 ++++++++++++----------- execdriver/lxc/driver.go | 2 +- execdriver/lxc/init.go | 12 ++++++------ sysinit/sysinit.go | 8 ++++---- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index be8eb58a1a..2aa23c9866 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -13,7 +13,7 @@ const ( ) func init() { - execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { + execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error { if err := mount.ForceMount("proc", "proc", "proc", ""); err != nil { return err } diff --git a/execdriver/driver.go b/execdriver/driver.go index 3de3369dab..e4d512f7bc 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -13,16 +13,16 @@ var ( ErrDriverNotFound = errors.New("The requested docker init has not been found") ) -var dockerInitFcts map[string]DockerInitFct +var dockerInitFcts map[string]InitFunc type ( StartCallback func(*Process) - DockerInitFct func(i *DockerInitArgs) error + InitFunc func(i *InitArgs) error ) -func RegisterDockerInitFct(name string, fct DockerInitFct) error { +func RegisterInitFunc(name string, fct InitFunc) error { if dockerInitFcts == nil { - dockerInitFcts = make(map[string]DockerInitFct) + dockerInitFcts = make(map[string]InitFunc) } if _, ok := dockerInitFcts[name]; ok { return ErrDriverAlreadyRegistered @@ -31,7 +31,7 @@ func RegisterDockerInitFct(name string, fct DockerInitFct) error { return nil } -func GetDockerInitFct(name string) (DockerInitFct, error) { +func GetInitFunc(name string) (InitFunc, error) { fct, ok := dockerInitFcts[name] if !ok { return nil, ErrDriverNotFound @@ -39,7 +39,8 @@ func GetDockerInitFct(name string) (DockerInitFct, error) { return fct, nil } -type DockerInitArgs struct { +// Args provided to the init function for a driver +type InitArgs struct { User string Gateway string Ip string @@ -51,6 +52,8 @@ type DockerInitArgs struct { Driver string } +// Driver specific information based on +// processes registered with the driver type Info interface { IsRunning() bool } @@ -58,12 +61,10 @@ type Info interface { type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Process, sig int) error - // TODO: @crosbymichael @creack wait should probably return the exit code Wait(id string) error // Wait on an out of process...process - lxc ghosts - Version() string - Name() string - - Info(id string) Info // "temporary" hack (until we move state from core to plugins) + Version() string // Driver version number + Name() string // Driver name + Info(id string) Info // "temporary" hack (until we move state from core to plugins) } // Network settings of the container diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 1ae136b512..3b4572af96 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -18,7 +18,7 @@ import ( const DriverName = "lxc" func init() { - execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { + execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error { if err := setupHostname(args); err != nil { return err } diff --git a/execdriver/lxc/init.go b/execdriver/lxc/init.go index dec33e3ad6..7c2b039c50 100644 --- a/execdriver/lxc/init.go +++ b/execdriver/lxc/init.go @@ -13,7 +13,7 @@ import ( "syscall" ) -func setupHostname(args *execdriver.DockerInitArgs) error { +func setupHostname(args *execdriver.InitArgs) error { hostname := getEnv(args, "HOSTNAME") if hostname == "" { return nil @@ -22,7 +22,7 @@ func setupHostname(args *execdriver.DockerInitArgs) error { } // Setup networking -func setupNetworking(args *execdriver.DockerInitArgs) error { +func setupNetworking(args *execdriver.InitArgs) error { if args.Ip != "" { // eth0 iface, err := net.InterfaceByName("eth0") @@ -67,7 +67,7 @@ func setupNetworking(args *execdriver.DockerInitArgs) error { } // Setup working directory -func setupWorkingDirectory(args *execdriver.DockerInitArgs) error { +func setupWorkingDirectory(args *execdriver.InitArgs) error { if args.WorkDir == "" { return nil } @@ -78,7 +78,7 @@ func setupWorkingDirectory(args *execdriver.DockerInitArgs) error { } // Takes care of dropping privileges to the desired user -func changeUser(args *execdriver.DockerInitArgs) error { +func changeUser(args *execdriver.InitArgs) error { if args.User == "" { return nil } @@ -106,7 +106,7 @@ func changeUser(args *execdriver.DockerInitArgs) error { return nil } -func setupCapabilities(args *execdriver.DockerInitArgs) error { +func setupCapabilities(args *execdriver.InitArgs) error { if args.Privileged { return nil @@ -142,7 +142,7 @@ func setupCapabilities(args *execdriver.DockerInitArgs) error { return nil } -func getEnv(args *execdriver.DockerInitArgs, key string) string { +func getEnv(args *execdriver.InitArgs, key string) string { for _, kv := range args.Env { parts := strings.SplitN(kv, "=", 2) if parts[0] == key && len(parts) == 2 { diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index 96339018ce..7e1ac519cd 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -12,7 +12,7 @@ import ( ) // Clear environment pollution introduced by lxc-start -func setupEnv(args *execdriver.DockerInitArgs) { +func setupEnv(args *execdriver.InitArgs) { os.Clearenv() for _, kv := range args.Env { parts := strings.SplitN(kv, "=", 2) @@ -23,9 +23,9 @@ func setupEnv(args *execdriver.DockerInitArgs) { } } -func executeProgram(args *execdriver.DockerInitArgs) error { +func executeProgram(args *execdriver.InitArgs) error { setupEnv(args) - dockerInitFct, err := execdriver.GetDockerInitFct(args.Driver) + dockerInitFct, err := execdriver.GetInitFunc(args.Driver) if err != nil { panic(err) } @@ -71,7 +71,7 @@ func SysInit() { // Propagate the plugin-specific container env variable env = append(env, "container="+os.Getenv("container")) - args := &execdriver.DockerInitArgs{ + args := &execdriver.InitArgs{ User: *user, Gateway: *gateway, Ip: *ip, From 9e9f4b925b4b6a58a03bbff09150f1314d5fe3b2 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 15 Jan 2014 14:36:13 -0800 Subject: [PATCH 29/34] Rename Capabilities in sysinfo and move it to its own subpackage Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes (github: creack) Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes (github: crosbymichael) --- api.go | 6 ++--- commands.go | 11 ++++---- container.go | 6 ++--- lxc_template.go | 11 ++++---- pkg/sysinfo/sysinfo.go | 55 +++++++++++++++++++++++++++++++++++++++ runtime.go | 58 +++++------------------------------------- server.go | 12 ++++----- 7 files changed, 85 insertions(+), 74 deletions(-) create mode 100644 pkg/sysinfo/sysinfo.go diff --git a/api.go b/api.go index cfef7a50ce..83b749b217 100644 --- a/api.go +++ b/api.go @@ -657,16 +657,16 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r for scanner.Scan() { out.Warnings = append(out.Warnings, scanner.Text()) } - if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.MemoryLimit { + if job.GetenvInt("Memory") > 0 && !srv.runtime.sysInfo.MemoryLimit { log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.") out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") } - if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.SwapLimit { + if job.GetenvInt("Memory") > 0 && !srv.runtime.sysInfo.SwapLimit { log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.") out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.") } - if !job.GetenvBool("NetworkDisabled") && srv.runtime.capabilities.IPv4ForwardingDisabled { + if !job.GetenvBool("NetworkDisabled") && srv.runtime.sysInfo.IPv4ForwardingDisabled { log.Println("Warning: IPv4 forwarding is disabled.") out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.") } diff --git a/commands.go b/commands.go index 09b65ad163..d23c7a9da5 100644 --- a/commands.go +++ b/commands.go @@ -12,6 +12,7 @@ import ( "github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/engine" flag "github.com/dotcloud/docker/pkg/mflag" + "github.com/dotcloud/docker/pkg/sysinfo" "github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/utils" @@ -1745,14 +1746,14 @@ func (cli *DockerCli) CmdTag(args ...string) error { } //FIXME Only used in tests -func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) { +func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) { cmd := flag.NewFlagSet("run", flag.ContinueOnError) cmd.SetOutput(ioutil.Discard) cmd.Usage = nil - return parseRun(cmd, args, capabilities) + return parseRun(cmd, args, sysInfo) } -func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) { +func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) { var ( // FIXME: use utils.ListOpts for attach and volumes? flAttach = NewListOpts(ValidateAttach) @@ -1802,7 +1803,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co } // Check if the kernel supports memory limit cgroup. - if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit { + if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit { *flMemoryString = "" } @@ -1934,7 +1935,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co PublishAllPorts: *flPublishAll, } - if capabilities != nil && flMemory > 0 && !capabilities.SwapLimit { + if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n") config.MemorySwap = -1 } diff --git a/container.go b/container.go index 257925fdda..23a3c05335 100644 --- a/container.go +++ b/container.go @@ -527,16 +527,16 @@ func (container *Container) Start() (err error) { } // Make sure the config is compatible with the current kernel - if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit { + if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit { log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") container.Config.Memory = 0 } - if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit { + if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit { log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n") container.Config.MemorySwap = -1 } - if container.runtime.capabilities.IPv4ForwardingDisabled { + if container.runtime.sysInfo.IPv4ForwardingDisabled { log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work") } diff --git a/lxc_template.go b/lxc_template.go index f96323b5ea..4e1aece94b 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -1,6 +1,7 @@ package docker import ( + "github.com/dotcloud/docker/pkg/sysinfo" "strings" "text/template" ) @@ -31,7 +32,7 @@ lxc.console = none lxc.tty = 1 {{if (getHostConfig .).Privileged}} -lxc.cgroup.devices.allow = a +lxc.cgroup.devices.allow = a {{else}} # no implicit access to devices lxc.cgroup.devices.deny = a @@ -82,7 +83,7 @@ lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstanc lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 {{if (getHostConfig .).Privileged}} -{{if (getCapabilities .).AppArmor}} +{{if (getSysInfo .).AppArmor}} lxc.aa_profile = unconfined {{else}} #lxc.aa_profile = unconfined @@ -129,8 +130,8 @@ func getHostConfig(container *Container) *HostConfig { return container.hostConfig } -func getCapabilities(container *Container) *Capabilities { - return container.runtime.capabilities +func getSysInfo(container *Container) *sysinfo.SysInfo { + return container.runtime.sysInfo } func init() { @@ -138,7 +139,7 @@ func init() { funcMap := template.FuncMap{ "getMemorySwap": getMemorySwap, "getHostConfig": getHostConfig, - "getCapabilities": getCapabilities, + "getSysInfo": getSysInfo, "escapeFstabSpaces": escapeFstabSpaces, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go new file mode 100644 index 0000000000..884fcbde7c --- /dev/null +++ b/pkg/sysinfo/sysinfo.go @@ -0,0 +1,55 @@ +package sysinfo + +import ( + "github.com/dotcloud/docker/cgroups" + "github.com/dotcloud/docker/utils" + "io/ioutil" + "log" + "os" + "path" +) + +type SysInfo struct { + MemoryLimit bool + SwapLimit bool + IPv4ForwardingDisabled bool + AppArmor bool +} + +func New(quiet bool) *SysInfo { + sysInfo := &SysInfo{} + if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { + if !quiet { + log.Printf("WARNING: %s\n", err) + } + } else { + _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) + _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) + sysInfo.MemoryLimit = err1 == nil && err2 == nil + if !sysInfo.MemoryLimit && !quiet { + log.Printf("WARNING: Your kernel does not support cgroup memory limit.") + } + + _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) + sysInfo.SwapLimit = err == nil + if !sysInfo.SwapLimit && !quiet { + log.Printf("WARNING: Your kernel does not support cgroup swap limit.") + } + } + + content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward") + sysInfo.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' + if sysInfo.IPv4ForwardingDisabled && !quiet { + log.Printf("WARNING: IPv4 forwarding is disabled.") + } + + // Check if AppArmor seems to be enabled on this system. + if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { + utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.") + sysInfo.AppArmor = false + } else { + utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.") + sysInfo.AppArmor = true + } + return sysInfo +} diff --git a/runtime.go b/runtime.go index 5a11faec9f..52f03f84be 100644 --- a/runtime.go +++ b/runtime.go @@ -4,7 +4,6 @@ import ( "container/list" "fmt" "github.com/dotcloud/docker/archive" - "github.com/dotcloud/docker/cgroups" "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/execdriver/chroot" "github.com/dotcloud/docker/execdriver/lxc" @@ -13,10 +12,10 @@ import ( _ "github.com/dotcloud/docker/graphdriver/devmapper" _ "github.com/dotcloud/docker/graphdriver/vfs" "github.com/dotcloud/docker/pkg/graphdb" + "github.com/dotcloud/docker/pkg/sysinfo" "github.com/dotcloud/docker/utils" "io" "io/ioutil" - "log" "os" "path" "regexp" @@ -37,13 +36,6 @@ var ( validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`) ) -type Capabilities struct { - MemoryLimit bool - SwapLimit bool - IPv4ForwardingDisabled bool - AppArmor bool -} - type Runtime struct { repository string sysInitPath string @@ -52,7 +44,7 @@ type Runtime struct { graph *Graph repositories *TagStore idIndex *utils.TruncIndex - capabilities *Capabilities + sysInfo *sysinfo.SysInfo volumes *Graph srv *Server config *DaemonConfig @@ -332,44 +324,6 @@ func (runtime *Runtime) restore() error { return nil } -func NewRuntimeCapabilities(quiet bool) *Capabilities { - capabilities := &Capabilities{} - if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil { - if !quiet { - log.Printf("WARNING: %s\n", err) - } - } else { - _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes")) - _, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes")) - capabilities.MemoryLimit = err1 == nil && err2 == nil - if !capabilities.MemoryLimit && !quiet { - log.Printf("WARNING: Your kernel does not support cgroup memory limit.") - } - - _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) - capabilities.SwapLimit = err == nil - if !capabilities.SwapLimit && !quiet { - log.Printf("WARNING: Your kernel does not support cgroup swap limit.") - } - } - - content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward") - capabilities.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1' - if capabilities.IPv4ForwardingDisabled && !quiet { - log.Printf("WARNING: IPv4 forwarding is disabled.") - } - - // Check if AppArmor seems to be enabled on this system. - if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { - utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.") - capabilities.AppArmor = false - } else { - utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.") - capabilities.AppArmor = true - } - return capabilities -} - // Create creates a new container from the given configuration with a given name. func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error) { // Lookup image @@ -732,7 +686,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { sysInitPath = localCopy } - capabilities := NewRuntimeCapabilities(false) + sysInfo := sysinfo.New(false) /* temporarilly disabled. @@ -740,14 +694,14 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { if false { var ed execdriver.Driver if driver := os.Getenv("EXEC_DRIVER"); driver == "lxc" { - ed, err = lxc.NewDriver(config.Root, capabilities.AppArmor) + ed, err = lxc.NewDriver(config.Root, sysInfo.AppArmor) } else { ed, err = chroot.NewDriver() } if ed != nil { } } - ed, err := lxc.NewDriver(config.Root, capabilities.AppArmor) + ed, err := lxc.NewDriver(config.Root, sysInfo.AppArmor) if err != nil { return nil, err } @@ -759,7 +713,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { graph: g, repositories: repositories, idIndex: utils.NewTruncIndex(), - capabilities: capabilities, + sysInfo: sysInfo, volumes: volumes, config: config, containerGraph: graph, diff --git a/server.go b/server.go index a9c83575e4..bc8a86ef2f 100644 --- a/server.go +++ b/server.go @@ -516,7 +516,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status { } defer file.Body.Close() - config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities) + config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo) if err != nil { job.Error(err) return engine.StatusErr @@ -678,9 +678,9 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { v.SetInt("Images", imgcount) v.Set("Driver", srv.runtime.driver.String()) v.SetJson("DriverStatus", srv.runtime.driver.Status()) - v.SetBool("MemoryLimit", srv.runtime.capabilities.MemoryLimit) - v.SetBool("SwapLimit", srv.runtime.capabilities.SwapLimit) - v.SetBool("IPv4Forwarding", !srv.runtime.capabilities.IPv4ForwardingDisabled) + v.SetBool("MemoryLimit", srv.runtime.sysInfo.MemoryLimit) + v.SetBool("SwapLimit", srv.runtime.sysInfo.SwapLimit) + v.SetBool("IPv4Forwarding", !srv.runtime.sysInfo.IPv4ForwardingDisabled) v.SetBool("Debug", os.Getenv("DEBUG") != "") v.SetInt("NFd", utils.GetTotalUsedFds()) v.SetInt("NGoroutines", runtime.NumGoroutine()) @@ -1470,10 +1470,10 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status { job.Errorf("Minimum memory limit allowed is 512k") return engine.StatusErr } - if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { + if config.Memory > 0 && !srv.runtime.sysInfo.MemoryLimit { config.Memory = 0 } - if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit { + if config.Memory > 0 && !srv.runtime.sysInfo.SwapLimit { config.MemorySwap = -1 } container, buildWarnings, err := srv.runtime.Create(&config, name) From 70a5cb95b31c7596886b7f94d292444654b9af8d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 15 Jan 2014 17:26:04 -0800 Subject: [PATCH 30/34] Move lxc template into lxc driver Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- cgroups/cgroups.go | 7 +- container.go | 39 ++++---- execdriver/driver.go | 26 ++--- execdriver/lxc/driver.go | 27 ++++- .../lxc/lxc_template.go | 58 +++++------ .../lxc/lxc_template_unit_test.go | 98 ++++++++++--------- 6 files changed, 144 insertions(+), 111 deletions(-) rename lxc_template.go => execdriver/lxc/lxc_template.go (75%) rename lxc_template_unit_test.go => execdriver/lxc/lxc_template_unit_test.go (60%) diff --git a/cgroups/cgroups.go b/cgroups/cgroups.go index 30de8d4d1e..fdcab8c3da 100644 --- a/cgroups/cgroups.go +++ b/cgroups/cgroups.go @@ -12,8 +12,13 @@ import ( "strings" ) -// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt +type Values struct { + Memory int64 `json:"memory"` + MemorySwap int64 `json:"memory_swap"` + CpuShares int64 `json:"cpu_shares"` +} +// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt func FindCgroupMountpoint(subsystem string) (string, error) { mounts, err := mount.GetMounts() if err != nil { diff --git a/container.go b/container.go index 23a3c05335..79d9511d7b 100644 --- a/container.go +++ b/container.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/cgroups" "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/mount" @@ -299,15 +300,6 @@ func (container *Container) generateEnvConfig(env []string) error { return nil } -func (container *Container) generateLXCConfig() error { - fo, err := os.Create(container.lxcConfigPath()) - if err != nil { - return err - } - defer fo.Close() - return LxcTemplateCompiled.Execute(fo, container) -} - func (container *Container) setupPty() error { ptyMaster, ptySlave, err := pty.Open() if err != nil { @@ -554,10 +546,6 @@ func (container *Container) Start() (err error) { return err } - if err := container.generateLXCConfig(); err != nil { - return err - } - // Setup environment env := []string{ "HOME=/", @@ -662,17 +650,33 @@ func (container *Container) Start() (err error) { } } - var en *execdriver.Network + var ( + en *execdriver.Network + driverConfig []string + ) + if !container.Config.NetworkDisabled { network := container.NetworkSettings en = &execdriver.Network{ Gateway: network.Gateway, + Bridge: network.Bridge, IPAddress: network.IPAddress, IPPrefixLen: network.IPPrefixLen, Mtu: container.runtime.config.Mtu, } } + if lxcConf := container.hostConfig.LxcConf; lxcConf != nil { + for _, pair := range lxcConf { + driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value)) + } + } + cgroupValues := &cgroups.Values{ + Memory: container.Config.Memory, + MemorySwap: container.Config.MemorySwap, + CpuShares: container.Config.CpuShares, + } + container.process = &execdriver.Process{ ID: container.ID, Privileged: container.hostConfig.Privileged, @@ -681,10 +685,11 @@ func (container *Container) Start() (err error) { Entrypoint: container.Path, Arguments: container.Args, WorkingDir: workingDir, - ConfigPath: container.lxcConfigPath(), Network: en, Tty: container.Config.Tty, User: container.Config.User, + Config: driverConfig, + Cgroups: cgroupValues, } container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} @@ -1381,10 +1386,6 @@ func (container *Container) EnvConfigPath() (string, error) { return p, nil } -func (container *Container) lxcConfigPath() string { - return path.Join(container.root, "config.lxc") -} - // This method must be exported to be used from the lxc template func (container *Container) RootfsPath() string { return container.rootfs diff --git a/execdriver/driver.go b/execdriver/driver.go index e4d512f7bc..0141eef18d 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -2,6 +2,7 @@ package execdriver import ( "errors" + "github.com/dotcloud/docker/cgroups" "os/exec" "syscall" ) @@ -71,6 +72,7 @@ type Driver interface { type Network struct { Gateway string `json:"gateway"` IPAddress string `json:"ip"` + Bridge string `json:"bridge"` IPPrefixLen int `json:"ip_prefix_len"` Mtu int `json:"mtu"` } @@ -79,17 +81,19 @@ type Network struct { type Process struct { exec.Cmd - ID string `json:"id"` - Privileged bool `json:"privileged"` - User string `json:"user"` - Rootfs string `json:"rootfs"` // root fs of the container - InitPath string `json:"initpath"` // dockerinit - Entrypoint string `json:"entrypoint"` - Arguments []string `json:"arguments"` - WorkingDir string `json:"working_dir"` - ConfigPath string `json:"config_path"` // This should be able to be removed when the lxc template is moved into the driver - Tty bool `json:"tty"` - Network *Network `json:"network"` // if network is nil then networking is disabled + ID string `json:"id"` + Privileged bool `json:"privileged"` + User string `json:"user"` + Rootfs string `json:"rootfs"` // root fs of the container + InitPath string `json:"initpath"` // dockerinit + Entrypoint string `json:"entrypoint"` + Arguments []string `json:"arguments"` + WorkingDir string `json:"working_dir"` + ConfigPath string `json:"config_path"` // This should be able to be removed when the lxc template is moved into the driver + Tty bool `json:"tty"` + Network *Network `json:"network"` // if network is nil then networking is disabled + Config []string `json:"config"` + Cgroups *cgroups.Values `json:"cgroups"` } // Return the pid of the process diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 3b4572af96..09203dfbf1 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -72,10 +72,14 @@ func (d *driver) Name() string { } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { + configPath, err := d.generateLXCConfig(c) + if err != nil { + return -1, err + } params := []string{ "lxc-start", "-n", c.ID, - "-f", c.ConfigPath, + "-f", configPath, "--", c.InitPath, "-driver", @@ -259,7 +263,6 @@ func (i *info) IsRunning() bool { } func (d *driver) Info(id string) execdriver.Info { - return &info{ ID: id, driver: d, @@ -297,3 +300,23 @@ func rootIsShared() bool { // No idea, probably safe to assume so return true } + +func (d *driver) generateLXCConfig(p *execdriver.Process) (string, error) { + root := path.Join(d.root, "containers", p.ID, "config.lxc") + fo, err := os.Create(root) + if err != nil { + return "", err + } + defer fo.Close() + + if err := LxcTemplateCompiled.Execute(fo, struct { + *execdriver.Process + AppArmor bool + }{ + Process: p, + AppArmor: d.apparmor, + }); err != nil { + return "", err + } + return root, nil +} diff --git a/lxc_template.go b/execdriver/lxc/lxc_template.go similarity index 75% rename from lxc_template.go rename to execdriver/lxc/lxc_template.go index 4e1aece94b..deaa7a66ae 100644 --- a/lxc_template.go +++ b/execdriver/lxc/lxc_template.go @@ -1,24 +1,24 @@ -package docker +package lxc import ( - "github.com/dotcloud/docker/pkg/sysinfo" + "github.com/dotcloud/docker/cgroups" "strings" "text/template" ) const LxcTemplate = ` -{{if .Config.NetworkDisabled}} -# network is disabled (-n=false) -lxc.network.type = empty -{{else}} +{{if .Network}} # network configuration lxc.network.type = veth -lxc.network.link = {{.NetworkSettings.Bridge}} +lxc.network.link = {{.Network.Bridge}} lxc.network.name = eth0 +{{else}} +# network is disabled (-n=false) +lxc.network.type = empty {{end}} # root filesystem -{{$ROOTFS := .RootfsPath}} +{{$ROOTFS := .Rootfs}} lxc.rootfs = {{$ROOTFS}} # use a dedicated pts for the container (and limit the number of pseudo terminal @@ -31,7 +31,7 @@ lxc.console = none # no controlling tty at all lxc.tty = 1 -{{if (getHostConfig .).Privileged}} +{{if .Privileged}} lxc.cgroup.devices.allow = a {{else}} # no implicit access to devices @@ -82,8 +82,8 @@ lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noe lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0 -{{if (getHostConfig .).Privileged}} -{{if (getSysInfo .).AppArmor}} +{{if .Privileged}} +{{if .AppArmor}} lxc.aa_profile = unconfined {{else}} #lxc.aa_profile = unconfined @@ -91,20 +91,22 @@ lxc.aa_profile = unconfined {{end}} # limits -{{if .Config.Memory}} -lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}} -lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}} -{{with $memSwap := getMemorySwap .Config}} +{{if .Cgroups}} +{{if .Cgroups.Memory}} +lxc.cgroup.memory.limit_in_bytes = {{.Cgroups.Memory}} +lxc.cgroup.memory.soft_limit_in_bytes = {{.Cgroups.Memory}} +{{with $memSwap := getMemorySwap .Cgroups}} lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}} {{end}} {{end}} -{{if .Config.CpuShares}} -lxc.cgroup.cpu.shares = {{.Config.CpuShares}} +{{if .Cgroups.CpuShares}} +lxc.cgroup.cpu.shares = {{.Cgroups.CpuShares}} +{{end}} {{end}} -{{if (getHostConfig .).LxcConf}} -{{range $pair := (getHostConfig .).LxcConf}} -{{$pair.Key}} = {{$pair.Value}} +{{if .Config}} +{{range $value := .Config}} +{{$value}} {{end}} {{end}} ` @@ -117,29 +119,19 @@ func escapeFstabSpaces(field string) string { return strings.Replace(field, " ", "\\040", -1) } -func getMemorySwap(config *Config) int64 { +func getMemorySwap(v *cgroups.Values) int64 { // By default, MemorySwap is set to twice the size of RAM. // If you want to omit MemorySwap, set it to `-1'. - if config.MemorySwap < 0 { + if v.MemorySwap < 0 { return 0 } - return config.Memory * 2 -} - -func getHostConfig(container *Container) *HostConfig { - return container.hostConfig -} - -func getSysInfo(container *Container) *sysinfo.SysInfo { - return container.runtime.sysInfo + return v.Memory * 2 } func init() { var err error funcMap := template.FuncMap{ "getMemorySwap": getMemorySwap, - "getHostConfig": getHostConfig, - "getSysInfo": getSysInfo, "escapeFstabSpaces": escapeFstabSpaces, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) diff --git a/lxc_template_unit_test.go b/execdriver/lxc/lxc_template_unit_test.go similarity index 60% rename from lxc_template_unit_test.go rename to execdriver/lxc/lxc_template_unit_test.go index f71f1dd6f5..fe65fe5cbc 100644 --- a/lxc_template_unit_test.go +++ b/execdriver/lxc/lxc_template_unit_test.go @@ -1,11 +1,14 @@ -package docker +package lxc import ( "bufio" "fmt" + "github.com/dotcloud/docker/cgroups" + "github.com/dotcloud/docker/execdriver" "io/ioutil" "math/rand" "os" + "path" "strings" "testing" "time" @@ -17,32 +20,39 @@ func TestLXCConfig(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) + + os.MkdirAll(path.Join(root, "containers", "1"), 0777) + // Memory is allocated randomly for testing rand.Seed(time.Now().UTC().UnixNano()) - memMin := 33554432 - memMax := 536870912 - mem := memMin + rand.Intn(memMax-memMin) - // CPU shares as well - cpuMin := 100 - cpuMax := 10000 - cpu := cpuMin + rand.Intn(cpuMax-cpuMin) - container := &Container{ - root: root, - Config: &Config{ - Memory: int64(mem), - CpuShares: int64(cpu), - NetworkDisabled: true, - }, - hostConfig: &HostConfig{ - Privileged: false, - }, - } - if err := container.generateLXCConfig(); err != nil { + var ( + memMin = 33554432 + memMax = 536870912 + mem = memMin + rand.Intn(memMax-memMin) + cpuMin = 100 + cpuMax = 10000 + cpu = cpuMin + rand.Intn(cpuMax-cpuMin) + ) + + driver, err := NewDriver(root, false) + if err != nil { t.Fatal(err) } - grepFile(t, container.lxcConfigPath(), + process := &execdriver.Process{ + ID: "1", + Cgroups: &cgroups.Values{ + Memory: int64(mem), + CpuShares: int64(cpu), + }, + } + p, err := driver.generateLXCConfig(process) + if err != nil { + t.Fatal(err) + } + grepFile(t, p, fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem)) - grepFile(t, container.lxcConfigPath(), + + grepFile(t, p, fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2)) } @@ -52,31 +62,29 @@ func TestCustomLxcConfig(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(root) - container := &Container{ - root: root, - Config: &Config{ - Hostname: "foobar", - NetworkDisabled: true, - }, - hostConfig: &HostConfig{ - Privileged: false, - LxcConf: []KeyValuePair{ - { - Key: "lxc.utsname", - Value: "docker", - }, - { - Key: "lxc.cgroup.cpuset.cpus", - Value: "0,1", - }, - }, - }, - } - if err := container.generateLXCConfig(); err != nil { + + os.MkdirAll(path.Join(root, "containers", "1"), 0777) + + driver, err := NewDriver(root, false) + if err != nil { t.Fatal(err) } - grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker") - grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1") + process := &execdriver.Process{ + ID: "1", + Privileged: false, + Config: []string{ + "lxc.utsname = docker", + "lxc.cgroup.cpuset.cpus = 0,1", + }, + } + + p, err := driver.generateLXCConfig(process) + if err != nil { + t.Fatal(err) + } + + grepFile(t, p, "lxc.utsname = docker") + grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") } func grepFile(t *testing.T, path string, pattern string) { From cdfebc2a20e76d4ea81eb154b748e0547a7e7ab5 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 16 Jan 2014 11:59:46 -0800 Subject: [PATCH 31/34] Change drvier name to append version Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/chroot/driver.go | 7 ++----- execdriver/driver.go | 5 ++--- execdriver/lxc/driver.go | 5 +++-- server.go | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index 2aa23c9866..f3eba8304d 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -1,6 +1,7 @@ package chroot import ( + "fmt" "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/mount" "os" @@ -82,9 +83,5 @@ func (d *driver) Info(id string) execdriver.Info { } func (d *driver) Name() string { - return DriverName -} - -func (d *driver) Version() string { - return Version + return fmt.Sprintf("%s-%s", DriverName, Version) } diff --git a/execdriver/driver.go b/execdriver/driver.go index 0141eef18d..7fc066ee29 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -63,7 +63,6 @@ type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Process, sig int) error Wait(id string) error // Wait on an out of process...process - lxc ghosts - Version() string // Driver version number Name() string // Driver name Info(id string) Info // "temporary" hack (until we move state from core to plugins) } @@ -89,10 +88,10 @@ type Process struct { Entrypoint string `json:"entrypoint"` Arguments []string `json:"arguments"` WorkingDir string `json:"working_dir"` - ConfigPath string `json:"config_path"` // This should be able to be removed when the lxc template is moved into the driver + ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver Tty bool `json:"tty"` Network *Network `json:"network"` // if network is nil then networking is disabled - Config []string `json:"config"` + Config []string `json:"config"` // generic values that specific drivers can consume Cgroups *cgroups.Values `json:"cgroups"` } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 09203dfbf1..6d1e1d29dc 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -68,7 +68,8 @@ func NewDriver(root string, apparmor bool) (*driver, error) { } func (d *driver) Name() string { - return DriverName + version := d.version() + return fmt.Sprintf("%s-%s", DriverName, version) } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { @@ -186,7 +187,7 @@ func (d *driver) Wait(id string) error { } } -func (d *driver) Version() string { +func (d *driver) version() string { version := "" if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil { outputStr := string(output) diff --git a/server.go b/server.go index bc8a86ef2f..a25ba74a06 100644 --- a/server.go +++ b/server.go @@ -684,7 +684,7 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { v.SetBool("Debug", os.Getenv("DEBUG") != "") v.SetInt("NFd", utils.GetTotalUsedFds()) v.SetInt("NGoroutines", runtime.NumGoroutine()) - v.Set("LXCVersion", srv.runtime.execDriver.Version()) + v.Set("ExecutionDriver", srv.runtime.execDriver.Name()) v.SetInt("NEventsListener", len(srv.events)) v.Set("KernelVersion", kernelVersion) v.Set("IndexServerAddress", auth.IndexServerAddress()) From 97c84507054a7379c8f8b461773b9c8d4972902b Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 16 Jan 2014 15:09:00 -0800 Subject: [PATCH 32/34] Make sure drivers are registerd within sysinit Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- commands.go | 2 +- execdriver/chroot/driver.go | 2 +- execdriver/lxc/driver.go | 2 +- sysinit/sysinit.go | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/commands.go b/commands.go index d23c7a9da5..b93f0b9686 100644 --- a/commands.go +++ b/commands.go @@ -471,7 +471,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "") fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd")) fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines")) - fmt.Fprintf(cli.out, "LXC Version: %s\n", remoteInfo.Get("LXCVersion")) + fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver")) fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener")) fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion")) diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index f3eba8304d..b9f3f04016 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -42,7 +42,7 @@ func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallba c.Rootfs, "/.dockerinit", "-driver", - d.Name(), + DriverName, } params = append(params, c.Entrypoint) params = append(params, c.Arguments...) diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 6d1e1d29dc..eeef1f5370 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -84,7 +84,7 @@ func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallba "--", c.InitPath, "-driver", - d.Name(), + DriverName, } if c.Network != nil { diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index 7e1ac519cd..af69795cb6 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -5,6 +5,8 @@ import ( "flag" "fmt" "github.com/dotcloud/docker/execdriver" + _ "github.com/dotcloud/docker/execdriver/chroot" + _ "github.com/dotcloud/docker/execdriver/lxc" "io/ioutil" "log" "os" From 5c30c4379af20b3cbd2d20cc9f0ccb6f04ac63ab Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 17 Jan 2014 16:46:39 -0800 Subject: [PATCH 33/34] Add todos for driver changes Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/driver.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/execdriver/driver.go b/execdriver/driver.go index 7fc066ee29..5f3d25a566 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -62,7 +62,7 @@ type Info interface { type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Process, sig int) error - Wait(id string) error // Wait on an out of process...process - lxc ghosts + Wait(id string) error // Wait on an out of process...process - lxc ghosts TODO: Rename to reattach, reconnect Name() string // Driver name Info(id string) Info // "temporary" hack (until we move state from core to plugins) } @@ -77,6 +77,7 @@ type Network struct { } // Process wrapps an os/exec.Cmd to add more metadata +// TODO: Rename to Command type Process struct { exec.Cmd From d5112ffce60625e470515578ccd446a191dd9e88 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 17 Jan 2014 16:53:13 -0800 Subject: [PATCH 34/34] Fix rebase for init error Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/lxc/driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index eeef1f5370..2a6663117a 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -43,7 +43,7 @@ func init() { os.Exit(127) } if err := syscall.Exec(path, args.Args, os.Environ()); err != nil { - panic(err) + return fmt.Errorf("dockerinit unable to execute %s - %s", path, err) } panic("Unreachable") })