This also moves `exec -i` test to _unix_test.go because it seems to need a
pty to reliably reproduce the behavior.
Signed-off-by: Daniel, Dao Quang Minh <dqminh89@gmail.com>
| ... | ... |
@@ -87,8 +87,6 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 87 | 87 |
return execdriver.ExitStatus{ExitCode: -1}, err
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 |
- var term execdriver.Terminal |
|
| 91 |
- |
|
| 92 | 90 |
p := &libcontainer.Process{
|
| 93 | 91 |
Args: append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...),
|
| 94 | 92 |
Env: c.ProcessConfig.Env, |
| ... | ... |
@@ -96,36 +94,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 96 | 96 |
User: c.ProcessConfig.User, |
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 |
- if c.ProcessConfig.Tty {
|
|
| 100 |
- rootuid, err := container.HostUID() |
|
| 101 |
- if err != nil {
|
|
| 102 |
- return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 103 |
- } |
|
| 104 |
- cons, err := p.NewConsole(rootuid) |
|
| 105 |
- if err != nil {
|
|
| 106 |
- return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 107 |
- } |
|
| 108 |
- term, err = NewTtyConsole(cons, pipes, rootuid) |
|
| 109 |
- } else {
|
|
| 110 |
- p.Stdout = pipes.Stdout |
|
| 111 |
- p.Stderr = pipes.Stderr |
|
| 112 |
- r, w, err := os.Pipe() |
|
| 113 |
- if err != nil {
|
|
| 114 |
- return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 115 |
- } |
|
| 116 |
- if pipes.Stdin != nil {
|
|
| 117 |
- go func() {
|
|
| 118 |
- io.Copy(w, pipes.Stdin) |
|
| 119 |
- w.Close() |
|
| 120 |
- }() |
|
| 121 |
- p.Stdin = r |
|
| 122 |
- } |
|
| 123 |
- term = &execdriver.StdConsole{}
|
|
| 124 |
- } |
|
| 125 |
- if err != nil {
|
|
| 99 |
+ if err := setupPipes(container, &c.ProcessConfig, p, pipes); err != nil {
|
|
| 126 | 100 |
return execdriver.ExitStatus{ExitCode: -1}, err
|
| 127 | 101 |
} |
| 128 |
- c.ProcessConfig.Terminal = term |
|
| 129 | 102 |
|
| 130 | 103 |
cont, err := d.factory.Create(c.ID, container) |
| 131 | 104 |
if err != nil {
|
| ... | ... |
@@ -398,3 +369,40 @@ func (t *TtyConsole) AttachPipes(pipes *execdriver.Pipes) error {
|
| 398 | 398 |
func (t *TtyConsole) Close() error {
|
| 399 | 399 |
return t.console.Close() |
| 400 | 400 |
} |
| 401 |
+ |
|
| 402 |
+func setupPipes(container *configs.Config, processConfig *execdriver.ProcessConfig, p *libcontainer.Process, pipes *execdriver.Pipes) error {
|
|
| 403 |
+ var term execdriver.Terminal |
|
| 404 |
+ var err error |
|
| 405 |
+ |
|
| 406 |
+ if processConfig.Tty {
|
|
| 407 |
+ rootuid, err := container.HostUID() |
|
| 408 |
+ if err != nil {
|
|
| 409 |
+ return err |
|
| 410 |
+ } |
|
| 411 |
+ cons, err := p.NewConsole(rootuid) |
|
| 412 |
+ if err != nil {
|
|
| 413 |
+ return err |
|
| 414 |
+ } |
|
| 415 |
+ term, err = NewTtyConsole(cons, pipes, rootuid) |
|
| 416 |
+ } else {
|
|
| 417 |
+ p.Stdout = pipes.Stdout |
|
| 418 |
+ p.Stderr = pipes.Stderr |
|
| 419 |
+ r, w, err := os.Pipe() |
|
| 420 |
+ if err != nil {
|
|
| 421 |
+ return err |
|
| 422 |
+ } |
|
| 423 |
+ if pipes.Stdin != nil {
|
|
| 424 |
+ go func() {
|
|
| 425 |
+ io.Copy(w, pipes.Stdin) |
|
| 426 |
+ w.Close() |
|
| 427 |
+ }() |
|
| 428 |
+ p.Stdin = r |
|
| 429 |
+ } |
|
| 430 |
+ term = &execdriver.StdConsole{}
|
|
| 431 |
+ } |
|
| 432 |
+ if err != nil {
|
|
| 433 |
+ return err |
|
| 434 |
+ } |
|
| 435 |
+ processConfig.Terminal = term |
|
| 436 |
+ return nil |
|
| 437 |
+} |
| ... | ... |
@@ -20,9 +20,6 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo |
| 20 | 20 |
return -1, fmt.Errorf("No active container exists with ID %s", c.ID)
|
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
- var term execdriver.Terminal |
|
| 24 |
- var err error |
|
| 25 |
- |
|
| 26 | 23 |
p := &libcontainer.Process{
|
| 27 | 24 |
Args: append([]string{processConfig.Entrypoint}, processConfig.Arguments...),
|
| 28 | 25 |
Env: c.ProcessConfig.Env, |
| ... | ... |
@@ -34,29 +31,11 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo |
| 34 | 34 |
p.Capabilities = execdriver.GetAllCapabilities() |
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
- if processConfig.Tty {
|
|
| 38 |
- config := active.Config() |
|
| 39 |
- rootuid, err := config.HostUID() |
|
| 40 |
- if err != nil {
|
|
| 41 |
- return -1, err |
|
| 42 |
- } |
|
| 43 |
- cons, err := p.NewConsole(rootuid) |
|
| 44 |
- if err != nil {
|
|
| 45 |
- return -1, err |
|
| 46 |
- } |
|
| 47 |
- term, err = NewTtyConsole(cons, pipes, rootuid) |
|
| 48 |
- } else {
|
|
| 49 |
- p.Stdout = pipes.Stdout |
|
| 50 |
- p.Stderr = pipes.Stderr |
|
| 51 |
- p.Stdin = pipes.Stdin |
|
| 52 |
- term = &execdriver.StdConsole{}
|
|
| 53 |
- } |
|
| 54 |
- if err != nil {
|
|
| 37 |
+ config := active.Config() |
|
| 38 |
+ if err := setupPipes(&config, processConfig, p, pipes); err != nil {
|
|
| 55 | 39 |
return -1, err |
| 56 | 40 |
} |
| 57 | 41 |
|
| 58 |
- processConfig.Terminal = term |
|
| 59 |
- |
|
| 60 | 42 |
if err := active.Start(p); err != nil {
|
| 61 | 43 |
return -1, err |
| 62 | 44 |
} |
| ... | ... |
@@ -38,44 +38,6 @@ func (s *DockerSuite) TestExec(c *check.C) {
|
| 38 | 38 |
|
| 39 | 39 |
} |
| 40 | 40 |
|
| 41 |
-func (s *DockerSuite) TestExecInteractiveStdinClose(c *check.C) {
|
|
| 42 |
- out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat")) |
|
| 43 |
- if err != nil {
|
|
| 44 |
- c.Fatal(err) |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- contId := strings.TrimSpace(out) |
|
| 48 |
- |
|
| 49 |
- returnchan := make(chan struct{})
|
|
| 50 |
- |
|
| 51 |
- go func() {
|
|
| 52 |
- var err error |
|
| 53 |
- cmd := exec.Command(dockerBinary, "exec", "-i", contId, "/bin/ls", "/") |
|
| 54 |
- cmd.Stdin = os.Stdin |
|
| 55 |
- if err != nil {
|
|
| 56 |
- c.Fatal(err) |
|
| 57 |
- } |
|
| 58 |
- |
|
| 59 |
- out, err := cmd.CombinedOutput() |
|
| 60 |
- if err != nil {
|
|
| 61 |
- c.Fatal(err, string(out)) |
|
| 62 |
- } |
|
| 63 |
- |
|
| 64 |
- if string(out) == "" {
|
|
| 65 |
- c.Fatalf("Output was empty, likely blocked by standard input")
|
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- returnchan <- struct{}{}
|
|
| 69 |
- }() |
|
| 70 |
- |
|
| 71 |
- select {
|
|
| 72 |
- case <-returnchan: |
|
| 73 |
- case <-time.After(10 * time.Second): |
|
| 74 |
- c.Fatal("timed out running docker exec")
|
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 | 41 |
func (s *DockerSuite) TestExecInteractive(c *check.C) {
|
| 80 | 42 |
|
| 81 | 43 |
runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") |
| 82 | 44 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,47 @@ |
| 0 |
+// +build !windows,!test_no_exec |
|
| 1 |
+ |
|
| 2 |
+package main |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "bytes" |
|
| 6 |
+ "io" |
|
| 7 |
+ "os/exec" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "time" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/go-check/check" |
|
| 12 |
+ "github.com/kr/pty" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// regression test for #12546 |
|
| 16 |
+func (s *DockerSuite) TestExecInteractiveStdinClose(c *check.C) {
|
|
| 17 |
+ out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat")) |
|
| 18 |
+ if err != nil {
|
|
| 19 |
+ c.Fatal(err) |
|
| 20 |
+ } |
|
| 21 |
+ contId := strings.TrimSpace(out) |
|
| 22 |
+ |
|
| 23 |
+ cmd := exec.Command(dockerBinary, "exec", "-i", contId, "echo", "-n", "hello") |
|
| 24 |
+ p, err := pty.Start(cmd) |
|
| 25 |
+ if err != nil {
|
|
| 26 |
+ c.Fatal(err) |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ b := bytes.NewBuffer(nil) |
|
| 30 |
+ go io.Copy(b, p) |
|
| 31 |
+ |
|
| 32 |
+ ch := make(chan error) |
|
| 33 |
+ go func() { ch <- cmd.Wait() }()
|
|
| 34 |
+ |
|
| 35 |
+ select {
|
|
| 36 |
+ case err := <-ch: |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ c.Errorf("cmd finished with error %v", err)
|
|
| 39 |
+ } |
|
| 40 |
+ if output := b.String(); strings.TrimSpace(output) != "hello" {
|
|
| 41 |
+ c.Fatalf("Unexpected output %s", output)
|
|
| 42 |
+ } |
|
| 43 |
+ case <-time.After(1 * time.Second): |
|
| 44 |
+ c.Fatal("timed out running docker exec")
|
|
| 45 |
+ } |
|
| 46 |
+} |