@@ -21,8 +21,8 @@ import (
2121)
2222
2323var (
24- dockerPortPattern = regexp .MustCompile (`^.* :(\d+)$` )
25- errMissingMetric = errors .New ("metric not found" )
24+ dockerIPv4PortPattern = regexp .MustCompile (`^\d+\.\d+\.\d+\.\d+ :(\d+)$` )
25+ errMissingMetric = errors .New ("metric not found" )
2626)
2727
2828// ConcreteService represents microservice with optional ports which will be discoverable from docker
@@ -118,7 +118,6 @@ func (s *ConcreteService) Start(networkName, sharedDir string) (err error) {
118118 // Get the dynamic local ports mapped to the container.
119119 for _ , containerPort := range s .networkPorts {
120120 var out []byte
121- var localPort int
122121
123122 out , err = RunCommandAndGetOutput ("docker" , "port" , s .containerName (), strconv .Itoa (containerPort ))
124123 if err != nil {
@@ -129,18 +128,14 @@ func (s *ConcreteService) Start(networkName, sharedDir string) (err error) {
129128 return errors .Wrapf (err , "unable to get mapping for port %d; service: %s; output: %q" , containerPort , s .name , out )
130129 }
131130
132- stdout := strings .TrimSpace (string (out ))
133- matches := dockerPortPattern .FindStringSubmatch (stdout )
134- if len (matches ) != 2 {
135- return fmt .Errorf ("unable to get mapping for port %d (output: %s); service: %s" , containerPort , stdout , s .name )
136- }
137-
138- localPort , err = strconv .Atoi (matches [1 ])
131+ localPort , err := parseDockerIPv4Port (string (out ))
139132 if err != nil {
140- return errors .Wrapf (err , "unable to get mapping for port %d; service: %s" , containerPort , s .name )
133+ return errors .Wrapf (err , "unable to get mapping for port %d (output: %s) ; service: %s" , containerPort , string ( out ) , s .name )
141134 }
135+
142136 s .networkPortsContainerToLocal [containerPort ] = localPort
143137 }
138+
144139 logger .Log ("Ports for container:" , s .containerName (), "Mapping:" , s .networkPortsContainerToLocal )
145140 return nil
146141}
@@ -668,3 +663,26 @@ func (s *HTTPService) WaitRemovedMetric(metricName string, opts ...MetricsOption
668663
669664 return fmt .Errorf ("the metric %s is still exported by %s" , metricName , s .name )
670665}
666+
667+ // parseDockerIPv4Port parses the input string which is expected to be the output of "docker port"
668+ // command and returns the first IPv4 port found.
669+ func parseDockerIPv4Port (out string ) (int , error ) {
670+ // The "docker port" output may be multiple lines if both IPv4 and IPv6 are supported,
671+ // so we need to parse each line.
672+ for _ , line := range strings .Split (out , "\n " ) {
673+ matches := dockerIPv4PortPattern .FindStringSubmatch (strings .TrimSpace (line ))
674+ if len (matches ) != 2 {
675+ continue
676+ }
677+
678+ port , err := strconv .Atoi (matches [1 ])
679+ if err != nil {
680+ continue
681+ }
682+
683+ return port , nil
684+ }
685+
686+ // We've not been able to parse the output format.
687+ return 0 , errors .New ("unknown output format" )
688+ }
0 commit comments