From 69e3d30bb6c5082aff2a7d5eafaf562e59246a49 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 5 Mar 2014 17:57:27 -0800 Subject: [PATCH] Return correct process pid for lxc Fixes #2875 Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- execdriver/driver.go | 10 +++----- execdriver/lxc/driver.go | 26 +++++++++++-------- execdriver/lxc/info.go | 50 +++++++++++++++++++++++++++++++++++++ execdriver/lxc/info_test.go | 36 ++++++++++++++++++++++++++ execdriver/native/driver.go | 1 + 5 files changed, 106 insertions(+), 17 deletions(-) create mode 100644 execdriver/lxc/info.go create mode 100644 execdriver/lxc/info_test.go diff --git a/execdriver/driver.go b/execdriver/driver.go index d64c08fa6c..2143cc4077 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -116,15 +116,13 @@ type Command struct { Config []string `json:"config"` // generic values that specific drivers can consume Resources *Resources `json:"resources"` - Terminal Terminal `json:"-"` // standard or tty terminal - Console string `json:"-"` // dev/console path + Terminal Terminal `json:"-"` // standard or tty terminal + Console string `json:"-"` // dev/console path + ContainerPid int `json:"container_pid"` // the pid for the process inside a container } // Return the pid of the process // If the process is nil -1 will be returned func (c *Command) Pid() int { - if c.Process == nil { - return -1 - } - return c.Process.Pid + return c.ContainerPid } diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 8f8dcbda17..6b68076a9b 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -166,9 +166,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba }() // Poll lxc for RUNNING status - if err := d.waitForStart(c, waitLock); err != nil { + pid, err := d.waitForStart(c, waitLock) + if err != nil { return -1, err } + c.ContainerPid = pid if startCallback != nil { startCallback(c) @@ -242,7 +244,8 @@ func (d *driver) kill(c *execdriver.Command, sig int) error { return nil } -func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) error { +// wait for the process to start and return the pid for the process +func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) { var ( err error output []byte @@ -255,10 +258,7 @@ func (d *driver) waitForStart(c *execdriver.Command, 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 - } + return -1, nil default: } @@ -266,19 +266,23 @@ func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) err if err != nil { output, err = d.getInfo(c.ID) if err != nil { - return err + return -1, err } } - if strings.Contains(string(output), "RUNNING") { - return nil + info, err := parseLxcInfo(string(output)) + if err != nil { + return -1, err + } + if info.Running { + return info.Pid, nil } time.Sleep(50 * time.Millisecond) } - return execdriver.ErrNotRunning + return -1, execdriver.ErrNotRunning } func (d *driver) getInfo(id string) ([]byte, error) { - return exec.Command("lxc-info", "-s", "-n", id).CombinedOutput() + return exec.Command("lxc-info", "-n", id).CombinedOutput() } type info struct { diff --git a/execdriver/lxc/info.go b/execdriver/lxc/info.go new file mode 100644 index 0000000000..3b2ea0d07f --- /dev/null +++ b/execdriver/lxc/info.go @@ -0,0 +1,50 @@ +package lxc + +import ( + "bufio" + "errors" + "strconv" + "strings" +) + +var ( + ErrCannotParse = errors.New("cannot parse raw input") +) + +type lxcInfo struct { + Running bool + Pid int +} + +func parseLxcInfo(raw string) (*lxcInfo, error) { + if raw == "" { + return nil, ErrCannotParse + } + var ( + err error + s = bufio.NewScanner(strings.NewReader(raw)) + info = &lxcInfo{} + ) + for s.Scan() { + text := s.Text() + + if s.Err() != nil { + return nil, s.Err() + } + + parts := strings.Split(text, ":") + if len(parts) < 2 { + continue + } + switch strings.TrimSpace(parts[0]) { + case "state": + info.Running = strings.TrimSpace(parts[1]) == "RUNNING" + case "pid": + info.Pid, err = strconv.Atoi(strings.TrimSpace(parts[1])) + if err != nil { + return nil, err + } + } + } + return info, nil +} diff --git a/execdriver/lxc/info_test.go b/execdriver/lxc/info_test.go new file mode 100644 index 0000000000..edafc02511 --- /dev/null +++ b/execdriver/lxc/info_test.go @@ -0,0 +1,36 @@ +package lxc + +import ( + "testing" +) + +func TestParseRunningInfo(t *testing.T) { + raw := ` + state: RUNNING + pid: 50` + + info, err := parseLxcInfo(raw) + if err != nil { + t.Fatal(err) + } + if !info.Running { + t.Fatal("info should return a running state") + } + if info.Pid != 50 { + t.Fatalf("info should have pid 50 got %d", info.Pid) + } +} + +func TestEmptyInfo(t *testing.T) { + _, err := parseLxcInfo("") + if err == nil { + t.Fatal("error should not be nil") + } +} + +func TestBadInfo(t *testing.T) { + _, err := parseLxcInfo("state") + if err != nil { + t.Fatal(err) + } +} diff --git a/execdriver/native/driver.go b/execdriver/native/driver.go index e53b06fba2..77461f3447 100644 --- a/execdriver/native/driver.go +++ b/execdriver/native/driver.go @@ -266,6 +266,7 @@ type dockerStateWriter struct { } func (d *dockerStateWriter) WritePid(pid int) error { + d.c.ContainerPid = pid err := d.dsw.WritePid(pid) if d.callback != nil { d.callback(d.c)