gitee.com/bomy/docker.git@v1.13.1/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 func (p *PortOpt) Set(value string) error { 28 longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value) 29 if err != nil { 30 return err 31 } 32 if longSyntax { 33 csvReader := csv.NewReader(strings.NewReader(value)) 34 fields, err := csvReader.Read() 35 if err != nil { 36 return err 37 } 38 39 pConfig := swarm.PortConfig{} 40 for _, field := range fields { 41 parts := strings.SplitN(field, "=", 2) 42 if len(parts) != 2 { 43 return fmt.Errorf("invalid field %s", field) 44 } 45 46 key := strings.ToLower(parts[0]) 47 value := strings.ToLower(parts[1]) 48 49 switch key { 50 case portOptProtocol: 51 if value != string(swarm.PortConfigProtocolTCP) && value != string(swarm.PortConfigProtocolUDP) { 52 return fmt.Errorf("invalid protocol value %s", value) 53 } 54 55 pConfig.Protocol = swarm.PortConfigProtocol(value) 56 case portOptMode: 57 if value != string(swarm.PortConfigPublishModeIngress) && value != string(swarm.PortConfigPublishModeHost) { 58 return fmt.Errorf("invalid publish mode value %s", value) 59 } 60 61 pConfig.PublishMode = swarm.PortConfigPublishMode(value) 62 case portOptTargetPort: 63 tPort, err := strconv.ParseUint(value, 10, 16) 64 if err != nil { 65 return err 66 } 67 68 pConfig.TargetPort = uint32(tPort) 69 case portOptPublishedPort: 70 pPort, err := strconv.ParseUint(value, 10, 16) 71 if err != nil { 72 return err 73 } 74 75 pConfig.PublishedPort = uint32(pPort) 76 default: 77 return fmt.Errorf("invalid field key %s", key) 78 } 79 } 80 81 if pConfig.TargetPort == 0 { 82 return fmt.Errorf("missing mandatory field %q", portOptTargetPort) 83 } 84 85 if pConfig.PublishMode == "" { 86 pConfig.PublishMode = swarm.PortConfigPublishModeIngress 87 } 88 89 if pConfig.Protocol == "" { 90 pConfig.Protocol = swarm.PortConfigProtocolTCP 91 } 92 93 p.ports = append(p.ports, pConfig) 94 } else { 95 // short syntax 96 portConfigs := []swarm.PortConfig{} 97 // We can ignore errors because the format was already validated by ValidatePort 98 ports, portBindings, _ := nat.ParsePortSpecs([]string{value}) 99 100 for port := range ports { 101 portConfigs = append(portConfigs, ConvertPortToPortConfig(port, portBindings)...) 102 } 103 p.ports = append(p.ports, portConfigs...) 104 } 105 return nil 106 } 107 108 // Type returns the type of this option 109 func (p *PortOpt) Type() string { 110 return "port" 111 } 112 113 // String returns a string repr of this option 114 func (p *PortOpt) String() string { 115 ports := []string{} 116 for _, port := range p.ports { 117 repr := fmt.Sprintf("%v:%v/%s/%s", port.PublishedPort, port.TargetPort, port.Protocol, port.PublishMode) 118 ports = append(ports, repr) 119 } 120 return strings.Join(ports, ", ") 121 } 122 123 // Value returns the ports 124 func (p *PortOpt) Value() []swarm.PortConfig { 125 return p.ports 126 } 127 128 // ConvertPortToPortConfig converts ports to the swarm type 129 func ConvertPortToPortConfig( 130 port nat.Port, 131 portBindings map[nat.Port][]nat.PortBinding, 132 ) []swarm.PortConfig { 133 ports := []swarm.PortConfig{} 134 135 for _, binding := range portBindings[port] { 136 hostPort, _ := strconv.ParseUint(binding.HostPort, 10, 16) 137 ports = append(ports, swarm.PortConfig{ 138 //TODO Name: ? 139 Protocol: swarm.PortConfigProtocol(strings.ToLower(port.Proto())), 140 TargetPort: uint32(port.Int()), 141 PublishedPort: uint32(hostPort), 142 PublishMode: swarm.PortConfigPublishModeIngress, 143 }) 144 } 145 return ports 146 }