浏览代码

Merge pull request #1005 from dotcloud/1004-stdin_piping-fix

- Runtime: Fix issue when attaching stdin alone
Guillaume J. Charmes 12 年之前
父节点
当前提交
cd0f22ef72
共有 6 个文件被更改,包括 253 次插入160 次删除
  1. 14 1
      api.go
  2. 144 94
      commands.go
  3. 42 54
      commands_test.go
  4. 37 3
      runtime_test.go
  5. 4 8
      term/term.go
  6. 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 {

+ 144 - 94
commands.go

@@ -40,7 +40,7 @@ func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
 }
 }
 
 
 func ParseCommands(proto, addr string, args ...string) error {
 func ParseCommands(proto, addr string, args ...string) error {
-	cli := NewDockerCli(proto, addr)
+	cli := NewDockerCli(os.Stdin, os.Stdout, os.Stderr, proto, addr)
 
 
 	if len(args) > 0 {
 	if len(args) > 0 {
 		method, exists := cli.getMethod(args[0])
 		method, exists := cli.getMethod(args[0])
@@ -64,7 +64,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 	if len(args) > 0 {
 	if len(args) > 0 {
 		method, exists := cli.getMethod(args[0])
 		method, exists := cli.getMethod(args[0])
 		if !exists {
 		if !exists {
-			fmt.Println("Error: Command not found:", args[0])
+			fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
 		} else {
 		} else {
 			method.Func.CallSlice([]reflect.Value{
 			method.Func.CallSlice([]reflect.Value{
 				reflect.ValueOf(cli),
 				reflect.ValueOf(cli),
@@ -74,7 +74,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 		}
 		}
 	}
 	}
 	help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n  -H=[tcp://%s:%d]: tcp://host:port to bind/connect to or unix://path/to/socker to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTHTTPHOST, DEFAULTHTTPPORT)
 	help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n  -H=[tcp://%s:%d]: tcp://host:port to bind/connect to or unix://path/to/socker to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTHTTPHOST, DEFAULTHTTPPORT)
-	for _, command := range [][2]string{
+	for _, command := range [][]string{
 		{"attach", "Attach to a running container"},
 		{"attach", "Attach to a running container"},
 		{"build", "Build a container from a Dockerfile"},
 		{"build", "Build a container from a Dockerfile"},
 		{"commit", "Create a new image from a container's changes"},
 		{"commit", "Create a new image from a container's changes"},
@@ -106,7 +106,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
 	} {
 	} {
 		help += fmt.Sprintf("    %-10.10s%s\n", command[0], command[1])
 		help += fmt.Sprintf("    %-10.10s%s\n", command[0], command[1])
 	}
 	}
-	fmt.Println(help)
+	fmt.Fprintf(cli.err, "%s\n", help)
 	return nil
 	return nil
 }
 }
 
 
@@ -124,7 +124,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
 	v.Set("url", cmd.Arg(1))
 	v.Set("url", cmd.Arg(1))
 	v.Set("path", cmd.Arg(2))
 	v.Set("path", cmd.Arg(2))
 
 
-	if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, os.Stdout); err != nil {
+	if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -175,7 +175,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	if cmd.Arg(0) == "-" {
 	if cmd.Arg(0) == "-" {
 		// As a special case, 'docker build -' will build from an empty context with the
 		// As a special case, 'docker build -' will build from an empty context with the
 		// contents of stdin as a Dockerfile
 		// contents of stdin as a Dockerfile
-		dockerfile, err := ioutil.ReadAll(os.Stdin)
+		dockerfile, err := ioutil.ReadAll(cli.in)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -190,7 +190,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	// FIXME: ProgressReader shouldn't be this annoyning to use
 	// FIXME: ProgressReader shouldn't be this annoyning to use
 	if context != nil {
 	if context != nil {
 		sf := utils.NewStreamFormatter(false)
 		sf := utils.NewStreamFormatter(false)
-		body = utils.ProgressReader(ioutil.NopCloser(context), 0, os.Stderr, sf.FormatProgress("Uploading context", "%v bytes%0.0s%0.0s"), sf)
+		body = utils.ProgressReader(ioutil.NopCloser(context), 0, cli.err, sf.FormatProgress("Uploading context", "%v bytes%0.0s%0.0s"), sf)
 	}
 	}
 	// Upload the build context
 	// Upload the build context
 	v := &url.Values{}
 	v := &url.Values{}
@@ -229,7 +229,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	}
 	}
 
 
 	// Output the result
 	// Output the result
-	if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
+	if _, err := io.Copy(cli.out, resp.Body); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -280,36 +280,38 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		return readStringOnRawTerminal(stdin, stdout, false)
 		return readStringOnRawTerminal(stdin, stdout, false)
 	}
 	}
 
 
-	oldState, err := term.SetRawTerminal()
+	oldState, err := term.SetRawTerminal(cli.terminalFd)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	defer term.RestoreTerminal(oldState)
+	defer term.RestoreTerminal(cli.terminalFd, oldState)
 
 
 	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
 	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
 
 
-	var username string
-	var password string
-	var email string
+	var (
+		username string
+		password string
+		email    string
+	)
 
 
-	fmt.Print("Username (", cli.authConfig.Username, "): ")
-	username = readAndEchoString(os.Stdin, os.Stdout)
+	fmt.Fprintf(cli.out, "Username (%s):", cli.authConfig.Username)
+	username = readAndEchoString(cli.in, cli.out)
 	if username == "" {
 	if username == "" {
 		username = cli.authConfig.Username
 		username = cli.authConfig.Username
 	}
 	}
 	if username != cli.authConfig.Username {
 	if username != cli.authConfig.Username {
-		fmt.Print("Password: ")
-		password = readString(os.Stdin, os.Stdout)
+		fmt.Fprintf(cli.out, "Password: ")
+		password = readString(cli.in, cli.out)
 
 
 		if password == "" {
 		if password == "" {
 			return fmt.Errorf("Error : Password Required")
 			return fmt.Errorf("Error : Password Required")
 		}
 		}
 
 
-		fmt.Print("Email (", cli.authConfig.Email, "): ")
-		email = readAndEchoString(os.Stdin, os.Stdout)
+		fmt.Fprintf(cli.out, "Email (%s): ", cli.authConfig.Email)
+		email = readAndEchoString(cli.in, cli.out)
 		if email == "" {
 		if email == "" {
 			email = cli.authConfig.Email
 			email = cli.authConfig.Email
 		}
 		}
@@ -317,7 +319,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		password = cli.authConfig.Password
 		password = cli.authConfig.Password
 		email = cli.authConfig.Email
 		email = cli.authConfig.Email
 	}
 	}
-	term.RestoreTerminal(oldState)
+	term.RestoreTerminal(cli.terminalFd, oldState)
 
 
 	cli.authConfig.Username = username
 	cli.authConfig.Username = username
 	cli.authConfig.Password = password
 	cli.authConfig.Password = password
@@ -343,7 +345,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 	}
 	}
 	auth.SaveConfig(cli.authConfig)
 	auth.SaveConfig(cli.authConfig)
 	if out2.Status != "" {
 	if out2.Status != "" {
-		fmt.Println(out2.Status)
+		fmt.Fprintln(cli.out, "%s\n", out2.Status)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -361,14 +363,14 @@ func (cli *DockerCli) CmdWait(args ...string) error {
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil)
 		body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil)
 		if err != nil {
 		if err != nil {
-			fmt.Printf("%s", err)
+			fmt.Fprintf(cli.err, "%s", err)
 		} else {
 		} else {
 			var out APIWait
 			var out APIWait
 			err = json.Unmarshal(body, &out)
 			err = json.Unmarshal(body, &out)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
-			fmt.Println(out.StatusCode)
+			fmt.Fprintf(cli.out, "%s\n", out.StatusCode)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -397,13 +399,13 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
 		utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
 		utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
 		return err
 		return err
 	}
 	}
-	fmt.Println("Client version:", VERSION)
-	fmt.Println("Server version:", out.Version)
+	fmt.Fprintf(cli.out, "Client version: %s\n", VERSION)
+	fmt.Fprintf(cli.out, "Server version: %s\n", out.Version)
 	if out.GitCommit != "" {
 	if out.GitCommit != "" {
-		fmt.Println("Git commit:", out.GitCommit)
+		fmt.Fprintf(cli.out, "Git commit: %s\n", out.GitCommit)
 	}
 	}
 	if out.GoVersion != "" {
 	if out.GoVersion != "" {
-		fmt.Println("Go version:", out.GoVersion)
+		fmt.Fprintln(cli.out, "Go version: %s\n", out.GoVersion)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -429,19 +431,19 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	fmt.Printf("Containers: %d\n", out.Containers)
-	fmt.Printf("Images: %d\n", out.Images)
+	fmt.Fprintf(cli.out, "Containers: %d\n", out.Containers)
+	fmt.Fprintf(cli.out, "Images: %d\n", out.Images)
 	if out.Debug || os.Getenv("DEBUG") != "" {
 	if out.Debug || os.Getenv("DEBUG") != "" {
-		fmt.Printf("Debug mode (server): %v\n", out.Debug)
-		fmt.Printf("Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
-		fmt.Printf("Fds: %d\n", out.NFd)
-		fmt.Printf("Goroutines: %d\n", out.NGoroutines)
+		fmt.Fprintf(cli.out, "Debug mode (server): %v\n", out.Debug)
+		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
+		fmt.Fprintf(cli.out, "Fds: %d\n", out.NFd)
+		fmt.Fprintf(cli.out, "Goroutines: %d\n", out.NGoroutines)
 	}
 	}
 	if !out.MemoryLimit {
 	if !out.MemoryLimit {
-		fmt.Println("WARNING: No memory limit support")
+		fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
 	}
 	}
 	if !out.SwapLimit {
 	if !out.SwapLimit {
-		fmt.Println("WARNING: No swap limit support")
+		fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -463,9 +465,9 @@ func (cli *DockerCli) CmdStop(args ...string) error {
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
 		_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
 		if err != nil {
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
+			fmt.Fprintf(cli.err, "%s\n", err)
 		} else {
 		} else {
-			fmt.Println(name)
+			fmt.Fprintf(cli.out, "%s\n", name)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -488,9 +490,9 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
 		_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
 		if err != nil {
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
+			fmt.Fprintf(cli.err, "%s\n", err)
 		} else {
 		} else {
-			fmt.Println(name)
+			fmt.Fprintf(cli.out, "%s\n", name)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -509,9 +511,9 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 	for _, name := range args {
 	for _, name := range args {
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
 		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
 		if err != nil {
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
+			fmt.Fprintf(cli.err, "%s\n", err)
 		} else {
 		} else {
-			fmt.Println(name)
+			fmt.Fprintln(cli.out, "%s\n", name)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -526,30 +528,30 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
-	fmt.Printf("[")
+	fmt.Fprintf(cli.out, "[")
 	for i, name := range args {
 	for i, name := range args {
 		if i > 0 {
 		if i > 0 {
-			fmt.Printf(",")
+			fmt.Fprintf(cli.out, ",")
 		}
 		}
 		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
 		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
 		if err != nil {
 		if err != nil {
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
 			if err != nil {
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "%s", err)
+				fmt.Fprintf(cli.err, "%s\n", err)
 				continue
 				continue
 			}
 			}
 		}
 		}
 
 
 		indented := new(bytes.Buffer)
 		indented := new(bytes.Buffer)
 		if err = json.Indent(indented, obj, "", "    "); err != nil {
 		if err = json.Indent(indented, obj, "", "    "); err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
+			fmt.Fprintf(cli.err, "%s\n", err)
 			continue
 			continue
 		}
 		}
-		if _, err := io.Copy(os.Stdout, indented); err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
+		if _, err := io.Copy(cli.out, indented); err != nil {
+			fmt.Fprintf(cli.err, "%s\n", err)
 		}
 		}
 	}
 	}
-	fmt.Printf("]")
+	fmt.Fprintf(cli.out, "]")
 	return nil
 	return nil
 }
 }
 
 
@@ -574,7 +576,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 	}
 	}
 
 
 	if frontend, exists := out.NetworkSettings.PortMapping[cmd.Arg(1)]; exists {
 	if frontend, exists := out.NetworkSettings.PortMapping[cmd.Arg(1)]; exists {
-		fmt.Println(frontend)
+		fmt.Fprintf(cli.out, "%s\n", frontend)
 	} else {
 	} else {
 		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
 		return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0))
 	}
 	}
@@ -595,7 +597,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		body, _, err := cli.call("DELETE", "/images/"+name, nil)
 		body, _, err := cli.call("DELETE", "/images/"+name, nil)
 		if err != nil {
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "%s", err)
+			fmt.Fprintf(cli.err, "%s", err)
 		} else {
 		} else {
 			var outs []APIRmi
 			var outs []APIRmi
 			err = json.Unmarshal(body, &outs)
 			err = json.Unmarshal(body, &outs)
@@ -604,9 +606,9 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
 			}
 			}
 			for _, out := range outs {
 			for _, out := range outs {
 				if out.Deleted != "" {
 				if out.Deleted != "" {
-					fmt.Println("Deleted:", out.Deleted)
+					fmt.Fprintf(cli.out, "Deleted: %s\n", out.Deleted)
 				} else {
 				} else {
-					fmt.Println("Untagged:", out.Untagged)
+					fmt.Fprintf(cli.out, "Untagged: %s\n", out.Untagged)
 				}
 				}
 			}
 			}
 		}
 		}
@@ -634,7 +636,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
 	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
 
 
 	for _, out := range outs {
 	for _, out := range outs {
@@ -664,9 +666,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
 	for _, name := range cmd.Args() {
 	for _, name := range cmd.Args() {
 		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
 		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
 		if err != nil {
 		if err != nil {
-			fmt.Printf("%s", err)
+			fmt.Fprintf(cli.err, "%s\n", err)
 		} else {
 		} else {
-			fmt.Println(name)
+			fmt.Fprintf(cli.out, "%s\n", name)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -686,9 +688,9 @@ func (cli *DockerCli) CmdKill(args ...string) error {
 	for _, name := range args {
 	for _, name := range args {
 		_, _, err := cli.call("POST", "/containers/"+name+"/kill", nil)
 		_, _, err := cli.call("POST", "/containers/"+name+"/kill", nil)
 		if err != nil {
 		if err != nil {
-			fmt.Printf("%s", err)
+			fmt.Fprintf(cli.err, "%s\n", err)
 		} else {
 		} else {
-			fmt.Println(name)
+			fmt.Fprintf(cli.out, "%s\n", name)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
@@ -710,7 +712,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
 	v.Set("tag", tag)
 	v.Set("tag", tag)
 	v.Set("fromSrc", src)
 	v.Set("fromSrc", src)
 
 
-	err := cli.stream("POST", "/images/create?"+v.Encode(), os.Stdin, os.Stdout)
+	err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -754,7 +756,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 
 
 	v := url.Values{}
 	v := url.Values{}
 	v.Set("registry", *registry)
 	v.Set("registry", *registry)
-	if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), os.Stdout); err != nil {
+	if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -785,7 +787,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
 	v.Set("tag", *tag)
 	v.Set("tag", *tag)
 	v.Set("registry", *registry)
 	v.Set("registry", *registry)
 
 
-	if err := cli.stream("POST", "/images/create?"+v.Encode(), nil, os.Stdout); err != nil {
+	if err := cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -812,7 +814,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		fmt.Printf("%s", body)
+		fmt.Fprintf(cli.out, "%s", body)
 	} else {
 	} else {
 		v := url.Values{}
 		v := url.Values{}
 		if cmd.NArg() == 1 {
 		if cmd.NArg() == 1 {
@@ -833,7 +835,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			return err
 			return err
 		}
 		}
 
 
-		w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+		w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 		if !*quiet {
 		if !*quiet {
 			fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE")
 			fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE")
 		}
 		}
@@ -919,7 +921,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	if !*quiet {
 	if !*quiet {
 		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
 		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
 		if *size {
 		if *size {
@@ -998,7 +1000,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	fmt.Println(apiID.ID)
+	fmt.Fprintf(cli.out, "%s\n", apiID.ID)
 	return nil
 	return nil
 }
 }
 
 
@@ -1013,7 +1015,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, os.Stdout); err != nil {
+	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -1040,7 +1042,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
 		return err
 		return err
 	}
 	}
 	for _, change := range changes {
 	for _, change := range changes {
-		fmt.Println(change.String())
+		fmt.Fprintf(cli.out, "%s\n", change.String())
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -1055,10 +1057,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 		return nil
 		return nil
 	}
 	}
 
 
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", false, nil, os.Stdout); err != nil {
+	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", false, nil, cli.out); err != nil {
 		return err
 		return err
 	}
 	}
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", false, nil, os.Stderr); err != nil {
+	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", false, nil, cli.err); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -1090,7 +1092,9 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 	}
 	}
 
 
 	if container.Config.Tty {
 	if container.Config.Tty {
-		cli.monitorTtySize(cmd.Arg(0))
+		if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
+			return err
+		}
 	}
 	}
 
 
 	v := url.Values{}
 	v := url.Values{}
@@ -1099,7 +1103,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 	v.Set("stdout", "1")
 	v.Set("stdout", "1")
 	v.Set("stderr", "1")
 	v.Set("stderr", "1")
 
 
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout); err != nil {
+	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, cli.in, cli.out); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -1127,8 +1131,8 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	fmt.Printf("Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0))
-	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+	fmt.Fprintf(cli.out, "Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0))
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\n")
 	fmt.Fprintf(w, "NAME\tDESCRIPTION\n")
 	for _, out := range outs {
 	for _, out := range outs {
 		desc := strings.Replace(out.Description, "\n", " ", -1)
 		desc := strings.Replace(out.Description, "\n", " ", -1)
@@ -1246,7 +1250,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	if statusCode == 404 {
 	if statusCode == 404 {
 		v := url.Values{}
 		v := url.Values{}
 		v.Set("fromImage", config.Image)
 		v.Set("fromImage", config.Image)
-		err = cli.stream("POST", "/images/create?"+v.Encode(), nil, os.Stderr)
+		err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -1259,27 +1263,31 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		return err
 		return err
 	}
 	}
 
 
-	out := &APIRun{}
-	err = json.Unmarshal(body, out)
+	runResult := &APIRun{}
+	err = json.Unmarshal(body, runResult)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	for _, warning := range out.Warnings {
-		fmt.Fprintln(os.Stderr, "WARNING: ", warning)
+	for _, warning := range runResult.Warnings {
+		fmt.Fprintln(cli.err, "WARNING: ", warning)
 	}
 	}
 
 
 	//start the container
 	//start the container
-	_, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil)
-	if err != nil {
+	if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", nil); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	if !config.AttachStdout && !config.AttachStderr {
 	if !config.AttachStdout && !config.AttachStderr {
-		fmt.Println(out.ID)
-	} else {
+		// 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 config.Tty {
-			cli.monitorTtySize(out.ID)
+			if err := cli.monitorTtySize(runResult.ID); err != nil {
+				return err
+			}
 		}
 		}
 
 
 		v := url.Values{}
 		v := url.Values{}
@@ -1295,7 +1303,8 @@ 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/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout); 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
 		}
 		}
@@ -1426,7 +1435,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
 	return nil
 	return nil
 }
 }
 
 
-func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.File, out io.Writer) error {
+func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, out io.Writer) error {
 
 
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil)
 	if err != nil {
 	if err != nil {
@@ -1453,15 +1462,18 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
 		return err
 		return err
 	})
 	})
 
 
-	if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
-		oldState, err := term.SetRawTerminal()
+	if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
+		oldState, err := term.SetRawTerminal(cli.terminalFd)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		defer term.RestoreTerminal(oldState)
+		defer term.RestoreTerminal(cli.terminalFd, oldState)
 	}
 	}
+
 	sendStdin := utils.Go(func() error {
 	sendStdin := utils.Go(func() error {
-		io.Copy(rwc, in)
+		if in != nil {
+			io.Copy(rwc, in)
+		}
 		if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
 		if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
 			utils.Debugf("Couldn't send EOF: %s\n", err)
 			utils.Debugf("Couldn't send EOF: %s\n", err)
 		}
 		}
@@ -1474,7 +1486,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
 		return err
 		return err
 	}
 	}
 
 
-	if !term.IsTerminal(in.Fd()) {
+	if !cli.isTerminal {
 		if err := <-sendStdin; err != nil {
 		if err := <-sendStdin; err != nil {
 			utils.Debugf("Error sendStdin: %s", err)
 			utils.Debugf("Error sendStdin: %s", err)
 			return err
 			return err
@@ -1485,7 +1497,10 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
 }
 }
 
 
 func (cli *DockerCli) resizeTty(id string) {
 func (cli *DockerCli) resizeTty(id string) {
-	ws, err := term.GetWinsize(os.Stdin.Fd())
+	if !cli.isTerminal {
+		return
+	}
+	ws, err := term.GetWinsize(cli.terminalFd)
 	if err != nil {
 	if err != nil {
 		utils.Debugf("Error getting size: %s", err)
 		utils.Debugf("Error getting size: %s", err)
 	}
 	}
@@ -1497,7 +1512,10 @@ func (cli *DockerCli) resizeTty(id string) {
 	}
 	}
 }
 }
 
 
-func (cli *DockerCli) monitorTtySize(id string) {
+func (cli *DockerCli) monitorTtySize(id string) error {
+	if !cli.isTerminal {
+		return fmt.Errorf("Impossible to monitor size on non-tty")
+	}
 	cli.resizeTty(id)
 	cli.resizeTty(id)
 
 
 	c := make(chan os.Signal, 1)
 	c := make(chan os.Signal, 1)
@@ -1509,24 +1527,56 @@ func (cli *DockerCli) monitorTtySize(id string) {
 			}
 			}
 		}
 		}
 	}()
 	}()
