Browse code

New package `nat`: utilities for manipulating the text description of network ports.

This facilitates the refactoring of commands.go

Docker-DCO-1.1-Signed-off-by: Solomon Hykes <solomon@docker.com> (github: shykes)

Solomon Hykes authored on 2014/02/12 09:48:44
Showing 13 changed files
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/dotcloud/docker/archive"
13 13
 	"github.com/dotcloud/docker/auth"
14 14
 	"github.com/dotcloud/docker/engine"
15
+	"github.com/dotcloud/docker/nat"
15 16
 	flag "github.com/dotcloud/docker/pkg/mflag"
16 17
 	"github.com/dotcloud/docker/pkg/sysinfo"
17 18
 	"github.com/dotcloud/docker/pkg/term"
... ...
@@ -799,7 +800,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
799 799
 		return err
800 800
 	}
801 801
 
802
-	if frontends, exists := out.NetworkSettings.Ports[Port(port+"/"+proto)]; exists && frontends != nil {
802
+	if frontends, exists := out.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
803 803
 		for _, frontend := range frontends {
804 804
 			fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
805 805
 		}
... ...
@@ -1792,7 +1793,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
1792 1792
 	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
1793 1793
 	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
1794 1794
 
1795
-	cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat))
1795
+	cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
1796 1796
 	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
1797 1797
 	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
1798 1798
 	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
... ...
@@ -1885,7 +1886,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
1885 1885
 		domainname = parts[1]
1886 1886
 	}
1887 1887
 
1888
-	ports, portBindings, err := parsePortSpecs(flPublish.GetAll())
1888
+	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
1889 1889
 	if err != nil {
1890 1890
 		return nil, nil, cmd, err
1891 1891
 	}
... ...
@@ -1895,7 +1896,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
1895 1895
 		if strings.Contains(e, ":") {
1896 1896
 			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
1897 1897
 		}
1898
-		p := NewPort(splitProtoPort(e))
1898
+		p := nat.NewPort(nat.SplitProtoPort(e))
1899 1899
 		if _, exists := ports[p]; !exists {
1900 1900
 			ports[p] = struct{}{}
1901 1901
 		}
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"github.com/dotcloud/docker/nat"
4 5
 	"testing"
5 6
 )
6 7
 
... ...
@@ -125,7 +126,7 @@ func TestMergeConfig(t *testing.T) {
125 125
 		t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
126 126
 	}
127 127
 
128
-	ports, _, err := parsePortSpecs([]string{"0000"})
128
+	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
129 129
 	if err != nil {
130 130
 		t.Error(err)
131 131
 	}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/dotcloud/docker/engine"
9 9
 	"github.com/dotcloud/docker/execdriver"
10 10
 	"github.com/dotcloud/docker/graphdriver"
11
+	"github.com/dotcloud/docker/nat"
11 12
 	"github.com/dotcloud/docker/pkg/mount"
12 13
 	"github.com/dotcloud/docker/pkg/term"
13 14
 	"github.com/dotcloud/docker/utils"
