1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as compute from "@distilled.cloud/gcp/compute_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import { withNetworkSlot } from "../networkQuota.ts";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17const waitUntilGone = (
18 project: string,
19 region: string,
20 subnetworkName: string,
21) =>
22 compute.getSubnetworks({ project, region, subnetwork: subnetworkName }).pipe(
23 Effect.as("found" as const),
24 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
25 Effect.repeat({
26 schedule: Schedule.spaced("2 seconds"),
27 until: (status) => status === "gone",
28 times: 10,
29 }),
30 );
31
32test.provider(
33 "create, update, and delete a subnetwork",
34 (stack) =>
35 Effect.gen(function* () {
36 yield* stack.destroy();
37
38 const created = yield* stack.deploy(
39 Effect.gen(function* () {
40 const network = yield* GCP.Compute.Network("Vpc", {
41 autoCreateSubnetworks: false,
42 });
43 const subnetwork = yield* GCP.Compute.Subnetwork("Subnet", {
44 network: network.networkName,
45 region: "us-central1",
46 ipCidrRange: "10.20.0.0/24",
47 description: "test subnet",
48 });
49 return { network, subnetwork };
50 }),
51 );
52
53 expect(created.subnetwork.subnetworkName).toEqual(expect.any(String));
54 expect(created.subnetwork.region).toEqual("us-central1");
55 expect(created.subnetwork.ipCidrRange).toEqual("10.20.0.0/24");
56 expect(created.subnetwork.privateIpGoogleAccess).toEqual(false);
57 expect(created.subnetwork.description).toEqual("test subnet");
58 expect(created.subnetwork.secondaryIpRanges).toEqual([]);
59
60 const fetched = yield* compute.getSubnetworks({
61 project: created.subnetwork.project,
62 region: created.subnetwork.region,
63 subnetwork: created.subnetwork.subnetworkName,
64 });
65 expect(fetched.name).toEqual(created.subnetwork.subnetworkName);
66 expect(fetched.ipCidrRange).toEqual("10.20.0.0/24");
67 expect(fetched.privateIpGoogleAccess).toEqual(false);
68 expect(fetched.description).toContain("alchemy-id=");
69 expect(fetched.description).toContain("test subnet");
70
71 const updated = yield* stack.deploy(
72 Effect.gen(function* () {
73 const network = yield* GCP.Compute.Network("Vpc", {
74 networkName: created.network.networkName,
75 autoCreateSubnetworks: false,
76 });
77 const subnetwork = yield* GCP.Compute.Subnetwork("Subnet", {
78 subnetworkName: created.subnetwork.subnetworkName,
79 network: network.networkName,
80 region: "us-central1",
81 ipCidrRange: "10.20.0.0/24",
82 description: "test subnet",
83 privateIpGoogleAccess: true,
84 secondaryIpRanges: [
85 { rangeName: "pods", ipCidrRange: "10.20.1.0/24" },
86 ],
87 });
88 return { network, subnetwork };
89 }),
90 );
91
92 expect(updated.subnetwork.subnetworkName).toEqual(
93 created.subnetwork.subnetworkName,
94 );
95 expect(updated.subnetwork.privateIpGoogleAccess).toEqual(true);
96 expect(updated.subnetwork.secondaryIpRanges).toEqual([
97 { rangeName: "pods", ipCidrRange: "10.20.1.0/24" },
98 ]);
99
100 const fetchedUpdate = yield* compute.getSubnetworks({
101 project: created.subnetwork.project,
102 region: created.subnetwork.region,
103 subnetwork: created.subnetwork.subnetworkName,
104 });
105 expect(fetchedUpdate.privateIpGoogleAccess).toEqual(true);
106 expect(
107 (fetchedUpdate.secondaryIpRanges ?? []).map((range) => ({
108 rangeName: range.rangeName,
109 ipCidrRange: range.ipCidrRange,
110 })),
111 ).toEqual([{ rangeName: "pods", ipCidrRange: "10.20.1.0/24" }]);
112
113 yield* stack.destroy();
114
115 const gone = yield* waitUntilGone(
116 created.subnetwork.project,
117 created.subnetwork.region,
118 created.subnetwork.subnetworkName,
119 );
120 expect(gone).toEqual("gone");
121 }).pipe(logLevel, withNetworkSlot),
122 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 360_000 },
123);