Skip to content

Commit

Permalink
fix: trying to bind to an outbound ip returns an empty list of port b…
Browse files Browse the repository at this point in the history
…indings (#533)

Keep calling docker inspect with a short delay until all port bindings are properly populated.
  • Loading branch information
atzoum authored Nov 15, 2024
1 parent 45d199c commit f845de5
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
33 changes: 32 additions & 1 deletion dockertest.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ func (d *Pool) RunWithOptions(opts *RunOptions, hcOpts ...func(*dc.HostConfig))
return nil, err
}

c, err = d.Client.InspectContainer(c.ID)
c, err = d.inspectContainerWithRetries(c.ID)
if err != nil {
return nil, err
}
Expand All @@ -505,6 +505,37 @@ func (d *Pool) RunWithOptions(opts *RunOptions, hcOpts ...func(*dc.HostConfig))
}, nil
}

// inspectContainerWithRetries will repeat the inspect call until the container has port bindings assigned.
func (d *Pool) inspectContainerWithRetries(id string) (*dc.Container, error) {
const maxRetries = 10
var (
retryNum int
c *dc.Container
err error
)
for retryNum <= maxRetries {
if retryNum > 0 {
time.Sleep(100 * time.Millisecond)
}
c, err = d.Client.InspectContainer(id)
if err != nil {
return nil, err
}
if hasEmptyPortBindings := func() bool {
for _, bindings := range c.NetworkSettings.Ports {
if len(bindings) == 0 {
return true
}
}
return false
}(); !hasEmptyPortBindings {
return c, nil
}
retryNum++
}
return c, err
}

// Run starts a docker container.
//
// pool.Run("mysql", "5.3", []string{"FOO=BAR", "BAR=BAZ"})
Expand Down
26 changes: 26 additions & 0 deletions dockertest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"database/sql"
"fmt"
"log"
"net"
"net/http"
"os"
"strconv"
Expand Down Expand Up @@ -490,3 +491,28 @@ func TestExecStatus(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 42, exitCode)
}

func TestOutboundIPPortBinding(t *testing.T) {
outboundIP := func() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
require.NoError(t, err)
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}()
resource, err := pool.RunWithOptions(
&dockertest.RunOptions{
Repository: "postgres",
Tag: "9.5",
Env: []string{"POSTGRES_PASSWORD=secret"},
PortBindings: map[dc.Port][]dc.PortBinding{
"5432/tcp": {{HostIP: outboundIP, HostPort: "0"}},
},
})
require.NoError(t, err)
mappedPort := resource.GetPort("5432/tcp")
require.NotEmpty(t, mappedPort)
boundIP := resource.GetBoundIP("5432/tcp")
require.Equal(t, outboundIP, boundIP)
require.NoError(t, pool.Purge(resource))
}

0 comments on commit f845de5

Please sign in to comment.