... ...
@@ -86,7 +87,7 @@ type Config struct {
86 86
 	AttachStdout    bool
87 87
 	AttachStderr    bool
88 88
 	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
89
-	ExposedPorts    map[Port]struct{}
89
+	ExposedPorts    map[nat.Port]struct{}
90 90
 	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
91 91
 	OpenStdin       bool // Open stdin
92 92
 	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
... ...
@@ -147,7 +148,7 @@ type HostConfig struct {
147 147
 	ContainerIDFile string
148 148
 	LxcConf         []KeyValuePair
149 149
 	Privileged      bool
150
-	PortBindings    map[Port][]PortBinding
150
+	PortBindings    nat.PortMap
151 151
 	Links           []string
152 152
 	PublishAllPorts bool
153 153
 }
... ...
@@ -189,38 +190,7 @@ type KeyValuePair struct {
189 189
 	Value string
190 190
 }
191 191
 
192
-type PortBinding struct {
193
-	HostIp   string
194
-	HostPort string
195
-}
196
-
197
-// 80/tcp
198
-type Port string
199
-
200
-func (p Port) Proto() string {
201
-	parts := strings.Split(string(p), "/")
202
-	if len(parts) == 1 {
203
-		return "tcp"
204
-	}
205
-	return parts[1]
206
-}
207
-
208
-func (p Port) Port() string {
209
-	return strings.Split(string(p), "/")[0]
210
-}
211
-
212
-func (p Port) Int() int {
213
-	i, err := parsePort(p.Port())
214
-	if err != nil {
215
-		panic(err)
216
-	}
217
-	return i
218
-}
219
-
220
-func NewPort(proto, port string) Port {
221
-	return Port(fmt.Sprintf("%s/%s", port, proto))
222
-}
223
-
192
+// FIXME: move deprecated port stuff to nat to clean up the core.
224 193
 type PortMapping map[string]string // Deprecated
225 194
 
226 195
 type NetworkSettings struct {
... ...
@@ -229,13 +199,13 @@ type NetworkSettings struct {
229 229
 	Gateway     string
230 230
 	Bridge      string
231 231
 	PortMapping map[string]PortMapping // Deprecated
232
-	Ports       map[Port][]PortBinding
232
+	Ports       nat.PortMap
233 233
 }
234 234
 
235 235
 func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
236 236
 	var outs = engine.NewTable("", 0)
237 237
 	for port, bindings := range settings.Ports {
238
-		p, _ := parsePort(port.Port())
238
+		p, _ := nat.ParsePort(port.Port())
239 239
 		if len(bindings) == 0 {
240 240
 			out := &engine.Env{}
241 241
 			out.SetInt("PublicPort", p)
... ...
@@ -245,7 +215,7 @@ func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
245 245
 		}
246 246
 		for _, binding := range bindings {
247 247
 			out := &engine.Env{}
248
-			h, _ := parsePort(binding.HostPort)
248
+			h, _ := nat.ParsePort(binding.HostPort)
249 249
 			out.SetInt("PrivatePort", p)
250 250
 			out.SetInt("PublicPort", h)
251 251
 			out.Set("Type", port.Proto())
... ...
@@ -1152,8 +1122,8 @@ func (container *Container) allocateNetwork() error {
1152 1152
 	}
1153 1153
 
1154 1154
 	var (
1155
-		portSpecs = make(map[Port]struct{})
1156
-		bindings  = make(map[Port][]PortBinding)
1155
+		portSpecs = make(nat.PortSet)
1156
+		bindings  = make(nat.PortMap)
1157 1157
 	)
1158 1158
 
1159 1159
 	if !container.State.IsGhost() {
... ...
@@ -1177,7 +1147,7 @@ func (container *Container) allocateNetwork() error {
1177 1177
 	for port := range portSpecs {
1178 1178
 		binding := bindings[port]
1179 1179
 		if container.hostConfig.PublishAllPorts && len(binding) == 0 {
1180
-			binding = append(binding, PortBinding{})
1180
+			binding = append(binding, nat.PortBinding{})
1181 1181
 		}
1182 1182
 
1183 1183
 		for i := 0; i < len(binding); i++ {
... ...
@@ -1593,7 +1563,7 @@ func (container *Container) Copy(resource string) (archive.Archive, error) {
1593 1593
 }
1594 1594
 
1595 1595
 // Returns true if the container exposes a certain port
1596
-func (container *Container) Exposes(p Port) bool {
1596
+func (container *Container) Exposes(p nat.Port) bool {
1597 1597
 	_, exists := container.Config.ExposedPorts[p]
1598 1598
 	return exists
1599 1599
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"github.com/dotcloud/docker/nat"
4 5
 	"testing"
5 6
 )
6 7
 
... ...
@@ -22,7 +23,7 @@ func TestParseLxcConfOpt(t *testing.T) {
22 22
 }
23 23
 
24 24
 func TestParseNetworkOptsPrivateOnly(t *testing.T) {
25
-	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::80"})
25
+	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
26 26
 	if err != nil {
27 27
 		t.Fatal(err)
28 28
 	}
... ...
@@ -64,7 +65,7 @@ func TestParseNetworkOptsPrivateOnly(t *testing.T) {
64 64
 }
65 65
 
66 66
 func TestParseNetworkOptsPublic(t *testing.T) {
67
-	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100:8080:80"})
67
+	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:8080:80"})
68 68
 	if err != nil {
69 69
 		t.Fatal(err)
70 70
 	}
... ...
@@ -106,7 +107,7 @@ func TestParseNetworkOptsPublic(t *testing.T) {
106 106
 }
107 107
 
108 108
 func TestParseNetworkOptsUdp(t *testing.T) {
109
-	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::6000/udp"})
109
+	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
110 110
 	if err != nil {
111 111
 		t.Fatal(err)
112 112
 	}
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"fmt"
6 6
 	"github.com/dotcloud/docker"
7 7
 	"github.com/dotcloud/docker/engine"
8
+	"github.com/dotcloud/docker/nat"
8 9
 	"github.com/dotcloud/docker/sysinit"
9 10
 	"github.com/dotcloud/docker/utils"
10 11
 	"io"
... ...
@@ -368,7 +369,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
368 368
 		eng     = NewTestEngine(t)
369 369
 		runtime = mkRuntimeFromEngine(eng, t)
370 370
 		port    = 5554
371
-		p       docker.Port
371
+		p       nat.Port
372 372
 	)
373 373
 	defer func() {
374 374
 		if err != nil {
... ...
@@ -387,8 +388,8 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
387 387
 		} else {
388 388
 			t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
389 389
 		}
390
-		ep := make(map[docker.Port]struct{}, 1)
391
-		p = docker.Port(fmt.Sprintf("%s/%s", strPort, proto))
390
+		ep := make(map[nat.Port]struct{}, 1)
391
+		p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto))
392 392
 		ep[p] = struct{}{}
393 393
 
394 394
 		jobCreate := eng.Job("create")
... ...
@@ -411,8 +412,8 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
411 411
 	}
412 412
 
413 413
 	jobStart := eng.Job("start", id)
414
-	portBindings := make(map[docker.Port][]docker.PortBinding)
415
-	portBindings[p] = []docker.PortBinding{
414
+	portBindings := make(map[nat.Port][]nat.PortBinding)
415
+	portBindings[p] = []nat.PortBinding{
416 416
 		{},
417 417
 	}
418 418
 	if err := jobStart.SetenvJson("PortsBindings", portBindings); err != nil {
... ...
@@ -3,6 +3,7 @@ package docker
3 3
 import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker/engine"
6
+	"github.com/dotcloud/docker/nat"
6 7
 	"path"
7 8
 	"strings"
8 9
 )
... ...
@@ -12,7 +13,7 @@ type Link struct {
12 12
 	ChildIP          string
13 13
 	Name             string
14 14
 	ChildEnvironment []string
15
-	Ports            []Port
15
+	Ports            []nat.Port
16 16
 	IsEnabled        bool
17 17
 	eng              *engine.Engine
18 18
 }
... ...
@@ -25,7 +26,7 @@ func NewLink(parent, child *Container, name string, eng *engine.Engine) (*Link,
25 25
 		return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, name)
26 26
 	}
27 27
 
28
-	ports := make([]Port, len(child.Config.ExposedPorts))
28
+	ports := make([]nat.Port, len(child.Config.ExposedPorts))
29 29
 	var i int
30 30
 	for p := range child.Config.ExposedPorts {
31 31
 		ports[i] = p
... ...
@@ -85,14 +86,14 @@ func (l *Link) ToEnv() []string {
85 85
 }
86 86
 
87 87
 // Default port rules
88
-func (l *Link) getDefaultPort() *Port {
89
-	var p Port
88
+func (l *Link) getDefaultPort() *nat.Port {
89
+	var p nat.Port
90 90
 	i := len(l.Ports)
91 91
 
92 92
 	if i == 0 {
93 93
 		return nil
94 94
 	} else if i > 1 {
95
-		sortPorts(l.Ports, func(ip, jp Port) bool {
95
+		nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
96 96
 			// If the two ports have the same number, tcp takes priority
97 97
 			// Sort in desc order
98 98
 			return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"github.com/dotcloud/docker/nat"
4 5
 	"strings"
5 6
 	"testing"
6 7
 )
... ...
@@ -22,9 +23,9 @@ func TestLinkNew(t *testing.T) {
22 22
 	from := newMockLinkContainer(fromID, "172.0.17.2")
23 23
 	from.Config.Env = []string{}
24 24
 	from.State = State{Running: true}
25
-	ports := make(map[Port]struct{})
25
+	ports := make(nat.PortSet)
26 26
 
27
-	ports[Port("6379/tcp")] = struct{}{}
27
+	ports[nat.Port("6379/tcp")] = struct{}{}
28 28
 
29 29
 	from.Config.ExposedPorts = ports
30 30
 
... ...
@@ -51,7 +52,7 @@ func TestLinkNew(t *testing.T) {
51 51
 		t.Fail()
52 52
 	}
53 53
 	for _, p := range link.Ports {
54
-		if p != Port("6379/tcp") {
54
+		if p != nat.Port("6379/tcp") {
55 55
 			t.Fail()
56 56
 		}
57 57
 	}
... ...
@@ -64,9 +65,9 @@ func TestLinkEnv(t *testing.T) {
64 64
 	from := newMockLinkContainer(fromID, "172.0.17.2")
65 65
 	from.Config.Env = []string{"PASSWORD=gordon"}
66 66
 	from.State = State{Running: true}
67
-	ports := make(map[Port]struct{})
67
+	ports := make(nat.PortSet)
68 68
 
69
-	ports[Port("6379/tcp")] = struct{}{}
69
+	ports[nat.Port("6379/tcp")] = struct{}{}
70 70
 
71 71
 	from.Config.ExposedPorts = ports
72 72
 
73 73
new file mode 100644
... ...
@@ -0,0 +1,133 @@
0
+package nat
1
+
2
+// nat is a convenience package for docker's manipulation of strings describing
3
+// network ports.
4
+
5
+import (
6
+	"fmt"
7
+	"github.com/dotcloud/docker/utils"
8
+	"strconv"
9
+	"strings"
10
+)
11
+
12
+const (
13
+	PortSpecTemplate       = "ip:hostPort:containerPort"
14
+	PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort"
15
+)
16
+
17
+type PortBinding struct {
18
+	HostIp   string
19
+	HostPort string
20
+}
21
+
22
+type PortMap map[Port][]PortBinding
23
+
24
+type PortSet map[Port]struct{}
25
+
26
+// 80/tcp
27
+type Port string
28
+
29
+func NewPort(proto, port string) Port {
30
+	return Port(fmt.Sprintf("%s/%s", port, proto))
31
+}
32
+
33
+func ParsePort(rawPort string) (int, error) {
34
+	port, err := strconv.ParseUint(rawPort, 10, 16)
35
+	if err != nil {
36
+		return 0, err
37
+	}
38
+	return int(port), nil
39
+}
40
+
41
+func (p Port) Proto() string {
42
+	parts := strings.Split(string(p), "/")
43
+	if len(parts) == 1 {
44
+		return "tcp"
45
+	}
46
+	return parts[1]
47
+}
48
+
49
+func (p Port) Port() string {
50
+	return strings.Split(string(p), "/")[0]
51
+}
52
+
53
+func (p Port) Int() int {
54
+	i, err := ParsePort(p.Port())
55
+	if err != nil {
56
+		panic(err)
57
+	}
58
+	return i
59
+}
60
+
61
+// Splits a port in the format of port/proto
62
+func SplitProtoPort(rawPort string) (string, string) {
63
+	parts := strings.Split(rawPort, "/")
64
+	l := len(parts)
65
+	if l == 0 {
66
+		return "", ""
67
+	}
68
+	if l == 1 {
69
+		return "tcp", rawPort
70
+	}
71
+	return parts[0], parts[1]
72
+}
73
+
74
+// We will receive port specs in the format of ip:public:private/proto and these need to be
75
+// parsed in the internal types
76
+func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
77
+	var (
78
+		exposedPorts = make(map[Port]struct{}, len(ports))
79
+		bindings     = make(map[Port][]PortBinding)
80
+	)
81
+
82
+	for _, rawPort := range ports {
83
+		proto := "tcp"
84
+
85
+		if i := strings.LastIndex(rawPort, "/"); i != -1 {
86
+			proto = rawPort[i+1:]
87
+			rawPort = rawPort[:i]
88
+		}
89
+		if !strings.Contains(rawPort, ":") {
90
+			rawPort = fmt.Sprintf("::%s", rawPort)
91
+		} else if len(strings.Split(rawPort, ":")) == 2 {
92
+			rawPort = fmt.Sprintf(":%s", rawPort)
93
+		}
94
+
95
+		parts, err := utils.PartParser(PortSpecTemplate, rawPort)
96
+		if err != nil {
97
+			return nil, nil, err
98
+		}
99
+
100
+		var (
101
+			containerPort = parts["containerPort"]
102
+			rawIp         = parts["ip"]
103
+			hostPort      = parts["hostPort"]
104
+		)
105
+
106
+		if containerPort == "" {
107
+			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
108
+		}
109
+		if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
110
+			return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
111
+		}
112
+		if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
113
+			return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
114
+		}
115
+
116
+		port := NewPort(proto, containerPort)
117
+		if _, exists := exposedPorts[port]; !exists {
118
+			exposedPorts[port] = struct{}{}
119
+		}
120
+
121
+		binding := PortBinding{
122
+			HostIp:   rawIp,
123
+			HostPort: hostPort,
124
+		}
125
+		bslice, exists := bindings[port]
126
+		if !exists {
127
+			bslice = []PortBinding{}
128
+		}
129
+		bindings[port] = append(bslice, binding)
130
+	}
131
+	return exposedPorts, bindings, nil
132
+}
0 133
new file mode 100644
... ...
@@ -0,0 +1,28 @@
0
+package nat
1
+
2
+import "sort"
3
+
4
+type portSorter struct {
5
+	ports []Port
6
+	by    func(i, j Port) bool
7
+}
8
+
9
+func (s *portSorter) Len() int {
10
+	return len(s.ports)
11
+}
12
+
13
+func (s *portSorter) Swap(i, j int) {
14
+	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
15
+}
16
+
17
+func (s *portSorter) Less(i, j int) bool {
18
+	ip := s.ports[i]
19
+	jp := s.ports[j]
20
+
21
+	return s.by(ip, jp)
22
+}
23
+
24
+func Sort(ports []Port, predicate func(i, j Port) bool) {
25
+	s := &portSorter{ports, predicate}
26
+	sort.Sort(s)
27
+}
0 28
new file mode 100644
... ...
@@ -0,0 +1,41 @@
0
+package nat
1
+
2
+import (
3
+	"fmt"
4
+	"testing"
5
+)
6
+
7
+func TestSortUniquePorts(t *testing.T) {
8
+	ports := []Port{
9
+		Port("6379/tcp"),
10
+		Port("22/tcp"),
11
+	}
12
+
13
+	Sort(ports, func(ip, jp Port) bool {
14
+		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
15
+	})
16
+
17
+	first := ports[0]
18
+	if fmt.Sprint(first) != "22/tcp" {
19
+		t.Log(fmt.Sprint(first))
20
+		t.Fail()
21
+	}
22
+}
23
+
24
+func TestSortSamePortWithDifferentProto(t *testing.T) {
25
+	ports := []Port{
26
+		Port("8888/tcp"),
27
+		Port("8888/udp"),
28
+		Port("6379/tcp"),
29
+		Port("6379/udp"),
30
+	}
31
+
32
+	Sort(ports, func(ip, jp Port) bool {
33
+		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
34
+	})
35
+
36
+	first := ports[0]
37
+	if fmt.Sprint(first) != "6379/tcp" {
38
+		t.Fail()
39
+	}
40
+}
... ...
@@ -2,31 +2,6 @@ package docker
2 2
 
3 3
 import "sort"
4 4
 
5
-type portSorter struct {
6
-	ports []Port
7
-	by    func(i, j Port) bool
8
-}
9
-
10
-func (s *portSorter) Len() int {
11
-	return len(s.ports)
12
-}
13
-
14
-func (s *portSorter) Swap(i, j int) {
15
-	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
16
-}
17
-
18
-func (s *portSorter) Less(i, j int) bool {
19
-	ip := s.ports[i]
20
-	jp := s.ports[j]
21
-
22
-	return s.by(ip, jp)
23
-}
24
-
25
-func sortPorts(ports []Port, predicate func(i, j Port) bool) {
26
-	s := &portSorter{ports, predicate}
27
-	sort.Sort(s)
28
-}
29
-
30 5
 type containerSorter struct {
31 6
 	containers []*Container
32 7
 	by         func(i, j *Container) bool
33 8
deleted file mode 100644
... ...
@@ -1,41 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"fmt"
5
-	"testing"
6
-)
7
-
8
-func TestSortUniquePorts(t *testing.T) {
9
-	ports := []Port{
10
-		Port("6379/tcp"),
11
-		Port("22/tcp"),
12
-	}
13
-
14
-	sortPorts(ports, func(ip, jp Port) bool {
15
-		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
16
-	})
17
-
18
-	first := ports[0]
19
-	if fmt.Sprint(first) != "22/tcp" {
20
-		t.Log(fmt.Sprint(first))
21
-		t.Fail()
22
-	}
23
-}
24
-
25
-func TestSortSamePortWithDifferentProto(t *testing.T) {
26
-	ports := []Port{
27
-		Port("8888/tcp"),
28
-		Port("8888/udp"),
29
-		Port("6379/tcp"),
30
-		Port("6379/udp"),
31
-	}
32
-
33
-	sortPorts(ports, func(ip, jp Port) bool {
34
-		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
35
-	})
36
-
37
-	first := ports[0]
38
-	if fmt.Sprint(first) != "6379/tcp" {
39
-		t.Fail()
40
-	}
41
-}
... ...
@@ -3,10 +3,10 @@ package docker
3 3
 import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker/archive"
6
+	"github.com/dotcloud/docker/nat"
6 7
 	"github.com/dotcloud/docker/pkg/namesgenerator"
7 8
 	"github.com/dotcloud/docker/utils"
8 9
 	"io"
9
-	"strconv"
10 10
 	"strings"
11 11
 	"sync/atomic"
12 12
 )
... ...
@@ -98,7 +98,7 @@ func MergeConfig(userConf, imageConf *Config) error {
98 98
 		userConf.ExposedPorts = imageConf.ExposedPorts
99 99
 	} else if imageConf.ExposedPorts != nil {
100 100
 		if userConf.ExposedPorts == nil {
101
-			userConf.ExposedPorts = make(map[Port]struct{})
101
+			userConf.ExposedPorts = make(nat.PortSet)
102 102
 		}
103 103
 		for port := range imageConf.ExposedPorts {
104 104
 			if _, exists := userConf.ExposedPorts[port]; !exists {
... ...
@@ -109,9 +109,9 @@ func MergeConfig(userConf, imageConf *Config) error {
109 109
 
110 110
 	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
111 111
 		if userConf.ExposedPorts == nil {
112
-			userConf.ExposedPorts = make(map[Port]struct{})
112
+			userConf.ExposedPorts = make(nat.PortSet)
113 113
 		}
114
-		ports, _, err := parsePortSpecs(userConf.PortSpecs)
114
+		ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
115 115
 		if err != nil {
116 116
 			return err
117 117
 		}
... ...
@@ -125,10 +125,10 @@ func MergeConfig(userConf, imageConf *Config) error {
125 125
 	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
126 126
 		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
127 127
 		if userConf.ExposedPorts == nil {
128
-			userConf.ExposedPorts = make(map[Port]struct{})
128
+			userConf.ExposedPorts = make(nat.PortSet)
129 129
 		}
130 130
 
131
-		ports, _, err := parsePortSpecs(imageConf.PortSpecs)
131
+		ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
132 132
 		if err != nil {
133 133
 			return err
134 134
 		}
... ...
@@ -212,96 +212,9 @@ func parseLxcOpt(opt string) (string, string, error) {
212 212
 	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
213 213
 }
214 214
 
215
-// FIXME: network related stuff (including parsing) should be grouped in network file
216
-const (
217
-	PortSpecTemplate       = "ip:hostPort:containerPort"
218
-	PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort"
219
-)
220
-
221
-// We will receive port specs in the format of ip:public:private/proto and these need to be
222
-// parsed in the internal types
223
-func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
224
-	var (
225
-		exposedPorts = make(map[Port]struct{}, len(ports))
226
-		bindings     = make(map[Port][]PortBinding)
227
-	)
228
-
229
-	for _, rawPort := range ports {
230
-		proto := "tcp"
231
-
232
-		if i := strings.LastIndex(rawPort, "/"); i != -1 {
233
-			proto = rawPort[i+1:]
234
-			rawPort = rawPort[:i]
235
-		}
236
-		if !strings.Contains(rawPort, ":") {
237
-			rawPort = fmt.Sprintf("::%s", rawPort)
238
-		} else if len(strings.Split(rawPort, ":")) == 2 {
239
-			rawPort = fmt.Sprintf(":%s", rawPort)
240
-		}
241
-
242
-		parts, err := utils.PartParser(PortSpecTemplate, rawPort)
243
-		if err != nil {
244
-			return nil, nil, err
245
-		}
246
-
247
-		var (
248
-			containerPort = parts["containerPort"]
249
-			rawIp         = parts["ip"]
250
-			hostPort      = parts["hostPort"]
251
-		)
252
-
253
-		if containerPort == "" {
254
-			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
255
-		}
256
-		if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
257
-			return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
258
-		}
259
-		if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
260
-			return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
261
-		}
262
-
263
-		port := NewPort(proto, containerPort)
264
-		if _, exists := exposedPorts[port]; !exists {
265
-			exposedPorts[port] = struct{}{}
266
-		}
267
-
268
-		binding := PortBinding{
269
-			HostIp:   rawIp,
270
-			HostPort: hostPort,
271
-		}
272
-		bslice, exists := bindings[port]
273
-		if !exists {
274
-			bslice = []PortBinding{}
275
-		}
276
-		bindings[port] = append(bslice, binding)
277
-	}
278
-	return exposedPorts, bindings, nil
279
-}
280
-
281
-// Splits a port in the format of port/proto
282
-func splitProtoPort(rawPort string) (string, string) {
283
-	parts := strings.Split(rawPort, "/")
284
-	l := len(parts)
285
-	if l == 0 {
286
-		return "", ""
287
-	}
288
-	if l == 1 {
289
-		return "tcp", rawPort
290
-	}
291
-	return parts[0], parts[1]
292
-}
293
-
294
-func parsePort(rawPort string) (int, error) {
295
-	port, err := strconv.ParseUint(rawPort, 10, 16)
296
-	if err != nil {
297
-		return 0, err
298
-	}
299
-	return int(port), nil
300
-}
301
-
302 215
 func migratePortMappings(config *Config, hostConfig *HostConfig) error {
303 216
 	if config.PortSpecs != nil {
304
-		ports, bindings, err := parsePortSpecs(config.PortSpecs)
217
+		ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
305 218
 		if err != nil {
306 219
 			return err
307 220
 		}
... ...
@@ -314,7 +227,7 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
314 314
 		}
315 315
 
316 316
 		if config.ExposedPorts == nil {
317
-			config.ExposedPorts = make(map[Port]struct{}, len(ports))
317
+			config.ExposedPorts = make(nat.PortSet, len(ports))
318 318
 		}
319 319
 		for k, v := range ports {
320 320
 			config.ExposedPorts[k] = v