Use last allocated port logic in port allocator
Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)
This commit is contained in:
parent
ffd68badc0
commit
e77729c2e0
|
@ -20,7 +20,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultNetworkBridge = "docker0"
|
DefaultNetworkBridge = "docker0"
|
||||||
|
MaxAllocatedPortAttempts = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// Network interface represents the networking stack of a container
|
// Network interface represents the networking stack of a container
|
||||||
|
@ -401,15 +402,15 @@ func AllocatePort(job *engine.Job) engine.Status {
|
||||||
return job.Errorf("unsupported address type %s", proto)
|
return job.Errorf("unsupported address type %s", proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
//
|
||||||
Try up to 10 times to get a port that's not already allocated.
|
// Try up to 10 times to get a port that's not already allocated.
|
||||||
|
//
|
||||||
In the event of failure to bind, return the error that portmapper.Map
|
// In the event of failure to bind, return the error that portmapper.Map
|
||||||
yields.
|
// yields.
|
||||||
*/
|
//
|
||||||
|
|
||||||
var host net.Addr
|
var host net.Addr
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < MaxAllocatedPortAttempts; i++ {
|
||||||
if host, err = portmapper.Map(container, ip, hostPort); err == nil {
|
if host, err = portmapper.Map(container, ip, hostPort); err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,13 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type portMap struct {
|
||||||
|
p map[int]struct{}
|
||||||
|
last int
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
portMap map[int]bool
|
protocolMap map[string]*portMap
|
||||||
protocolMap map[string]portMap
|
|
||||||
ipMapping map[string]protocolMap
|
ipMapping map[string]protocolMap
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -71,8 +75,8 @@ func RequestPort(ip net.IP, proto string, port int) (int, error) {
|
||||||
mapping := getOrCreate(ip)
|
mapping := getOrCreate(ip)
|
||||||
|
|
||||||
if port > 0 {
|
if port > 0 {
|
||||||
if !mapping[proto][port] {
|
if _, ok := mapping[proto].p[port]; !ok {
|
||||||
mapping[proto][port] = true
|
mapping[proto].p[port] = struct{}{}
|
||||||
return port, nil
|
return port, nil
|
||||||
} else {
|
} else {
|
||||||
return 0, NewErrPortAlreadyAllocated(ip.String(), port)
|
return 0, NewErrPortAlreadyAllocated(ip.String(), port)
|
||||||
|
@ -94,8 +98,8 @@ func ReleasePort(ip net.IP, proto string, port int) error {
|
||||||
|
|
||||||
ip = getDefault(ip)
|
ip = getDefault(ip)
|
||||||
|
|
||||||
mapping := getOrCreate(ip)
|
mapping := getOrCreate(ip)[proto]
|
||||||
delete(mapping[proto], port)
|
delete(mapping.p, port)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -114,8 +118,8 @@ func getOrCreate(ip net.IP) protocolMap {
|
||||||
|
|
||||||
if _, ok := globalMap[ipstr]; !ok {
|
if _, ok := globalMap[ipstr]; !ok {
|
||||||
globalMap[ipstr] = protocolMap{
|
globalMap[ipstr] = protocolMap{
|
||||||
"tcp": portMap{},
|
"tcp": &portMap{p: map[int]struct{}{}, last: 0},
|
||||||
"udp": portMap{},
|
"udp": &portMap{p: map[int]struct{}{}, last: 0},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,21 +127,28 @@ func getOrCreate(ip net.IP) protocolMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
func findPort(ip net.IP, proto string) (int, error) {
|
func findPort(ip net.IP, proto string) (int, error) {
|
||||||
port := BeginPortRange
|
mapping := getOrCreate(ip)[proto]
|
||||||
|
|
||||||
mapping := getOrCreate(ip)
|
if mapping.last == 0 {
|
||||||
|
mapping.p[BeginPortRange] = struct{}{}
|
||||||
for mapping[proto][port] {
|
mapping.last = BeginPortRange
|
||||||
port++
|
return BeginPortRange, nil
|
||||||
|
|
||||||
if port > EndPortRange {
|
|
||||||
return 0, ErrAllPortsAllocated
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping[proto][port] = true
|
for port := mapping.last + 1; port != mapping.last; port++ {
|
||||||
|
if port > EndPortRange {
|
||||||
|
port = BeginPortRange
|
||||||
|
}
|
||||||
|
|
||||||
return port, nil
|
if _, ok := mapping.p[port]; !ok {
|
||||||
|
mapping.p[port] = struct{}{}
|
||||||
|
mapping.last = port
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, ErrAllPortsAllocated
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefault(ip net.IP) net.IP {
|
func getDefault(ip net.IP) net.IP {
|
||||||
|
|
|
@ -48,6 +48,13 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (net.Addr, error) {
|
||||||
allocatedHostPort int
|
allocatedHostPort int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// release the port on any error during return.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
switch container.(type) {
|
switch container.(type) {
|
||||||
case *net.TCPAddr:
|
case *net.TCPAddr:
|
||||||
proto = "tcp"
|
proto = "tcp"
|
||||||
|
@ -74,33 +81,22 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (net.Addr, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// When binding fails:
|
|
||||||
// - for a specifically requested port: it might be locked by some other
|
|
||||||
// process, so we want to allow for an ulterior retry
|
|
||||||
// - for an automatically allocated port: it falls in the Docker range of
|
|
||||||
// ports, so we'll just remember it as used and try the next free one
|
|
||||||
defer func() {
|
|
||||||
if err != nil && hostPort != 0 {
|
|
||||||
portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
key := getKey(m.host)
|
key := getKey(m.host)
|
||||||
if _, exists := currentMappings[key]; exists {
|
if _, exists := currentMappings[key]; exists {
|
||||||
err = ErrPortMappedForIP
|
err = ErrPortMappedForIP
|
||||||
return m.host, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
containerIP, containerPort := getIPAndPort(m.container)
|
containerIP, containerPort := getIPAndPort(m.container)
|
||||||
if err := forward(iptables.Add, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
|
if err := forward(iptables.Add, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
|
||||||
return m.host, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := newProxy(m.host, m.container)
|
p, err := newProxy(m.host, m.container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// need to undo the iptables rules before we return
|
// need to undo the iptables rules before we return
|
||||||
forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
|
forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
|
||||||
return m.host, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.userlandProxy = p
|
m.userlandProxy = p
|
||||||
|
|
Loading…
Reference in New Issue