From 2aad4a34785a1b0d02d910a14dbb9b0d6d3edc17 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 4 Apr 2013 22:58:01 -0700 Subject: [PATCH] Choose which TCP frontend port to allocate with '-p :PORT' --- container.go | 12 ++++++------ network.go | 55 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/container.go b/container.go index 981b9f29c0..86c0f5b1df 100644 --- a/container.go +++ b/container.go @@ -55,7 +55,7 @@ type Config struct { AttachStdin bool AttachStdout bool AttachStderr bool - Ports []int + PortSpecs []string Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin 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") 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)") var flEnv ListOpts @@ -112,7 +112,7 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) { } config := &Config{ Hostname: *flHostname, - Ports: flPorts, + PortSpecs: flPorts, User: *flUser, Tty: *flTty, OpenStdin: *flStdin, @@ -482,12 +482,12 @@ func (container *Container) allocateNetwork() error { return err } container.NetworkSettings.PortMapping = make(map[string]string) - for _, port := range container.Config.Ports { - if extPort, err := iface.AllocatePort(port); err != nil { + for _, spec := range container.Config.PortSpecs { + if nat, err := iface.AllocatePort(spec); err != nil { iface.Release() return err } else { - container.NetworkSettings.PortMapping[strconv.Itoa(port)] = strconv.Itoa(extPort) + container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend) } } container.network = iface diff --git a/network.go b/network.go index 6a23d9b88d..2cbe011fdb 100644 --- a/network.go +++ b/network.go @@ -161,9 +161,9 @@ func newPortMapper() (*PortMapper, error) { // Port allocator: Atomatically allocate and release networking ports type PortAllocator struct { - inUse map[int]struct{} + inUse map[int]struct{} fountain chan (int) - lock sync.Mutex + lock sync.Mutex } func (alloc *PortAllocator) runFountain() { @@ -207,7 +207,7 @@ func (alloc *PortAllocator) Acquire(port int) (int, error) { func newPortAllocator() (*PortAllocator, error) { allocator := &PortAllocator{ - inUse: make(map[int]struct{}), + inUse: make(map[int]struct{}), } go allocator.runFountain() return allocator, nil @@ -318,17 +318,50 @@ type NetworkInterface struct { } // Allocate an external TCP port and map it to the interface -func (iface *NetworkInterface) AllocatePort(port int) (int, error) { - extPort, err := iface.manager.portAllocator.Acquire(0) +func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) { + nat, err := parseNat(spec) 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 { - iface.manager.portAllocator.Release(extPort) - return -1, err + // Allocate a random port if Frontend==0 + if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil { + return nil, err + } else { + nat.Frontend = extPort } - iface.extPorts = append(iface.extPorts, extPort) - return extPort, nil + if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != 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