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