mirror of https://github.com/docker/docs.git
Merge pull request #1277 from dotcloud/add_commands_unit_tests
* Tests: Reimplement old Commands unit tests in order to insure behavior
This commit is contained in:
commit
6ae3305040
|
@ -1409,7 +1409,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
|
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
|
||||||
if config.Tty {
|
if config.Tty {
|
||||||
if err := cli.monitorTtySize(runResult.ID); err != nil {
|
if err := cli.monitorTtySize(runResult.ID); err != nil {
|
||||||
return err
|
utils.Debugf("Error monitoring TTY size: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1580,6 +1580,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||||
|
|
||||||
receiveStdout := utils.Go(func() error {
|
receiveStdout := utils.Go(func() error {
|
||||||
_, err := io.Copy(out, br)
|
_, err := io.Copy(out, br)
|
||||||
|
utils.Debugf("[hijack] End of stdout")
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1594,6 +1595,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||||
sendStdin := utils.Go(func() error {
|
sendStdin := utils.Go(func() error {
|
||||||
if in != nil {
|
if in != nil {
|
||||||
io.Copy(rwc, in)
|
io.Copy(rwc, in)
|
||||||
|
utils.Debugf("[hijack] End of stdin")
|
||||||
}
|
}
|
||||||
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
if tcpc, ok := rwc.(*net.TCPConn); ok {
|
||||||
if err := tcpc.CloseWrite(); err != nil {
|
if err := tcpc.CloseWrite(); err != nil {
|
||||||
|
|
223
commands_test.go
223
commands_test.go
|
@ -73,7 +73,7 @@ func TestRunHostname(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
utils.Debugf("--")
|
|
||||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,6 +90,157 @@ func TestRunHostname(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunExit(t *testing.T) {
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalRuntime)
|
||||||
|
|
||||||
|
c1 := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
cli.CmdRun("-i", unitTestImageID, "/bin/cat")
|
||||||
|
close(c1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
|
||||||
|
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
container := globalRuntime.List()[0]
|
||||||
|
|
||||||
|
// Closing /bin/cat stdin, expect it to exit
|
||||||
|
if err := stdin.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// as the process exited, CmdRun must finish and unblock. Wait for it
|
||||||
|
setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
|
||||||
|
<-c1
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
cli.CmdWait(container.ID)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Make sure that the client has been disconnected
|
||||||
|
setTimeout(t, "The client should have been disconnected once the remote process exited.", 2*time.Second, func() {
|
||||||
|
// Expecting pipe i/o error, just check that read does not block
|
||||||
|
stdin.Read([]byte{})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Cleanup pipes
|
||||||
|
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expected behaviour: the process dies when the client disconnects
|
||||||
|
func TestRunDisconnect(t *testing.T) {
|
||||||
|
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalRuntime)
|
||||||
|
|
||||||
|
c1 := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
// We're simulating a disconnect so the return value doesn't matter. What matters is the
|
||||||
|
// fact that CmdRun returns.
|
||||||
|
cli.CmdRun("-i", unitTestImageID, "/bin/cat")
|
||||||
|
close(c1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
|
||||||
|
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close pipes (simulate disconnect)
|
||||||
|
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// as the pipes are close, we expect the process to die,
|
||||||
|
// therefore CmdRun to unblock. Wait for CmdRun
|
||||||
|
setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
|
||||||
|
<-c1
|
||||||
|
})
|
||||||
|
|
||||||
|
// Client disconnect after run -i should cause stdin to be closed, which should
|
||||||
|
// cause /bin/cat to exit.
|
||||||
|
setTimeout(t, "Waiting for /bin/cat to exit timed out", 2*time.Second, func() {
|
||||||
|
container := globalRuntime.List()[0]
|
||||||
|
container.Wait()
|
||||||
|
if container.State.Running {
|
||||||
|
t.Fatalf("/bin/cat is still running after closing stdin")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expected behaviour: the process dies when the client disconnects
|
||||||
|
func TestRunDisconnectTty(t *testing.T) {
|
||||||
|
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalRuntime)
|
||||||
|
|
||||||
|
c1 := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
// We're simulating a disconnect so the return value doesn't matter. What matters is the
|
||||||
|
// fact that CmdRun returns.
|
||||||
|
if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil {
|
||||||
|
utils.Debugf("Error CmdRun: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
close(c1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
|
||||||
|
for {
|
||||||
|
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||||
|
l := globalRuntime.List()
|
||||||
|
if len(l) == 1 && l[0].State.Running {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Client disconnect after run -i should keep stdin out in TTY mode
|
||||||
|
container := globalRuntime.List()[0]
|
||||||
|
|
||||||
|
setTimeout(t, "Read/Write assertion timed out", 2000*time.Second, func() {
|
||||||
|
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close pipes (simulate disconnect)
|
||||||
|
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In tty mode, we expect the process to stay alive even after client's stdin closes.
|
||||||
|
// Do not wait for run to finish
|
||||||
|
|
||||||
|
// Give some time to monitor to do his thing
|
||||||
|
container.WaitTimeout(500 * time.Millisecond)
|
||||||
|
if !container.State.Running {
|
||||||
|
t.Fatalf("/bin/cat should still be running after closing stdin (tty mode)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestAttachStdin checks attaching to stdin without stdout and stderr.
|
// TestAttachStdin checks attaching to stdin without stdout and stderr.
|
||||||
// 'docker run -i -a stdin' should sends the client's stdin to the command,
|
// 'docker run -i -a stdin' should sends the client's stdin to the command,
|
||||||
// then detach from it and print the container id.
|
// then detach from it and print the container id.
|
||||||
|
@ -157,3 +308,73 @@ func TestRunAttachStdin(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expected behaviour, the process stays alive when the client disconnects
|
||||||
|
func TestAttachDisconnect(t *testing.T) {
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalRuntime)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// Start a process in daemon mode
|
||||||
|
if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil {
|
||||||
|
utils.Debugf("Error CmdRun: %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
|
||||||
|
if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
|
||||||
|
for {
|
||||||
|
l := globalRuntime.List()
|
||||||
|
if len(l) == 1 && l[0].State.Running {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
container := globalRuntime.List()[0]
|
||||||
|
|
||||||
|
// Attach to it
|
||||||
|
c1 := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
// We're simulating a disconnect so the return value doesn't matter. What matters is the
|
||||||
|
// fact that CmdAttach returns.
|
||||||
|
cli.CmdAttach(container.ID)
|
||||||
|
close(c1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||||
|
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Close pipes (client disconnects)
|
||||||
|
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for attach to finish, the client disconnected, therefore, Attach finished his job
|
||||||
|
setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
|
||||||
|
<-c1
|
||||||
|
})
|
||||||
|
|
||||||
|
// We closed stdin, expect /bin/cat to still be running
|
||||||
|
// Wait a little bit to make sure container.monitor() did his thing
|
||||||
|
err := container.WaitTimeout(500 * time.Millisecond)
|
||||||
|
if err == nil || !container.State.Running {
|
||||||
|
t.Fatalf("/bin/cat is not running after closing stdin")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to avoid the timeoout in destroy. Best effort, don't check error
|
||||||
|
cStdin, _ := container.StdinPipe()
|
||||||
|
cStdin.Close()
|
||||||
|
container.Wait()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue