links.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package docker
  2. import (
  3. "fmt"
  4. "github.com/dotcloud/docker/pkg/iptables"
  5. "path"
  6. "strings"
  7. )
  8. type Link struct {
  9. ParentIP string
  10. ChildIP string
  11. Name string
  12. BridgeInterface string
  13. ChildEnvironment []string
  14. Ports []Port
  15. IsEnabled bool
  16. }
  17. func NewLink(parent, child *Container, name, bridgeInterface string) (*Link, error) {
  18. if parent.ID == child.ID {
  19. return nil, fmt.Errorf("Cannot link to self: %s == %s", parent.ID, child.ID)
  20. }
  21. if !child.State.IsRunning() {
  22. return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, name)
  23. }
  24. ports := make([]Port, len(child.Config.ExposedPorts))
  25. var i int
  26. for p := range child.Config.ExposedPorts {
  27. ports[i] = p
  28. i++
  29. }
  30. l := &Link{
  31. BridgeInterface: bridgeInterface,
  32. Name: name,
  33. ChildIP: child.NetworkSettings.IPAddress,
  34. ParentIP: parent.NetworkSettings.IPAddress,
  35. ChildEnvironment: child.Config.Env,
  36. Ports: ports,
  37. }
  38. return l, nil
  39. }
  40. func (l *Link) Alias() string {
  41. _, alias := path.Split(l.Name)
  42. return alias
  43. }
  44. func (l *Link) ToEnv() []string {
  45. env := []string{}
  46. alias := strings.ToUpper(l.Alias())
  47. if p := l.getDefaultPort(); p != nil {
  48. env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
  49. }
  50. // Load exposed ports into the environment
  51. for _, p := range l.Ports {
  52. 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()))
  53. env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
  54. env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
  55. env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
  56. }
  57. // Load the linked container's name into the environment
  58. env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
  59. if l.ChildEnvironment != nil {
  60. for _, v := range l.ChildEnvironment {
  61. parts := strings.Split(v, "=")
  62. if len(parts) != 2 {
  63. continue
  64. }
  65. // Ignore a few variables that are added during docker build
  66. if parts[0] == "HOME" || parts[0] == "PATH" {
  67. continue
  68. }
  69. env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
  70. }
  71. }
  72. return env
  73. }
  74. // Default port rules
  75. func (l *Link) getDefaultPort() *Port {
  76. var p Port
  77. i := len(l.Ports)
  78. if i == 0 {
  79. return nil
  80. } else if i > 1 {
  81. sortPorts(l.Ports, func(ip, jp Port) bool {
  82. // If the two ports have the same number, tcp takes priority
  83. // Sort in desc order
  84. return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
  85. })
  86. }
  87. p = l.Ports[0]
  88. return &p
  89. }
  90. func (l *Link) Enable() error {
  91. if err := l.toggle("-I", false); err != nil {
  92. return err
  93. }
  94. l.IsEnabled = true
  95. return nil
  96. }
  97. func (l *Link) Disable() {
  98. // We do not care about errors here because the link may not
  99. // exist in iptables
  100. l.toggle("-D", true)
  101. l.IsEnabled = false
  102. }
  103. func (l *Link) toggle(action string, ignoreErrors bool) error {
  104. for _, p := range l.Ports {
  105. if output, err := iptables.Raw(action, "FORWARD",
  106. "-i", l.BridgeInterface, "-o", l.BridgeInterface,
  107. "-p", p.Proto(),
  108. "-s", l.ParentIP,
  109. "--dport", p.Port(),
  110. "-d", l.ChildIP,
  111. "-j", "ACCEPT"); !ignoreErrors && err != nil {
  112. return err
  113. } else if len(output) != 0 {
  114. return fmt.Errorf("Error toggle iptables forward: %s", output)
  115. }
  116. if output, err := iptables.Raw(action, "FORWARD",
  117. "-i", l.BridgeInterface, "-o", l.BridgeInterface,
  118. "-p", p.Proto(),
  119. "-s", l.ChildIP,
  120. "--sport", p.Port(),
  121. "-d", l.ParentIP,
  122. "-j", "ACCEPT"); !ignoreErrors && err != nil {
  123. return err
  124. } else if len(output) != 0 {
  125. return fmt.Errorf("Error toggle iptables forward: %s", output)
  126. }
  127. }
  128. return nil
  129. }