github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/daemon/links/links.go (about) 1 package links 2 3 import ( 4 "fmt" 5 "path" 6 "strings" 7 8 "github.com/docker/go-connections/nat" 9 ) 10 11 // Link struct holds informations about parent/child linked container 12 type Link struct { 13 // Parent container IP address 14 ParentIP string 15 // Child container IP address 16 ChildIP string 17 // Link name 18 Name string 19 // Child environments variables 20 ChildEnvironment []string 21 // Child exposed ports 22 Ports []nat.Port 23 } 24 25 // NewLink initializes a new Link struct with the provided options. 26 func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}) *Link { 27 var ( 28 i int 29 ports = make([]nat.Port, len(exposedPorts)) 30 ) 31 32 for p := range exposedPorts { 33 ports[i] = p 34 i++ 35 } 36 37 return &Link{ 38 Name: name, 39 ChildIP: childIP, 40 ParentIP: parentIP, 41 ChildEnvironment: env, 42 Ports: ports, 43 } 44 } 45 46 // ToEnv creates a string's slice containing child container informations in 47 // the form of environment variables which will be later exported on container 48 // startup. 49 func (l *Link) ToEnv() []string { 50 env := []string{} 51 52 _, n := path.Split(l.Name) 53 alias := strings.Replace(strings.ToUpper(n), "-", "_", -1) 54 55 if p := l.getDefaultPort(); p != nil { 56 env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port())) 57 } 58 59 //sort the ports so that we can bulk the continuous ports together 60 nat.Sort(l.Ports, func(ip, jp nat.Port) bool { 61 // If the two ports have the same number, tcp takes priority 62 // Sort in desc order 63 return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp") 64 }) 65 66 for i := 0; i < len(l.Ports); { 67 p := l.Ports[i] 68 j := nextContiguous(l.Ports, p.Int(), i) 69 if j > i+1 { 70 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())) 71 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP)) 72 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto())) 73 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port())) 74 75 q := l.Ports[j] 76 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())) 77 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port())) 78 79 i = j + 1 80 continue 81 } else { 82 i++ 83 } 84 } 85 for _, p := range l.Ports { 86 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())) 87 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP)) 88 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port())) 89 env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto())) 90 } 91 92 // Load the linked container's name into the environment 93 env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name)) 94 95 if l.ChildEnvironment != nil { 96 for _, v := range l.ChildEnvironment { 97 parts := strings.SplitN(v, "=", 2) 98 if len(parts) < 2 { 99 continue 100 } 101 // Ignore a few variables that are added during docker build (and not really relevant to linked containers) 102 if parts[0] == "HOME" || parts[0] == "PATH" { 103 continue 104 } 105 env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1])) 106 } 107 } 108 return env 109 } 110 111 func nextContiguous(ports []nat.Port, value int, index int) int { 112 if index+1 == len(ports) { 113 return index 114 } 115 for i := index + 1; i < len(ports); i++ { 116 if ports[i].Int() > value+1 { 117 return i - 1 118 } 119 120 value++ 121 } 122 return len(ports) - 1 123 } 124 125 // Default port rules 126 func (l *Link) getDefaultPort() *nat.Port { 127 var p nat.Port 128 i := len(l.Ports) 129 130 if i == 0 { 131 return nil 132 } else if i > 1 { 133 nat.Sort(l.Ports, func(ip, jp nat.Port) bool { 134 // If the two ports have the same number, tcp takes priority 135 // Sort in desc order 136 return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp") 137 }) 138 } 139 p = l.Ports[0] 140 return &p 141 }