github.com/oyvindsk/docker@v1.5.0/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 } 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.Split(v, "=") 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 l.IsEnabled = true 148 return nil 149 } 150 151 func (l *Link) Disable() { 152 // We do not care about errors here because the link may not 153 // exist in iptables 154 // -D == iptables delete flag 155 l.toggle("-D", true) 156 157 l.IsEnabled = false 158 } 159 160 func (l *Link) toggle(action string, ignoreErrors bool) error { 161 job := l.eng.Job("link", action) 162 163 job.Setenv("ParentIP", l.ParentIP) 164 job.Setenv("ChildIP", l.ChildIP) 165 job.SetenvBool("IgnoreErrors", ignoreErrors) 166 167 out := make([]string, len(l.Ports)) 168 for i, p := range l.Ports { 169 out[i] = string(p) 170 } 171 job.SetenvList("Ports", out) 172 173 if err := job.Run(); err != nil { 174 // TODO: get ouput from job 175 return err 176 } 177 return nil 178 }