瀏覽代碼

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

Solomon Hykes 12 年之前
父節點
當前提交
2aad4a3478
共有 2 個文件被更改,包括 50 次插入17 次删除
  1. 6 6
      container.go
  2. 44 11
      network.go

+ 6 - 6
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

+ 44 - 11
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
+	}
+	// 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
+	}
+	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
 	}
-	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
+	nat.Backend = int(port)
+	if sameFrontend {
+		nat.Frontend = nat.Backend
 	}
-	iface.extPorts = append(iface.extPorts, extPort)
-	return extPort, nil
+	nat.Proto = "tcp"
+	return &nat, nil
 }
 
 // Release: Network cleanup - release all resources