+	return nil
 }
 }
 
 
 func Subcmd(name, signature, description string) *flag.FlagSet {
 func Subcmd(name, signature, description string) *flag.FlagSet {
 	flags := flag.NewFlagSet(name, flag.ContinueOnError)
 	flags := flag.NewFlagSet(name, flag.ContinueOnError)
 	flags.Usage = func() {
 	flags.Usage = func() {
-		fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
+		// FIXME: use custom stdout or return error
+		fmt.Fprintf(os.Stdout, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
 		flags.PrintDefaults()
 		flags.PrintDefaults()
 	}
 	}
 	return flags
 	return flags
 }
 }
 
 
-func NewDockerCli(proto, addr string) *DockerCli {
+func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
+	var (
+		isTerminal bool = false
+		terminalFd uintptr
+	)
+
+	if in != nil {
+		if file, ok := in.(*os.File); ok {
+			terminalFd = file.Fd()
+			isTerminal = term.IsTerminal(terminalFd)
+		}
+	}
+
+	if err == nil {
+		err = out
+	}
+
 	authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
 	authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
-	return &DockerCli{proto, addr, authConfig}
+	return &DockerCli{
+		proto:      proto,
+		addr:       addr,
+		authConfig: authConfig,
+		in:         in,
+		out:        out,
+		err:        err,
+		isTerminal: isTerminal,
+		terminalFd: terminalFd,
+	}
 }
 }
 
 
 type DockerCli struct {
 type DockerCli struct {
 	proto      string
 	proto      string
 	addr       string
 	addr       string
 	authConfig *auth.AuthConfig
 	authConfig *auth.AuthConfig
+	in         io.ReadCloser
+	out        io.Writer
+	err        io.Writer
+	isTerminal bool
+	terminalFd uintptr
 }
 }

