8
8
"errors"
9
9
"fmt"
10
10
"net"
11
+ "net/url"
11
12
"os"
12
13
"path/filepath"
13
14
"strconv"
@@ -21,6 +22,7 @@ import (
21
22
"github.com/ory/dockertest/v3/docker"
22
23
"golang.org/x/xerrors"
23
24
25
+ "github.com/coder/coder/v2/coderd/database/dbtestutil/dbpool"
24
26
"github.com/coder/coder/v2/coderd/database/migrations"
25
27
"github.com/coder/coder/v2/cryptorand"
26
28
"github.com/coder/retry"
@@ -38,6 +40,39 @@ func (p ConnectionParams) DSN() string {
38
40
return fmt .Sprintf ("postgres://%s:%s@%s:%s/%s?sslmode=disable" , p .Username , p .Password , p .Host , p .Port , p .DBName )
39
41
}
40
42
43
+ func ParseDSN (dsn string ) (ConnectionParams , error ) {
44
+ u , err := url .Parse (dsn )
45
+ if err != nil {
46
+ return ConnectionParams {}, xerrors .Errorf ("parse dsn: %w" , err )
47
+ }
48
+
49
+ if u .Scheme != "postgres" {
50
+ return ConnectionParams {}, xerrors .Errorf ("invalid dsn scheme: %s" , u .Scheme )
51
+ }
52
+
53
+ var params ConnectionParams
54
+ if u .User != nil {
55
+ params .Username = u .User .Username ()
56
+ params .Password , _ = u .User .Password ()
57
+ }
58
+
59
+ params .Host = u .Hostname ()
60
+ params .Port = u .Port ()
61
+ if params .Port == "" {
62
+ // Default PostgreSQL port
63
+ params .Port = "5432"
64
+ }
65
+
66
+ // The path includes a leading slash, remove it.
67
+ if len (u .Path ) > 1 {
68
+ params .DBName = u .Path [1 :]
69
+ } else {
70
+ return ConnectionParams {}, xerrors .New ("database name missing in dsn" )
71
+ }
72
+
73
+ return params , nil
74
+ }
75
+
41
76
// These variables are global because all tests share them.
42
77
var (
43
78
connectionParamsInitOnce sync.Once
@@ -138,24 +173,75 @@ type TBSubset interface {
138
173
Logf (format string , args ... any )
139
174
}
140
175
176
+ func RemoveDB (t TBSubset , dbName string ) error {
177
+ cleanupDbURL := defaultConnectionParams .DSN ()
178
+ cleanupConn , err := sql .Open ("postgres" , cleanupDbURL )
179
+ if err != nil {
180
+ return xerrors .Errorf ("cleanup database %q: failed to connect to postgres: %w" , dbName , err )
181
+ }
182
+ defer func () {
183
+ if err := cleanupConn .Close (); err != nil {
184
+ t .Logf ("cleanup database %q: failed to close connection: %s\n " , dbName , err .Error ())
185
+ }
186
+ }()
187
+ _ , err = cleanupConn .Exec ("DROP DATABASE " + dbName + ";" )
188
+ if err != nil {
189
+ return xerrors .Errorf ("cleanup database %q: failed to drop database: %w" , dbName , err )
190
+ }
191
+ return nil
192
+ }
193
+
194
+ func getDBPoolClient () (* dbpool.Client , error ) {
195
+ dbpoolURL := os .Getenv ("DBPOOL" )
196
+ if dbpoolURL == "" {
197
+ return nil , nil //nolint:nilnil
198
+ }
199
+ client , err := dbpool .NewClient (dbpoolURL )
200
+ if err != nil {
201
+ return nil , xerrors .Errorf ("create db pool client: %w" , err )
202
+ }
203
+ return client , nil
204
+ }
205
+
141
206
// Open creates a new PostgreSQL database instance.
142
207
// If there's a database running at localhost:5432, it will use that.
143
208
// Otherwise, it will start a new postgres container.
144
209
func Open (t TBSubset , opts ... OpenOption ) (string , error ) {
145
210
t .Helper ()
146
211
212
+ openOptions := OpenOptions {}
213
+ for _ , opt := range opts {
214
+ opt (& openOptions )
215
+ }
216
+
217
+ if openOptions .DBFrom == nil {
218
+ dbPoolClient , err := getDBPoolClient ()
219
+ if err != nil {
220
+ return "" , xerrors .Errorf ("get db pool client: %w" , err )
221
+ }
222
+ if dbPoolClient != nil {
223
+ dbURL , err := dbPoolClient .GetDB ()
224
+ if err != nil {
225
+ return "" , xerrors .Errorf ("get db from pool: %w" , err )
226
+ }
227
+ t .Cleanup (func () {
228
+ defer dbPoolClient .Close ()
229
+ err := dbPoolClient .DisposeDB (dbURL )
230
+ if err != nil {
231
+ t .Logf ("cleanup database %s: failed to dispose db: %+v\n " , dbURL , err )
232
+ }
233
+ })
234
+ return dbURL , nil
235
+ }
236
+ }
237
+
147
238
connectionParamsInitOnce .Do (func () {
148
239
errDefaultConnectionParamsInit = initDefaultConnection (t )
149
240
})
150
241
if errDefaultConnectionParamsInit != nil {
151
242
return "" , xerrors .Errorf ("init default connection params: %w" , errDefaultConnectionParamsInit )
152
243
}
153
244
154
- openOptions := OpenOptions {}
155
- for _ , opt := range opts {
156
- opt (& openOptions )
157
- }
158
-
159
245
var (
160
246
username = defaultConnectionParams .Username
161
247
password = defaultConnectionParams .Password
@@ -182,22 +268,7 @@ func Open(t TBSubset, opts ...OpenOption) (string, error) {
182
268
}
183
269
184
270
t .Cleanup (func () {
185
- cleanupDbURL := defaultConnectionParams .DSN ()
186
- cleanupConn , err := sql .Open ("postgres" , cleanupDbURL )
187
- if err != nil {
188
- t .Logf ("cleanup database %q: failed to connect to postgres: %s\n " , dbName , err .Error ())
189
- return
190
- }
191
- defer func () {
192
- if err := cleanupConn .Close (); err != nil {
193
- t .Logf ("cleanup database %q: failed to close connection: %s\n " , dbName , err .Error ())
194
- }
195
- }()
196
- _ , err = cleanupConn .Exec ("DROP DATABASE " + dbName + ";" )
197
- if err != nil {
198
- t .Logf ("failed to clean up database %q: %s\n " , dbName , err .Error ())
199
- return
200
- }
271
+ RemoveDB (t , dbName )
201
272
})
202
273
203
274
dsn := ConnectionParams {
0 commit comments