port.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package opts
  2. import (
  3. "encoding/csv"
  4. "fmt"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "github.com/docker/docker/api/types/swarm"
  9. "github.com/docker/go-connections/nat"
  10. )
  11. const (
  12. portOptTargetPort = "target"
  13. portOptPublishedPort = "published"
  14. portOptProtocol = "protocol"
  15. portOptMode = "mode"
  16. )
  17. // PortOpt represents a port config in swarm mode.
  18. type PortOpt struct {
  19. ports []swarm.PortConfig
  20. }
  21. // Set a new port value
  22. func (p *PortOpt) Set(value string) error {
  23. longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
  24. if err != nil {
  25. return err
  26. }
  27. if longSyntax {
  28. csvReader := csv.NewReader(strings.NewReader(value))
  29. fields, err := csvReader.Read()
  30. if err != nil {
  31. return err
  32. }
  33. pConfig := swarm.PortConfig{}
  34. for _, field := range fields {
  35. parts := strings.SplitN(field, "=", 2)
  36. if len(parts) != 2 {
  37. return fmt.Errorf("invalid field %s", field)
  38. }
  39. key := strings.ToLower(parts[0])
  40. value := strings.ToLower(parts[1])
  41. switch key {
  42. case portOptProtocol:
  43. if value != string(swarm.PortConfigProtocolTCP) && value != string(swarm.PortConfigProtocolUDP) {
  44. return fmt.Errorf("invalid protocol value %s", value)
  45. }
  46. pConfig.Protocol = swarm.PortConfigProtocol(value)
  47. case portOptMode:
  48. if value != string(swarm.PortConfigPublishModeIngress) && value != string(swarm.PortConfigPublishModeHost) {
  49. return fmt.Errorf("invalid publish mode value %s", value)
  50. }
  51. pConfig.PublishMode = swarm.PortConfigPublishMode(value)
  52. case portOptTargetPort:
  53. tPort, err := strconv.ParseUint(value, 10, 16)
  54. if err != nil {
  55. return err
  56. }
  57. pConfig.TargetPort = uint32(tPort)
  58. case portOptPublishedPort:
  59. pPort, err := strconv.ParseUint(value, 10, 16)
  60. if err != nil {
  61. return err
  62. }
  63. pConfig.PublishedPort = uint32(pPort)
  64. default:
  65. return fmt.Errorf("invalid field key %s", key)
  66. }
  67. }
  68. if pConfig.TargetPort == 0 {
  69. return fmt.Errorf("missing mandatory field %q", portOptTargetPort)
  70. }
  71. if pConfig.PublishMode == "" {
  72. pConfig.PublishMode = swarm.PortConfigPublishModeIngress
  73. }
  74. if pConfig.Protocol == "" {
  75. pConfig.Protocol = swarm.PortConfigProtocolTCP
  76. }
  77. p.ports = append(p.ports, pConfig)
  78. } else {
  79. // short syntax
  80. portConfigs := []swarm.PortConfig{}
  81. // We can ignore errors because the format was already validated by ValidatePort
  82. ports, portBindings, _ := nat.ParsePortSpecs([]string{value})
  83. for port := range ports {
  84. portConfig, err := ConvertPortToPortConfig(port, portBindings)
  85. if err != nil {
  86. return err
  87. }
  88. portConfigs = append(portConfigs, portConfig...)
  89. }
  90. p.ports = append(p.ports, portConfigs...)
  91. }
  92. return nil
  93. }
  94. // Type returns the type of this option
  95. func (p *PortOpt) Type() string {
  96. return "port"
  97. }
  98. // String returns a string repr of this option
  99. func (p *PortOpt) String() string {
  100. ports := []string{}
  101. for _, port := range p.ports {
  102. repr := fmt.Sprintf("%v:%v/%s/%s", port.PublishedPort, port.TargetPort, port.Protocol, port.PublishMode)
  103. ports = append(ports, repr)
  104. }
  105. return strings.Join(ports, ", ")
  106. }
  107. // Value returns the ports
  108. func (p *PortOpt) Value() []swarm.PortConfig {
  109. return p.ports
  110. }
  111. // ConvertPortToPortConfig converts ports to the swarm type
  112. func ConvertPortToPortConfig(
  113. port nat.Port,
  114. portBindings map[nat.Port][]nat.PortBinding,
  115. ) ([]swarm.PortConfig, error) {
  116. ports := []swarm.PortConfig{}
  117. for _, binding := range portBindings[port] {
  118. hostPort, err := strconv.ParseUint(binding.HostPort, 10, 16)
  119. if err != nil && binding.HostPort != "" {
  120. return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port())
  121. }
  122. ports = append(ports, swarm.PortConfig{
  123. //TODO Name: ?
  124. Protocol: swarm.PortConfigProtocol(strings.ToLower(port.Proto())),
  125. TargetPort: uint32(port.Int()),
  126. PublishedPort: uint32(hostPort),
  127. PublishMode: swarm.PortConfigPublishModeIngress,
  128. })
  129. }
  130. return ports, nil
  131. }