1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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)
This commit is contained in:
Solomon Hykes 2014-02-11 16:48:44 -08:00
parent 0bcabdbdc7
commit 3ecd8ff0c8
12 changed files with 212 additions and 187 deletions

View file

@ -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{}{}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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")

View file

@ -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
nat/nat.go Normal file
View file

@ -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
nat/sort.go Normal file
View file

@ -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)
}

View file

@ -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")
})

View file

@ -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

103
utils.go
View file

@ -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