Kaynağa Gözat

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 11 yıl önce
ebeveyn
işleme
3ecd8ff0c8
12 değiştirilmiş dosya ile 212 ekleme ve 187 silme
  1. 5 4
      commands.go
  2. 2 1
      config_test.go
  3. 11 41
      container.go
  4. 4 3
      container_unit_test.go
  5. 6 5
      integration/runtime_test.go
  6. 6 5
      links.go
  7. 6 5
      links_test.go
  8. 133 0
      nat/nat.go
  9. 28 0
      nat/sort.go
  10. 3 3
      nat/sort_test.go
  11. 0 25
      sorter.go
  12. 8 95
      utils.go

+ 5 - 4
commands.go

@@ -12,6 +12,7 @@ import (
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/auth"
 	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/nat"
 	flag "github.com/dotcloud/docker/pkg/mflag"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/term"
@@ -799,7 +800,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
 		return err
 	}
 
-	if frontends, exists := out.NetworkSettings.Ports[Port(port+"/"+proto)]; exists && frontends != nil {
+	if frontends, exists := out.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
 		for _, frontend := range frontends {
 			fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
 		}
@@ -1792,7 +1793,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
 	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
 
-	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))
+	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))
 	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
 	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
 	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
 		domainname = parts[1]
 	}
 
-	ports, portBindings, err := parsePortSpecs(flPublish.GetAll())
+	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
 	if err != nil {
 		return nil, nil, cmd, err
 	}
@@ -1895,7 +1896,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
 		if strings.Contains(e, ":") {
 			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
 		}
-		p := NewPort(splitProtoPort(e))
+		p := nat.NewPort(nat.SplitProtoPort(e))
 		if _, exists := ports[p]; !exists {
 			ports[p] = struct{}{}
 		}

+ 2 - 1
config_test.go

@@ -1,6 +1,7 @@
 package docker
 
 import (
+	"github.com/dotcloud/docker/nat"
 	"testing"
 )
 
@@ -125,7 +126,7 @@ func TestMergeConfig(t *testing.T) {
 		t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
 	}
 
-	ports, _, err := parsePortSpecs([]string{"0000"})
+	ports, _, err := nat.ParsePortSpecs([]string{"0000"})
 	if err != nil {
 		t.Error(err)
 	}

+ 11 - 41
container.go

