github.com/adxhyt/docker@v1.4.2-0.20150117221845-467b7c821390/links/links.go (about) 1 package links 2 3 import ( 4 "fmt" 5 "github.com/docker/docker/engine" 6 "github.com/docker/docker/nat" 7 "path" 8 "strings" 9 ) 10 11 type Link struct { 12 ParentIP string 13 ChildIP string 14 Name string 15 ChildEnvironment []string 16 Ports []nat.Port 17 IsEnabled bool 18 eng *engine.Engine 19 } 20 21 func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}, eng *engine.Engine) (*Link, error) { 22 23 var ( 24 i int 25 ports = make([]nat.Port, len(exposedPorts)) 26 ) 27 28 for p := range exposedPorts { 29 ports[i] = p 30 i++ 31 } 32 33 l := &Link{ 34 Name: name, 35 ChildIP: childIP, 36 ParentIP: parentIP, 37 ChildEnvironment: env, 38 Ports: ports, 39 eng: eng, 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 } 95 96 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())) 97 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP)) 98 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port())) 99 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto())) 100 i++ 101 } 102 103 // Load the linked container's name into the environment 104 env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name)) 105 106 if l.ChildEnvironment != nil { 107 for _, v := range l.ChildEnvironment { 108 parts := strings.Split(v, "=") 109 if len(parts) != 2 { 110 continue 111 } 112 // Ignore a few variables that are added during docker build (and not really relevant to linked containers) 113 if parts[0] == "HOME" || parts[0] == "PATH" { 114 continue 115 } 116 env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1])) 117 } 118 } 119 return env 120 } 121 122 // Default port rules 123 func (l *Link) getDefaultPort() *nat.Port { 124 var p nat.Port 125 i := len(l.Ports) 126 127 if i == 0 { 128 return nil 129 } else if i > 1 { 130 nat.Sort(l.Ports, func(ip, jp nat.Port) bool { 131 // If the two ports have the same number, tcp takes priority 132 // Sort in desc order 133 return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp") 134 }) 135 } 136 p = l.Ports[0] 137 return &p 138 } 139 140 func (l *Link) Enable() error { 141 // -A == iptables append flag 142 if err := l.toggle("-A", false); err != nil { 143 return err 144 } 145 l.IsEnabled = true 146 return nil 147 } 148 149 func (l *Link) Disable() { 150 // We do not care about errors here because the link may not 151 // exist in iptables 152 // -D == iptables delete flag 153 l.toggle("-D", true) 154 155 l.IsEnabled = false 156 } 157 158 func (l *Link) toggle(action string, ignoreErrors bool) error { 159 job := l.eng.Job("link", action) 160 161 job.Setenv("ParentIP", l.ParentIP) 162 job.Setenv("ChildIP", l.ChildIP) 163 job.SetenvBool("IgnoreErrors", ignoreErrors) 164 165 out := make([]string, len(l.Ports)) 166 for i, p := range l.Ports { 167 out[i] = string(p) 168 } 169 job.SetenvList("Ports", out) 170 171 if err := job.Run(); err != nil { 172 // TODO: get ouput from job 173 return err 174 } 175 return nil 176 }