Browse code

reuse same code for setting pipes in run/exec

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>

Daniel, Dao Quang Minh authored on 2015/04/23 08:37:15
Showing 4 changed files
... ...
@@ -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
+}