nat.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package nat
  2. // nat is a convenience package for docker's manipulation of strings describing
  3. // network ports.
  4. import (
  5. "fmt"
  6. "github.com/dotcloud/docker/utils"
  7. "strconv"
  8. "strings"
  9. )
  10. const (
  11. PortSpecTemplate = "ip:hostPort:containerPort"
  12. PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort"
  13. )
  14. type PortBinding struct {
  15. HostIp string
  16. HostPort string
  17. }
  18. type PortMap map[Port][]PortBinding
  19. type PortSet map[Port]struct{}
  20. // 80/tcp
  21. type Port string
  22. func NewPort(proto, port string) Port {
  23. return Port(fmt.Sprintf("%s/%s", port, proto))
  24. }
  25. func ParsePort(rawPort string) (int, error) {
  26. port, err := strconv.ParseUint(rawPort, 10, 16)
  27. if err != nil {
  28. return 0, err
  29. }
  30. return int(port), nil
  31. }
  32. func (p Port) Proto() string {
  33. parts := strings.Split(string(p), "/")
  34. if len(parts) == 1 {
  35. return "tcp"
  36. }
  37. return parts[1]
  38. }
  39. func (p Port) Port() string {
  40. return strings.Split(string(p), "/")[0]
  41. }
  42. func (p Port) Int() int {
  43. i, err := ParsePort(p.Port())
  44. if err != nil {
  45. panic(err)
  46. }
  47. return i
  48. }
  49. // Splits a port in the format of port/proto
  50. func SplitProtoPort(rawPort string) (string, string) {
  51. parts := strings.Split(rawPort, "/")
  52. l := len(parts)
  53. if l == 0 {
  54. return "", ""
  55. }
  56. if l == 1 {
  57. return "tcp", rawPort
  58. }
  59. return parts[1], parts[0]
  60. }
  61. // We will receive port specs in the format of ip:public:private/proto and these need to be
  62. // parsed in the internal types
  63. func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
  64. var (
  65. exposedPorts = make(map[Port]struct{}, len(ports))
  66. bindings = make(map[Port][]PortBinding)
  67. )
  68. for _, rawPort := range ports {
  69. proto := "tcp"
  70. if i := strings.LastIndex(rawPort, "/"); i != -1 {
  71. proto = rawPort[i+1:]
  72. rawPort = rawPort[:i]
  73. }
  74. if !strings.Contains(rawPort, ":") {
  75. rawPort = fmt.Sprintf("::%s", rawPort)
  76. } else if len(strings.Split(rawPort, ":")) == 2 {
  77. rawPort = fmt.Sprintf(":%s", rawPort)
  78. }
  79. parts, err := utils.PartParser(PortSpecTemplate, rawPort)
  80. if err != nil {
  81. return nil, nil, err
  82. }
  83. var (
  84. containerPort = parts["containerPort"]
  85. rawIp = parts["ip"]
  86. hostPort = parts["hostPort"]
  87. )
  88. if containerPort == "" {
  89. return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
  90. }
  91. if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
  92. return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
  93. }
  94. if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
  95. return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
  96. }
  97. port := NewPort(proto, containerPort)
  98. if _, exists := exposedPorts[port]; !exists {
  99. exposedPorts[port] = struct{}{}
  100. }
  101. binding := PortBinding{
  102. HostIp: rawIp,
  103. HostPort: hostPort,
  104. }
  105. bslice, exists := bindings[port]
  106. if !exists {
  107. bslice = []PortBinding{}
  108. }
  109. bindings[port] = append(bslice, binding)
  110. }
  111. return exposedPorts, bindings, nil
  112. }