OpenContainerized creates a new PostgreSQL server using a Docker container. If port is nonzero, forward host traffic to that port to the database. If port is zero, allocate a free port from the OS. The user is responsible for calling the returned cleanup function.
(t TBSubset, opts DBContainerOptions)
| 437 | // to that port to the database. If port is zero, allocate a free port from the OS. |
| 438 | // The user is responsible for calling the returned cleanup function. |
| 439 | func OpenContainerized(t TBSubset, opts DBContainerOptions) (string, func(), error) { |
| 440 | container, containerCleanup, err := openContainer(t, opts) |
| 441 | if err != nil { |
| 442 | return "", nil, xerrors.Errorf("open container: %w", err) |
| 443 | } |
| 444 | defer func() { |
| 445 | if err != nil { |
| 446 | containerCleanup() |
| 447 | } |
| 448 | }() |
| 449 | dbURL := ConnectionParams{ |
| 450 | Username: "postgres", |
| 451 | Password: "postgres", |
| 452 | Host: container.Host, |
| 453 | Port: container.Port, |
| 454 | DBName: "postgres", |
| 455 | }.DSN() |
| 456 | |
| 457 | // Docker should hard-kill the container after 120 seconds. |
| 458 | err = container.Resource.Expire(120) |
| 459 | if err != nil { |
| 460 | return "", nil, xerrors.Errorf("expire resource: %w", err) |
| 461 | } |
| 462 | |
| 463 | container.Pool.MaxWait = 120 * time.Second |
| 464 | |
| 465 | // Record the error that occurs during the retry. |
| 466 | // The 'pool' pkg hardcodes a deadline error devoid |
| 467 | // of any useful context. |
| 468 | var retryErr error |
| 469 | err = container.Pool.Retry(func() error { |
| 470 | db, err := sql.Open("postgres", dbURL) |
| 471 | if err != nil { |
| 472 | retryErr = xerrors.Errorf("open postgres: %w", err) |
| 473 | return retryErr |
| 474 | } |
| 475 | defer db.Close() |
| 476 | |
| 477 | err = db.Ping() |
| 478 | if err != nil { |
| 479 | retryErr = xerrors.Errorf("ping postgres: %w", err) |
| 480 | return retryErr |
| 481 | } |
| 482 | |
| 483 | err = migrations.Up(db) |
| 484 | if err != nil { |
| 485 | retryErr = xerrors.Errorf("migrate db: %w", err) |
| 486 | // Only try to migrate once. |
| 487 | return backoff.Permanent(retryErr) |
| 488 | } |
| 489 | |
| 490 | return nil |
| 491 | }) |
| 492 | if err != nil { |
| 493 | return "", nil, retryErr |
| 494 | } |
| 495 | |
| 496 | return dbURL, containerCleanup, nil |