Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1834 expose range #4795

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 43 additions & 7 deletions links/links.go
Expand Up @@ -47,6 +47,20 @@ func (l *Link) Alias() string {
return alias
}

func nextContiguous(ports []nat.Port, value int, index int) int {
if index + 1 == len(ports) {
return index
}
for i := index + 1; i < len(ports); i++ {
if ports[i].Int() > value + 1 {
return i - 1
}

value++
}
return len(ports) - 1
}

func (l *Link) ToEnv() []string {
env := []string{}
alias := strings.ToUpper(l.Alias())
Expand All @@ -55,12 +69,34 @@ func (l *Link) ToEnv() []string {
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
}

// Load exposed ports into the environment
for _, p := range l.Ports {
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
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")
})

for i := 0; i < len(l.Ports); {
p := l.Ports[i]
j := nextContiguous(l.Ports, p.Int(), i)
if j > i + 1 {
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_START=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))

q := l.Ports[j]
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_END=%s://%s:%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Proto(), l.ChildIP, q.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port()))

i = j + 1
} else {
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))

i++
}
}

// Load the linked container's name into the environment
Expand Down Expand Up @@ -125,7 +161,7 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {

out := make([]string, len(l.Ports))
for i, p := range l.Ports {
out[i] = fmt.Sprintf("%s/%s", p.Port(), p.Proto())
out[i] = string(p)
}
job.SetenvList("Ports", out)

Expand Down
49 changes: 49 additions & 0 deletions links/links_test.go
Expand Up @@ -77,3 +77,52 @@ func TestLinkEnv(t *testing.T) {
t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
}
}

func TestLinkMultipleEnv(t *testing.T) {
ports := make(nat.PortSet)
ports[nat.Port("6379/tcp")] = struct{}{}
ports[nat.Port("6380/tcp")] = struct{}{}
ports[nat.Port("6381/tcp")] = struct{}{}

link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports, nil)
if err != nil {
t.Fatal(err)
}

rawEnv := link.ToEnv()
env := make(map[string]string, len(rawEnv))
for _, e := range rawEnv {
parts := strings.Split(e, "=")
if len(parts) != 2 {
t.FailNow()
}
env[parts[0]] = parts[1]
}
if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
t.Fatalf("Expected 172.0.17.2:6379, got %s", env["DOCKER_PORT"])
}
if env["DOCKER_PORT_6379_TCP_START"] != "tcp://172.0.17.2:6379" {
t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT_6379_TCP_START"])
}
if env["DOCKER_PORT_6379_TCP_END"] != "tcp://172.0.17.2:6381" {
t.Fatalf("Expected tcp://172.0.17.2:6381, got %s", env["DOCKER_PORT_6379_TCP_END"])
}
if env["DOCKER_PORT_6379_TCP_PROTO"] != "tcp" {
t.Fatalf("Expected tcp, got %s", env["DOCKER_PORT_6379_TCP_PROTO"])
}
if env["DOCKER_PORT_6379_TCP_ADDR"] != "172.0.17.2" {
t.Fatalf("Expected 172.0.17.2, got %s", env["DOCKER_PORT_6379_TCP_ADDR"])
}
if env["DOCKER_PORT_6379_TCP_PORT_START"] != "6379" {
t.Fatalf("Expected 6379, got %s", env["DOCKER_PORT_6379_TCP_PORT_START"])
}
if env["DOCKER_PORT_6379_TCP_PORT_END"] != "6381" {
t.Fatalf("Expected 6381, got %s", env["DOCKER_PORT_6379_TCP_PORT_END"])
}
if env["DOCKER_NAME"] != "/db/docker" {
t.Fatalf("Expected /db/docker, got %s", env["DOCKER_NAME"])
}
if env["DOCKER_ENV_PASSWORD"] != "gordon" {
t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
}
}
16 changes: 7 additions & 9 deletions nat/nat.go
Expand Up @@ -40,23 +40,21 @@ func ParsePort(rawPort string) (int, error) {
}

