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 zone = "us-central1-a";
18
19const waitUntilGone = (
20 projectId: string,
21 policyZone: string,
22 vmExtensionPolicy: string,
23) =>
24 compute
25 .getZoneVmExtensionPolicies({
26 project: projectId,
27 zone: policyZone,
28 vmExtensionPolicy,
29 })
30 .pipe(
31 Effect.as("found" as const),
32 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
33 Effect.repeat({
34 schedule: Schedule.spaced("1 second"),
35 until: (status) => status === "gone",
36 times: 10,
37 }),
38 );
39
40test.provider(
41 "getZoneVmExtensionPolicies on a missing policy fails with NotFound",
42 (stack) =>
43 Effect.gen(function* () {
44 const { project } = yield* GcpEnvironment.current;
45 yield* stack.destroy();
46
47 const error = yield* Effect.flip(
48 compute.getZoneVmExtensionPolicies({
49 project,
50 zone,
51 vmExtensionPolicy: "alchemy-missing-vm-extension-policy",
52 }),
53 );
54 expect(error._tag).toBe("NotFound");
55
56 yield* stack.destroy();
57 }).pipe(logLevel),
58 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
59);
60
61test.provider(
62 "create, update, and delete a zone VM extension policy",
63 (stack) =>
64 Effect.gen(function* () {
65 yield* stack.destroy();
66
67 const created = yield* stack.deploy(
68 Effect.gen(function* () {
69 return yield* GCP.Compute.ZoneVmExtensionPolicy("Ops", {
70 zone,
71 description: "ops-agent for test vms",
72 priority: 500,
73 extensionPolicies: { "ops-agent": {} },
74 instanceSelectors: [
75 {
76 labelSelector: {
77 inclusionLabels: { "alchemy-vm-extension": "never" },
78 },
79 },
80 ],
81 });
82 }),
83 );
84
85 expect(created.vmExtensionPolicyName).toEqual(expect.any(String));
86 expect(created.zone).toEqual(zone);
87 expect(created.description).toEqual("ops-agent for test vms");
88 expect(created.priority).toEqual(500);
89 expect(created.extensionPolicies?.["ops-agent"]).toBeDefined();
90
91 const fetched = yield* compute.getZoneVmExtensionPolicies({
92 project: created.project,
93 zone: created.zone,
94 vmExtensionPolicy: created.vmExtensionPolicyName,
95 });
96 expect(fetched.name).toEqual(created.vmExtensionPolicyName);
97 expect(fetched.description).toContain("[alchemy ");
98 expect(fetched.description).toContain("ops-agent for test vms");
99 expect(fetched.priority).toEqual(500);
100
101 const updated = yield* stack.deploy(
102 Effect.gen(function* () {
103 return yield* GCP.Compute.ZoneVmExtensionPolicy("Ops", {
104 vmExtensionPolicyName: created.vmExtensionPolicyName,
105 zone,
106 description: "ops-agent for labeled vms",
107 priority: 400,
108 extensionPolicies: { "ops-agent": { pinnedVersion: "2.58.0" } },
109 instanceSelectors: [
110 {
111 labelSelector: {
112 inclusionLabels: { "alchemy-vm-extension": "never" },
113 },
114 },
115 ],
116 });
117 }),
118 );
119
120 expect(updated.vmExtensionPolicyName).toEqual(
121 created.vmExtensionPolicyName,
122 );
123 expect(updated.description).toEqual("ops-agent for labeled vms");
124 expect(updated.priority).toEqual(400);
125 expect(updated.extensionPolicies?.["ops-agent"]?.pinnedVersion).toEqual(
126 "2.58.0",
127 );
128
129 const fetchedUpdated = yield* compute.getZoneVmExtensionPolicies({
130 project: updated.project,
131 zone: updated.zone,
132 vmExtensionPolicy: updated.vmExtensionPolicyName,
133 });
134 expect(fetchedUpdated.description).toContain("ops-agent for labeled vms");
135 expect(fetchedUpdated.priority).toEqual(400);
136
137 yield* stack.destroy();
138 const gone = yield* waitUntilGone(
139 created.project,
140 created.zone,
141 created.vmExtensionPolicyName,
142 );
143 expect(gone).toEqual("gone");
144 }).pipe(logLevel),
145 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
146);