1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Choose which TCP frontend port to allocate with '-p :PORT'

This commit is contained in:
Solomon Hykes 2013-04-04 22:58:01 -07:00
parent a5fb1d6c01
commit 2aad4a3478
2 changed files with 50 additions and 17 deletions

View file

@ -55,7 +55,7 @@ type Config struct {
AttachStdin bool AttachStdin bool
AttachStdout bool AttachStdout bool
AttachStderr bool AttachStderr bool
Ports []int PortSpecs []string
Tty bool // Attach standard streams to a tty, including stdin if it is not closed. Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin OpenStdin bool // Open stdin
StdinOnce bool // If true, close stdin after the 1 attached client disconnects. StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
@ -79,7 +79,7 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)") flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
var flPorts ports var flPorts ListOpts
cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)") cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
var flEnv ListOpts var flEnv ListOpts
@ -112,7 +112,7 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
} }
config := &Config{ config := &Config{
Hostname: *flHostname, Hostname: *flHostname,
Ports: flPorts, PortSpecs: flPorts,
User: *flUser, User: *flUser,
Tty: *flTty, Tty: *flTty,
OpenStdin: *flStdin, OpenStdin: *flStdin,
@ -482,12 +482,12 @@ func (container *Container) allocateNetwork() error {
return err return err
} }
container.NetworkSettings.PortMapping = make(map[string]string) container.NetworkSettings.PortMapping = make(map[string]string)
for _, port := range container.Config.Ports { for _, spec := range container.Config.PortSpecs {
if extPort, err := iface.AllocatePort(port); err != nil { if nat, err := iface.AllocatePort(spec); err != nil {
iface.Release() iface.Release()
return err return err
} else { } else {
container.NetworkSettings.PortMapping[strconv.Itoa(port)] = strconv.Itoa(extPort) container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
} }
} }
container.network = iface container.network = iface

View file

@ -161,9 +161,9 @@ func newPortMapper() (*PortMapper, error) {
// Port allocator: Atomatically allocate and release networking ports // Port allocator: Atomatically allocate and release networking ports
type PortAllocator struct { type PortAllocator struct {
inUse map[int]struct{} inUse map[int]struct{}
fountain chan (int) fountain chan (int)
lock sync.Mutex lock sync.Mutex
} }
func (alloc *PortAllocator) runFountain() { func (alloc *PortAllocator) runFountain() {
@ -207,7 +207,7 @@ func (alloc *PortAllocator) Acquire(port int) (int, error) {
func newPortAllocator() (*PortAllocator, error) { func newPortAllocator() (*PortAllocator, error) {
allocator := &PortAllocator{ allocator := &PortAllocator{
inUse: make(map[int]struct{}), inUse: make(map[int]struct{}),
} }
go allocator.runFountain() go allocator.runFountain()
return allocator, nil return allocator, nil
@ -318,17 +318,50 @@ type NetworkInterface struct {
} }
// Allocate an external TCP port and map it to the interface // Allocate an external TCP port and map it to the interface
func (iface *NetworkInterface) AllocatePort(port int) (int, error) { func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
extPort, err := iface.manager.portAllocator.Acquire(0) nat, err := parseNat(spec)
if err != nil { if err != nil {
return -1, err return nil, err
} }
if err := iface.manager.portMapper.Map(extPort, net.TCPAddr{IP: iface.IPNet.IP, Port: port}); err != nil { // Allocate a random port if Frontend==0
iface.manager.portAllocator.Release(extPort) if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil {
return -1, err return nil, err
} else {
nat.Frontend = extPort
} }
iface.extPorts = append(iface.extPorts, extPort) if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
return extPort, nil iface.manager.portAllocator.Release(nat.Frontend)
return nil, err
}
iface.extPorts = append(iface.extPorts, nat.Frontend)
return nat, nil
}
type Nat struct {
Proto string
Frontend int
Backend int
}
func parseNat(spec string) (*Nat, error) {
var nat Nat
// If spec starts with ':', external and internal ports must be the same.
// This might fail if the requested external port is not available.
var sameFrontend bool
if spec[0] == ':' {
sameFrontend = true
spec = spec[1:]
}
port, err := strconv.ParseUint(spec, 10, 16)
if err != nil {
return nil, err
}
nat.Backend = int(port)
if sameFrontend {
nat.Frontend = nat.Backend
}
nat.Proto = "tcp"
return &nat, nil
} }
// Release: Network cleanup - release all resources // Release: Network cleanup - release all resources