+ 42 - 54
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 {
@@ -334,29 +319,27 @@ func TestRunDisconnectTty(t *testing.T) {
 		t.Fatalf("/bin/cat should  still be running after closing stdin (tty mode)")
 		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.
 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}
 
 
 	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)
 		}
 		}
@@ -365,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()
 	})
 	})
 
 
@@ -400,6 +387,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) {
 	runtime, err := newTestRuntime()
 	runtime, err := newTestRuntime()

+ 37 - 3
runtime_test.go

@@ -16,9 +16,15 @@ import (
 	"time"
 	"time"
 )
 )
 
 
-const unitTestImageName string = "docker-ut"
-const unitTestImageId string = "e9aa60c60128cad1"
-const unitTestStoreBase string = "/var/lib/docker/unit-tests"
+const (
+	unitTestImageName = "docker-ut"
+	unitTestImageId   = "e9aa60c60128cad1"
+	unitTestStoreBase = "/var/lib/docker/unit-tests"
+	testDaemonAddr    = "127.0.0.1:4270"
+	testDaemonProto   = "tcp"
+)
+
+var globalRuntime *Runtime
 
 
 func nuke(runtime *Runtime) error {
 func nuke(runtime *Runtime) error {
 	var wg sync.WaitGroup
 	var wg sync.WaitGroup
@@ -33,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)
@@ -60,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{
@@ -73,6 +97,16 @@ func init() {
 	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil {
 	if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
+
+	// Spawn a Daemon
+	go func() {
+		if err := ListenAndServe(testDaemonProto, testDaemonAddr, srv, os.Getenv("DEBUG") != ""); err != nil {
+			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

+ 4 - 8
term/term.go

@@ -38,13 +38,13 @@ func IsTerminal(fd uintptr) bool {
 
 
 // Restore restores the terminal connected to the given file descriptor to a
 // Restore restores the terminal connected to the given file descriptor to a
 // previous state.
 // previous state.
-func Restore(fd uintptr, state *State) error {
+func RestoreTerminal(fd uintptr, state *State) error {
 	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
 	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
 	return err
 	return err
 }
 }
 
 
-func SetRawTerminal() (*State, error) {
-	oldState, err := MakeRaw(os.Stdin.Fd())
+func SetRawTerminal(fd uintptr) (*State, error) {
+	oldState, err := MakeRaw(fd)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -52,12 +52,8 @@ func SetRawTerminal() (*State, error) {
 	signal.Notify(c, os.Interrupt)
 	signal.Notify(c, os.Interrupt)
 	go func() {
 	go func() {
 		_ = <-c
 		_ = <-c
-		Restore(os.Stdin.Fd(), oldState)
+		RestoreTerminal(fd, oldState)
 		os.Exit(0)
 		os.Exit(0)
 	}()
 	}()
 	return oldState, err
 	return oldState, err
 }
 }
-
-func RestoreTerminal(state *State) {
-	Restore(os.Stdin.Fd(), state)
-}

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