Skip to content

Commit

Permalink
fix: endpoint and http wait use of wrong port
Browse files Browse the repository at this point in the history
Ensure Container.Endpoint returns the lowest numbered aka first port
there is more than one exposed.

Ensure that wait.ForHTTP uses the lowest numbered aka first port
when there is more than one exposed.

Prior to this a random port would be returned leading to unpredictable
results.

Fixed: #2640
  • Loading branch information
stevenh committed Jul 12, 2024
1 parent 53b0ddf commit f8a09cb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 37 deletions.
17 changes: 8 additions & 9 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,24 +113,23 @@ func (c *DockerContainer) IsRunning() bool {
return c.isRunning
}

// Endpoint gets proto://host:port string for the first exposed port
// Endpoint gets proto://host:port string for the lowest numbered exposed port
// Will returns just host:port if proto is ""
func (c *DockerContainer) Endpoint(ctx context.Context, proto string) (string, error) {
inspect, err := c.Inspect(ctx)
if err != nil {
return "", err
}

ports := inspect.NetworkSettings.Ports

// get first port
var firstPort nat.Port
for p := range ports {
firstPort = p
break
// Get lowest numbered bound port.
var lowestPort nat.Port
for port := range inspect.NetworkSettings.Ports {
if lowestPort == "" || port.Int() < lowestPort.Int() {
lowestPort = port
}
}

return c.PortEndpoint(ctx, firstPort, proto)
return c.PortEndpoint(ctx, lowestPort, proto)
}

// PortEndpoint gets proto://host:port string for the given exposed port
Expand Down
10 changes: 3 additions & 7 deletions wait/host_port.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,14 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT

internalPort := hp.Port
if internalPort == "" {
var ports nat.PortMap
inspect, err := target.Inspect(ctx)
if err != nil {
return err
}

ports = inspect.NetworkSettings.Ports

if len(ports) > 0 {
for p := range ports {
internalPort = p
break
for port := range inspect.NetworkSettings.Ports {
if internalPort == "" || port.Int() < internalPort.Int() {
internalPort = port
}
}
}
Expand Down
47 changes: 26 additions & 21 deletions wait/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func (ws *HTTPStrategy) WithStartupTimeout(timeout time.Duration) *HTTPStrategy
return ws
}

// WithPort set the port to wait for.
// Default is the lowest numbered port.
func (ws *HTTPStrategy) WithPort(port nat.Port) *HTTPStrategy {
ws.Port = port
return ws
Expand Down Expand Up @@ -173,38 +175,41 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge

var mappedPort nat.Port
if ws.Port == "" {
var err error
var ports nat.PortMap
// we wait one polling interval before we grab the ports otherwise they might not be bound yet on startup
for err != nil || ports == nil {
select {
case <-ctx.Done():
return fmt.Errorf("%w: %w", ctx.Err(), err)
case <-time.After(ws.PollInterval):
if err := checkTarget(ctx, target); err != nil {
return err
}
select {
case <-ctx.Done():
return fmt.Errorf("%w: %w", ctx.Err(), err)
case <-time.After(ws.PollInterval):
}

inspect, err := target.Inspect(ctx)
if err != nil {
return err
}
if err := checkTarget(ctx, target); err != nil {
return err
}

ports = inspect.NetworkSettings.Ports
}
inspect, err := target.Inspect(ctx)
if err != nil {
return err
}

for k, bindings := range ports {
if len(bindings) == 0 || k.Proto() != "tcp" {
// Find the lowest numbered exposed tcp port.
var lowestPort nat.Port
var hostPort string
for port, bindings := range inspect.NetworkSettings.Ports {
if len(bindings) == 0 || port.Proto() != "tcp" {
continue
}
mappedPort, _ = nat.NewPort(k.Proto(), bindings[0].HostPort)
break

if lowestPort == "" || port.Int() < mappedPort.Int() {
lowestPort = port
hostPort = bindings[0].HostPort
}
}

if mappedPort == "" {
if lowestPort == "" {
return errors.New("No exposed tcp ports or mapped ports - cannot wait for status")
}

mappedPort, _ = nat.NewPort(lowestPort.Proto(), hostPort)
} else {
mappedPort, err = target.MappedPort(ctx, ws.Port)

Expand Down

0 comments on commit f8a09cb

Please sign in to comment.