1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as monitoring from "@distilled.cloud/gcp/monitoring_v3";
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 cpuFilter =
17 'resource.type = "gce_instance" AND metric.type = "compute.googleapis.com/instance/cpu/utilization"';
18
19const waitUntilGone = (name: string) =>
20 monitoring.getProjectsAlertPolicies({ name }).pipe(
21 Effect.as("found" as const),
22 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
23 Effect.repeat({
24 schedule: Schedule.spaced("1 second"),
25 until: (status) => status === "gone",
26 times: 10,
27 }),
28 );
29
30test.provider(
31 "create, update, and delete an alert policy",
32 (stack) =>
33 Effect.gen(function* () {
34 yield* stack.destroy();
35
36 const created = yield* stack.deploy(
37 Effect.gen(function* () {
38 return yield* GCP.Monitoring.AlertPolicy("CpuHigh", {
39 combiner: "OR",
40 conditions: [
41 {
42 displayName: "CPU > 90%",
43 conditionThreshold: {
44 filter: cpuFilter,
45 comparison: "COMPARISON_GT",
46 thresholdValue: 0.9,
47 duration: "60s",
48 aggregations: [
49 {
50 alignmentPeriod: "60s",
51 perSeriesAligner: "ALIGN_MEAN",
52 },
53 ],
54 },
55 },
56 ],
57 labels: { env: "test" },
58 });
59 }),
60 );
61
62 expect(created.name).toContain("/alertPolicies/");
63 expect(created.alertPolicyId).toEqual(expect.any(String));
64 expect(created.combiner).toEqual("OR");
65 expect(created.enabled).toEqual(true);
66 expect(created.conditions.length).toEqual(1);
67 expect(created.conditions[0]?.displayName).toEqual("CPU > 90%");
68 expect(created.conditions[0]?.conditionThreshold?.thresholdValue).toEqual(
69 0.9,
70 );
71 expect(created.labels).toMatchObject({ env: "test" });
72 expect(created.notificationChannels).toEqual([]);
73
74 const fetched = yield* monitoring.getProjectsAlertPolicies({
75 name: created.name,
76 });
77 expect(fetched.name).toEqual(created.name);
78 expect(fetched.combiner).toEqual("OR");
79 expect(
80 fetched.conditions?.[0]?.conditionThreshold?.thresholdValue,
81 ).toEqual(0.9);
82 expect(fetched.userLabels?.env).toEqual("test");
83 expect(
84 Object.keys(fetched.userLabels ?? {}).some((key) =>
85 key.startsWith("alchemy-"),
86 ),
87 ).toEqual(true);
88
89 const updated = yield* stack.deploy(
90 Effect.gen(function* () {
91 return yield* GCP.Monitoring.AlertPolicy("CpuHigh", {
92 displayName: created.displayName,
93 combiner: "OR",
94 enabled: false,
95 conditions: [
96 {
97 displayName: "CPU > 50%",
98 conditionThreshold: {
99 filter: cpuFilter,
100 comparison: "COMPARISON_GT",
101 thresholdValue: 0.5,
102 duration: "60s",
103 aggregations: [
104 {
105 alignmentPeriod: "60s",
106 perSeriesAligner: "ALIGN_MEAN",
107 },
108 ],
109 },
110 },
111 ],
112 labels: { env: "prod", role: "alerts" },
113 });
114 }),
115 );
116
117 expect(updated.name).toEqual(created.name);
118 expect(updated.enabled).toEqual(false);
119 expect(updated.conditions[0]?.displayName).toEqual("CPU > 50%");
120 expect(updated.conditions[0]?.conditionThreshold?.thresholdValue).toEqual(
121 0.5,
122 );
123 expect(updated.labels).toMatchObject({ env: "prod", role: "alerts" });
124
125 const fetchedUpdate = yield* monitoring.getProjectsAlertPolicies({
126 name: updated.name,
127 });
128 expect(fetchedUpdate.enabled).toEqual(false);
129 expect(
130 fetchedUpdate.conditions?.[0]?.conditionThreshold?.thresholdValue,
131 ).toEqual(0.5);
132 expect(fetchedUpdate.userLabels?.env).toEqual("prod");
133
134 yield* stack.destroy();
135
136 const gone = yield* waitUntilGone(created.name);
137 expect(gone).toEqual("gone");
138 }).pipe(logLevel),
139 {
140 tags: ["provider:gcp", "provider:gcp:monitoring", "live"],
141 timeout: 90_000,
142 },
143);