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