Browse code

Make exec driver run a blocking command

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/01/14 08:02:12
Showing 5 changed files
... ...
@@ -308,10 +308,10 @@ func (container *Container) generateLXCConfig() error {
308 308
 	return LxcTemplateCompiled.Execute(fo, container)
309 309
 }
310 310
 
311
-func (container *Container) startPty() error {
311
+func (container *Container) startPty(startCallback execdriver.StartCallback) (int, error) {
312 312
 	ptyMaster, ptySlave, err := pty.Open()
313 313
 	if err != nil {
314
-		return err
314
+		return -1, err
315 315
 	}
316 316
 	container.ptyMaster = ptyMaster
317 317
 	container.process.Stdout = ptySlave
... ...
@@ -336,20 +336,17 @@ func (container *Container) startPty() error {
336 336
 			utils.Debugf("startPty: end of stdin pipe")
337 337
 		}()
338 338
 	}
339
-	if err := container.runtime.Start(container); err != nil {
340
-		return err
341
-	}
342
-	ptySlave.Close()
343
-	return nil
339
+
340
+	return container.runtime.Run(container, startCallback)
344 341
 }
345 342
 
346
-func (container *Container) start() error {
343
+func (container *Container) start(startCallback execdriver.StartCallback) (int, error) {
347 344
 	container.process.Stdout = container.stdout
348 345
 	container.process.Stderr = container.stderr
349 346
 	if container.Config.OpenStdin {
350 347
 		stdin, err := container.process.StdinPipe()
351 348
 		if err != nil {
352
-			return err
349
+			return -1, err
353 350
 		}
354 351
 		go func() {
355 352
 			defer stdin.Close()
... ...
@@ -358,7 +355,7 @@ func (container *Container) start() error {
358 358
 			utils.Debugf("start: end of stdin pipe")
359 359
 		}()
360 360
 	}
361
-	return container.runtime.Start(container)
361
+	return container.runtime.Run(container, startCallback)
362 362
 }
363 363
 
364 364
 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) {
689 689
 		Network:     en,
690 690
 		Tty:         container.Config.Tty,
691 691
 		User:        container.Config.User,
692
-		WaitLock:    make(chan struct{}),
693 692
 		SysInitPath: runtime.sysInitPath,
694 693
 	}
695 694
 	container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
... ...
@@ -703,21 +699,26 @@ func (container *Container) Start() (err error) {
703 703
 	}
704 704
 	container.waitLock = make(chan struct{})
705 705
 	container.State.SetRunning(0)
706
-	go container.monitor()
707 706
 
708
-	if container.Config.Tty {
709
-		err = container.startPty()
710
-	} else {
711
-		err = container.start()
712
-	}
713
-	if err != nil {
714
-		return err
707
+	waitLock := make(chan struct{})
708
+	f := func(process *execdriver.Process) {
709
+		container.State.SetRunning(process.Pid())
710
+		if process.Tty {
711
+			if c, ok := process.Stdout.(io.Closer); ok {
712
+				c.Close()
713
+			}
714
+		}
715
+		if err := container.ToDisk(); err != nil {
716
+			utils.Debugf("%s", err)
717
+		}
718
+		close(waitLock)
715 719
 	}
716 720
 
717
-	// TODO: @crosbymichael @creack
718
-	// find a way to update this
719
-	//	container.State.SetRunning(container.process.Pid())
720
-	container.ToDisk()
721
+	go container.monitor(f)
722
+
723
+	// Start should not return until the process is actually running
724
+	<-waitLock
725
+
721 726
 	return nil
722 727
 }
723 728
 
... ...
@@ -1091,17 +1092,24 @@ func (container *Container) releaseNetwork() {
1091 1091
 	container.NetworkSettings = &NetworkSettings{}
1092 1092
 }
1093 1093
 
1094
-func (container *Container) monitor() {
1095
-	// Wait for the program to exit
1094
+func (container *Container) monitor(f execdriver.StartCallback) {
1095
+	var (
1096
+		err      error
1097
+		exitCode int
1098
+	)
1099
+
1096 1100
 	if container.process == nil {
1097
-		if err := container.runtime.Wait(container, 0); err != nil {
1098
-			utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID)
1099
-		}
1101
+		// This happends when you have a GHOST container with lxc
1102
+		err = container.runtime.Wait(container, 0)
1100 1103
 	} else {
1101
-		<-container.process.WaitLock
1104
+		if container.Config.Tty {
1105
+			exitCode, err = container.startPty(f)
1106
+		} else {
1107
+			exitCode, err = container.start(f)
1108
+		}
1102 1109
 	}
1103 1110
 
1104
-	if err := container.process.WaitError; err != nil {
1111
+	if err != nil { //TODO: @crosbymichael @creack report error
1105 1112
 		// Since non-zero exit status and signal terminations will cause err to be non-nil,
1106 1113
 		// we have to actually discard it. Still, log it anyway, just in case.
1107 1114
 		utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID)
... ...
@@ -1118,7 +1126,6 @@ func (container *Container) monitor() {
1118 1118
 		container.stdin, container.stdinPipe = io.Pipe()
1119 1119
 	}
1120 1120
 
1121
-	exitCode := container.process.GetExitCode()
1122 1121
 	container.State.SetStopped(exitCode)
1123 1122
 
1124 1123
 	close(container.waitLock)
... ...
@@ -17,7 +17,7 @@ func (d *driver) String() string {
17 17
 	return "chroot"
18 18
 }
19 19
 
20
-func (d *driver) Start(c *execdriver.Process) error {
20
+func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) {
21 21
 	params := []string{
22 22
 		"chroot",
23 23
 		c.Rootfs,
... ...
@@ -40,17 +40,27 @@ func (d *driver) Start(c *execdriver.Process) error {
40 40
 	c.Args = append([]string{name}, arg...)
41 41
 
42 42
 	if err := c.Start(); err != nil {
43
-		return err
43
+		return -1, err
44 44
 	}
45 45
 
46
+	var (
47
+		waitErr  error
48
+		waitLock = make(chan struct{})
49
+	)
46 50
 	go func() {
47 51
 		if err := c.Wait(); err != nil {
48
-			c.WaitError = err
52
+			waitErr = err
49 53
 		}
50
-		close(c.WaitLock)
54
+		close(waitLock)
51 55
 	}()
52 56
 
53
-	return nil
57
+	if startCallback != nil {
58
+		startCallback(c)
59
+	}
60
+
61
+	<-waitLock
62
+
63
+	return c.GetExitCode(), waitErr
54 64
 }
55 65
 
56 66
 func (d *driver) Kill(p *execdriver.Process, sig int) error {
... ...
@@ -6,10 +6,13 @@ import (
6 6
 	"time"
7 7
 )
8 8
 
9
+type StartCallback func(*Process)
10
+
9 11
 type Driver interface {
10
-	Start(c *Process) error
12
+	Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code
11 13
 	Kill(c *Process, sig int) error
12
-	Wait(id string, duration time.Duration) error // Wait on an out of process option - lxc ghosts
14
+	// TODO: @crosbymichael @creack wait should probably return the exit code
15
+	Wait(id string, duration time.Duration) error // Wait on an out of process...process - lxc ghosts
13 16
 	Version() string
14 17
 	String() string
15 18
 }
... ...
@@ -38,8 +41,6 @@ type Process struct {
38 38
 	Tty         bool
39 39
 	Network     *Network // if network is nil then networking is disabled
40 40
 	SysInitPath string
41
-	WaitLock    chan struct{}
42
-	WaitError   error
43 41
 }
44 42
 
45 43
 func (c *Process) Pid() int {
... ...
@@ -45,7 +45,7 @@ func (d *driver) String() string {
45 45
 	return "lxc"
46 46
 }
47 47
 
48
-func (d *driver) Start(c *execdriver.Process) error {
48
+func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) {
49 49
 	params := []string{
50 50
 		startPath,
51 51
 		"-n", c.ID,
... ...
@@ -111,21 +111,32 @@ func (d *driver) Start(c *execdriver.Process) error {
111 111
 	c.Args = append([]string{name}, arg...)
112 112
 
113 113
 	if err := c.Start(); err != nil {
114
-		return err
114
+		return -1, err
115 115
 	}
116 116
 
117
+	var (
118
+		waitErr  error
119
+		waitLock = make(chan struct{})
120
+	)
117 121
 	go func() {
118 122
 		if err := c.Wait(); err != nil {
119
-			c.WaitError = err
123
+			waitErr = err
120 124
 		}
121
-		close(c.WaitLock)
125
+		close(waitLock)
122 126
 	}()
123 127
 
124
-	// Poll for running
125
-	if err := d.waitForStart(c); err != nil {
126
-		return err
128
+	// Poll lxc for RUNNING status
129
+	if err := d.waitForStart(c, waitLock); err != nil {
130
+		return -1, err
127 131
 	}
128
-	return nil
132
+
133
+	if startCallback != nil {
134
+		startCallback(c)
135
+	}
136
+
137
+	<-waitLock
138
+
139
+	return c.GetExitCode(), waitErr
129 140
 }
130 141
 
131 142
 func (d *driver) Kill(c *execdriver.Process, sig int) error {
... ...
@@ -171,7 +182,7 @@ func (d *driver) kill(c *execdriver.Process, sig int) error {
171 171
 	return nil
172 172
 }
173 173
 
174
-func (d *driver) waitForStart(c *execdriver.Process) error {
174
+func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) error {
175 175
 	var (
176 176
 		err    error
177 177
 		output []byte
... ...
@@ -182,7 +193,7 @@ func (d *driver) waitForStart(c *execdriver.Process) error {
182 182
 	// the end of this loop
183 183
 	for now := time.Now(); time.Since(now) < 5*time.Second; {
184 184
 		select {
185
-		case <-c.WaitLock:
185
+		case <-waitLock:
186 186
 			// If the process dies while waiting for it, just return
187 187
 			if c.ProcessState != nil && c.ProcessState.Exited() {
188 188
 				return nil
... ...
@@ -192,7 +192,7 @@ func (runtime *Runtime) Register(container *Container) error {
192 192
 			}
193 193
 
194 194
 			container.waitLock = make(chan struct{})
195
-			go container.monitor()
195
+			go container.monitor(nil)
196 196
 		}
197 197
 	}
198 198
 	return nil
... ...
@@ -841,8 +841,8 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
841 841
 	return archive.ExportChanges(cDir, changes)
842 842
 }
843 843
 
844
-func (runtime *Runtime) Start(c *Container) error {
845
-	return runtime.execDriver.Start(c.process)
844
+func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error) {
845
+	return runtime.execDriver.Run(c.process, startCallback)
846 846
 }
847 847
 
848 848
 func (runtime *Runtime) Kill(c *Container, sig int) error {