Forbid `docker run -t` with a redirected stdin (such as `echo test |
docker run -ti busybox cat`). Forbid `docker exec -t` with a redirected
stdin. Forbid `docker attach` with a redirect stdin toward a tty enabled
container.
Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
... | ... |
@@ -3,6 +3,7 @@ package client |
3 | 3 |
import ( |
4 | 4 |
"crypto/tls" |
5 | 5 |
"encoding/json" |
6 |
+ "errors" |
|
6 | 7 |
"fmt" |
7 | 8 |
"io" |
8 | 9 |
"net" |
... | ... |
@@ -104,6 +105,16 @@ func (cli *DockerCli) LoadConfigFile() (err error) { |
104 | 104 |
return err |
105 | 105 |
} |
106 | 106 |
|
107 |
+func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { |
|
108 |
+ // In order to attach to a container tty, input stream for the client must |
|
109 |
+ // be a tty itself: redirecting or piping the client standard input is |
|
110 |
+ // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. |
|
111 |
+ if ttyMode && attachStdin && !cli.isTerminalIn { |
|
112 |
+ return errors.New("cannot enable tty mode on non tty input") |
|
113 |
+ } |
|
114 |
+ return nil |
|
115 |
+} |
|
116 |
+ |
|
107 | 117 |
func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey, proto, addr string, tlsConfig *tls.Config) *DockerCli { |
108 | 118 |
var ( |
109 | 119 |
inFd uintptr |
... | ... |
@@ -1974,6 +1974,10 @@ func (cli *DockerCli) CmdAttach(args ...string) error { |
1974 | 1974 |
tty = config.GetBool("Tty") |
1975 | 1975 |
) |
1976 | 1976 |
|
1977 |
+ if err := cli.CheckTtyInput(!*noStdin, tty); err != nil { |
|
1978 |
+ return err |
|
1979 |
+ } |
|
1980 |
+ |
|
1977 | 1981 |
if tty && cli.isTerminalOut { |
1978 | 1982 |
if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { |
1979 | 1983 |
log.Debugf("Error monitoring TTY size: %s", err) |
... | ... |
@@ -2288,7 +2292,11 @@ func (cli *DockerCli) CmdRun(args ...string) error { |
2288 | 2288 |
return nil |
2289 | 2289 |
} |
2290 | 2290 |
|
2291 |
- if *flDetach { |
|
2291 |
+ if !*flDetach { |
|
2292 |
+ if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil { |
|
2293 |
+ return err |
|
2294 |
+ } |
|
2295 |
+ } else { |
|
2292 | 2296 |
if fl := cmd.Lookup("attach"); fl != nil { |
2293 | 2297 |
flAttach = fl.Value.(*opts.ListOpts) |
2294 | 2298 |
if flAttach.Len() != 0 { |
... | ... |
@@ -2600,7 +2608,11 @@ func (cli *DockerCli) CmdExec(args ...string) error { |
2600 | 2600 |
return nil |
2601 | 2601 |
} |
2602 | 2602 |
|
2603 |
- if execConfig.Detach { |
|
2603 |
+ if !execConfig.Detach { |
|
2604 |
+ if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { |
|
2605 |
+ return err |
|
2606 |
+ } |
|
2607 |
+ } else { |
|
2604 | 2608 |
if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil { |
2605 | 2609 |
return err |
2606 | 2610 |
} |
... | ... |
@@ -20,6 +20,9 @@ container, or `CTRL-\` to get a stacktrace of the Docker client when it quits. |
20 | 20 |
When you detach from a container the exit code will be returned to |
21 | 21 |
the client. |
22 | 22 |
|
23 |
+It is forbidden to redirect the standard input of a docker attach command while |
|
24 |
+attaching to a tty-enabled container (i.e.: launched with -t`). |
|
25 |
+ |
|
23 | 26 |
# OPTIONS |
24 | 27 |
**--no-stdin**=*true*|*false* |
25 | 28 |
Do not attach STDIN. The default is *false*. |
... | ... |
@@ -31,5 +31,8 @@ container is unpaused, and then run |
31 | 31 |
**-t**, **--tty**=*true*|*false* |
32 | 32 |
Allocate a pseudo-TTY. The default is *false*. |
33 | 33 |
|
34 |
+The **-t** option is incompatible with a redirection of the docker client |
|
35 |
+standard input. |
|
36 |
+ |
|
34 | 37 |
# HISTORY |
35 | 38 |
November 2014, updated by Sven Dowideit <SvenDowideit@home.org.au> |
... | ... |
@@ -267,6 +267,9 @@ outside of a container on the host. |
267 | 267 |
input of any container. This can be used, for example, to run a throwaway |
268 | 268 |
interactive shell. The default is value is false. |
269 | 269 |
|
270 |
+The **-t** option is incompatible with a redirection of the docker client |
|
271 |
+standard input. |
|
272 |
+ |
|
270 | 273 |
**-u**, **--user**="" |
271 | 274 |
Username or UID |
272 | 275 |
|
... | ... |
@@ -94,9 +94,10 @@ specify to which of the three standard streams (`STDIN`, `STDOUT`, |
94 | 94 |
|
95 | 95 |
$ sudo docker run -a stdin -a stdout -i -t ubuntu /bin/bash |
96 | 96 |
|
97 |
-For interactive processes (like a shell) you will typically want a tty |
|
98 |
-as well as persistent standard input (`STDIN`), so you'll use `-i -t` |
|
99 |
-together in most interactive cases. |
|
97 |
+For interactive processes (like a shell), you must use `-i -t` together in |
|
98 |
+order to allocate a tty for the container process. Specifying `-t` is however |
|
99 |
+forbidden when the client standard output is redirected or pipe, such as in: |
|
100 |
+`echo test | docker run -i busybox cat`. |
|
100 | 101 |
|
101 | 102 |
## Container identification |
102 | 103 |
|
... | ... |
@@ -87,3 +87,50 @@ func TestAttachMultipleAndRestart(t *testing.T) { |
87 | 87 |
|
88 | 88 |
logDone("attach - multiple attach") |
89 | 89 |
} |
90 |
+ |
|
91 |
+func TestAttachTtyWithoutStdin(t *testing.T) { |
|
92 |
+ defer deleteAllContainers() |
|
93 |
+ |
|
94 |
+ cmd := exec.Command(dockerBinary, "run", "-d", "-ti", "busybox") |
|
95 |
+ out, _, err := runCommandWithOutput(cmd) |
|
96 |
+ if err != nil { |
|
97 |
+ t.Fatalf("failed to start container: %v (%v)", out, err) |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ id := strings.TrimSpace(out) |
|
101 |
+ if err := waitRun(id); err != nil { |
|
102 |
+ t.Fatal(err) |
|
103 |
+ } |
|
104 |
+ |
|
105 |
+ defer func() { |
|
106 |
+ cmd := exec.Command(dockerBinary, "kill", id) |
|
107 |
+ if out, _, err := runCommandWithOutput(cmd); err != nil { |
|
108 |
+ t.Fatalf("failed to kill container: %v (%v)", out, err) |
|
109 |
+ } |
|
110 |
+ }() |
|
111 |
+ |
|
112 |
+ done := make(chan struct{}) |
|
113 |
+ go func() { |
|
114 |
+ defer close(done) |
|
115 |
+ |
|
116 |
+ cmd := exec.Command(dockerBinary, "attach", id) |
|
117 |
+ if _, err := cmd.StdinPipe(); err != nil { |
|
118 |
+ t.Fatal(err) |
|
119 |
+ } |
|
120 |
+ |
|
121 |
+ expected := "cannot enable tty mode" |
|
122 |
+ if out, _, err := runCommandWithOutput(cmd); err == nil { |
|
123 |
+ t.Fatal("attach should have failed") |
|
124 |
+ } else if !strings.Contains(out, expected) { |
|
125 |
+ t.Fatal("attach failed with error %q: expected %q", out, expected) |
|
126 |
+ } |
|
127 |
+ }() |
|
128 |
+ |
|
129 |
+ select { |
|
130 |
+ case <-done: |
|
131 |
+ case <-time.After(attachWait): |
|
132 |
+ t.Fatal("attach is running but should have failed") |
|
133 |
+ } |
|
134 |
+ |
|
135 |
+ logDone("attach - forbid piped stdin to tty enabled container") |
|
136 |
+} |
... | ... |
@@ -273,7 +273,7 @@ func TestExecTtyCloseStdin(t *testing.T) { |
273 | 273 |
t.Fatal(out, err) |
274 | 274 |
} |
275 | 275 |
|
276 |
- cmd = exec.Command(dockerBinary, "exec", "-it", "exec_tty_stdin", "cat") |
|
276 |
+ cmd = exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat") |
|
277 | 277 |
stdinRw, err := cmd.StdinPipe() |
278 | 278 |
if err != nil { |
279 | 279 |
t.Fatal(err) |
... | ... |
@@ -304,3 +304,50 @@ func TestExecTtyCloseStdin(t *testing.T) { |
304 | 304 |
|
305 | 305 |
logDone("exec - stdin is closed properly with tty enabled") |
306 | 306 |
} |
307 |
+ |
|
308 |
+func TestExecTtyWithoutStdin(t *testing.T) { |
|
309 |
+ defer deleteAllContainers() |
|
310 |
+ |
|
311 |
+ cmd := exec.Command(dockerBinary, "run", "-d", "-ti", "busybox") |
|
312 |
+ out, _, err := runCommandWithOutput(cmd) |
|
313 |
+ if err != nil { |
|
314 |
+ t.Fatalf("failed to start container: %v (%v)", out, err) |
|
315 |
+ } |
|
316 |
+ |
|
317 |
+ id := strings.TrimSpace(out) |
|
318 |
+ if err := waitRun(id); err != nil { |
|
319 |
+ t.Fatal(err) |
|
320 |
+ } |
|
321 |
+ |
|
322 |
+ defer func() { |
|
323 |
+ cmd := exec.Command(dockerBinary, "kill", id) |
|
324 |
+ if out, _, err := runCommandWithOutput(cmd); err != nil { |
|
325 |
+ t.Fatalf("failed to kill container: %v (%v)", out, err) |
|
326 |
+ } |
|
327 |
+ }() |
|
328 |
+ |
|
329 |
+ done := make(chan struct{}) |
|
330 |
+ go func() { |
|
331 |
+ defer close(done) |
|
332 |
+ |
|
333 |
+ cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true") |
|
334 |
+ if _, err := cmd.StdinPipe(); err != nil { |
|
335 |
+ t.Fatal(err) |
|
336 |
+ } |
|
337 |
+ |
|
338 |
+ expected := "cannot enable tty mode" |
|
339 |
+ if out, _, err := runCommandWithOutput(cmd); err == nil { |
|
340 |
+ t.Fatal("exec should have failed") |
|
341 |
+ } else if !strings.Contains(out, expected) { |
|
342 |
+ t.Fatal("exec failed with error %q: expected %q", out, expected) |
|
343 |
+ } |
|
344 |
+ }() |
|
345 |
+ |
|
346 |
+ select { |
|
347 |
+ case <-done: |
|
348 |
+ case <-time.After(3 * time.Second): |
|
349 |
+ t.Fatal("exec is running but should have failed") |
|
350 |
+ } |
|
351 |
+ |
|
352 |
+ logDone("exec - forbid piped stdin to tty enabled container") |
|
353 |
+} |
... | ... |
@@ -2742,3 +2742,32 @@ func TestRunPortFromDockerRangeInUse(t *testing.T) { |
2742 | 2742 |
|
2743 | 2743 |
logDone("run - find another port if port from autorange already bound") |
2744 | 2744 |
} |
2745 |
+ |
|
2746 |
+func TestRunTtyWithPipe(t *testing.T) { |
|
2747 |
+ defer deleteAllContainers() |
|
2748 |
+ |
|
2749 |
+ done := make(chan struct{}) |
|
2750 |
+ go func() { |
|
2751 |
+ defer close(done) |
|
2752 |
+ |
|
2753 |
+ cmd := exec.Command(dockerBinary, "run", "-ti", "busybox", "true") |
|
2754 |
+ if _, err := cmd.StdinPipe(); err != nil { |
|
2755 |
+ t.Fatal(err) |
|
2756 |
+ } |
|
2757 |
+ |
|
2758 |
+ expected := "cannot enable tty mode" |
|
2759 |
+ if out, _, err := runCommandWithOutput(cmd); err == nil { |
|
2760 |
+ t.Fatal("run should have failed") |
|
2761 |
+ } else if !strings.Contains(out, expected) { |
|
2762 |
+ t.Fatal("run failed with error %q: expected %q", out, expected) |
|
2763 |
+ } |
|
2764 |
+ }() |
|
2765 |
+ |
|
2766 |
+ select { |
|
2767 |
+ case <-done: |
|
2768 |
+ case <-time.After(3 * time.Second): |
|
2769 |
+ t.Fatal("container is running but should have failed") |
|
2770 |
+ } |
|
2771 |
+ |
|
2772 |
+ logDone("run - forbid piped stdin with tty") |
|
2773 |
+} |
... | ... |
@@ -15,6 +15,7 @@ import ( |
15 | 15 |
"github.com/docker/docker/pkg/term" |
16 | 16 |
"github.com/docker/docker/utils" |
17 | 17 |
"github.com/docker/libtrust" |
18 |
+ "github.com/kr/pty" |
|
18 | 19 |
) |
19 | 20 |
|
20 | 21 |
func closeWrap(args ...io.Closer) error { |
... | ... |
@@ -162,72 +163,20 @@ func TestRunDisconnect(t *testing.T) { |
162 | 162 |
}) |
163 | 163 |
} |
164 | 164 |
|
165 |
-// Expected behaviour: the process stay alive when the client disconnects |
|
166 |
-// but the client detaches. |
|
167 |
-func TestRunDisconnectTty(t *testing.T) { |
|
168 |
- |
|
169 |
- stdin, stdinPipe := io.Pipe() |
|
165 |
+// TestRunDetach checks attaching and detaching with the escape sequence. |
|
166 |
+func TestRunDetach(t *testing.T) { |
|
170 | 167 |
stdout, stdoutPipe := io.Pipe() |
171 |
- key, err := libtrust.GenerateECP256PrivateKey() |
|
168 |
+ cpty, tty, err := pty.Open() |
|
172 | 169 |
if err != nil { |
173 | 170 |
t.Fatal(err) |
174 | 171 |
} |
175 | 172 |
|
176 |
- cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
177 |
- defer cleanup(globalEngine, t) |
|
178 |
- |
|
179 |
- c1 := make(chan struct{}) |
|
180 |
- go func() { |
|
181 |
- defer close(c1) |
|
182 |
- // We're simulating a disconnect so the return value doesn't matter. What matters is the |
|
183 |
- // fact that CmdRun returns. |
|
184 |
- if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil { |
|
185 |
- log.Debugf("Error CmdRun: %s", err) |
|
186 |
- } |
|
187 |
- }() |
|
188 |
- |
|
189 |
- container := waitContainerStart(t, 10*time.Second) |
|
190 |
- |
|
191 |
- state := setRaw(t, container) |
|
192 |
- defer unsetRaw(t, container, state) |
|
193 |
- |
|
194 |
- // Client disconnect after run -i should keep stdin out in TTY mode |
|
195 |
- setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() { |
|
196 |
- if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil { |
|
197 |
- t.Fatal(err) |
|
198 |
- } |
|
199 |
- }) |
|
200 |
- |
|
201 |
- // Close pipes (simulate disconnect) |
|
202 |
- if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { |
|
203 |
- t.Fatal(err) |
|
204 |
- } |
|
205 |
- |
|
206 |
- // wait for CmdRun to return |
|
207 |
- setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() { |
|
208 |
- <-c1 |
|
209 |
- }) |
|
210 |
- |
|
211 |
- // In tty mode, we expect the process to stay alive even after client's stdin closes. |
|
212 |
- |
|
213 |
- // Give some time to monitor to do his thing |
|
214 |
- container.WaitStop(500 * time.Millisecond) |
|
215 |
- if !container.IsRunning() { |
|
216 |
- t.Fatalf("/bin/cat should still be running after closing stdin (tty mode)") |
|
217 |
- } |
|
218 |
-} |
|
219 |
- |
|
220 |
-// TestRunDetach checks attaching and detaching with the escape sequence. |
|
221 |
-func TestRunDetach(t *testing.T) { |
|
222 |
- |
|
223 |
- stdin, stdinPipe := io.Pipe() |
|
224 |
- stdout, stdoutPipe := io.Pipe() |
|
225 | 173 |
key, err := libtrust.GenerateECP256PrivateKey() |
226 | 174 |
if err != nil { |
227 | 175 |
t.Fatal(err) |
228 | 176 |
} |
229 | 177 |
|
230 |
- cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
178 |
+ cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
231 | 179 |
defer cleanup(globalEngine, t) |
232 | 180 |
|
233 | 181 |
ch := make(chan struct{}) |
... | ... |
@@ -242,22 +191,22 @@ func TestRunDetach(t *testing.T) { |
242 | 242 |
defer unsetRaw(t, container, state) |
243 | 243 |
|
244 | 244 |
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() { |
245 |
- if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil { |
|
245 |
+ if err := assertPipe("hello\n", "hello", stdout, cpty, 150); err != nil { |
|
246 | 246 |
t.Fatal(err) |
247 | 247 |
} |
248 | 248 |
}) |
249 | 249 |
|
250 | 250 |
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { |
251 |
- stdinPipe.Write([]byte{16}) |
|
251 |
+ cpty.Write([]byte{16}) |
|
252 | 252 |
time.Sleep(100 * time.Millisecond) |
253 |
- stdinPipe.Write([]byte{17}) |
|
253 |
+ cpty.Write([]byte{17}) |
|
254 | 254 |
}) |
255 | 255 |
|
256 | 256 |
// wait for CmdRun to return |
257 | 257 |
setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() { |
258 | 258 |
<-ch |
259 | 259 |
}) |
260 |
- closeWrap(stdin, stdinPipe, stdout, stdoutPipe) |
|
260 |
+ closeWrap(cpty, stdout, stdoutPipe) |
|
261 | 261 |
|
262 | 262 |
time.Sleep(500 * time.Millisecond) |
263 | 263 |
if !container.IsRunning() { |
... | ... |
@@ -271,14 +220,18 @@ func TestRunDetach(t *testing.T) { |
271 | 271 |
|
272 | 272 |
// TestAttachDetach checks that attach in tty mode can be detached using the long container ID |
273 | 273 |
func TestAttachDetach(t *testing.T) { |
274 |
- stdin, stdinPipe := io.Pipe() |
|
275 | 274 |
stdout, stdoutPipe := io.Pipe() |
275 |
+ cpty, tty, err := pty.Open() |
|
276 |
+ if err != nil { |
|
277 |
+ t.Fatal(err) |
|
278 |
+ } |
|
279 |
+ |
|
276 | 280 |
key, err := libtrust.GenerateECP256PrivateKey() |
277 | 281 |
if err != nil { |
278 | 282 |
t.Fatal(err) |
279 | 283 |
} |
280 | 284 |
|
281 |
- cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
285 |
+ cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
282 | 286 |
defer cleanup(globalEngine, t) |
283 | 287 |
|
284 | 288 |
ch := make(chan struct{}) |
... | ... |
@@ -309,9 +262,13 @@ func TestAttachDetach(t *testing.T) { |
309 | 309 |
state := setRaw(t, container) |
310 | 310 |
defer unsetRaw(t, container, state) |
311 | 311 |
|
312 |
- stdin, stdinPipe = io.Pipe() |
|
313 | 312 |
stdout, stdoutPipe = io.Pipe() |
314 |
- cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
313 |
+ cpty, tty, err = pty.Open() |
|
314 |
+ if err != nil { |
|
315 |
+ t.Fatal(err) |
|
316 |
+ } |
|
317 |
+ |
|
318 |
+ cli = client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
315 | 319 |
|
316 | 320 |
ch = make(chan struct{}) |
317 | 321 |
go func() { |
... | ... |
@@ -324,7 +281,7 @@ func TestAttachDetach(t *testing.T) { |
324 | 324 |
}() |
325 | 325 |
|
326 | 326 |
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() { |
327 |
- if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil { |
|
327 |
+ if err := assertPipe("hello\n", "hello", stdout, cpty, 150); err != nil { |
|
328 | 328 |
if err != io.ErrClosedPipe { |
329 | 329 |
t.Fatal(err) |
330 | 330 |
} |
... | ... |
@@ -332,9 +289,9 @@ func TestAttachDetach(t *testing.T) { |
332 | 332 |
}) |
333 | 333 |
|
334 | 334 |
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { |
335 |
- stdinPipe.Write([]byte{16}) |
|
335 |
+ cpty.Write([]byte{16}) |
|
336 | 336 |
time.Sleep(100 * time.Millisecond) |
337 |
- stdinPipe.Write([]byte{17}) |
|
337 |
+ cpty.Write([]byte{17}) |
|
338 | 338 |
}) |
339 | 339 |
|
340 | 340 |
// wait for CmdRun to return |
... | ... |
@@ -342,7 +299,7 @@ func TestAttachDetach(t *testing.T) { |
342 | 342 |
<-ch |
343 | 343 |
}) |
344 | 344 |
|
345 |
- closeWrap(stdin, stdinPipe, stdout, stdoutPipe) |
|
345 |
+ closeWrap(cpty, stdout, stdoutPipe) |
|
346 | 346 |
|
347 | 347 |
time.Sleep(500 * time.Millisecond) |
348 | 348 |
if !container.IsRunning() { |
... | ... |
@@ -356,14 +313,18 @@ func TestAttachDetach(t *testing.T) { |
356 | 356 |
|
357 | 357 |
// TestAttachDetachTruncatedID checks that attach in tty mode can be detached |
358 | 358 |
func TestAttachDetachTruncatedID(t *testing.T) { |
359 |
- stdin, stdinPipe := io.Pipe() |
|
360 | 359 |
stdout, stdoutPipe := io.Pipe() |
360 |
+ cpty, tty, err := pty.Open() |
|
361 |
+ if err != nil { |
|
362 |
+ t.Fatal(err) |
|
363 |
+ } |
|
364 |
+ |
|
361 | 365 |
key, err := libtrust.GenerateECP256PrivateKey() |
362 | 366 |
if err != nil { |
363 | 367 |
t.Fatal(err) |
364 | 368 |
} |
365 | 369 |
|
366 |
- cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
370 |
+ cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
367 | 371 |
defer cleanup(globalEngine, t) |
368 | 372 |
|
369 | 373 |
// Discard the CmdRun output |
... | ... |
@@ -379,9 +340,13 @@ func TestAttachDetachTruncatedID(t *testing.T) { |
379 | 379 |
state := setRaw(t, container) |
380 | 380 |
defer unsetRaw(t, container, state) |
381 | 381 |
|
382 |
- stdin, stdinPipe = io.Pipe() |
|
383 | 382 |
stdout, stdoutPipe = io.Pipe() |
384 |
- cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
383 |
+ cpty, tty, err = pty.Open() |
|
384 |
+ if err != nil { |
|
385 |
+ t.Fatal(err) |
|
386 |
+ } |
|
387 |
+ |
|
388 |
+ cli = client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
385 | 389 |
|
386 | 390 |
ch := make(chan struct{}) |
387 | 391 |
go func() { |
... | ... |
@@ -394,7 +359,7 @@ func TestAttachDetachTruncatedID(t *testing.T) { |
394 | 394 |
}() |
395 | 395 |
|
396 | 396 |
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() { |
397 |
- if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil { |
|
397 |
+ if err := assertPipe("hello\n", "hello", stdout, cpty, 150); err != nil { |
|
398 | 398 |
if err != io.ErrClosedPipe { |
399 | 399 |
t.Fatal(err) |
400 | 400 |
} |
... | ... |
@@ -402,16 +367,16 @@ func TestAttachDetachTruncatedID(t *testing.T) { |
402 | 402 |
}) |
403 | 403 |
|
404 | 404 |
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() { |
405 |
- stdinPipe.Write([]byte{16}) |
|
405 |
+ cpty.Write([]byte{16}) |
|
406 | 406 |
time.Sleep(100 * time.Millisecond) |
407 |
- stdinPipe.Write([]byte{17}) |
|
407 |
+ cpty.Write([]byte{17}) |
|
408 | 408 |
}) |
409 | 409 |
|
410 | 410 |
// wait for CmdRun to return |
411 | 411 |
setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() { |
412 | 412 |
<-ch |
413 | 413 |
}) |
414 |
- closeWrap(stdin, stdinPipe, stdout, stdoutPipe) |
|
414 |
+ closeWrap(cpty, stdout, stdoutPipe) |
|
415 | 415 |
|
416 | 416 |
time.Sleep(500 * time.Millisecond) |
417 | 417 |
if !container.IsRunning() { |
... | ... |
@@ -425,14 +390,18 @@ func TestAttachDetachTruncatedID(t *testing.T) { |
425 | 425 |
|
426 | 426 |
// Expected behaviour, the process stays alive when the client disconnects |
427 | 427 |
func TestAttachDisconnect(t *testing.T) { |
428 |
- stdin, stdinPipe := io.Pipe() |
|
429 | 428 |
stdout, stdoutPipe := io.Pipe() |
429 |
+ cpty, tty, err := pty.Open() |
|
430 |
+ if err != nil { |
|
431 |
+ t.Fatal(err) |
|
432 |
+ } |
|
433 |
+ |
|
430 | 434 |
key, err := libtrust.GenerateECP256PrivateKey() |
431 | 435 |
if err != nil { |
432 | 436 |
t.Fatal(err) |
433 | 437 |
} |
434 | 438 |
|
435 |
- cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
439 |
+ cli := client.NewDockerCli(tty, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) |
|
436 | 440 |
defer cleanup(globalEngine, t) |
437 | 441 |
|
438 | 442 |
go func() { |
... | ... |
@@ -470,12 +439,12 @@ func TestAttachDisconnect(t *testing.T) { |
470 | 470 |
}() |
471 | 471 |
|
472 | 472 |
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() { |
473 |
- if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil { |
|
473 |
+ if err := assertPipe("hello\n", "hello", stdout, cpty, 150); err != nil { |
|
474 | 474 |
t.Fatal(err) |
475 | 475 |
} |
476 | 476 |
}) |
477 | 477 |
// Close pipes (client disconnects) |
478 |
- if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { |
|
478 |
+ if err := closeWrap(cpty, stdout, stdoutPipe); err != nil { |
|
479 | 479 |
t.Fatal(err) |
480 | 480 |
} |
481 | 481 |
|