Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
| ... | ... |
@@ -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 {
|