links/links.go
147b09fa
 package links
1cbdaeba
 
 import (
 	"fmt"
 	"path"
 	"strings"
53582321
 
 	"github.com/docker/docker/nat"
1cbdaeba
 )
 
 type Link struct {
 	ParentIP         string
 	ChildIP          string
 	Name             string
 	ChildEnvironment []string
3ecd8ff0
 	Ports            []nat.Port
1cbdaeba
 	IsEnabled        bool
 }
 
75600185
 func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}) (*Link, error) {
fc952e0d
 
 	var (
 		i     int
 		ports = make([]nat.Port, len(exposedPorts))
 	)
1cbdaeba
 
fc952e0d
 	for p := range exposedPorts {
1cbdaeba
 		ports[i] = p
 		i++
 	}
 
 	l := &Link{
 		Name:             name,
fc952e0d
 		ChildIP:          childIP,
 		ParentIP:         parentIP,
 		ChildEnvironment: env,
1cbdaeba
 		Ports:            ports,
 	}
 	return l, nil
 
 }
 
 func (l *Link) Alias() string {
 	_, alias := path.Split(l.Name)
 	return alias
 }
 
fd774a81
 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
 }
 
1cbdaeba
 func (l *Link) ToEnv() []string {
 	env := []string{}
6e74754a
 	alias := strings.Replace(strings.ToUpper(l.Alias()), "-", "_", -1)
1cbdaeba
 
 	if p := l.getDefaultPort(); p != nil {
0d292440
 		env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
1cbdaeba
 	}
 
fd774a81
 	//sort the ports so that we can bulk the continuous ports together
 	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
 			continue
611a23aa
 		} else {
 			i++
fd774a81
 		}
611a23aa
 	}
 	for _, p := range l.Ports {
1cbdaeba
 		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()))
1de23f1b
 		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()))
1cbdaeba
 	}
 
 	// Load the linked container's name into the environment
 	env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
 
 	if l.ChildEnvironment != nil {
 		for _, v := range l.ChildEnvironment {
bc149be6
 			parts := strings.SplitN(v, "=", 2)
 			if len(parts) < 2 {
1cbdaeba
 				continue
 			}
b3ffc1f8
 			// Ignore a few variables that are added during docker build (and not really relevant to linked containers)
1cbdaeba
 			if parts[0] == "HOME" || parts[0] == "PATH" {
 				continue
 			}
 			env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
 		}
 	}
 	return env
 }
 
 // Default port rules
3ecd8ff0
 func (l *Link) getDefaultPort() *nat.Port {
 	var p nat.Port
1cbdaeba
 	i := len(l.Ports)
 
 	if i == 0 {
 		return nil
 	} else if i > 1 {
3ecd8ff0
 		nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
1cbdaeba
 			// 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")
 		})
 	}
 	p = l.Ports[0]
 	return &p
 }
 
 func (l *Link) Enable() error {
 	l.IsEnabled = true
 	return nil
 }
 
 func (l *Link) Disable() {
 	l.IsEnabled = false
 }