OpenContainer 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. If name is set, we'll ensure that only one container is started with that name. If it's already running, w
(t TBSubset, opts DBContainerOptions)
| 318 | // If name is set, we'll ensure that only one container is started with that name. If it's already running, we'll use that. |
| 319 | // Otherwise, we'll start a new container. |
| 320 | func openContainer(t TBSubset, opts DBContainerOptions) (container, func(), error) { |
| 321 | if opts.Name != "" { |
| 322 | // We only want to start the container once per unique name, |
| 323 | // so we take an inter-process lock to avoid concurrent test runs |
| 324 | // racing with us. |
| 325 | nameHash := sha256.Sum256([]byte(opts.Name)) |
| 326 | nameHashStr := hex.EncodeToString(nameHash[:]) |
| 327 | lock := flock.New(filepath.Join(os.TempDir(), "coder-postgres-container-"+nameHashStr[:8])) |
| 328 | if err := lock.Lock(); err != nil { |
| 329 | return container{}, nil, xerrors.Errorf("lock: %w", err) |
| 330 | } |
| 331 | defer func() { |
| 332 | err := lock.Unlock() |
| 333 | if err != nil { |
| 334 | t.Logf("create database from template: failed to unlock: %s\n", err.Error()) |
| 335 | } |
| 336 | }() |
| 337 | } |
| 338 | |
| 339 | pool, err := dockertest.NewPool("") |
| 340 | if err != nil { |
| 341 | return container{}, nil, xerrors.Errorf("create pool: %w", err) |
| 342 | } |
| 343 | |
| 344 | var resource *dockertest.Resource |
| 345 | var tempDir string |
| 346 | if opts.Name != "" { |
| 347 | // If the container already exists, we'll use it. |
| 348 | resource, _ = pool.ContainerByName(opts.Name) |
| 349 | } |
| 350 | if resource == nil { |
| 351 | tempDir, err = os.MkdirTemp(os.TempDir(), "postgres") |
| 352 | if err != nil { |
| 353 | return container{}, nil, xerrors.Errorf("create tempdir: %w", err) |
| 354 | } |
| 355 | runOptions := dockertest.RunOptions{ |
| 356 | Repository: postgresImage, |
| 357 | Tag: strconv.Itoa(minimumPostgreSQLVersion), |
| 358 | Env: []string{ |
| 359 | "POSTGRES_PASSWORD=postgres", |
| 360 | "POSTGRES_USER=postgres", |
| 361 | "POSTGRES_DB=postgres", |
| 362 | // The location for temporary database files! |
| 363 | "PGDATA=/tmp", |
| 364 | "listen_addresses = '*'", |
| 365 | }, |
| 366 | PortBindings: map[docker.Port][]docker.PortBinding{ |
| 367 | "5432/tcp": {{ |
| 368 | // Manually specifying a host IP tells Docker just to use an IPV4 address. |
| 369 | // If we don't do this, we hit a fun bug: |
| 370 | // https://github.com/moby/moby/issues/42442 |
| 371 | // where the ipv4 and ipv6 ports might be _different_ and collide with other running docker containers. |
| 372 | HostIP: "0.0.0.0", |
| 373 | HostPort: strconv.FormatInt(int64(opts.Port), 10), |
| 374 | }}, |
| 375 | }, |
| 376 | Mounts: []string{ |
| 377 | // The postgres image has a VOLUME parameter in it's image. |