func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error { binding := bindings[port] if container.hostConfig.PublishAllPorts && len(binding) == 0 { binding = append(binding, nat.PortBinding{}) } for i := 0; i < len(binding); i++ { b := binding[i] job := eng.Job("allocate_port", container.ID) job.Setenv("HostIP", b.HostIp) job.Setenv("HostPort", b.HostPort) job.Setenv("Proto", port.Proto()) job.Setenv("ContainerPort", port.Port()) portEnv, err := job.Stdout.AddEnv() if err != nil { return err } if err := job.Run(); err != nil { eng.Job("release_interface", container.ID).Run() return err } b.HostIp = portEnv.Get("HostIP") b.HostPort = portEnv.Get("HostPort") binding[i] = b } bindings[port] = binding return nil }
// Allocate an external port and map it to the interface func AllocatePort(id string, port nat.Port, binding nat.PortBinding) (nat.PortBinding, error) { var ( ip = defaultBindingIP proto = port.Proto() containerPort = port.Int() network = currentInterfaces.Get(id) ) if binding.HostIp != "" { ip = net.ParseIP(binding.HostIp) if ip == nil { return nat.PortBinding{}, fmt.Errorf("Bad parameter: invalid host ip %s", binding.HostIp) } } // host ip, proto, and host port var container net.Addr switch proto { case "tcp": container = &net.TCPAddr{IP: network.IP, Port: containerPort} case "udp": container = &net.UDPAddr{IP: network.IP, Port: containerPort} default: return nat.PortBinding{}, fmt.Errorf("unsupported address type %s", proto) } // // 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 // yields. // var ( host net.Addr err error ) hostPort, err := nat.ParsePort(binding.HostPort) if err != nil { return nat.PortBinding{}, err } for i := 0; i < MaxAllocatedPortAttempts; i++ { if host, err = portMapper.Map(container, ip, hostPort); err == nil { break } // There is no point in immediately retrying to map an explicitly // chosen port. if hostPort != 0 { logrus.Warnf("Failed to allocate and map port %d: %s", hostPort, err) break } logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) } if err != nil { return nat.PortBinding{}, err } network.PortMappings = append(network.PortMappings, host) switch netAddr := host.(type) { case *net.TCPAddr: return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil case *net.UDPAddr: return nat.PortBinding{HostIp: netAddr.IP.String(), HostPort: strconv.Itoa(netAddr.Port)}, nil default: return nat.PortBinding{}, fmt.Errorf("unsupported address type %T", netAddr) } }