Explorar o código

Merge pull request #8450 from dqminh/fix-proxy-exit

dont close proxy's stdout/stderr prematurely
Michael Crosby %!s(int64=10) %!d(string=hai) anos
pai
achega
0486bd022e
Modificáronse 2 ficheiros con 45 adicións e 13 borrados
  1. 12 13
      daemon/networkdriver/portmapper/proxy.go
  2. 33 0
      integration-cli/docker_cli_run_test.go

+ 12 - 13
daemon/networkdriver/portmapper/proxy.go

@@ -36,16 +36,18 @@ type proxyCommand struct {
 
 // execProxy is the reexec function that is registered to start the userland proxies
 func execProxy() {
+	f := os.NewFile(3, "signal-parent")
 	host, container := parseHostContainerAddrs()
 
 	p, err := proxy.NewProxy(host, container)
 	if err != nil {
-		os.Stdout.WriteString("1\n")
-		fmt.Fprint(os.Stderr, err)
+		fmt.Fprintf(f, "1\n%s", err)
+		f.Close()
 		os.Exit(1)
 	}
 	go handleStopSignals(p)
-	os.Stdout.WriteString("0\n")
+	fmt.Fprint(f, "0\n")
+	f.Close()
 
 	// Run will block until the proxy stops
 	p.Run()
@@ -111,27 +113,24 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.
 }
 
 func (p *proxyCommand) Start() error {
-	stdout, err := p.cmd.StdoutPipe()
+	r, w, err := os.Pipe()
 	if err != nil {
-		return err
-	}
-	defer stdout.Close()
-	stderr, err := p.cmd.StderrPipe()
-	if err != nil {
-		return err
+		return fmt.Errorf("proxy unable to open os.Pipe %s", err)
 	}
-	defer stderr.Close()
+	defer r.Close()
+	p.cmd.ExtraFiles = []*os.File{w}
 	if err := p.cmd.Start(); err != nil {
 		return err
 	}
+	w.Close()
 
 	errchan := make(chan error, 1)
 	go func() {
 		buf := make([]byte, 2)
-		stdout.Read(buf)
+		r.Read(buf)
 
 		if string(buf) != "0\n" {
-			errStr, _ := ioutil.ReadAll(stderr)
+			errStr, _ := ioutil.ReadAll(r)
 			errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr)
 			return
 		}

+ 33 - 0
integration-cli/docker_cli_run_test.go

@@ -2093,6 +2093,39 @@ func TestRunPortInUse(t *testing.T) {
 	logDone("run - fail if port already in use")
 }
 
+// https://github.com/docker/docker/issues/8428
+func TestRunPortProxy(t *testing.T) {
+	defer deleteAllContainers()
+
+	port := "12345"
+	cmd := exec.Command(dockerBinary, "run", "-p", port+":80", "busybox", "true")
+
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatalf("Failed to run and bind port %s, output: %s, error: %s", port, out, err)
+	}
+
+	// connect for 10 times here. This will trigger 10 EPIPES in the child
+	// process and kill it when it writes to a closed stdout/stderr
+	for i := 0; i < 10; i++ {
+		net.Dial("tcp", fmt.Sprintf("0.0.0.0:%s", port))
+	}
+
+	listPs := exec.Command("sh", "-c", "ps ax | grep docker")
+	out, _, err = runCommandWithOutput(listPs)
+	if err != nil {
+		t.Errorf("list docker process failed with output %s, error %s", out, err)
+	}
+	if strings.Contains(out, "docker <defunct>") {
+		t.Errorf("Unexpected defunct docker process")
+	}
+	if !strings.Contains(out, "docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 12345") {
+		t.Errorf("Failed to find docker-proxy process, got %s", out)
+	}
+
+	logDone("run - proxy should work with unavailable port")
+}
+
 // Regression test for #7792
 func TestRunMountOrdering(t *testing.T) {
 	tmpDir, err := ioutil.TempDir("", "docker_nested_mount_test")