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 { GcpEnvironment } from "@/GCP/Environment";
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 = (projectId: string, templateName: string) =>
18 compute
19 .getInstanceTemplates({
20 project: projectId,
21 instanceTemplate: templateName,
22 })
23 .pipe(
24 Effect.as("found" as const),
25 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
26 Effect.repeat({
27 schedule: Schedule.spaced("1 second"),
28 until: (status) => status === "gone",
29 times: 10,
30 }),
31 );
32
33test.provider(
34 "create, replace, and delete an instance template",
35 (stack) =>
36 Effect.gen(function* () {
37 const { project } = yield* GcpEnvironment.current;
38 yield* stack.destroy();
39
40 const created = yield* stack.deploy(
41 Effect.gen(function* () {
42 return yield* GCP.Compute.InstanceTemplate("Web", {
43 machineType: "e2-micro",
44 labels: { env: "test" },
45 disks: [
46 {
47 boot: true,
48 autoDelete: true,
49 sourceImage:
50 "projects/debian-cloud/global/images/family/debian-12",
51 diskSizeGb: 10,
52 },
53 ],
54 networkInterfaces: [{ network: "global/networks/default" }],
55 });
56 }),
57 );
58
59 expect(created.templateName).toEqual(expect.any(String));
60 expect(created.machineType).toEqual("e2-micro");
61 expect(created.labels).toMatchObject({ env: "test" });
62 expect(created.project).toEqual(project);
63
64 const fetched = yield* compute.getInstanceTemplates({
65 project,
66 instanceTemplate: created.templateName,
67 });
68 expect(fetched.name).toEqual(created.templateName);
69 expect(fetched.properties?.machineType).toEqual("e2-micro");
70 expect(fetched.properties?.labels?.env).toEqual("test");
71 expect(fetched.properties?.labels?.["alchemy-id"]).toEqual(
72 expect.any(String),
73 );
74
75 const updated = yield* stack.deploy(
76 Effect.gen(function* () {
77 return yield* GCP.Compute.InstanceTemplate("Web", {
78 templateName: created.templateName,
79 machineType: "e2-small",
80 labels: { env: "prod", role: "web" },
81 disks: [
82 {
83 boot: true,
84 autoDelete: true,
85 sourceImage:
86 "projects/debian-cloud/global/images/family/debian-12",
87 diskSizeGb: 10,
88 },
89 ],
90 networkInterfaces: [{ network: "global/networks/default" }],
91 });
92 }),
93 );
94
95 expect(updated.templateName).toEqual(created.templateName);
96 expect(updated.machineType).toEqual("e2-small");
97 expect(updated.labels).toMatchObject({ env: "prod", role: "web" });
98
99 const refetched = yield* compute.getInstanceTemplates({
100 project,
101 instanceTemplate: updated.templateName,
102 });
103 expect(refetched.properties?.machineType).toEqual("e2-small");
104 expect(refetched.properties?.labels?.env).toEqual("prod");
105 expect(refetched.properties?.labels?.role).toEqual("web");
106
107 yield* stack.destroy();
108
109 const gone = yield* waitUntilGone(created.project, created.templateName);
110 expect(gone).toEqual("gone");
111 }).pipe(logLevel),
112 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
113);