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";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const region = "us-central1";
17
18const waitUntilGone = (project: string, instantSnapshot: string) =>
19 compute.getRegionInstantSnapshots({ project, region, instantSnapshot }).pipe(
20 Effect.as("found" as const),
21 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
22 Effect.repeat({
23 schedule: Schedule.spaced("1 second"),
24 until: (status) => status === "gone",
25 times: 10,
26 }),
27 );
28
29test.provider.skipIf(!!process.env.FAST)(
30 "create, update labels, replace, and delete a regional instant snapshot",
31 (stack) =>
32 Effect.gen(function* () {
33 yield* stack.destroy();
34
35 const created = yield* stack.deploy(
36 Effect.gen(function* () {
37 const disk = yield* GCP.Compute.RegionDisk("Data", {
38 region,
39 replicaZones: ["us-central1-a", "us-central1-b"],
40 type: "pd-balanced",
41 sizeGb: 200,
42 });
43 const snapshot = yield* GCP.Compute.RegionInstantSnapshot(
44 "Checkpoint",
45 {
46 region,
47 sourceDisk: disk.selfLink.as<string>(),
48 description: "first checkpoint",
49 labels: { env: "test" },
50 },
51 );
52 return { disk, snapshot };
53 }),
54 );
55
56 expect(created.snapshot.instantSnapshotName).toEqual(expect.any(String));
57 expect(created.snapshot.region).toEqual(region);
58 expect(created.snapshot.status).toEqual("READY");
59 expect(created.snapshot.labels).toMatchObject({ env: "test" });
60 expect(created.snapshot.description).toEqual("first checkpoint");
61
62 const fetched = yield* compute.getRegionInstantSnapshots({
63 project: created.snapshot.project,
64 region,
65 instantSnapshot: created.snapshot.instantSnapshotName,
66 });
67 expect(fetched.name).toEqual(created.snapshot.instantSnapshotName);
68 expect(fetched.labels?.env).toEqual("test");
69 expect(fetched.status).toEqual("READY");
70
71 const updated = yield* stack.deploy(
72 Effect.gen(function* () {
73 const disk = yield* GCP.Compute.RegionDisk("Data", {
74 diskName: created.disk.diskName,
75 region,
76 replicaZones: ["us-central1-a", "us-central1-b"],
77 type: "pd-balanced",
78 sizeGb: 200,
79 });
80 const snapshot = yield* GCP.Compute.RegionInstantSnapshot(
81 "Checkpoint",
82 {
83 instantSnapshotName: created.snapshot.instantSnapshotName,
84 region,
85 sourceDisk: disk.selfLink.as<string>(),
86 description: "first checkpoint",
87 labels: { env: "prod", role: "data" },
88 },
89 );
90 return { disk, snapshot };
91 }),
92 );
93
94 expect(updated.snapshot.instantSnapshotName).toEqual(
95 created.snapshot.instantSnapshotName,
96 );
97 expect(updated.snapshot.labels).toMatchObject({
98 env: "prod",
99 role: "data",
100 });
101
102 const nextName = `r${created.snapshot.instantSnapshotName}`
103 .slice(0, 63)
104 .replace(/-+$/, "x");
105 const replaced = yield* stack.deploy(
106 Effect.gen(function* () {
107 const disk = yield* GCP.Compute.RegionDisk("Data", {
108 diskName: created.disk.diskName,
109 region,
110 replicaZones: ["us-central1-a", "us-central1-b"],
111 type: "pd-balanced",
112 sizeGb: 200,
113 });
114 const snapshot = yield* GCP.Compute.RegionInstantSnapshot(
115 "Checkpoint",
116 {
117 instantSnapshotName: nextName,
118 region,
119 sourceDisk: disk.selfLink.as<string>(),
120 description: "replaced checkpoint",
121 },
122 );
123 return { disk, snapshot };
124 }),
125 );
126
127 expect(replaced.snapshot.instantSnapshotName).toEqual(nextName);
128 expect(replaced.snapshot.description).toEqual("replaced checkpoint");
129
130 const oldGone = yield* waitUntilGone(
131 created.snapshot.project,
132 created.snapshot.instantSnapshotName,
133 );
134 expect(oldGone).toEqual("gone");
135
136 yield* stack.destroy();
137
138 const gone = yield* waitUntilGone(
139 replaced.snapshot.project,
140 replaced.snapshot.instantSnapshotName,
141 );
142 expect(gone).toEqual("gone");
143 }).pipe(logLevel),
144 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 180_000 },
145);