From 3b6a29b81a5280187b3d03c91950cf93f7e263ec Mon Sep 17 00:00:00 2001 From: Erik Hollensbe Date: Sun, 7 Sep 2014 12:33:51 -0700 Subject: [PATCH] Fix an issue where already allocated ports would not trigger an error. Docker-DCO-1.1-Signed-off-by: Erik Hollensbe (github: erikh) --- daemon/networkdriver/portmapper/mapper.go | 21 ++++++++- daemon/networkdriver/portmapper/proxy.go | 55 +++++++++++++++++++---- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/daemon/networkdriver/portmapper/mapper.go b/daemon/networkdriver/portmapper/mapper.go index 7e8e266bb4..774b580593 100644 --- a/daemon/networkdriver/portmapper/mapper.go +++ b/daemon/networkdriver/portmapper/mapper.go @@ -100,11 +100,28 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er m.userlandProxy = proxy currentMappings[key] = m - if err := proxy.Start(); err != nil { + cleanup := func() error { // need to undo the iptables rules before we return forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) + proxy.Stop() + forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) + m.userlandProxy = nil + delete(currentMappings, key) + 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) + } + + if err == ErrPortMappingFailure { + return nil, portallocator.NewErrPortAlreadyAllocated(hostIP.String(), allocatedHostPort) + } } return m.host, nil diff --git a/daemon/networkdriver/portmapper/proxy.go b/daemon/networkdriver/portmapper/proxy.go index b24723727b..67cd9710b0 100644 --- a/daemon/networkdriver/portmapper/proxy.go +++ b/daemon/networkdriver/portmapper/proxy.go @@ -1,6 +1,7 @@ package portmapper import ( + "errors" "flag" "log" "net" @@ -9,11 +10,14 @@ import ( "os/signal" "strconv" "syscall" + "time" "github.com/docker/docker/pkg/proxy" "github.com/docker/docker/reexec" ) +var ErrPortMappingFailure = errors.New("Failure Mapping Port") + const userlandProxyCommandName = "docker-proxy" func init() { @@ -37,9 +41,12 @@ func execProxy() { p, err := proxy.NewProxy(host, container) if err != nil { - log.Fatal(err) + os.Stdout.WriteString("1\n") + os.Exit(1) } + os.Stdout.WriteString("0\n") + go handleStopSignals(p) // Run will block until the proxy stops @@ -96,10 +103,8 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net. return &proxyCommand{ cmd: &exec.Cmd{ - Path: reexec.Self(), - Args: args, - Stdout: os.Stdout, - Stderr: os.Stderr, + Path: reexec.Self(), + Args: args, SysProcAttr: &syscall.SysProcAttr{ Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies }, @@ -108,12 +113,44 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net. } func (p *proxyCommand) Start() error { - return p.cmd.Start() + stdout, err := p.cmd.StdoutPipe() + if err != nil { + return err + } + if err := p.cmd.Start(); err != nil { + return err + } + + errchan := make(chan error) + after := time.After(1 * time.Second) + go func() { + buf := make([]byte, 2) + stdout.Read(buf) + + if string(buf) != "0\n" { + errchan <- ErrPortMappingFailure + } else { + errchan <- nil + } + }() + + var readErr error + + select { + case readErr = <-errchan: + case <-after: + readErr = ErrPortMappingFailure + } + + return readErr } func (p *proxyCommand) Stop() error { - err := p.cmd.Process.Signal(os.Interrupt) - p.cmd.Wait() + if p.cmd.Process != nil { + err := p.cmd.Process.Signal(os.Interrupt) + p.cmd.Wait() + return err + } - return err + return nil }