123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package links
- import (
- "fmt"
- "github.com/docker/docker/engine"
- "github.com/docker/docker/nat"
- "path"
- "strings"
- )
- type Link struct {
- ParentIP string
- ChildIP string
- Name string
- ChildEnvironment []string
- Ports []nat.Port
- IsEnabled bool
- eng *engine.Engine
- }
- func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}, eng *engine.Engine) (*Link, error) {
- var (
- i int
- ports = make([]nat.Port, len(exposedPorts))
- )
- for p := range exposedPorts {
- ports[i] = p
- i++
- }
- l := &Link{
- Name: name,
- ChildIP: childIP,
- ParentIP: parentIP,
- ChildEnvironment: env,
- Ports: ports,
- eng: eng,
- }
- return l, nil
- }
- func (l *Link) Alias() string {
- _, alias := path.Split(l.Name)
- 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.Replace(strings.ToUpper(l.Alias()), "-", "_", -1)
- if p := l.getDefaultPort(); p != nil {
- env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
- }
- //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
- }
- 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
- env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
- if l.ChildEnvironment != nil {
- for _, v := range l.ChildEnvironment {
- parts := strings.Split(v, "=")
- if len(parts) != 2 {
- continue
- }
- // Ignore a few variables that are added during docker build (and not really relevant to linked containers)
- 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
- func (l *Link) getDefaultPort() *nat.Port {
- var p nat.Port
- i := len(l.Ports)
- if i == 0 {
- return nil
- } else if i > 1 {
- 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")
- })
- }
- p = l.Ports[0]
- return &p
- }
- func (l *Link) Enable() error {
- // -A == iptables append flag
- if err := l.toggle("-A", false); err != nil {
- return err
- }
- l.IsEnabled = true
- return nil
- }
- func (l *Link) Disable() {
- // We do not care about errors here because the link may not
- // exist in iptables
- // -D == iptables delete flag
- l.toggle("-D", true)
- l.IsEnabled = false
- }
- func (l *Link) toggle(action string, ignoreErrors bool) error {
- job := l.eng.Job("link", action)
- job.Setenv("ParentIP", l.ParentIP)
- job.Setenv("ChildIP", l.ChildIP)
- job.SetenvBool("IgnoreErrors", ignoreErrors)
- out := make([]string, len(l.Ports))
- for i, p := range l.Ports {
- out[i] = string(p)
- }
- job.SetenvList("Ports", out)
- if err := job.Run(); err != nil {
- // TODO: get ouput from job
- return err
- }
- return nil
- }
|