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 waitUntilGone = (project: string, image: string) =>
17 compute.getImages({ project, image }).pipe(
18 Effect.as("found" as const),
19 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
20 Effect.repeat({
21 schedule: Schedule.spaced("2 seconds"),
22 until: (status) => status === "gone",
23 times: 18,
24 }),
25 );
26
27test.provider.skipIf(!!process.env.FAST)(
28 "create, update, and delete an image",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const created = yield* stack.deploy(
34 Effect.gen(function* () {
35 const disk = yield* GCP.Compute.Disk("Data", {
36 zone: "us-central1-a",
37 type: "pd-standard",
38 sizeGb: 10,
39 });
40 const image = yield* GCP.Compute.Image("Boot", {
41 sourceDisk: disk.selfLink.as<string>(),
42 family: "alchemy-test",
43 description: "alchemy test image",
44 labels: { env: "test" },
45 storageLocations: ["us-central1"],
46 });
47 return { disk, image };
48 }),
49 );
50
51 expect(created.image.imageName).toEqual(expect.any(String));
52 expect(created.image.status).toEqual("READY");
53 expect(created.image.family).toEqual("alchemy-test");
54 expect(created.image.description).toEqual("alchemy test image");
55 expect(created.image.labels).toMatchObject({ env: "test" });
56 expect(created.image.project).toEqual(expect.any(String));
57 expect(created.image.selfLink).toEqual(expect.any(String));
58
59 const fetched = yield* compute.getImages({
60 project: created.image.project,
61 image: created.image.imageName,
62 });
63 expect(fetched.name).toEqual(created.image.imageName);
64 expect(fetched.status).toEqual("READY");
65 expect(fetched.family).toEqual("alchemy-test");
66 expect(fetched.labels?.env).toEqual("test");
67 expect(fetched.labels?.["alchemy-id"]).toEqual(expect.any(String));
68
69 const updated = yield* stack.deploy(
70 Effect.gen(function* () {
71 const disk = yield* GCP.Compute.Disk("Data", {
72 diskName: created.disk.diskName,
73 zone: "us-central1-a",
74 type: "pd-standard",
75 sizeGb: 10,
76 });
77 const image = yield* GCP.Compute.Image("Boot", {
78 imageName: created.image.imageName,
79 sourceDisk: disk.selfLink.as<string>(),
80 family: "alchemy-prod",
81 description: "updated alchemy test image",
82 labels: { env: "prod", role: "boot" },
83 storageLocations: ["us-central1"],
84 });
85 return { disk, image };
86 }),
87 );
88
89 expect(updated.image.imageName).toEqual(created.image.imageName);
90 expect(updated.image.family).toEqual("alchemy-prod");
91 expect(updated.image.description).toEqual("updated alchemy test image");
92 expect(updated.image.labels).toMatchObject({ env: "prod", role: "boot" });
93
94 const refetched = yield* compute.getImages({
95 project: updated.image.project,
96 image: updated.image.imageName,
97 });
98 expect(refetched.family).toEqual("alchemy-prod");
99 expect(refetched.description).toEqual("updated alchemy test image");
100 expect(refetched.labels?.env).toEqual("prod");
101 expect(refetched.labels?.role).toEqual("boot");
102
103 yield* stack.destroy();
104
105 const gone = yield* waitUntilGone(
106 created.image.project,
107 created.image.imageName,
108 );
109 expect(gone).toEqual("gone");
110 }).pipe(logLevel),
111 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 240_000 },
112);