4
4
"database/sql"
5
5
"fmt"
6
6
"io/ioutil"
7
+ "net"
7
8
"os"
8
9
"time"
9
10
@@ -22,6 +23,13 @@ func Open() (string, func(), error) {
22
23
if err != nil {
23
24
return "" , nil , xerrors .Errorf ("create tempdir: %w" , err )
24
25
}
26
+ // Pick an explicit port on the host to connect to 5432.
27
+ // This is necessary so we can configure the port to only use ipv4.
28
+ port , err := getFreePort ()
29
+ if err != nil {
30
+ return "" , nil , xerrors .Errorf ("Unable to get free port: %w" , err )
31
+ }
32
+
25
33
resource , err := pool .RunWithOptions (& dockertest.RunOptions {
26
34
Repository : "postgres" ,
27
35
Tag : "11" ,
@@ -33,6 +41,15 @@ func Open() (string, func(), error) {
33
41
"PGDATA=/tmp" ,
34
42
"listen_addresses = '*'" ,
35
43
},
44
+ PortBindings : map [docker.Port ][]docker.PortBinding {
45
+ "5432/tcp" : {{
46
+ // Manually specifying a host IP tells Docker just to use an IPV4 address.
47
+ // If we don't do this, we hit a fun bug:
48
+ // https://github.com/moby/moby/issues/42442
49
+ // where the ipv4 and ipv6 ports might be _different_ and collide with other running docker containers.
50
+ HostIP : "0.0.0.0" ,
51
+ HostPort : fmt .Sprintf ("%d" , port )}},
52
+ },
36
53
Mounts : []string {
37
54
// The postgres image has a VOLUME parameter in it's image.
38
55
// If we don't mount at this point, Docker will allocate a
@@ -76,3 +93,16 @@ func Open() (string, func(), error) {
76
93
_ = os .RemoveAll (tempDir )
77
94
}, nil
78
95
}
96
+
97
+ // getFreePort asks the kernel for a free open port that is ready to use.
98
+ func getFreePort () (port int , err error ) {
99
+ // Binding to port 0 tells the OS to grab a port for us:
100
+ // https://stackoverflow.com/questions/1365265/on-localhost-how-do-i-pick-a-free-port-number
101
+ listener , err := net .Listen ("tcp" , "localhost:0" )
102
+ if err != nil {
103
+ return 0 , err
104
+ }
105
+
106
+ defer listener .Close ()
107
+ return listener .Addr ().(* net.TCPAddr ).Port , nil
108
+ }
0 commit comments