Implement regression test for stdin attach

This commit is contained in:
Guillaume J. Charmes 2013-06-24 18:22:02 -07:00
parent 873a5aa8e7
commit 5190f7f33a
5 changed files with 96 additions and 62 deletions

15
api.go
View File

@ -660,7 +660,20 @@ func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r
if err != nil {
return err
}
defer in.Close()
defer func() {
if tcpc, ok := in.(*net.TCPConn); ok {
tcpc.CloseWrite()
} else {
in.Close()
}
}()
defer func() {
if tcpc, ok := out.(*net.TCPConn); ok {
tcpc.CloseWrite()
} else if closer, ok := out.(io.Closer); ok {
closer.Close()
}
}()
fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, in, out); err != nil {

View File

@ -1279,8 +1279,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
if !config.AttachStdout && !config.AttachStderr {
fmt.Fprintf(cli.out, "%s\n", runResult.ID)
// Make this asynchrone in order to let the client write to stdin before having to read the ID
go fmt.Fprintf(cli.out, "%s\n", runResult.ID)
}
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
if config.Tty {
if err := cli.monitorTtySize(runResult.ID); err != nil {
@ -1301,6 +1303,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if config.AttachStderr {
v.Set("stderr", "1")
}
if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, cli.out); err != nil {
utils.Debugf("Error hijack: %s", err)
return err
@ -1466,6 +1469,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
}
defer term.RestoreTerminal(cli.terminalFd, oldState)
}
sendStdin := utils.Go(func() error {
if in != nil {
io.Copy(rwc, in)

View File

@ -3,8 +3,9 @@ package docker
import (
"bufio"
"fmt"
"github.com/dotcloud/docker/utils"
"io"
_ "io/ioutil"
"io/ioutil"
"strings"
"testing"
"time"
@ -59,20 +60,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
}
/*TODO
func cmdWait(srv *Server, container *Container) error {
stdout, stdoutPipe := io.Pipe()
go func() {
srv.CmdWait(nil, stdoutPipe, container.Id)
}()
if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
return err
}
// Cleanup pipes
return closeWrap(stdout, stdoutPipe)
}
func cmdImages(srv *Server, args ...string) (string, error) {
stdout, stdoutPipe := io.Pipe()
@ -144,41 +131,39 @@ func TestImages(t *testing.T) {
// todo: add checks for -a
}
*/
// TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
func TestRunHostname(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
stdin, _ := io.Pipe()
stdout, stdoutPipe := io.Pipe()
cli := NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr)
defer cleanup(globalRuntime)
c := make(chan struct{})
go func() {
if err := srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-h", "foobar", GetTestImage(runtime).Id, "hostname"); err != nil {
defer close(c)
if err := cli.CmdRun("-h", "foobar", unitTestImageId, "hostname"); err != nil {
t.Fatal(err)
}
close(c)
}()
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
if err != nil {
t.Fatal(err)
}
if cmdOutput != "foobar\n" {
t.Fatalf("'hostname' should display '%s', not '%s'", "foobar\n", cmdOutput)
}
utils.Debugf("--")
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
if err != nil {
t.Fatal(err)
}
if cmdOutput != "foobar\n" {
t.Fatalf("'hostname' should display '%s', not '%s'", "foobar\n", cmdOutput)
}
})
setTimeout(t, "CmdRun timed out", 2*time.Second, func() {
setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
<-c
cmdWait(srv, srv.runtime.List()[0])
})
}
/*
func TestRunExit(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
@ -335,33 +320,26 @@ func TestRunDisconnectTty(t *testing.T) {
}
}
*/
/*
// TestAttachStdin checks attaching to stdin without stdout and stderr.
// 'docker run -i -a stdin' should sends the client's stdin to the command,
// then detach from it and print the container id.
func TestRunAttachStdin(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
// enableCors: false,
// lock: &sync.Mutex{},
// pullingPool: make(map[string]struct{}),
// pushingPool: make(map[string]struct{}),
stdin, stdinPipe := io.Pipe()
stdout, stdoutPipe := io.Pipe()
cli := NewDockerCli(stdin, stdoutPipe, nil, testDaemonProto, testDaemonAddr)
defer cleanup(globalRuntime)
ch := make(chan struct{})
go func() {
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", "-a", "stdin", GetTestImage(runtime).Id, "sh", "-c", "echo hello; cat")
close(ch)
defer close(ch)
cli.CmdRun("-i", "-a", "stdin", unitTestImageId, "sh", "-c", "echo hello && cat")
}()
// Send input to the command, close stdin
setTimeout(t, "Write timed out", 2*time.Second, func() {
setTimeout(t, "Write timed out", 10*time.Second, func() {
if _, err := stdinPipe.Write([]byte("hi there\n")); err != nil {
t.Fatal(err)
}
@ -370,23 +348,27 @@ func TestRunAttachStdin(t *testing.T) {
}
})
container := runtime.List()[0]
container := globalRuntime.List()[0]
// Check output
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
if err != nil {
t.Fatal(err)
}
if cmdOutput != container.ShortId()+"\n" {
t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ShortId()+"\n", cmdOutput)
}
setTimeout(t, "Reading command output time out", 10*time.Second, func() {
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
if err != nil {
t.Fatal(err)
}
if cmdOutput != container.ShortID()+"\n" {
t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ShortID()+"\n", cmdOutput)
}
})
// wait for CmdRun to return
setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
// Unblock hijack end
stdout.Read([]byte{})
<-ch
})
setTimeout(t, "Waiting for command to exit timed out", 2*time.Second, func() {
setTimeout(t, "Waiting for command to exit timed out", 5*time.Second, func() {
container.Wait()
})
@ -404,7 +386,7 @@ func TestRunAttachStdin(t *testing.T) {
}
}
}
*/
/*
// Expected behaviour, the process stays alive when the client disconnects
func TestAttachDisconnect(t *testing.T) {

View File

@ -24,6 +24,8 @@ const (
testDaemonProto = "tcp"
)
var globalRuntime *Runtime
func nuke(runtime *Runtime) error {
var wg sync.WaitGroup
for _, container := range runtime.List() {
@ -37,6 +39,23 @@ func nuke(runtime *Runtime) error {
return os.RemoveAll(runtime.root)
}
func cleanup(runtime *Runtime) error {
for _, container := range runtime.List() {
container.Kill()
runtime.Destroy(container)
}
images, err := runtime.graph.All()
if err != nil {
return err
}
for _, image := range images {
if image.ID != unitTestImageId {
runtime.graph.Delete(image.ID)
}
}
return nil
}
func layerArchive(tarfile string) (io.Reader, error) {
// FIXME: need to close f somewhere
f, err := os.Open(tarfile)
@ -64,6 +83,7 @@ func init() {
if err != nil {
panic(err)
}
globalRuntime = runtime
// Create the "Server"
srv := &Server{
@ -84,6 +104,9 @@ func init() {
panic(err)
}
}()
// Give some time to ListenAndServer to actually start
time.Sleep(time.Second)
}
// FIXME: test that ImagePull(json=true) send correct json output

12
z_final_test.go Normal file
View File

@ -0,0 +1,12 @@
package docker
import (
"github.com/dotcloud/docker/utils"
"runtime"
"testing"
)
func TestFinal(t *testing.T) {
cleanup(globalRuntime)
t.Logf("Fds: %d, Goroutines: %d", utils.GetTotalUsedFds(), runtime.NumGoroutine())
}