1import * as Effect from "effect/Effect";
2import * as Semaphore from "effect/Semaphore";
3
4/**
5 * Region and zone for capacity-bound tests: GKE clusters and node pools,
6 * Dataproc clusters, Compute VMs, Workstations, and zonal Filestore.
7 *
8 * The profile's default region (`us-central1`) is the busiest GCP region;
9 * full-suite runs regularly hit "Google Compute Engine does not have enough
10 * resources available" stockouts there, which turn minute-long creates into
11 * hour-long retries. Keep API-only and regional-control-plane resources on
12 * the default region, and pin anything that schedules VMs here.
13 */
14export const CAPACITY_REGION = "us-west1";
15
16/** Primary zone in {@link CAPACITY_REGION}. */
17export const CAPACITY_ZONE = "us-west1-b";
18
19/**
20 * Region for the GKE smoke test. Its LoadBalancer needs an external IP, and
21 * the project allows 8 in-use external addresses per region; the other GKE,
22 * Dataproc and Composer tests use up {@link CAPACITY_REGION}'s during a full
23 * run, which leaves the smoke Service without an address.
24 */
25export const SMOKE_REGION = "us-east1";
26
27/** Second zone in {@link CAPACITY_REGION}, for tests that need two zones. */
28export const CAPACITY_ZONE_2 = "us-west1-c";
29
30/**
31 * The project allows 3 GKE clusters per location. Every test that creates a
32 * cluster holds a slot for its whole body (deploy through destroy), so a
33 * full `pnpm test test/GCP` run never exceeds the quota.
34 */
35const gkeClusterSlots = Semaphore.makeUnsafe(3);
36
37/** Run a cluster-creating test body while holding one GKE cluster slot. */
38export const withGkeClusterSlot = <A, E, R>(
39 self: Effect.Effect<A, E, R>,
40): Effect.Effect<A, E, R> => gkeClusterSlots.withPermits(1)(self);