Explorar o código

Implement regression test for stdin attach

Guillaume J. Charmes %!s(int64=12) %!d(string=hai) anos
pai
achega
5190f7f33a
Modificáronse 5 ficheiros con 96 adicións e 62 borrados
  1. 14 1
      api.go
  2. 5 1
      commands.go
  3. 42 60
      commands_test.go
  4. 23 0
      runtime_test.go
  5. 12 0
      z_final_test.go

+ 14 - 1
api.go

@@ -660,7 +660,20 @@ func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r
 	if err != nil {
 	if err != nil {
 		return err
 		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")
 	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 {
 	if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, in, out); err != nil {

+ 5 - 1
commands.go

@@ -1279,8 +1279,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	}
 	}
 
 
 	if !config.AttachStdout && !config.AttachStderr {
 	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.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 {
@@ -1301,6 +1303,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		if config.AttachStderr {
 		if config.AttachStderr {
 			v.Set("stderr", "1")
 			v.Set("stderr", "1")
 		}
 		}
+
 		if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, cli.out); err != nil {
 		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)
 			utils.Debugf("Error hijack: %s", err)
 			return 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)
 		defer term.RestoreTerminal(cli.terminalFd, oldState)
 	}
 	}
+
 	sendStdin := utils.Go(func() error {
 	sendStdin := utils.Go(func() error {
 		if in != nil {
 		if in != nil {
 			io.Copy(rwc, in)
 			io.Copy(rwc, in)

+ 42 - 60
commands_test.go

@@ -3,8 +3,9 @@ package docker
 import (
 import (
 	"bufio"
 	"bufio"
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
-	_ "io/ioutil"
+	"io/ioutil"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -59,20 +60,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
 }
 }
 
 
 /*TODO
 /*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) {
 func cmdImages(srv *Server, args ...string) (string, error) {
 	stdout, stdoutPipe := io.Pipe()
 	stdout, stdoutPipe := io.Pipe()
 
 
@@ -144,41 +131,39 @@ func TestImages(t *testing.T) {
 	// todo: add checks for -a
 	// todo: add checks for -a
 }
 }
 
 
+*/
 // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
 // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
 func TestRunHostname(t *testing.T) {
 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()
 	stdout, stdoutPipe := io.Pipe()
 
 
+	cli := NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr)
+	defer cleanup(globalRuntime)
+
 	c := make(chan struct{})
 	c := make(chan struct{})
 	go func() {
 	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)
 			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
 		<-c
-		cmdWait(srv, srv.runtime.List()[0])
 	})
 	})
 
 
 }
 }
 
 
+/*
 func TestRunExit(t *testing.T) {
 func TestRunExit(t *testing.T) {
 	runtime, err := newTestRuntime()
 	runtime, err := newTestRuntime()
 	if err != nil {
 	if err != nil {
@@ -335,33 +320,26 @@ func TestRunDisconnectTty(t *testing.T) {
 	}
 	}
 }
 }
 */
 */
-/*
+
 // 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.
 func TestRunAttachStdin(t *testing.T) {
 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()
 	stdin, stdinPipe := io.Pipe()
 	stdout, stdoutPipe := io.Pipe()
 	stdout, stdoutPipe := io.Pipe()
 
 
+	cli := NewDockerCli(stdin, stdoutPipe, nil, testDaemonProto, testDaemonAddr)
+	defer cleanup(globalRuntime)
+
 	ch := make(chan struct{})
 	ch := make(chan struct{})
 	go func() {
 	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
 	// 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 {
 		if _, err := stdinPipe.Write([]byte("hi there\n")); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -370,23 +348,27 @@ func TestRunAttachStdin(t *testing.T) {
 		}
 		}
 	})
 	})
 
 
-	container := runtime.List()[0]
+	container := globalRuntime.List()[0]
 
 
 	// Check output
 	// 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
 	// 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
 		<-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()
 		container.Wait()
 	})
 	})
 
 
@@ -404,7 +386,7 @@ func TestRunAttachStdin(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
-*/
+
 /*
 /*
 // Expected behaviour, the process stays alive when the client disconnects
 // Expected behaviour, the process stays alive when the client disconnects
 func TestAttachDisconnect(t *testing.T) {
 func TestAttachDisconnect(t *testing.T) {

+ 23 - 0
runtime_test.go

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

+ 12 - 0
z_final_test.go

@@ -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())
+}