func (p Port) Proto() string {
parts := strings.Split(string(p), "/")
if len(parts) == 1 {
return "tcp"
}
return parts[1]
proto, _ := SplitProtoPort(string(p))
return proto
}

func (p Port) Port() string {
return strings.Split(string(p), "/")[0]
_, port := SplitProtoPort(string(p))
return port
}

func (p Port) Int() int {
i, err := ParsePort(p.Port())
port, err := ParsePort(p.Port())
if err != nil {
panic(err)
}
return i
return port
}

// Splits a port in the format of port/proto
Expand All @@ -69,7 +67,7 @@ func SplitProtoPort(rawPort string) (string, string) {
if l == 1 {
return "tcp", rawPort
}
return parts[0], parts[1]
return parts[1], parts[0]
}

// We will receive port specs in the format of ip:public:private/proto and these need to be
Expand Down
20 changes: 17 additions & 3 deletions runconfig/parse.go
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/dotcloud/docker/utils"
"io/ioutil"
"path"
"strconv"
"strings"
)

Expand Down Expand Up @@ -173,9 +174,22 @@ 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 := nat.NewPort(nat.SplitProtoPort(e))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}

if strings.Contains(e, "-") {
parts := strings.Split(e, "-")
start,_ := strconv.Atoi(parts[0])
end,_ := strconv.Atoi(parts[1])
for i := start; i <= end; i++ {
p := nat.NewPort("tcp",strconv.Itoa(i))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
}
}
} else {
p := nat.NewPort(nat.SplitProtoPort(e))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
}
}
}

Expand Down
45 changes: 22 additions & 23 deletions runtime/networkdriver/lxc/driver.go
Expand Up @@ -2,6 +2,7 @@ package lxc

import (
"fmt"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/iptables"
"github.com/dotcloud/docker/pkg/netlink"
Expand All @@ -13,7 +14,7 @@ import (
"io/ioutil"
"log"
"net"
"strings"
"strconv"
"syscall"
"unsafe"
)
Expand Down Expand Up @@ -443,40 +444,38 @@ func LinkContainers(job *engine.Job) engine.Status {
ignoreErrors = job.GetenvBool("IgnoreErrors")
ports = job.GetenvList("Ports")
)
split := func(p string) (string, string) {
parts := strings.Split(p, "/")
return parts[0], parts[1]
}

for _, p := range ports {
port, proto := split(p)
for _, value := range ports {
port := nat.Port(value)

if output, err := iptables.Raw(action, "FORWARD",
"-i", bridgeIface, "-o", bridgeIface,
"-p", proto,
"-p", port.Proto(),
"-s", parentIP,
"--dport", port,
"--dport", strconv.Itoa(port.Int()),
"-d", childIP,
"-j", "ACCEPT"); !ignoreErrors && err != nil {
job.Error(err)
return engine.StatusErr
} else if len(output) != 0 {
job.Errorf("Error toggle iptables forward: %s", output)
return engine.StatusErr
}
job.Error(err)
return engine.StatusErr
} else if len(output) != 0 {
job.Errorf("Error toggle iptables forward: %s", output)
return engine.StatusErr
}

if output, err := iptables.Raw(action, "FORWARD",
"-i", bridgeIface, "-o", bridgeIface,
"-p", proto,
"-p", port.Proto(),
"-s", childIP,
"--sport", port,
"--sport", strconv.Itoa(port.Int()),
"-d", parentIP,
"-j", "ACCEPT"); !ignoreErrors && err != nil {
job.Error(err)
return engine.StatusErr
} else if len(output) != 0 {
job.Errorf("Error toggle iptables forward: %s", output)
return engine.StatusErr
}
job.Error(err)
return engine.StatusErr
} else if len(output) != 0 {
job.Errorf("Error toggle iptables forward: %s", output)
return engine.StatusErr
}

}
return engine.StatusOK
}