Browse code

Merge pull request #4488 from crosbymichael/return-correct-lxc-pid

Return correct process pid for lxc

unclejack authored on 2014/03/07 07:14:08
Showing 5 changed files
... ...
@@ -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
+}
... ...
@@ -271,6 +271,7 @@ type dockerStateWriter struct {
271 271
 }
272 272
 
273 273
 func (d *dockerStateWriter) WritePid(pid int) error {
274
+	d.c.ContainerPid = pid
274 275
 	err := d.dsw.WritePid(pid)
275 276
 	if d.callback != nil {
276 277
 		d.callback(d.c)