Преглед изворни кода

Add a stdincloser to container.Attach in order to close the client connection when needed

Guillaume J. Charmes пре 12 година
родитељ
комит
6882c78ce4
3 измењених фајлова са 39 додато и 16 уклоњено
  1. 12 4
      commands.go
  2. 4 8
      commands_test.go
  3. 23 4
      container.go

+ 12 - 4
commands.go

@@ -799,7 +799,8 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...stri
 	if container == nil {
 		return fmt.Errorf("No such container: %s", name)
 	}
-	return <-container.Attach(stdin, stdout, stdout)
+
+	return <-container.Attach(stdin, nil, stdout, stdout)
 }
 
 // Ports type - Used to parse multiple -p flags
@@ -901,11 +902,17 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 		}
 	}
 	var (
-		cStdin           io.Reader
+		cStdin           io.ReadCloser
 		cStdout, cStderr io.Writer
 	)
 	if config.AttachStdin {
-		cStdin = stdin
+		r, w := io.Pipe()
+		go func() {
+			defer w.Close()
+			defer Debugf("Closing buffered stdin pipe")
+			io.Copy(w, stdin)
+		}()
+		cStdin = r
 	}
 	if config.AttachStdout {
 		cStdout = stdout
@@ -913,7 +920,8 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 	if config.AttachStderr {
 		cStderr = stdout // FIXME: rcli can't differentiate stdout from stderr
 	}
-	attachErr := container.Attach(cStdin, cStdout, cStderr)
+
+	attachErr := container.Attach(cStdin, stdin, cStdout, cStderr)
 	Debugf("Starting\n")
 	if err := container.Start(); err != nil {
 		return err

+ 4 - 8
commands_test.go

@@ -93,9 +93,7 @@ func TestRunExit(t *testing.T) {
 	stdout, stdoutPipe := io.Pipe()
 	c1 := make(chan struct{})
 	go func() {
-		if err := srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat"); err != nil {
-			t.Error(err)
-		}
+		srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat")
 		close(c1)
 	}()
 
@@ -123,11 +121,8 @@ func TestRunExit(t *testing.T) {
 
 	// 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() {
-		if _, err := stdin.Read([]byte{}); err != nil {
-			if err != io.EOF {
-				t.Fatal(err)
-			}
-		}
+		// Expecting pipe i/o error, just check that read does not block
+		stdin.Read([]byte{})
 	})
 
 	// Cleanup pipes
@@ -293,6 +288,7 @@ func TestAttachDisconnect(t *testing.T) {
 	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)

+ 23 - 4
container.go

@@ -257,9 +257,9 @@ func (container *Container) start() error {
 	return container.cmd.Start()
 }
 
-func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io.Writer) chan error {
-	var cStdout io.ReadCloser
-	var cStderr io.ReadCloser
+func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
+	var cStdout, cStderr io.ReadCloser
+
 	var nJobs int
 	errors := make(chan error, 3)
 	if stdin != nil && container.Config.OpenStdin {
@@ -277,7 +277,8 @@ func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io.
 				if err != nil {
 					Debugf("[error] attach stdout: %s\n", err)
 				}
-				errors <- err
+				// Discard error, expecting pipe error
+				errors <- nil
 			}()
 		}
 	}
@@ -290,6 +291,15 @@ func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io.
 			go func() {
 				Debugf("[start] attach stdout\n")
 				defer Debugf("[end]  attach stdout\n")
+				// If we are in StdinOnce mode, then close stdin
+				if container.Config.StdinOnce {
+					if stdin != nil {
+						defer stdin.Close()
+					}
+					if stdinCloser != nil {
+						defer stdinCloser.Close()
+					}
+				}
 				_, err := io.Copy(stdout, cStdout)
 				if err != nil {
 					Debugf("[error] attach stdout: %s\n", err)
@@ -307,6 +317,15 @@ func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io.
 			go func() {
 				Debugf("[start] attach stderr\n")
 				defer Debugf("[end]  attach stderr\n")
+				// If we are in StdinOnce mode, then close stdin
+				if container.Config.StdinOnce {
+					if stdin != nil {
+						defer stdin.Close()
+					}
+					if stdinCloser != nil {
+						defer stdinCloser.Close()
+					}
+				}
 				_, err := io.Copy(stderr, cStderr)
 				if err != nil {
 					Debugf("[error] attach stderr: %s\n", err)