/
ports.go
91 lines (74 loc) · 2.06 KB
/
ports.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package dockerutil
import (
"fmt"
"net"
"strconv"
"sync"
"github.com/docker/go-connections/nat"
)
var mu sync.RWMutex
type Listeners []net.Listener
func (l Listeners) CloseAll() {
for _, listener := range l {
listener.Close()
}
}
// openListener opens a listener on a port. Set to 0 to get a random port.
func openListener(port int) (*net.TCPListener, error) {
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return nil, err
}
mu.Lock()
defer mu.Unlock()
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return nil, err
}
return l, nil
}
// getPort generates a docker PortBinding by using the port provided.
// If port is set to 0, the next avaliable port will be used.
// The listener will be closed in the case of an error, otherwise it will be left open.
// This allows multiple getPort calls to find multiple available ports
// before closing them so they are available for the PortBinding.
func getPort(port int) (nat.PortBinding, *net.TCPListener, error) {
l, err := openListener(port)
if err != nil {
l.Close()
return nat.PortBinding{}, nil, err
}
return nat.PortBinding{
HostIP: "0.0.0.0",
HostPort: fmt.Sprint(l.Addr().(*net.TCPAddr).Port),
}, l, nil
}
// GeneratePortBindings will find open ports on the local
// machine and create a PortBinding for every port in the portSet.
// If a port is already bound, it will use that port as an override.
func GeneratePortBindings(pairs nat.PortMap) (nat.PortMap, Listeners, error) {
m := make(nat.PortMap)
listeners := make(Listeners, 0, len(pairs))
var pb nat.PortBinding
var l *net.TCPListener
var err error
for p, bind := range pairs {
if len(bind) == 0 {
// random port
pb, l, err = getPort(0)
} else {
var pNum int
if pNum, err = strconv.Atoi(bind[0].HostPort); err != nil {
return nat.PortMap{}, nil, err
}
pb, l, err = getPort(pNum)
}
if err != nil {
listeners.CloseAll()
return nat.PortMap{}, nil, err
}
listeners = append(listeners, l)
m[p] = []nat.PortBinding{pb}
}
return m, listeners, nil
}