Kaynağa Gözat

Merge pull request #7934 from LK4D4/fix_double_allocation

Fix error propagation from userland-proxy
Michael Crosby 10 yıl önce
ebeveyn
işleme
a96811272a

+ 14 - 5
daemon/networkdriver/portmapper/mapper.go

@@ -97,16 +97,25 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	m.userlandProxy = proxy
-	currentMappings[key] = m
-
-	if err := proxy.Start(); err != nil {
+	cleanup := func() error {
 		// need to undo the iptables rules before we return
 		// need to undo the iptables rules before we return
+		proxy.Stop()
 		forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
 		forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
+		if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
+			return err
+		}
 
 
-		return nil, err
+		return nil
 	}
 	}
 
 
+	if err := proxy.Start(); err != nil {
+		if err := cleanup(); err != nil {
+			return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
+		}
+		return nil, err
+	}
+	m.userlandProxy = proxy
+	currentMappings[key] = m
 	return m.host, nil
 	return m.host, nil
 }
 }
 
 

+ 49 - 11
daemon/networkdriver/portmapper/proxy.go

@@ -2,6 +2,8 @@ package portmapper
 
 
 import (
 import (
 	"flag"
 	"flag"
+	"fmt"
+	"io/ioutil"
 	"log"
 	"log"
 	"net"
 	"net"
 	"os"
 	"os"
@@ -9,6 +11,7 @@ import (
 	"os/signal"
 	"os/signal"
 	"strconv"
 	"strconv"
 	"syscall"
 	"syscall"
+	"time"
 
 
 	"github.com/docker/docker/pkg/proxy"
 	"github.com/docker/docker/pkg/proxy"
 	"github.com/docker/docker/reexec"
 	"github.com/docker/docker/reexec"
@@ -37,10 +40,12 @@ func execProxy() {
 
 
 	p, err := proxy.NewProxy(host, container)
 	p, err := proxy.NewProxy(host, container)
 	if err != nil {
 	if err != nil {
-		log.Fatal(err)
+		os.Stdout.WriteString("1\n")
+		fmt.Fprint(os.Stderr, err)
+		os.Exit(1)
 	}
 	}
-
 	go handleStopSignals(p)
 	go handleStopSignals(p)
+	os.Stdout.WriteString("0\n")
 
 
 	// Run will block until the proxy stops
 	// Run will block until the proxy stops
 	p.Run()
 	p.Run()
@@ -96,10 +101,8 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.
 
 
 	return &proxyCommand{
 	return &proxyCommand{
 		cmd: &exec.Cmd{
 		cmd: &exec.Cmd{
-			Path:   reexec.Self(),
-			Args:   args,
-			Stdout: os.Stdout,
-			Stderr: os.Stderr,
+			Path: reexec.Self(),
+			Args: args,
 			SysProcAttr: &syscall.SysProcAttr{
 			SysProcAttr: &syscall.SysProcAttr{
 				Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies
 				Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies
 			},
 			},
@@ -108,12 +111,47 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.
 }
 }
 
 
 func (p *proxyCommand) Start() error {
 func (p *proxyCommand) Start() error {
-	return p.cmd.Start()
+	stdout, err := p.cmd.StdoutPipe()
+	if err != nil {
+		return err
+	}
+	defer stdout.Close()
+	stderr, err := p.cmd.StderrPipe()
+	if err != nil {
+		return err
+	}
+	defer stderr.Close()
+	if err := p.cmd.Start(); err != nil {
+		return err
+	}
+
+	errchan := make(chan error, 1)
+	go func() {
+		buf := make([]byte, 2)
+		stdout.Read(buf)
+
+		if string(buf) != "0\n" {
+			errStr, _ := ioutil.ReadAll(stderr)
+			errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr)
+			return
+		}
+		errchan <- nil
+	}()
+
+	select {
+	case err := <-errchan:
+		return err
+	case <-time.After(1 * time.Second):
+		return fmt.Errorf("Timed out proxy starting the userland proxy")
+	}
 }
 }
 
 
 func (p *proxyCommand) Stop() error {
 func (p *proxyCommand) Stop() error {
-	err := p.cmd.Process.Signal(os.Interrupt)
-	p.cmd.Wait()
-
-	return err
+	if p.cmd.Process != nil {
+		if err := p.cmd.Process.Signal(os.Interrupt); err != nil {
+			return err
+		}
+		return p.cmd.Wait()
+	}
+	return nil
 }
 }

+ 21 - 0
integration-cli/docker_cli_run_test.go

@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"bufio"
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
+	"net"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path"
 	"path"
@@ -1895,3 +1896,23 @@ func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
 	deleteAllContainers()
 	deleteAllContainers()
 	logDone("run - port should be deallocated even on iptables error")
 	logDone("run - port should be deallocated even on iptables error")
 }
 }
+
+func TestRunPortInUse(t *testing.T) {
+	port := "1234"
+	l, err := net.Listen("tcp", ":"+port)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer l.Close()
+	cmd := exec.Command(dockerBinary, "run", "-p", port+":80", "busybox", "true")
+	out, _, err := runCommandWithOutput(cmd)
+	if err == nil {
+		t.Fatalf("Binding on used port must fail")
+	}
+	if !strings.Contains(out, "address already in use") {
+		t.Fatalf("Out must be about \"address already in use\", got %s", out)
+	}
+
+	deleteAllContainers()
+	logDone("run - fail if port already in use")
+}