Add default port for link

Add links unit test file
This commit is contained in:
Michael Crosby
2013-09-18 12:15:26 -07:00
parent a07d919ea1
commit 27fc64dbc6
6 changed files with 241 additions and 9 deletions
+5
View File
@@ -1122,6 +1122,11 @@ func (cli *DockerCli) CmdLinks(args ...string) error {
return err
}
if cmd.NArg() < 1 && !*flAll {
cmd.Usage()
return nil
}
v := url.Values{}
v.Set("name", cmd.Arg(0))
+11
View File
@@ -125,6 +125,14 @@ 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))
}
@@ -830,6 +838,9 @@ func (container *Container) Start(hostConfig *HostConfig) error {
return err
}
linkedContainer := runtime.Get(parts["id"])
if linkedContainer == nil {
return fmt.Errorf("Cannot find container: %s", parts["id"])
}
link, err := runtime.links.NewLink(container, linkedContainer, runtime.networkManager.bridgeIface, parts["alias"])
if err != nil {
+42 -9
View File
@@ -26,6 +26,9 @@ type LinkRepository struct {
}
func (r *LinkRepository) NewLink(to, from *Container, bridgeInterface string, alias string) (*Link, error) {
if to.ID == from.ID {
return nil, fmt.Errorf("Cannot link to self: %s == %s", to.ID, from.ID)
}
if !from.State.Running {
return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", from.ID, alias)
}
@@ -39,7 +42,7 @@ func (r *LinkRepository) NewLink(to, from *Container, bridgeInterface string, al
FromID: utils.TruncateID(from.ID),
ToID: utils.TruncateID(to.ID),
BridgeInterface: bridgeInterface,
Alias: alias,
Alias: strings.ToUpper(alias),
FromIP: from.NetworkSettings.IPAddress,
ToIP: to.NetworkSettings.IPAddress,
FromEnvironment: from.Config.Env,
@@ -57,25 +60,55 @@ func (l *Link) ID() string {
func (l *Link) ToEnv() []string {
env := []string{}
if p := l.getDefaultPort(); p != nil {
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", l.Alias, p.Proto(), l.FromIP, p.Port()))
}
// Load exposed ports into the environment
for _, p := range l.ports {
env = append(env, fmt.Sprintf("%s_%s_ADDR=%s://%s:%s", strings.ToUpper(l.Alias), p.Port(), p.Proto(), l.FromIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", l.Alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.FromIP, p.Port()))
}
// Load the linked container's ID into the environment
env = append(env, fmt.Sprintf("%s_ID=%s", l.Alias, l.FromID))
if l.FromEnvironment != nil {
for _, v := range l.FromEnvironment {
parts := strings.Split(v, "=")
if len(parts) < 2 {
if len(parts) != 2 {
continue
}
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", strings.ToUpper(l.Alias), parts[0], parts[1]))
// Ignore a few variables that are added during docker build
if parts[0] == "HOME" || parts[0] == "PATH" {
continue
}
env = append(env, fmt.Sprintf("%s_ENV_%s=%s", l.Alias, parts[0], parts[1]))
}
}
return env
}
// Default port rules
func (l *Link) getDefaultPort() *Port {
var p Port
i := len(l.ports)
if i == 0 {
return nil
} else if i > 1 {
sortPorts(l.ports, func(ip, jp 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() && ip.Proto() == "tcp")
})
}
p = l.ports[0]
return &p
}
func (l *Link) Enable() error {
if err := l.toggle("-I"); err != nil {
if err := l.toggle("-I", false); err != nil {
return err
}
l.isEnabled = true
@@ -85,12 +118,12 @@ func (l *Link) Enable() error {
func (l *Link) Disable() {
// We do not care about erros here because the link may not
// exist in iptables
l.toggle("-D")
l.toggle("-D", true)
l.isEnabled = false
}
func (l *Link) toggle(action string) error {
func (l *Link) toggle(action string, ignoreErrors bool) error {
for _, p := range l.ports {
if err := iptables.Raw(action, "FORWARD",
"-i", l.BridgeInterface, "-o", l.BridgeInterface,
@@ -98,7 +131,7 @@ func (l *Link) toggle(action string) error {
"-s", l.ToIP,
"--dport", p.Port(),
"-d", l.FromIP,
"-j", "ACCEPT"); err != nil {
"-j", "ACCEPT"); !ignoreErrors && err != nil {
return err
}
@@ -108,7 +141,7 @@ func (l *Link) toggle(action string) error {
"-s", l.FromIP,
"--sport", p.Port(),
"-d", l.ToIP,
"-j", "ACCEPT"); err != nil {
"-j", "ACCEPT"); !ignoreErrors && err != nil {
return err
}
}
+122
View File
@@ -0,0 +1,122 @@
package docker
import (
"fmt"
"github.com/dotcloud/docker/utils"
"strings"
"testing"
)
func newTestLinkRepository(t *testing.T) *LinkRepository {
r, err := NewLinkRepository("")
if err != nil {
t.Fatal(err)
}
return r
}
func newMockLinkContainer(id string, ip string) *Container {
return &Container{
Config: &Config{},
ID: id,
NetworkSettings: &NetworkSettings{
IPAddress: ip,
},
}
}
func TestLinkNew(t *testing.T) {
r := newTestLinkRepository(t)
toID := GenerateID()
fromID := GenerateID()
from := newMockLinkContainer(fromID, "172.0.17.2")
from.Config.Env = []string{}
from.State = State{Running: true}
ports := make(map[Port]struct{})
ports[Port("6379/tcp")] = struct{}{}
from.Config.ExposedPorts = ports
to := newMockLinkContainer(toID, "172.0.17.3")
link, err := r.NewLink(to, from, "172.0.17.1", "docker")
if err != nil {
t.Fatal(err)
}
if link == nil {
t.FailNow()
}
if link.ID() != fmt.Sprintf("%s:%s", utils.TruncateID(to.ID), "DOCKER") {
t.Fail()
}
if link.Alias != "DOCKER" {
t.Fail()
}
if link.FromID != utils.TruncateID(from.ID) {
t.Fail()
}
if link.ToID != utils.TruncateID(to.ID) {
t.Fail()
}
if link.ToIP != "172.0.17.3" {
t.Fail()
}
if link.FromIP != "172.0.17.2" {
t.Fail()
}
if link.BridgeInterface != "172.0.17.1" {
t.Fail()
}
for _, p := range link.ports {
if p != Port("6379/tcp") {
t.Fail()
}
}
}
func TestLinkEnv(t *testing.T) {
r := newTestLinkRepository(t)
toID := GenerateID()
fromID := GenerateID()
from := newMockLinkContainer(fromID, "172.0.17.2")
from.Config.Env = []string{"PASSWORD=gordon"}
from.State = State{Running: true}
ports := make(map[Port]struct{})
ports[Port("6379/tcp")] = struct{}{}
from.Config.ExposedPorts = ports
to := newMockLinkContainer(toID, "172.0.17.3")
link, err := r.NewLink(to, from, "172.0.17.1", "docker")
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.Fail()
}
if env["DOCKER_PORT_6379_TCP"] != "tcp://172.0.17.2:6379" {
t.Fail()
}
if env["DOCKER_ID"] != utils.TruncateID(from.ID) {
t.Fail()
}
if env["DOCKER_ENV_PASSWORD"] != "gordon" {
t.Fail()
}
}
+25
View File
@@ -34,3 +34,28 @@ func sortImagesByCreationAndTag(images []APIImages) {
sort.Sort(sorter)
}
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)
}
+36
View File
@@ -1,6 +1,7 @@
package docker
import (
"fmt"
"testing"
)
@@ -55,3 +56,38 @@ func TestServerListOrderedImagesByCreationDateAndTag(t *testing.T) {
t.Error("Expected []APIImges to be ordered by most recent creation date and tag name.")
}
}
func TestSortUniquePorts(t *testing.T) {
ports := []Port{
Port("6379/tcp"),
Port("22/tcp"),
}
sortPorts(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"),
}
sortPorts(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()
}
}