github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/links/links.go (about) 1 package links 2 3 import ( 4 "fmt" 5 "path" 6 "strings" 7 8 "github.com/docker/docker/daemon/networkdriver/bridge" 9 "github.com/docker/docker/nat" 10 "github.com/docker/docker/pkg/iptables" 11 ) 12 13 type Link struct { 14 ParentIP string 15 ChildIP string 16 Name string 17 ChildEnvironment []string 18 Ports []nat.Port 19 IsEnabled bool 20 } 21 22 func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}) (*Link, error) { 23 24 var ( 25 i int 26 ports = make([]nat.Port, len(exposedPorts)) 27 ) 28 29 for p := range exposedPorts { 30 ports[i] = p 31 i++ 32 } 33 34 l := &Link{ 35 Name: name, 36 ChildIP: childIP, 37 ParentIP: parentIP, 38 ChildEnvironment: env, 39 Ports: ports, 40 } 41 return l, nil 42 43 } 44 45 func (l *Link) Alias() string { 46 _, alias := path.Split(l.Name) 47 return alias 48 } 49 50 func nextContiguous(ports []nat.Port, value int, index int) int { 51 if index+1 == len(ports) { 52 return index 53 } 54 for i := index + 1; i < len(ports); i++ { 55 if ports[i].Int() > value+1 { 56 return i - 1 57 } 58 59 value++ 60 } 61 return len(ports) - 1 62 } 63 64 func (l *Link) ToEnv() []string { 65 env := []string{} 66 alias := strings.Replace(strings.ToUpper(l.Alias()), "-", "_", -1) 67 68 if p := l.getDefaultPort(); p != nil { 69 env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port())) 70 } 71 72 //sort the ports so that we can bulk the continuous ports together 73 nat.Sort(l.Ports, func(ip, jp nat.Port) bool { 74 // If the two ports have the same number, tcp takes priority 75 // Sort in desc order 76 return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp") 77 }) 78 79 for i := 0; i < len(l.Ports); { 80 p := l.Ports[i] 81 j := nextContiguous(l.Ports, p.Int(), i) 82 if j > i+1 { 83 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())) 84 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP)) 85 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto())) 86 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port())) 87 88 q := l.Ports[j] 89 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())) 90 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port())) 91 92 i = j + 1 93 continue 94 } else { 95 i++ 96 } 97 } 98 for _, p := range l.Ports { 99 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())) 100 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP)) 101 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port())) 102 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto())) 103 } 104 105 // Load the linked container's name into the environment 106 env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name)) 107 108 if l.ChildEnvironment != nil { 109 for _, v := range l.ChildEnvironment { 110 parts := strings.SplitN(v, "=", 2) 111 if len(parts) < 2 { 112 continue 113 } 114 // Ignore a few variables that are added during docker build (and not really relevant to linked containers) 115 if parts[0] == "HOME" || parts[0] == "PATH" { 116 continue 117 } 118 env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1])) 119 } 120 } 121 return env 122 } 123 124 // Default port rules 125 func (l *Link) getDefaultPort() *nat.Port { 126 var p nat.Port 127 i := len(l.Ports) 128 129 if i == 0 { 130 return nil 131 } else if i > 1 { 132 nat.Sort(l.Ports, func(ip, jp nat.Port) bool { 133 // If the two ports have the same number, tcp takes priority 134 // Sort in desc order 135 return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp") 136 }) 137 } 138 p = l.Ports[0] 139 return &p 140 } 141 142 func (l *Link) Enable() error { 143 // -A == iptables append flag 144 if err := l.toggle("-A", false); err != nil { 145 return err 146 } 147 // call this on Firewalld reload 148 iptables.OnReloaded(func() { l.toggle("-I", false) }) 149 l.IsEnabled = true 150 return nil 151 } 152 153 func (l *Link) Disable() { 154 // We do not care about errors here because the link may not 155 // exist in iptables 156 // -D == iptables delete flag 157 l.toggle("-D", true) 158 // call this on Firewalld reload 159 iptables.OnReloaded(func() { l.toggle("-D", true) }) 160 l.IsEnabled = false 161 } 162 163 func (l *Link) toggle(action string, ignoreErrors bool) error { 164 return bridge.LinkContainers(action, l.ParentIP, l.ChildIP, l.Ports, ignoreErrors) 165 }