Browse code

Refactoring portallocator

Faster, more documented, less code.
Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)

LK4D4 authored on 2014/07/02 02:59:11
Showing 1 changed files
... ...
@@ -12,10 +12,22 @@ type portMap struct {
12 12
 	last int
13 13
 }
14 14
 
15
-type (
16
-	protocolMap map[string]*portMap
17
-	ipMapping   map[string]protocolMap
18
-)
15
+func newPortMap() *portMap {
16
+	return &portMap{
17
+		p: map[int]struct{}{},
18
+	}
19
+}
20
+
21
+type protoMap map[string]*portMap
22
+
23
+func newProtoMap() protoMap {
24
+	return protoMap{
25
+		"tcp": newPortMap(),
26
+		"udp": newPortMap(),
27
+	}
28
+}
29
+
30
+type ipMapping map[string]protoMap
19 31
 
20 32
 const (
21 33
 	BeginPortRange = 49153
... ...
@@ -62,107 +74,83 @@ func (e ErrPortAlreadyAllocated) Error() string {
62 62
 	return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
63 63
 }
64 64
 
65
+// RequestPort requests new port from global ports pool for specified ip and proto.
66
+// If port is 0 it returns first free port. Otherwise it cheks port availability
67
+// in pool and return that port or error if port is already busy.
65 68
 func RequestPort(ip net.IP, proto string, port int) (int, error) {
66 69
 	mutex.Lock()
67 70
 	defer mutex.Unlock()
68 71
 
69
-	if err := validateProto(proto); err != nil {
70
-		return 0, err
72
+	if proto != "tcp" && proto != "udp" {
73
+		return 0, ErrUnknownProtocol
71 74
 	}
72 75
 
73
-	ip = getDefault(ip)
74
-
75
-	mapping := getOrCreate(ip)
76
-
76
+	if ip == nil {
77
+		ip = defaultIP
78
+	}
79
+	ipstr := ip.String()
80
+	protomap, ok := globalMap[ipstr]
81
+	if !ok {
82
+		protomap = newProtoMap()
83
+		globalMap[ipstr] = protomap
84
+	}
85
+	mapping := protomap[proto]
77 86
 	if port > 0 {
78
-		if _, ok := mapping[proto].p[port]; !ok {
79
-			mapping[proto].p[port] = struct{}{}
87
+		if _, ok := mapping.p[port]; !ok {
88
+			mapping.p[port] = struct{}{}
80 89
 			return port, nil
81
-		} else {
82
-			return 0, NewErrPortAlreadyAllocated(ip.String(), port)
83
-		}
84
-	} else {
85
-		port, err := findPort(ip, proto)
86
-
87
-		if err != nil {
88
-			return 0, err
89 90
 		}
91
+		return 0, NewErrPortAlreadyAllocated(ipstr, port)
92
+	}
90 93
 
91
-		return port, nil
94
+	port, err := mapping.findPort()
95
+	if err != nil {
96
+		return 0, err
92 97
 	}
98
+	return port, nil
93 99
 }
94 100
 
101
+// ReleasePort releases port from global ports pool for specified ip and proto.
95 102
 func ReleasePort(ip net.IP, proto string, port int) error {
96 103
 	mutex.Lock()
97 104
 	defer mutex.Unlock()
98 105
 
99
-	ip = getDefault(ip)
100
-
101
-	mapping := getOrCreate(ip)[proto]
102
-	delete(mapping.p, port)
103
-
106
+	if ip == nil {
107
+		ip = defaultIP
108
+	}
109
+	protomap, ok := globalMap[ip.String()]
110
+	if !ok {
111
+		return nil
112
+	}
113
+	delete(protomap[proto].p, port)
104 114
 	return nil
105 115
 }
106 116
 
117
+// ReleaseAll releases all ports for all ips.
107 118
 func ReleaseAll() error {
108 119
 	mutex.Lock()
109
-	defer mutex.Unlock()
110
-
111 120
 	globalMap = ipMapping{}
112
-
121
+	mutex.Unlock()
113 122
 	return nil
114 123
 }
115 124
 
116
-func getOrCreate(ip net.IP) protocolMap {
117
-	ipstr := ip.String()
118
-
119
-	if _, ok := globalMap[ipstr]; !ok {
120
-		globalMap[ipstr] = protocolMap{
121
-			"tcp": &portMap{p: map[int]struct{}{}, last: 0},
122
-			"udp": &portMap{p: map[int]struct{}{}, last: 0},
123
-		}
124
-	}
125
-
126
-	return globalMap[ipstr]
127
-}
128
-
129
-func findPort(ip net.IP, proto string) (int, error) {
130
-	mapping := getOrCreate(ip)[proto]
131
-
132
-	if mapping.last == 0 {
133
-		mapping.p[BeginPortRange] = struct{}{}
134
-		mapping.last = BeginPortRange
125
+func (pm *portMap) findPort() (int, error) {
126
+	if pm.last == 0 {
127
+		pm.p[BeginPortRange] = struct{}{}
128
+		pm.last = BeginPortRange
135 129
 		return BeginPortRange, nil
136 130
 	}
137 131
 
138
-	for port := mapping.last + 1; port != mapping.last; port++ {
132
+	for port := pm.last + 1; port != pm.last; port++ {
139 133
 		if port > EndPortRange {
140 134
 			port = BeginPortRange
141 135
 		}
142 136
 
143
-		if _, ok := mapping.p[port]; !ok {
144
-			mapping.p[port] = struct{}{}
145
-			mapping.last = port
137
+		if _, ok := pm.p[port]; !ok {
138
+			pm.p[port] = struct{}{}
139
+			pm.last = port
146 140
 			return port, nil
147 141
 		}
148
-
149 142
 	}
150
-
151 143
 	return 0, ErrAllPortsAllocated
152 144
 }
153
-
154
-func getDefault(ip net.IP) net.IP {
155
-	if ip == nil {
156
-		return defaultIP
157
-	}
158
-
159
-	return ip
160
-}
161
-
162
-func validateProto(proto string) error {
163
-	if proto != "tcp" && proto != "udp" {
164
-		return ErrUnknownProtocol
165
-	}
166
-
167
-	return nil
168
-}