mirror of https://github.com/docker/docs.git
Merge pull request #4079 from shykes/separate-nat
New package `nat`: utilities for manipulating the text description of network ports
This commit is contained in:
commit
6da1e092dc
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/dotcloud/docker/archive"
|
"github.com/dotcloud/docker/archive"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
flag "github.com/dotcloud/docker/pkg/mflag"
|
flag "github.com/dotcloud/docker/pkg/mflag"
|
||||||
"github.com/dotcloud/docker/pkg/sysinfo"
|
"github.com/dotcloud/docker/pkg/sysinfo"
|
||||||
"github.com/dotcloud/docker/pkg/term"
|
"github.com/dotcloud/docker/pkg/term"
|
||||||
|
@ -799,7 +800,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
||||||
return err
|
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 {
|
for _, frontend := range frontends {
|
||||||
fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
|
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(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
|
||||||
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
|
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(&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(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
|
||||||
cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
|
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]
|
domainname = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
ports, portBindings, err := parsePortSpecs(flPublish.GetAll())
|
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
@ -1895,7 +1896,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
||||||
if strings.Contains(e, ":") {
|
if strings.Contains(e, ":") {
|
||||||
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", 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 {
|
if _, exists := ports[p]; !exists {
|
||||||
ports[p] = struct{}{}
|
ports[p] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ func TestMergeConfig(t *testing.T) {
|
||||||
t.Fatalf("Expected VolumesFrom to be 1111, found %s", configUser.VolumesFrom)
|
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 {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
52
container.go
52
container.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
"github.com/dotcloud/docker/execdriver"
|
"github.com/dotcloud/docker/execdriver"
|
||||||
"github.com/dotcloud/docker/graphdriver"
|
"github.com/dotcloud/docker/graphdriver"
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"github.com/dotcloud/docker/pkg/mount"
|
"github.com/dotcloud/docker/pkg/mount"
|
||||||
"github.com/dotcloud/docker/pkg/term"
|
"github.com/dotcloud/docker/pkg/term"
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
|
@ -86,7 +87,7 @@ type Config struct {
|
||||||
AttachStdout bool
|
AttachStdout bool
|
||||||
AttachStderr bool
|
AttachStderr bool
|
||||||
PortSpecs []string // Deprecated - Can be in the format of 8080/tcp
|
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.
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
OpenStdin bool // Open stdin
|
OpenStdin bool // Open stdin
|
||||||
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||||
|
@ -147,7 +148,7 @@ type HostConfig struct {
|
||||||
ContainerIDFile string
|
ContainerIDFile string
|
||||||
LxcConf []KeyValuePair
|
LxcConf []KeyValuePair
|
||||||
Privileged bool
|
Privileged bool
|
||||||
PortBindings map[Port][]PortBinding
|
PortBindings nat.PortMap
|
||||||
Links []string
|
Links []string
|
||||||
PublishAllPorts bool
|
PublishAllPorts bool
|
||||||
}
|
}
|
||||||
|
@ -189,38 +190,7 @@ type KeyValuePair struct {
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortBinding struct {
|
// FIXME: move deprecated port stuff to nat to clean up the core.
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
type PortMapping map[string]string // Deprecated
|
type PortMapping map[string]string // Deprecated
|
||||||
|
|
||||||
type NetworkSettings struct {
|
type NetworkSettings struct {
|
||||||
|
@ -229,13 +199,13 @@ type NetworkSettings struct {
|
||||||
Gateway string
|
Gateway string
|
||||||
Bridge string
|
Bridge string
|
||||||
PortMapping map[string]PortMapping // Deprecated
|
PortMapping map[string]PortMapping // Deprecated
|
||||||
Ports map[Port][]PortBinding
|
Ports nat.PortMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
||||||
var outs = engine.NewTable("", 0)
|
var outs = engine.NewTable("", 0)
|
||||||
for port, bindings := range settings.Ports {
|
for port, bindings := range settings.Ports {
|
||||||
p, _ := parsePort(port.Port())
|
p, _ := nat.ParsePort(port.Port())
|
||||||
if len(bindings) == 0 {
|
if len(bindings) == 0 {
|
||||||
out := &engine.Env{}
|
out := &engine.Env{}
|
||||||
out.SetInt("PublicPort", p)
|
out.SetInt("PublicPort", p)
|
||||||
|
@ -245,7 +215,7 @@ func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
||||||
}
|
}
|
||||||
for _, binding := range bindings {
|
for _, binding := range bindings {
|
||||||
out := &engine.Env{}
|
out := &engine.Env{}
|
||||||
h, _ := parsePort(binding.HostPort)
|
h, _ := nat.ParsePort(binding.HostPort)
|
||||||
out.SetInt("PrivatePort", p)
|
out.SetInt("PrivatePort", p)
|
||||||
out.SetInt("PublicPort", h)
|
out.SetInt("PublicPort", h)
|
||||||
out.Set("Type", port.Proto())
|
out.Set("Type", port.Proto())
|
||||||
|
@ -1152,8 +1122,8 @@ func (container *Container) allocateNetwork() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
portSpecs = make(map[Port]struct{})
|
portSpecs = make(nat.PortSet)
|
||||||
bindings = make(map[Port][]PortBinding)
|
bindings = make(nat.PortMap)
|
||||||
)
|
)
|
||||||
|
|
||||||
if !container.State.IsGhost() {
|
if !container.State.IsGhost() {
|
||||||
|
@ -1177,7 +1147,7 @@ func (container *Container) allocateNetwork() error {
|
||||||
for port := range portSpecs {
|
for port := range portSpecs {
|
||||||
binding := bindings[port]
|
binding := bindings[port]
|
||||||
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
||||||
binding = append(binding, PortBinding{})
|
binding = append(binding, nat.PortBinding{})
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(binding); i++ {
|
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
|
// 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]
|
_, exists := container.Config.ExposedPorts[p]
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ func TestParseLxcConfOpt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNetworkOptsPrivateOnly(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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -64,7 +65,7 @@ func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNetworkOptsPublic(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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ func TestParseNetworkOptsPublic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseNetworkOptsUdp(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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker"
|
"github.com/dotcloud/docker"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"github.com/dotcloud/docker/sysinit"
|
"github.com/dotcloud/docker/sysinit"
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
|
@ -368,7 +369,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
|
||||||
eng = NewTestEngine(t)
|
eng = NewTestEngine(t)
|
||||||
runtime = mkRuntimeFromEngine(eng, t)
|
runtime = mkRuntimeFromEngine(eng, t)
|
||||||
port = 5554
|
port = 5554
|
||||||
p docker.Port
|
p nat.Port
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -387,8 +388,8 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
|
||||||
} else {
|
} else {
|
||||||
t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
|
t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
|
||||||
}
|
}
|
||||||
ep := make(map[docker.Port]struct{}, 1)
|
ep := make(map[nat.Port]struct{}, 1)
|
||||||
p = docker.Port(fmt.Sprintf("%s/%s", strPort, proto))
|
p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto))
|
||||||
ep[p] = struct{}{}
|
ep[p] = struct{}{}
|
||||||
|
|
||||||
jobCreate := eng.Job("create")
|
jobCreate := eng.Job("create")
|
||||||
|
@ -411,8 +412,8 @@ func startEchoServerContainer(t *testing.T, proto string) (*docker.Runtime, *doc
|
||||||
}
|
}
|
||||||
|
|
||||||
jobStart := eng.Job("start", id)
|
jobStart := eng.Job("start", id)
|
||||||
portBindings := make(map[docker.Port][]docker.PortBinding)
|
portBindings := make(map[nat.Port][]nat.PortBinding)
|
||||||
portBindings[p] = []docker.PortBinding{
|
portBindings[p] = []nat.PortBinding{
|
||||||
{},
|
{},
|
||||||
}
|
}
|
||||||
if err := jobStart.SetenvJson("PortsBindings", portBindings); err != nil {
|
if err := jobStart.SetenvJson("PortsBindings", portBindings); err != nil {
|
||||||
|
|
11
links.go
11
links.go
|
@ -3,6 +3,7 @@ package docker
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -12,7 +13,7 @@ type Link struct {
|
||||||
ChildIP string
|
ChildIP string
|
||||||
Name string
|
Name string
|
||||||
ChildEnvironment []string
|
ChildEnvironment []string
|
||||||
Ports []Port
|
Ports []nat.Port
|
||||||
IsEnabled bool
|
IsEnabled bool
|
||||||
eng *engine.Engine
|
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)
|
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
|
var i int
|
||||||
for p := range child.Config.ExposedPorts {
|
for p := range child.Config.ExposedPorts {
|
||||||
ports[i] = p
|
ports[i] = p
|
||||||
|
@ -85,14 +86,14 @@ func (l *Link) ToEnv() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default port rules
|
// Default port rules
|
||||||
func (l *Link) getDefaultPort() *Port {
|
func (l *Link) getDefaultPort() *nat.Port {
|
||||||
var p Port
|
var p nat.Port
|
||||||
i := len(l.Ports)
|
i := len(l.Ports)
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return nil
|
return nil
|
||||||
} else if i > 1 {
|
} 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
|
// If the two ports have the same number, tcp takes priority
|
||||||
// Sort in desc order
|
// Sort in desc order
|
||||||
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
|
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -22,9 +23,9 @@ func TestLinkNew(t *testing.T) {
|
||||||
from := newMockLinkContainer(fromID, "172.0.17.2")
|
from := newMockLinkContainer(fromID, "172.0.17.2")
|
||||||
from.Config.Env = []string{}
|
from.Config.Env = []string{}
|
||||||
from.State = State{Running: true}
|
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
|
from.Config.ExposedPorts = ports
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ func TestLinkNew(t *testing.T) {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
for _, p := range link.Ports {
|
for _, p := range link.Ports {
|
||||||
if p != Port("6379/tcp") {
|
if p != nat.Port("6379/tcp") {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,9 +65,9 @@ func TestLinkEnv(t *testing.T) {
|
||||||
from := newMockLinkContainer(fromID, "172.0.17.2")
|
from := newMockLinkContainer(fromID, "172.0.17.2")
|
||||||
from.Config.Env = []string{"PASSWORD=gordon"}
|
from.Config.Env = []string{"PASSWORD=gordon"}
|
||||||
from.State = State{Running: true}
|
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
|
from.Config.ExposedPorts = ports
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package docker
|
package nat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,7 +11,7 @@ func TestSortUniquePorts(t *testing.T) {
|
||||||
Port("22/tcp"),
|
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")
|
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ func TestSortSamePortWithDifferentProto(t *testing.T) {
|
||||||
Port("6379/udp"),
|
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")
|
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
|
||||||
})
|
})
|
||||||
|
|
25
sorter.go
25
sorter.go
|
@ -2,31 +2,6 @@ package docker
|
||||||
|
|
||||||
import "sort"
|
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 {
|
type containerSorter struct {
|
||||||
containers []*Container
|
containers []*Container
|
||||||
by func(i, j *Container) bool
|
by func(i, j *Container) bool
|
||||||
|
|
103
utils.go
103
utils.go
|
@ -3,10 +3,10 @@ package docker
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/archive"
|
"github.com/dotcloud/docker/archive"
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
"github.com/dotcloud/docker/pkg/namesgenerator"
|
"github.com/dotcloud/docker/pkg/namesgenerator"
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
@ -98,7 +98,7 @@ func MergeConfig(userConf, imageConf *Config) error {
|
||||||
userConf.ExposedPorts = imageConf.ExposedPorts
|
userConf.ExposedPorts = imageConf.ExposedPorts
|
||||||
} else if imageConf.ExposedPorts != nil {
|
} else if imageConf.ExposedPorts != nil {
|
||||||
if userConf.ExposedPorts == nil {
|
if userConf.ExposedPorts == nil {
|
||||||
userConf.ExposedPorts = make(map[Port]struct{})
|
userConf.ExposedPorts = make(nat.PortSet)
|
||||||
}
|
}
|
||||||
for port := range imageConf.ExposedPorts {
|
for port := range imageConf.ExposedPorts {
|
||||||
if _, exists := userConf.ExposedPorts[port]; !exists {
|
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.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
|
||||||
if userConf.ExposedPorts == nil {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -125,10 +125,10 @@ func MergeConfig(userConf, imageConf *Config) error {
|
||||||
if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
|
if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
|
||||||
utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
|
utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
|
||||||
if userConf.ExposedPorts == nil {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -212,96 +212,9 @@ func parseLxcOpt(opt string) (string, string, error) {
|
||||||
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
|
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 {
|
func migratePortMappings(config *Config, hostConfig *HostConfig) error {
|
||||||
if config.PortSpecs != nil {
|
if config.PortSpecs != nil {
|
||||||
ports, bindings, err := parsePortSpecs(config.PortSpecs)
|
ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -314,7 +227,7 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ExposedPorts == nil {
|
if config.ExposedPorts == nil {
|
||||||
config.ExposedPorts = make(map[Port]struct{}, len(ports))
|
config.ExposedPorts = make(nat.PortSet, len(ports))
|
||||||
}
|
}
|
||||||
for k, v := range ports {
|
for k, v := range ports {
|
||||||
config.ExposedPorts[k] = v
|
config.ExposedPorts[k] = v
|
||||||
|
|
Loading…
Reference in New Issue