Replace usage of pkg/nat with go-connections/nat.
Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
0ceabb37b0
commit
3d3aaa0d2e
4 changed files with 0 additions and 935 deletions
227
nat/nat.go
227
nat/nat.go
|
@ -1,227 +0,0 @@
|
||||||
package nat
|
|
||||||
|
|
||||||
// nat is a convenience package for docker's manipulation of strings describing
|
|
||||||
// network ports.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/parsers"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// portSpecTemplate is the expected format for port specifications
|
|
||||||
portSpecTemplate = "ip:hostPort:containerPort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PortBinding represents a binding between a Host IP address and a Host Port
|
|
||||||
type PortBinding struct {
|
|
||||||
// HostIP is the host IP Address
|
|
||||||
HostIP string `json:"HostIp"`
|
|
||||||
// HostPort is the host port number
|
|
||||||
HostPort string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PortMap is a collection of PortBinding indexed by Port
|
|
||||||
type PortMap map[Port][]PortBinding
|
|
||||||
|
|
||||||
// PortSet is a collection of structs indexed by Port
|
|
||||||
type PortSet map[Port]struct{}
|
|
||||||
|
|
||||||
// Port is a string containing port number and protocol in the format "80/tcp"
|
|
||||||
type Port string
|
|
||||||
|
|
||||||
// NewPort creates a new instance of a Port given a protocol and port number or port range
|
|
||||||
func NewPort(proto, port string) (Port, error) {
|
|
||||||
// Check for parsing issues on "port" now so we can avoid having
|
|
||||||
// to check it later on.
|
|
||||||
|
|
||||||
portStartInt, portEndInt, err := ParsePortRange(port)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if portStartInt == portEndInt {
|
|
||||||
return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil
|
|
||||||
}
|
|
||||||
return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsePort parses the port number string and returns an int
|
|
||||||
func ParsePort(rawPort string) (int, error) {
|
|
||||||
if len(rawPort) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
port, err := strconv.ParseUint(rawPort, 10, 16)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return int(port), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsePortRange parses the port range string and returns start/end ints
|
|
||||||
func ParsePortRange(rawPort string) (int, int, error) {
|
|
||||||
if len(rawPort) == 0 {
|
|
||||||
return 0, 0, nil
|
|
||||||
}
|
|
||||||
start, end, err := parsers.ParsePortRange(rawPort)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
return int(start), int(end), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proto returns the protocol of a Port
|
|
||||||
func (p Port) Proto() string {
|
|
||||||
proto, _ := SplitProtoPort(string(p))
|
|
||||||
return proto
|
|
||||||
}
|
|
||||||
|
|
||||||
// Port returns the port number of a Port
|
|
||||||
func (p Port) Port() string {
|
|
||||||
_, port := SplitProtoPort(string(p))
|
|
||||||
return port
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int returns the port number of a Port as an int
|
|
||||||
func (p Port) Int() int {
|
|
||||||
portStr := p.Port()
|
|
||||||
if len(portStr) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to check for an error because we're going to
|
|
||||||
// assume that any error would have been found, and reported, in NewPort()
|
|
||||||
port, _ := strconv.ParseUint(portStr, 10, 16)
|
|
||||||
return int(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range returns the start/end port numbers of a Port range as ints
|
|
||||||
func (p Port) Range() (int, int, error) {
|
|
||||||
return ParsePortRange(p.Port())
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitProtoPort splits a port in the format of proto/port
|
|
||||||
func SplitProtoPort(rawPort string) (string, string) {
|
|
||||||
parts := strings.Split(rawPort, "/")
|
|
||||||
l := len(parts)
|
|
||||||
if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
if l == 1 {
|
|
||||||
return "tcp", rawPort
|
|
||||||
}
|
|
||||||
if len(parts[1]) == 0 {
|
|
||||||
return "tcp", parts[0]
|
|
||||||
}
|
|
||||||
return parts[1], parts[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateProto(proto string) bool {
|
|
||||||
for _, availableProto := range []string{"tcp", "udp"} {
|
|
||||||
if availableProto == proto {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses
|
|
||||||
// these in to 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 := parsers.PartParser(portSpecTemplate, rawPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
containerPort = parts["containerPort"]
|
|
||||||
rawIP = parts["ip"]
|
|
||||||
hostPort = parts["hostPort"]
|
|
||||||
)
|
|
||||||
|
|
||||||
if rawIP != "" && net.ParseIP(rawIP) == nil {
|
|
||||||
return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP)
|
|
||||||
}
|
|
||||||
if containerPort == "" {
|
|
||||||
return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
startPort, endPort, err := parsers.ParsePortRange(containerPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
var startHostPort, endHostPort uint64 = 0, 0
|
|
||||||
if len(hostPort) > 0 {
|
|
||||||
startHostPort, endHostPort, err = parsers.ParsePortRange(hostPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
|
|
||||||
// Allow host port range iff containerPort is not a range.
|
|
||||||
// In this case, use the host port range as the dynamic
|
|
||||||
// host port range to allocate into.
|
|
||||||
if endPort != startPort {
|
|
||||||
return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validateProto(strings.ToLower(proto)) {
|
|
||||||
return nil, nil, fmt.Errorf("Invalid proto: %s", proto)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := uint64(0); i <= (endPort - startPort); i++ {
|
|
||||||
containerPort = strconv.FormatUint(startPort+i, 10)
|
|
||||||
if len(hostPort) > 0 {
|
|
||||||
hostPort = strconv.FormatUint(startHostPort+i, 10)
|
|
||||||
}
|
|
||||||
// Set hostPort to a range only if there is a single container port
|
|
||||||
// and a dynamic host port.
|
|
||||||
if startPort == endPort && startHostPort != endHostPort {
|
|
||||||
hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
|
|
||||||
}
|
|
||||||
port, err := NewPort(strings.ToLower(proto), containerPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
525
nat/nat_test.go
525
nat/nat_test.go
|
@ -1,525 +0,0 @@
|
||||||
package nat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParsePort(t *testing.T) {
|
|
||||||
var (
|
|
||||||
p int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
p, err = ParsePort("1234")
|
|
||||||
|
|
||||||
if err != nil || p != 1234 {
|
|
||||||
t.Fatal("Parsing '1234' did not succeed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME currently this is a valid port. I don't think it should be.
|
|
||||||
// I'm leaving this test commented out until we make a decision.
|
|
||||||
// - erikh
|
|
||||||
|
|
||||||
/*
|
|
||||||
p, err = ParsePort("0123")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Successfully parsed port '0123' to '123'")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
p, err = ParsePort("asdf")
|
|
||||||
|
|
||||||
if err == nil || p != 0 {
|
|
||||||
t.Fatal("Parsing port 'asdf' succeeded")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = ParsePort("1asdf")
|
|
||||||
|
|
||||||
if err == nil || p != 0 {
|
|
||||||
t.Fatal("Parsing port '1asdf' succeeded")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortRange(t *testing.T) {
|
|
||||||
var (
|
|
||||||
begin int
|
|
||||||
end int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
type TestRange struct {
|
|
||||||
Range string
|
|
||||||
Begin int
|
|
||||||
End int
|
|
||||||
}
|
|
||||||
validRanges := []TestRange{
|
|
||||||
{"1234", 1234, 1234},
|
|
||||||
{"1234-1234", 1234, 1234},
|
|
||||||
{"1234-1235", 1234, 1235},
|
|
||||||
{"8000-9000", 8000, 9000},
|
|
||||||
{"0", 0, 0},
|
|
||||||
{"0-0", 0, 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range validRanges {
|
|
||||||
begin, end, err = ParsePortRange(r.Range)
|
|
||||||
|
|
||||||
if err != nil || begin != r.Begin {
|
|
||||||
t.Fatalf("Parsing port range '%s' did not succeed. Expected begin %d, got %d", r.Range, r.Begin, begin)
|
|
||||||
}
|
|
||||||
if err != nil || end != r.End {
|
|
||||||
t.Fatalf("Parsing port range '%s' did not succeed. Expected end %d, got %d", r.Range, r.End, end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidRanges := []string{
|
|
||||||
"asdf",
|
|
||||||
"1asdf",
|
|
||||||
"9000-8000",
|
|
||||||
"9000-",
|
|
||||||
"-8000",
|
|
||||||
"-8000-",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range invalidRanges {
|
|
||||||
begin, end, err = ParsePortRange(r)
|
|
||||||
|
|
||||||
if err == nil || begin != 0 || end != 0 {
|
|
||||||
t.Fatalf("Parsing port range '%s' succeeded", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPort(t *testing.T) {
|
|
||||||
p, err := NewPort("tcp", "1234")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("tcp, 1234 had a parsing issue: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(p) != "1234/tcp" {
|
|
||||||
t.Fatal("tcp, 1234 did not result in the string 1234/tcp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Proto() != "tcp" {
|
|
||||||
t.Fatal("protocol was not tcp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Port() != "1234" {
|
|
||||||
t.Fatal("port string value was not 1234")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Int() != 1234 {
|
|
||||||
t.Fatal("port int value was not 1234")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = NewPort("tcp", "asd1234")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("tcp, asd1234 was supposed to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = NewPort("tcp", "1234-1230")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("tcp, 1234-1230 was supposed to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err = NewPort("tcp", "1234-1242")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("tcp, 1234-1242 had a parsing issue: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(p) != "1234-1242/tcp" {
|
|
||||||
t.Fatal("tcp, 1234-1242 did not result in the string 1234-1242/tcp")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitProtoPort(t *testing.T) {
|
|
||||||
var (
|
|
||||||
proto string
|
|
||||||
port string
|
|
||||||
)
|
|
||||||
|
|
||||||
proto, port = SplitProtoPort("1234/tcp")
|
|
||||||
|
|
||||||
if proto != "tcp" || port != "1234" {
|
|
||||||
t.Fatal("Could not split 1234/tcp properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
proto, port = SplitProtoPort("")
|
|
||||||
|
|
||||||
if proto != "" || port != "" {
|
|
||||||
t.Fatal("parsing an empty string yielded surprising results", proto, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
proto, port = SplitProtoPort("1234")
|
|
||||||
|
|
||||||
if proto != "tcp" || port != "1234" {
|
|
||||||
t.Fatal("tcp is not the default protocol for portspec '1234'", proto, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
proto, port = SplitProtoPort("1234/")
|
|
||||||
|
|
||||||
if proto != "tcp" || port != "1234" {
|
|
||||||
t.Fatal("parsing '1234/' yielded:" + port + "/" + proto)
|
|
||||||
}
|
|
||||||
|
|
||||||
proto, port = SplitProtoPort("/tcp")
|
|
||||||
|
|
||||||
if proto != "" || port != "" {
|
|
||||||
t.Fatal("parsing '/tcp' yielded:" + port + "/" + proto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortSpecs(t *testing.T) {
|
|
||||||
var (
|
|
||||||
portMap map[Port]struct{}
|
|
||||||
bindingMap map[Port][]PortBinding
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
portMap, bindingMap, err = ParsePortSpecs([]string{"1234/tcp", "2345/udp"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("1234/tcp")]; !ok {
|
|
||||||
t.Fatal("1234/tcp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("2345/udp")]; !ok {
|
|
||||||
t.Fatal("2345/udp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
for portspec, bindings := range bindingMap {
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Fatalf("%s should have exactly one binding", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostIP != "" {
|
|
||||||
t.Fatalf("HostIP should not be set for %s", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostPort != "" {
|
|
||||||
t.Fatalf("HostPort should not be set for %s", portspec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
portMap, bindingMap, err = ParsePortSpecs([]string{"1234:1234/tcp", "2345:2345/udp"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("1234/tcp")]; !ok {
|
|
||||||
t.Fatal("1234/tcp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("2345/udp")]; !ok {
|
|
||||||
t.Fatal("2345/udp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
for portspec, bindings := range bindingMap {
|
|
||||||
_, port := SplitProtoPort(string(portspec))
|
|
||||||
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Fatalf("%s should have exactly one binding", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostIP != "" {
|
|
||||||
t.Fatalf("HostIP should not be set for %s", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostPort != port {
|
|
||||||
t.Fatalf("HostPort should be %s for %s", port, portspec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
portMap, bindingMap, err = ParsePortSpecs([]string{"0.0.0.0:1234:1234/tcp", "0.0.0.0:2345:2345/udp"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("1234/tcp")]; !ok {
|
|
||||||
t.Fatal("1234/tcp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("2345/udp")]; !ok {
|
|
||||||
t.Fatal("2345/udp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
for portspec, bindings := range bindingMap {
|
|
||||||
_, port := SplitProtoPort(string(portspec))
|
|
||||||
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Fatalf("%s should have exactly one binding", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostIP != "0.0.0.0" {
|
|
||||||
t.Fatalf("HostIP is not 0.0.0.0 for %s", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostPort != port {
|
|
||||||
t.Fatalf("HostPort should be %s for %s", port, portspec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = ParsePortSpecs([]string{"localhost:1234:1234/tcp"})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Received no error while trying to parse a hostname instead of ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortSpecsWithRange(t *testing.T) {
|
|
||||||
var (
|
|
||||||
portMap map[Port]struct{}
|
|
||||||
bindingMap map[Port][]PortBinding
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
portMap, bindingMap, err = ParsePortSpecs([]string{"1234-1236/tcp", "2345-2347/udp"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("1235/tcp")]; !ok {
|
|
||||||
t.Fatal("1234/tcp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("2346/udp")]; !ok {
|
|
||||||
t.Fatal("2345/udp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
for portspec, bindings := range bindingMap {
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Fatalf("%s should have exactly one binding", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostIP != "" {
|
|
||||||
t.Fatalf("HostIP should not be set for %s", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostPort != "" {
|
|
||||||
t.Fatalf("HostPort should not be set for %s", portspec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
portMap, bindingMap, err = ParsePortSpecs([]string{"1234-1236:1234-1236/tcp", "2345-2347:2345-2347/udp"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("1235/tcp")]; !ok {
|
|
||||||
t.Fatal("1234/tcp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("2346/udp")]; !ok {
|
|
||||||
t.Fatal("2345/udp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
for portspec, bindings := range bindingMap {
|
|
||||||
_, port := SplitProtoPort(string(portspec))
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Fatalf("%s should have exactly one binding", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostIP != "" {
|
|
||||||
t.Fatalf("HostIP should not be set for %s", portspec)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bindings[0].HostPort != port {
|
|
||||||
t.Fatalf("HostPort should be %s for %s", port, portspec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
portMap, bindingMap, err = ParsePortSpecs([]string{"0.0.0.0:1234-1236:1234-1236/tcp", "0.0.0.0:2345-2347:2345-2347/udp"})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error while processing ParsePortSpecs: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("1235/tcp")]; !ok {
|
|
||||||
t.Fatal("1234/tcp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := portMap[Port("2346/udp")]; !ok {
|
|
||||||
t.Fatal("2345/udp was not parsed properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
for portspec, bindings := range bindingMap {
|
|
||||||
_, port := SplitProtoPort(string(portspec))
|
|
||||||
if len(bindings) != 1 || bindings[0].HostIP != "0.0.0.0" || bindings[0].HostPort != port {
|
|
||||||
t.Fatalf("Expect single binding to port %s but found %s", port, bindings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = ParsePortSpecs([]string{"localhost:1234-1236:1234-1236/tcp"})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Received no error while trying to parse a hostname instead of ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
|
||||||
ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100::80"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(ports) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(ports))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(bindings))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
for k := range ports {
|
|
||||||
if k.Proto() != "tcp" {
|
|
||||||
t.Logf("Expected tcp got %s", k.Proto())
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if k.Port() != "80" {
|
|
||||||
t.Logf("Expected 80 got %s", k.Port())
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, exists := bindings[k]
|
|
||||||
if !exists {
|
|
||||||
t.Log("Binding does not exist")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if len(b) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(b))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
s := b[0]
|
|
||||||
if s.HostPort != "" {
|
|
||||||
t.Logf("Expected \"\" got %s", s.HostPort)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if s.HostIP != "192.168.1.100" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNetworkOptsPublic(t *testing.T) {
|
|
||||||
ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100:8080:80"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(ports) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(ports))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(bindings))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
for k := range ports {
|
|
||||||
if k.Proto() != "tcp" {
|
|
||||||
t.Logf("Expected tcp got %s", k.Proto())
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if k.Port() != "80" {
|
|
||||||
t.Logf("Expected 80 got %s", k.Port())
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, exists := bindings[k]
|
|
||||||
if !exists {
|
|
||||||
t.Log("Binding does not exist")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if len(b) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(b))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
s := b[0]
|
|
||||||
if s.HostPort != "8080" {
|
|
||||||
t.Logf("Expected 8080 got %s", s.HostPort)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if s.HostIP != "192.168.1.100" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNetworkOptsPublicNoPort(t *testing.T) {
|
|
||||||
ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100"})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Logf("Expected error Invalid containerPort")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if ports != nil {
|
|
||||||
t.Logf("Expected nil got %s", ports)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if bindings != nil {
|
|
||||||
t.Logf("Expected nil got %s", bindings)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNetworkOptsNegativePorts(t *testing.T) {
|
|
||||||
ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100:-1:-1"})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if len(ports) != 0 {
|
|
||||||
t.Logf("Expected nil got %d", len(ports))
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if len(bindings) != 0 {
|
|
||||||
t.Logf("Expected 0 got %d", len(bindings))
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNetworkOptsUdp(t *testing.T) {
|
|
||||||
ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(ports) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(ports))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if len(bindings) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(bindings))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
for k := range ports {
|
|
||||||
if k.Proto() != "udp" {
|
|
||||||
t.Logf("Expected udp got %s", k.Proto())
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if k.Port() != "6000" {
|
|
||||||
t.Logf("Expected 6000 got %s", k.Port())
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, exists := bindings[k]
|
|
||||||
if !exists {
|
|
||||||
t.Log("Binding does not exist")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if len(b) != 1 {
|
|
||||||
t.Logf("Expected 1 got %d", len(b))
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
s := b[0]
|
|
||||||
if s.HostPort != "" {
|
|
||||||
t.Logf("Expected \"\" got %s", s.HostPort)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if s.HostIP != "192.168.1.100" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
98
nat/sort.go
98
nat/sort.go
|
@ -1,98 +0,0 @@
|
||||||
package nat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/parsers"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort sorts a list of ports using the provided predicate
|
|
||||||
// This function should compare `i` and `j`, returning true if `i` is
|
|
||||||
// considered to be less than `j`
|
|
||||||
func Sort(ports []Port, predicate func(i, j Port) bool) {
|
|
||||||
s := &portSorter{ports, predicate}
|
|
||||||
sort.Sort(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
type portMapEntry struct {
|
|
||||||
port Port
|
|
||||||
binding PortBinding
|
|
||||||
}
|
|
||||||
|
|
||||||
type portMapSorter []portMapEntry
|
|
||||||
|
|
||||||
func (s portMapSorter) Len() int { return len(s) }
|
|
||||||
func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
// sort the port so that the order is:
|
|
||||||
// 1. port with larger specified bindings
|
|
||||||
// 2. larger port
|
|
||||||
// 3. port with tcp protocol
|
|
||||||
func (s portMapSorter) Less(i, j int) bool {
|
|
||||||
pi, pj := s[i].port, s[j].port
|
|
||||||
hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort)
|
|
||||||
return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortPortMap sorts the list of ports and their respected mapping. The ports
|
|
||||||
// will explicit HostPort will be placed first.
|
|
||||||
func SortPortMap(ports []Port, bindings PortMap) {
|
|
||||||
s := portMapSorter{}
|
|
||||||
for _, p := range ports {
|
|
||||||
if binding, ok := bindings[p]; ok {
|
|
||||||
for _, b := range binding {
|
|
||||||
s = append(s, portMapEntry{port: p, binding: b})
|
|
||||||
}
|
|
||||||
bindings[p] = []PortBinding{}
|
|
||||||
} else {
|
|
||||||
s = append(s, portMapEntry{port: p})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(s)
|
|
||||||
var (
|
|
||||||
i int
|
|
||||||
pm = make(map[Port]struct{})
|
|
||||||
)
|
|
||||||
// reorder ports
|
|
||||||
for _, entry := range s {
|
|
||||||
if _, ok := pm[entry.port]; !ok {
|
|
||||||
ports[i] = entry.port
|
|
||||||
pm[entry.port] = struct{}{}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
// reorder bindings for this port
|
|
||||||
if _, ok := bindings[entry.port]; ok {
|
|
||||||
bindings[entry.port] = append(bindings[entry.port], entry.binding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toInt(s string) uint64 {
|
|
||||||
i, _, err := parsers.ParsePortRange(s)
|
|
||||||
if err != nil {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package nat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSortUniquePorts(t *testing.T) {
|
|
||||||
ports := []Port{
|
|
||||||
Port("6379/tcp"),
|
|
||||||
Port("22/tcp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
Sort(ports, func(ip, jp Port) bool {
|
|
||||||
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
|
|
||||||
})
|
|
||||||
|
|
||||||
first := ports[0]
|
|
||||||
if fmt.Sprint(first) != "22/tcp" {
|
|
||||||
t.Log(fmt.Sprint(first))
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSortSamePortWithDifferentProto(t *testing.T) {
|
|
||||||
ports := []Port{
|
|
||||||
Port("8888/tcp"),
|
|
||||||
Port("8888/udp"),
|
|
||||||
Port("6379/tcp"),
|
|
||||||
Port("6379/udp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
Sort(ports, func(ip, jp Port) bool {
|
|
||||||
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
|
|
||||||
})
|
|
||||||
|
|
||||||
first := ports[0]
|
|
||||||
if fmt.Sprint(first) != "6379/tcp" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSortPortMap(t *testing.T) {
|
|
||||||
ports := []Port{
|
|
||||||
Port("22/tcp"),
|
|
||||||
Port("22/udp"),
|
|
||||||
Port("8000/tcp"),
|
|
||||||
Port("6379/tcp"),
|
|
||||||
Port("9999/tcp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
portMap := PortMap{
|
|
||||||
Port("22/tcp"): []PortBinding{
|
|
||||||
{},
|
|
||||||
},
|
|
||||||
Port("8000/tcp"): []PortBinding{
|
|
||||||
{},
|
|
||||||
},
|
|
||||||
Port("6379/tcp"): []PortBinding{
|
|
||||||
{},
|
|
||||||
{HostIP: "0.0.0.0", HostPort: "32749"},
|
|
||||||
},
|
|
||||||
Port("9999/tcp"): []PortBinding{
|
|
||||||
{HostIP: "0.0.0.0", HostPort: "40000"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
SortPortMap(ports, portMap)
|
|
||||||
if !reflect.DeepEqual(ports, []Port{
|
|
||||||
Port("9999/tcp"),
|
|
||||||
Port("6379/tcp"),
|
|
||||||
Port("8000/tcp"),
|
|
||||||
Port("22/tcp"),
|
|
||||||
Port("22/udp"),
|
|
||||||
}) {
|
|
||||||
t.Errorf("failed to prioritize port with explicit mappings, got %v", ports)
|
|
||||||
}
|
|
||||||
if pm := portMap[Port("6379/tcp")]; !reflect.DeepEqual(pm, []PortBinding{
|
|
||||||
{HostIP: "0.0.0.0", HostPort: "32749"},
|
|
||||||
{},
|
|
||||||
}) {
|
|
||||||
t.Errorf("failed to prioritize bindings with explicit mappings, got %v", pm)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue