@@ -85,14 +85,10 @@ func (p Port) Port() string {
8585// Int returns the port number of a Port as an int
8686func (p Port ) Int () int {
8787 portStr := p .Port ()
88- if len (portStr ) == 0 {
89- return 0
90- }
91-
9288 // We don't need to check for an error because we're going to
9389 // assume that any error would have been found, and reported, in NewPort()
94- port , _ := strconv . ParseUint (portStr , 10 , 16 )
95- return int ( port )
90+ port , _ := ParsePort (portStr )
91+ return port
9692}
9793
9894// Range returns the start/end port numbers of a Port range as ints
@@ -132,92 +128,115 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
132128 exposedPorts = make (map [Port ]struct {}, len (ports ))
133129 bindings = make (map [Port ][]PortBinding )
134130 )
135-
136131 for _ , rawPort := range ports {
137- proto := "tcp"
138-
139- if i := strings .LastIndex (rawPort , "/" ); i != - 1 {
140- proto = rawPort [i + 1 :]
141- rawPort = rawPort [:i ]
142- }
143- if ! strings .Contains (rawPort , ":" ) {
144- rawPort = fmt .Sprintf ("::%s" , rawPort )
145- } else if len (strings .Split (rawPort , ":" )) == 2 {
146- rawPort = fmt .Sprintf (":%s" , rawPort )
147- }
148-
149- parts , err := PartParser (portSpecTemplate , rawPort )
132+ portMappings , err := ParsePortSpec (rawPort )
150133 if err != nil {
151134 return nil , nil , err
152135 }
153136
154- var (
155- containerPort = parts ["containerPort" ]
156- rawIP = parts ["ip" ]
157- hostPort = parts ["hostPort" ]
158- )
159-
160- if rawIP != "" && net .ParseIP (rawIP ) == nil {
161- return nil , nil , fmt .Errorf ("Invalid ip address: %s" , rawIP )
162- }
163- if containerPort == "" {
164- return nil , nil , fmt .Errorf ("No port specified: %s<empty>" , rawPort )
137+ for _ , portMapping := range portMappings {
138+ port := portMapping .Port
139+ if _ , exists := exposedPorts [port ]; ! exists {
140+ exposedPorts [port ] = struct {}{}
141+ }
142+ bslice , exists := bindings [port ]
143+ if ! exists {
144+ bslice = []PortBinding {}
145+ }
146+ bindings [port ] = append (bslice , portMapping .Binding )
165147 }
148+ }
149+ return exposedPorts , bindings , nil
150+ }
151+
152+ // PortMapping is a data object mapping a Port to a PortBinding
153+ type PortMapping struct {
154+ Port Port
155+ Binding PortBinding
156+ }
157+
158+ func splitParts (rawport string ) (string , string , string ) {
159+ parts := strings .Split (rawport , ":" )
160+ n := len (parts )
161+ containerport := parts [n - 1 ]
162+
163+ switch n {
164+ case 1 :
165+ return "" , "" , containerport
166+ case 2 :
167+ return "" , parts [0 ], containerport
168+ case 3 :
169+ return parts [0 ], parts [1 ], containerport
170+ default :
171+ return strings .Join (parts [:n - 2 ], ":" ), parts [n - 2 ], containerport
172+ }
173+ }
174+
175+ // ParsePortSpec parses a port specification string into a slice of PortMappings
176+ func ParsePortSpec (rawPort string ) ([]PortMapping , error ) {
177+ var proto string
178+ rawIP , hostPort , containerPort := splitParts (rawPort )
179+ proto , containerPort = SplitProtoPort (containerPort )
180+
181+ // Strip [] from IPV6 addresses
182+ ip , _ , err := net .SplitHostPort (rawIP + ":" )
183+ if err != nil {
184+ return nil , fmt .Errorf ("Invalid ip address %v: %s" , rawIP , err )
185+ }
186+ if ip != "" && net .ParseIP (ip ) == nil {
187+ return nil , fmt .Errorf ("Invalid ip address: %s" , ip )
188+ }
189+ if containerPort == "" {
190+ return nil , fmt .Errorf ("No port specified: %s<empty>" , rawPort )
191+ }
192+
193+ startPort , endPort , err := ParsePortRange (containerPort )
194+ if err != nil {
195+ return nil , fmt .Errorf ("Invalid containerPort: %s" , containerPort )
196+ }
166197
167- startPort , endPort , err := ParsePortRange (containerPort )
198+ var startHostPort , endHostPort uint64 = 0 , 0
199+ if len (hostPort ) > 0 {
200+ startHostPort , endHostPort , err = ParsePortRange (hostPort )
168201 if err != nil {
169- return nil , nil , fmt .Errorf ("Invalid containerPort : %s" , containerPort )
202+ return nil , fmt .Errorf ("Invalid hostPort : %s" , hostPort )
170203 }
204+ }
171205
172- var startHostPort , endHostPort uint64 = 0 , 0
173- if len ( hostPort ) > 0 {
174- startHostPort , endHostPort , err = ParsePortRange ( hostPort )
175- if err != nil {
176- return nil , nil , fmt . Errorf ( "Invalid hostPort: %s" , hostPort )
177- }
206+ if hostPort != "" && ( endPort - startPort ) != ( endHostPort - startHostPort ) {
207+ // Allow host port range iff containerPort is not a range.
208+ // In this case, use the host port range as the dynamic
209+ // host port range to allocate into.
210+ if endPort != startPort {
211+ return nil , fmt . Errorf ( "Invalid ranges specified for container and host Ports: %s and %s" , containerPort , hostPort )
178212 }
213+ }
179214
180- if hostPort != "" && (endPort - startPort ) != (endHostPort - startHostPort ) {
181- // Allow host port range iff containerPort is not a range.
182- // In this case, use the host port range as the dynamic
183- // host port range to allocate into.
184- if endPort != startPort {
185- return nil , nil , fmt .Errorf ("Invalid ranges specified for container and host Ports: %s and %s" , containerPort , hostPort )
186- }
187- }
215+ if ! validateProto (strings .ToLower (proto )) {
216+ return nil , fmt .Errorf ("Invalid proto: %s" , proto )
217+ }
188218
189- if ! validateProto (strings .ToLower (proto )) {
190- return nil , nil , fmt .Errorf ("Invalid proto: %s" , proto )
219+ ports := []PortMapping {}
220+ for i := uint64 (0 ); i <= (endPort - startPort ); i ++ {
221+ containerPort = strconv .FormatUint (startPort + i , 10 )
222+ if len (hostPort ) > 0 {
223+ hostPort = strconv .FormatUint (startHostPort + i , 10 )
224+ }
225+ // Set hostPort to a range only if there is a single container port
226+ // and a dynamic host port.
227+ if startPort == endPort && startHostPort != endHostPort {
228+ hostPort = fmt .Sprintf ("%s-%s" , hostPort , strconv .FormatUint (endHostPort , 10 ))
229+ }
230+ port , err := NewPort (strings .ToLower (proto ), containerPort )
231+ if err != nil {
232+ return nil , err
191233 }
192234
193- for i := uint64 (0 ); i <= (endPort - startPort ); i ++ {
194- containerPort = strconv .FormatUint (startPort + i , 10 )
195- if len (hostPort ) > 0 {
196- hostPort = strconv .FormatUint (startHostPort + i , 10 )
197- }
198- // Set hostPort to a range only if there is a single container port
199- // and a dynamic host port.
200- if startPort == endPort && startHostPort != endHostPort {
201- hostPort = fmt .Sprintf ("%s-%s" , hostPort , strconv .FormatUint (endHostPort , 10 ))
202- }
203- port , err := NewPort (strings .ToLower (proto ), containerPort )
204- if err != nil {
205- return nil , nil , err
206- }
207- if _ , exists := exposedPorts [port ]; ! exists {
208- exposedPorts [port ] = struct {}{}
209- }
210-
211- binding := PortBinding {
212- HostIP : rawIP ,
213- HostPort : hostPort ,
214- }
215- bslice , exists := bindings [port ]
216- if ! exists {
217- bslice = []PortBinding {}
218- }
219- bindings [port ] = append (bslice , binding )
235+ binding := PortBinding {
236+ HostIP : ip ,
237+ HostPort : hostPort ,
220238 }
239+ ports = append (ports , PortMapping {Port : port , Binding : binding })
221240 }
222- return exposedPorts , bindings , nil
241+ return ports , nil
223242}
0 commit comments