@@ -8,6 +8,7 @@ import (
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/execdriver"
 	"github.com/dotcloud/docker/graphdriver"
+	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/pkg/mount"
 	"github.com/dotcloud/docker/pkg/term"
 	"github.com/dotcloud/docker/utils"
@@ -86,7 +87,7 @@ type Config struct {
 	AttachStdout    bool
 	AttachStderr    bool
 	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
-	ExposedPorts    map[Port]struct{}
+	ExposedPorts    map[nat.Port]struct{}
 	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.
@@ -147,7 +148,7 @@ type HostConfig struct {
 	ContainerIDFile string
 	LxcConf         []KeyValuePair
 	Privileged      bool
-	PortBindings    map[Port][]PortBinding
+	PortBindings    nat.PortMap
 	Links           []string
 	PublishAllPorts bool
 }
@@ -189,38 +190,7 @@ type KeyValuePair struct {
 	Value string
 }
 
-type PortBinding struct {
-	HostIp   string
-	HostPort string
-}
-
-// 80/tcp
-type Port string
-
-func (p Port) Proto() string {
-	parts := strings.Split(string(p), "/")
-	if len(parts) == 1 {
-		return "tcp"
-	}
-	return parts[1]
-}
-
-func (p Port) Port() string {
-	return strings.Split(string(p), "/")[0]
-}
-
-func (p Port) Int() int {
-	i, err := parsePort(p.Port())
-	if err != nil {
-		panic(err)
-	}
-	return i
-}
-
-func NewPort(proto, port string) Port {
-	return Port(fmt.Sprintf("%s/%s", port, proto))
-}
-
+// FIXME: move deprecated port stuff to nat to clean up the core.
 type PortMapping map[string]string // Deprecated
 
 type NetworkSettings struct {
@@ -229,13 +199,13 @@ type NetworkSettings struct {
 	Gateway     string
 	Bridge      string
 	PortMapping map[string]PortMapping // Deprecated
-	Ports       map[Port][]PortBinding
+	Ports       nat.PortMap
 }
 
 func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
 	var outs = engine.NewTable("", 0)
 	for port, bindings := range settings.Ports {
-		p, _ := parsePort(port.Port())
+		p, _ := nat.ParsePort(port.Port())
 		if len(bindings) == 0 {
 			out := &engine.Env{}
 			out.SetInt("PublicPort", p)
@@ -245,7 +215,7 @@ func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
 		}
 		for _, binding := range bindings {
 			out := &engine.Env{}
-			h, _ := parsePort(binding.HostPort)
+			h, _ := nat.ParsePort(binding.HostPort)
 			out.SetInt("PrivatePort", p)
 			out.SetInt("PublicPort", h)
 			out.Set("Type", port.Proto())
@@ -1152,8 +1122,8 @@ func (container *Container) allocateNetwork() error {
 	}
 
 	var (
-		portSpecs = make(map[Port]struct{})
-		bindings  = make(map[Port][]PortBinding)
+		portSpecs = make(nat.PortSet)
+		bindings  = make(nat.PortMap)
 	)
 
 	if !container.State.IsGhost() {
@@ -1177,7 +1147,7 @@ func (container *Container) allocateNetwork() error {
 	for port := range portSpecs {
 		binding := bindings[port]
 		if container.hostConfig.PublishAllPorts && len(binding) == 0 {
-			binding = append(binding, PortBinding{})
+			binding = append(binding, nat.PortBinding{})
 		}
 
 		for i := 0; i < len(binding); i++ {
@@ -1593,7 +1563,7 @@ func (container *Container) Copy(resource string) (archive.Archive, error) {
 }
 
 // Returns true if the container exposes a certain port
-func (container *Container) Exposes(p Port) bool {
+func (container *Container) Exposes(p nat.Port) bool {
 	_, exists := container.Config.ExposedPorts[p]
 	return exists
 }

+ 4 - 3
container_unit_test.go

@@ -1,6 +1,7 @@
 package docker
 
 import (
+	"github.com/dotcloud/docker/nat"
 	"testing"
 )
 
@@ -22,7 +23,7 @@ func TestParseLxcConfOpt(t *testing.T) {
 }
 
 func TestParseNetworkOptsPrivateOnly(t *testing.T) {
-	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::80"})
+	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -64,7 +65,7 @@ func TestParseNetworkOptsPrivateOnly(t *testing.T) {
 }
 
 func TestParseNetworkOptsPublic(t *testing.T) {
-	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100:8080:80"})
+	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:8080:80"})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -106,7 +107,7 @@ func TestParseNetworkOptsPublic(t *testing.T) {
 }
 
 func TestParseNetworkOptsUdp(t *testing.T) {
-	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::6000/udp"})
+	ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
 	if err != nil {
 		t.Fatal(err)
 	}

+ 6 - 5
integration/runtime_test.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/sysinit"
 	"github.com/dotcloud/docker/utils"
 	"io"
@@ -368,7 +369,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
 		eng     = NewTestEngine(t)
 		runtime = mkRuntimeFromEngine(eng, t)
 		port    = 5554
-		p       docker.Port
+		p       nat.Port
 	)
 	defer func() {
 		if err != nil {
@@ -387,8 +388,8 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
 		} else {
 			t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
 		}
-		ep := make(map[docker.Port]struct{}, 1)
-		p = docker.Port(fmt.Sprintf("%s/%s", strPort, proto))
+		ep := make(map[nat.Port]struct{}, 1)
+		p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto))
 		ep[p] = struct{}{}
 
 		jobCreate := eng.Job("create")
@@ -411,8 +412,8 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
 	}
 
 	jobStart := eng.Job("start", id)
-	portBindings := make(map[docker.Port][]docker.PortBinding)
-	portBindings[p] = []docker.PortBinding{
+	portBindings := make(map[nat.Port][]nat.PortBinding)
+	portBindings[p] = []nat.PortBinding{
 		{},
 	}
 	if err := jobStart.SetenvJson("PortsBindings", portBindings); err != nil {

+ 6 - 5
links.go

@@ -3,6 +3,7 @@ package docker
 import (
 	"fmt"
 	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/nat"
 	"path"
 	"strings"
 )
@@ -12,7 +13,7 @@ type Link struct {
 	ChildIP          string
 	Name             string
 	ChildEnvironment []string
-	Ports            []Port
+	Ports            []nat.Port
 	IsEnabled        bool
 	eng              *engine.Engine
 }
@@ -25,7 +26,7 @@ func NewLink(parent, child *Container, name string, eng *engine.Engine) (*Link,
 		return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, name)
 	}
 
-	ports := make([]Port, len(child.Config.ExposedPorts))
+	ports := make([]nat.Port, len(child.Config.ExposedPorts))
 	var i int
 	for p := range child.Config.ExposedPorts {
 		ports[i] = p
@@ -85,14 +86,14 @@ func (l *Link) ToEnv() []string {
 }
 
 // Default port rules
-func (l *Link) getDefaultPort() *Port {
-	var p Port
+func (l *Link) getDefaultPort() *nat.Port {
+	var p nat.Port
 	i := len(l.Ports)
 
 	if i == 0 {
 		return nil
 	} else if i > 1 {
-		sortPorts(l.Ports, func(ip, jp Port) bool {
+		nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
 			// If the two ports have the same number, tcp takes priority
 			// Sort in desc order
 			return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")

+ 6 - 5
links_test.go

@@ -1,6 +1,7 @@
 package docker
 
 import (
+	"github.com/dotcloud/docker/nat"
 	"strings"
 	"testing"
 )
@@ -22,9 +23,9 @@ func TestLinkNew(t *testing.T) {
 	from := newMockLinkContainer(fromID, "172.0.17.2")
 	from.Config.Env = []string{}
 	from.State = State{Running: true}
-	ports := make(map[Port]struct{})
+	ports := make(nat.PortSet)
 
-	ports[Port("6379/tcp")] = struct{}{}
+	ports[nat.Port("6379/tcp")] = struct{}{}
 
 	from.Config.ExposedPorts = ports
 
@@ -51,7 +52,7 @@ func TestLinkNew(t *testing.T) {
 		t.Fail()
 	}
 	for _, p := range link.Ports {
-		if p != Port("6379/tcp") {
+		if p != nat.Port("6379/tcp") {
 			t.Fail()
 		}
 	}
@@ -64,9 +65,9 @@ func TestLinkEnv(t *testing.T) {
 	from := newMockLinkContainer(fromID, "172.0.17.2")
 	from.Config.Env = []string{"PASSWORD=gordon"}
 	from.State = State{Running: true}
-	ports := make(map[Port]struct{})
+	ports := make(nat.PortSet)
 
-	ports[Port("6379/tcp")] = struct{}{}
+	ports[nat.Port("6379/tcp")] = struct{}{}
 
 	from.Config.ExposedPorts = ports
 

+ 133 - 0
nat/nat.go

@@ -0,0 +1,133 @@
+package nat
+
+// nat is a convenience package for docker's manipulation of strings describing
+// network ports.
+
+import (
+	"fmt"
+	"github.com/dotcloud/docker/utils"
+	"strconv"
+	"strings"
+)
+
+const (
+	PortSpecTemplate       = "ip:hostPort:containerPort"
+	PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort"
+)
+
+type PortBinding struct {
+	HostIp   string
+	HostPort string
+}
+
+type PortMap map[Port][]PortBinding
+
+type PortSet map[Port]struct{}
+
+// 80/tcp
+type Port string
+
+func NewPort(proto, port string) Port {
+	return Port(fmt.Sprintf("%s/%s", port, proto))
+}
+
+func ParsePort(rawPort string) (int, error) {
+	port, err := strconv.ParseUint(rawPort, 10, 16)
+	if err != nil {
+		return 0, err
+	}
+	return int(port), nil
+}
+
+func (p Port) Proto() string {
+	parts := strings.Split(string(p), "/")
+	if len(parts) == 1 {
+		return "tcp"
+	}
+	return parts[1]
+}
+
+func (p Port) Port() string {
+	return strings.Split(string(p), "/")[0]
+}
+
+func (p Port) Int() int {
+	i, err := ParsePort(p.Port())
+	if err != nil {
+		panic(err)
+	}
+	return i
+}
+
+// Splits a port in the format of port/proto
+func SplitProtoPort(rawPort string) (string, string) {
+	parts := strings.Split(rawPort, "/")
+	l := len(parts)
+	if l == 0 {
+		return "", ""
+	}
+	if l == 1 {
+		return "tcp", rawPort
+	}
+	return parts[0], parts[1]
+}
+
+// We will receive port specs in the format of ip:public:private/proto and these need to be
+// parsed in the internal types
+func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
+	var (
+		exposedPorts = make(map[Port]struct{}, len(ports))
+		bindings     = make(map[Port][]PortBinding)
+	)
+
+	for _, rawPort := range ports {
+		proto := "tcp"
+
+		if i := strings.LastIndex(rawPort, "/"); i != -1 {
+			proto = rawPort[i+1:]
+			rawPort = rawPort[:i]
+		}
+		if !strings.Contains(rawPort, ":") {
+			rawPort = fmt.Sprintf("::%s", rawPort)
+		} else if len(strings.Split(rawPort, ":")) == 2 {
+			rawPort = fmt.Sprintf(":%s", rawPort)
+		}
+
+		parts, err := utils.PartParser(PortSpecTemplate, rawPort)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		var (
+			containerPort = parts["containerPort"]
+			rawIp         = parts["ip"]
+			hostPort      = parts["hostPort"]
+		)
+
+		if containerPort == "" {
+			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
+		}
+		if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
+			return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
+		}
+		if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
+			return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
+		}
+
+		port := NewPort(proto, containerPort)
+		if _, exists := exposedPorts[port]; !exists {
+			exposedPorts[port] = struct{}{}
+		}
+
+		binding := PortBinding{
+			HostIp:   rawIp,
+			HostPort: hostPort,
+		}
+		bslice, exists := bindings[port]
+		if !exists {
+			bslice = []PortBinding{}
+		}
+		bindings[port] = append(bslice, binding)
+	}
+	return exposedPorts, bindings, nil
+}

+ 28 - 0
nat/sort.go

@@ -0,0 +1,28 @@
+package nat
+
+import "sort"
+
+type portSorter struct {
+	ports []Port
+	by    func(i, j Port) bool
+}
+
+func (s *portSorter) Len() int {
+	return len(s.ports)
+}
+
+func (s *portSorter) Swap(i, j int) {
+	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
+}
+
+func (s *portSorter) Less(i, j int) bool {
+	ip := s.ports[i]
+	jp := s.ports[j]
+
+	return s.by(ip, jp)
+}
+
+func Sort(ports []Port, predicate func(i, j Port) bool) {
+	s := &portSorter{ports, predicate}
+	sort.Sort(s)
+}

+ 3 - 3
sorter_unit_test.go → nat/sort_test.go

@@ -1,4 +1,4 @@
-package docker
+package nat
 
 import (
 	"fmt"
@@ -11,7 +11,7 @@ func TestSortUniquePorts(t *testing.T) {
 		Port("22/tcp"),
 	}
 
-	sortPorts(ports, func(ip, jp Port) bool {
+	Sort(ports, func(ip, jp Port) bool {
 		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
 	})
 
@@ -30,7 +30,7 @@ func TestSortSamePortWithDifferentProto(t *testing.T) {
 		Port("6379/udp"),
 	}
 
-	sortPorts(ports, func(ip, jp Port) bool {
+	Sort(ports, func(ip, jp Port) bool {
 		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
 	})
 

+ 0 - 25
sorter.go

@@ -2,31 +2,6 @@ package docker
 
 import "sort"
 
-type portSorter struct {
-	ports []Port
-	by    func(i, j Port) bool
-}
-
-func (s *portSorter) Len() int {
-	return len(s.ports)
-}
-
-func (s *portSorter) Swap(i, j int) {
-	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
-}
-
-func (s *portSorter) Less(i, j int) bool {
-	ip := s.ports[i]
-	jp := s.ports[j]
-
-	return s.by(ip, jp)
-}
-
-func sortPorts(ports []Port, predicate func(i, j Port) bool) {
-	s := &portSorter{ports, predicate}
-	sort.Sort(s)
-}
-
 type containerSorter struct {
 	containers []*Container
 	by         func(i, j *Container) bool

+ 8 - 95
utils.go

@@ -3,10 +3,10 @@ package docker
 import (
 	"fmt"
 	"github.com/dotcloud/docker/archive"
+	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/pkg/namesgenerator"
 	"github.com/dotcloud/docker/utils"
 	"io"
-	"strconv"
 	"strings"
 	"sync/atomic"
 )
@@ -98,7 +98,7 @@ func MergeConfig(userConf, imageConf *Config) error {
 		userConf.ExposedPorts = imageConf.ExposedPorts
 	} else if imageConf.ExposedPorts != nil {
 		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(map[Port]struct{})
+			userConf.ExposedPorts = make(nat.PortSet)
 		}
 		for port := range imageConf.ExposedPorts {
 			if _, exists := userConf.ExposedPorts[port]; !exists {
@@ -109,9 +109,9 @@ func MergeConfig(userConf, imageConf *Config) error {
 
 	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
 		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(map[Port]struct{})
+			userConf.ExposedPorts = make(nat.PortSet)
 		}
-		ports, _, err := parsePortSpecs(userConf.PortSpecs)
+		ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
 		if err != nil {
 			return err
 		}
@@ -125,10 +125,10 @@ func MergeConfig(userConf, imageConf *Config) error {
 	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
 		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
 		if userConf.ExposedPorts == nil {
-			userConf.ExposedPorts = make(map[Port]struct{})
+			userConf.ExposedPorts = make(nat.PortSet)
 		}
 
-		ports, _, err := parsePortSpecs(imageConf.PortSpecs)
+		ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
 		if err != nil {
 			return err
 		}
@@ -212,96 +212,9 @@ func parseLxcOpt(opt string) (string, string, error) {
 	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
 }
 
-// FIXME: network related stuff (including parsing) should be grouped in network file
-const (
-	PortSpecTemplate       = "ip:hostPort:containerPort"
-	PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort"
-)
-
-// We will receive port specs in the format of ip:public:private/proto and these need to be
-// parsed in the internal types
-func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
-	var (
-		exposedPorts = make(map[Port]struct{}, len(ports))
-		bindings     = make(map[Port][]PortBinding)
-	)
-
-	for _, rawPort := range ports {
-		proto := "tcp"
-
-		if i := strings.LastIndex(rawPort, "/"); i != -1 {
-			proto = rawPort[i+1:]
-			rawPort = rawPort[:i]
-		}
-		if !strings.Contains(rawPort, ":") {
-			rawPort = fmt.Sprintf("::%s", rawPort)
-		} else if len(strings.Split(rawPort, ":")) == 2 {
-			rawPort = fmt.Sprintf(":%s", rawPort)
-		}
-
-		parts, err := utils.PartParser(PortSpecTemplate, rawPort)
-		if err != nil {
-			return nil, nil, err
-		}
-
-		var (
-			containerPort = parts["containerPort"]
-			rawIp         = parts["ip"]
-			hostPort      = parts["hostPort"]
-		)
-
-		if containerPort == "" {
-			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
-		}
-		if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
-			return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
-		}
-		if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
-			return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
-		}
-
-		port := NewPort(proto, containerPort)
-		if _, exists := exposedPorts[port]; !exists {
-			exposedPorts[port] = struct{}{}
-		}
-
-		binding := PortBinding{
-			HostIp:   rawIp,
-			HostPort: hostPort,
-		}
-		bslice, exists := bindings[port]
-		if !exists {
-			bslice = []PortBinding{}
-		}
-		bindings[port] = append(bslice, binding)
-	}
-	return exposedPorts, bindings, nil
-}
-
-// Splits a port in the format of port/proto
-func splitProtoPort(rawPort string) (string, string) {
-	parts := strings.Split(rawPort, "/")
-	l := len(parts)
-	if l == 0 {
-		return "", ""
-	}
-	if l == 1 {
-		return "tcp", rawPort
-	}
-	return parts[0], parts[1]
-}
-
-func parsePort(rawPort string) (int, error) {
-	port, err := strconv.ParseUint(rawPort, 10, 16)
-	if err != nil {
-		return 0, err
-	}
-	return int(port), nil
-}
-
 func migratePortMappings(config *Config, hostConfig *HostConfig) error {
 	if config.PortSpecs != nil {
-		ports, bindings, err := parsePortSpecs(config.PortSpecs)
+		ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
 		if err != nil {
 			return err
 		}
@@ -314,7 +227,7 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
 		}
 
 		if config.ExposedPorts == nil {
-			config.ExposedPorts = make(map[Port]struct{}, len(ports))
+			config.ExposedPorts = make(nat.PortSet, len(ports))
 		}
 		for k, v := range ports {
 			config.ExposedPorts[k] = v