links.go 4.2 KB

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