| ... | ... |
@@ -116,15 +116,13 @@ type Command struct {
|
| 116 | 116 |
Config []string `json:"config"` // generic values that specific drivers can consume |
| 117 | 117 |
Resources *Resources `json:"resources"` |
| 118 | 118 |
|
| 119 |
- Terminal Terminal `json:"-"` // standard or tty terminal |
|
| 120 |
- Console string `json:"-"` // dev/console path |
|
| 119 |
+ Terminal Terminal `json:"-"` // standard or tty terminal |
|
| 120 |
+ Console string `json:"-"` // dev/console path |
|
| 121 |
+ ContainerPid int `json:"container_pid"` // the pid for the process inside a container |
|
| 121 | 122 |
} |
| 122 | 123 |
|
| 123 | 124 |
// Return the pid of the process |
| 124 | 125 |
// If the process is nil -1 will be returned |
| 125 | 126 |
func (c *Command) Pid() int {
|
| 126 |
- if c.Process == nil {
|
|
| 127 |
- return -1 |
|
| 128 |
- } |
|
| 129 |
- return c.Process.Pid |
|
| 127 |
+ return c.ContainerPid |
|
| 130 | 128 |
} |
| ... | ... |
@@ -166,9 +166,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 166 | 166 |
}() |
| 167 | 167 |
|
| 168 | 168 |
// Poll lxc for RUNNING status |
| 169 |
- if err := d.waitForStart(c, waitLock); err != nil {
|
|
| 169 |
+ pid, err := d.waitForStart(c, waitLock) |
|
| 170 |
+ if err != nil {
|
|
| 170 | 171 |
return -1, err |
| 171 | 172 |
} |
| 173 |
+ c.ContainerPid = pid |
|
| 172 | 174 |
|
| 173 | 175 |
if startCallback != nil {
|
| 174 | 176 |
startCallback(c) |
| ... | ... |
@@ -242,7 +244,8 @@ func (d *driver) kill(c *execdriver.Command, sig int) error {
|
| 242 | 242 |
return nil |
| 243 | 243 |
} |
| 244 | 244 |
|
| 245 |
-func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) error {
|
|
| 245 |
+// wait for the process to start and return the pid for the process |
|
| 246 |
+func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
|
|
| 246 | 247 |
var ( |
| 247 | 248 |
err error |
| 248 | 249 |
output []byte |
| ... | ... |
@@ -255,10 +258,7 @@ func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) err
|
| 255 | 255 |
select {
|
| 256 | 256 |
case <-waitLock: |
| 257 | 257 |
// If the process dies while waiting for it, just return |
| 258 |
- return nil |
|
| 259 |
- if c.ProcessState != nil && c.ProcessState.Exited() {
|
|
| 260 |
- return nil |
|
| 261 |
- } |
|
| 258 |
+ return -1, nil |
|
| 262 | 259 |
default: |
| 263 | 260 |
} |
| 264 | 261 |
|
| ... | ... |
@@ -266,19 +266,23 @@ func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) err
|
| 266 | 266 |
if err != nil {
|
| 267 | 267 |
output, err = d.getInfo(c.ID) |
| 268 | 268 |
if err != nil {
|
| 269 |
- return err |
|
| 269 |
+ return -1, err |
|
| 270 | 270 |
} |
| 271 | 271 |
} |
| 272 |
- if strings.Contains(string(output), "RUNNING") {
|
|
| 273 |
- return nil |
|
| 272 |
+ info, err := parseLxcInfo(string(output)) |
|
| 273 |
+ if err != nil {
|
|
| 274 |
+ return -1, err |
|
| 275 |
+ } |
|
| 276 |
+ if info.Running {
|
|
| 277 |
+ return info.Pid, nil |
|
| 274 | 278 |
} |
| 275 | 279 |
time.Sleep(50 * time.Millisecond) |
| 276 | 280 |
} |
| 277 |
- return execdriver.ErrNotRunning |
|
| 281 |
+ return -1, execdriver.ErrNotRunning |
|
| 278 | 282 |
} |
| 279 | 283 |
|
| 280 | 284 |
func (d *driver) getInfo(id string) ([]byte, error) {
|
| 281 |
- return exec.Command("lxc-info", "-s", "-n", id).CombinedOutput()
|
|
| 285 |
+ return exec.Command("lxc-info", "-n", id).CombinedOutput()
|
|
| 282 | 286 |
} |
| 283 | 287 |
|
| 284 | 288 |
type info struct {
|
| 285 | 289 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,50 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "strconv" |
|
| 6 |
+ "strings" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ ErrCannotParse = errors.New("cannot parse raw input")
|
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+type lxcInfo struct {
|
|
| 14 |
+ Running bool |
|
| 15 |
+ Pid int |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func parseLxcInfo(raw string) (*lxcInfo, error) {
|
|
| 19 |
+ if raw == "" {
|
|
| 20 |
+ return nil, ErrCannotParse |
|
| 21 |
+ } |
|
| 22 |
+ var ( |
|
| 23 |
+ err error |
|
| 24 |
+ s = bufio.NewScanner(strings.NewReader(raw)) |
|
| 25 |
+ info = &lxcInfo{}
|
|
| 26 |
+ ) |
|
| 27 |
+ for s.Scan() {
|
|
| 28 |
+ text := s.Text() |
|
| 29 |
+ |
|
| 30 |
+ if s.Err() != nil {
|
|
| 31 |
+ return nil, s.Err() |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ parts := strings.Split(text, ":") |
|
| 35 |
+ if len(parts) < 2 {
|
|
| 36 |
+ continue |
|
| 37 |
+ } |
|
| 38 |
+ switch strings.TrimSpace(parts[0]) {
|
|
| 39 |
+ case "state": |
|
| 40 |
+ info.Running = strings.TrimSpace(parts[1]) == "RUNNING" |
|
| 41 |
+ case "pid": |
|
| 42 |
+ info.Pid, err = strconv.Atoi(strings.TrimSpace(parts[1])) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return nil, err |
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+ } |
|
| 48 |
+ return info, nil |
|
| 49 |
+} |
| 0 | 50 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,36 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func TestParseRunningInfo(t *testing.T) {
|
|
| 7 |
+ raw := ` |
|
| 8 |
+ state: RUNNING |
|
| 9 |
+ pid: 50` |
|
| 10 |
+ |
|
| 11 |
+ info, err := parseLxcInfo(raw) |
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ t.Fatal(err) |
|
| 14 |
+ } |
|
| 15 |
+ if !info.Running {
|
|
| 16 |
+ t.Fatal("info should return a running state")
|
|
| 17 |
+ } |
|
| 18 |
+ if info.Pid != 50 {
|
|
| 19 |
+ t.Fatalf("info should have pid 50 got %d", info.Pid)
|
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func TestEmptyInfo(t *testing.T) {
|
|
| 24 |
+ _, err := parseLxcInfo("")
|
|
| 25 |
+ if err == nil {
|
|
| 26 |
+ t.Fatal("error should not be nil")
|
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func TestBadInfo(t *testing.T) {
|
|
| 31 |
+ _, err := parseLxcInfo("state")
|
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ t.Fatal(err) |
|
| 34 |
+ } |
|
| 35 |
+} |