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 latencyFilter =
17 'metric.type="serviceruntime.googleapis.com/api/request_latencies" AND resource.type="consumed_api"';
18
19const waitUntilGone = (name: string) =>
20 monitoring.getServicesServiceLevelObjectives({ 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 a service level objective",
32 (stack) =>
33 Effect.gen(function* () {
34 yield* stack.destroy();
35
36 const created = yield* stack.deploy(
37 Effect.gen(function* () {
38 const checkout = yield* GCP.Monitoring.Service("Checkout", {
39 displayName: "Checkout",
40 labels: { env: "test" },
41 });
42 const slo = yield* GCP.Monitoring.ServicesServiceLevelObjective(
43 "Latency",
44 {
45 service: checkout.name,
46 displayName: "99% latency",
47 goal: 0.99,
48 rollingPeriod: "86400s",
49 serviceLevelIndicator: {
50 requestBased: {
51 distributionCut: {
52 distributionFilter: latencyFilter,
53 range: { max: 500 },
54 },
55 },
56 },
57 labels: { env: "test" },
58 },
59 );
60 return { checkout, slo };
61 }),
62 );
63
64 expect(created.slo.name).toContain("/serviceLevelObjectives/");
65 expect(created.slo.service).toEqual(created.checkout.name);
66 expect(created.slo.displayName).toEqual("99% latency");
67 expect(created.slo.goal).toEqual(0.99);
68 expect(created.slo.rollingPeriod).toEqual("86400s");
69 expect(created.slo.labels).toMatchObject({ env: "test" });
70 expect(
71 created.slo.serviceLevelIndicator?.requestBased?.distributionCut?.range
72 ?.max,
73 ).toEqual(500);
74
75 const fetched = yield* monitoring.getServicesServiceLevelObjectives({
76 name: created.slo.name,
77 });
78 expect(fetched.name).toEqual(created.slo.name);
79 expect(fetched.goal).toEqual(0.99);
80 expect(fetched.userLabels?.env).toEqual("test");
81 expect(
82 Object.keys(fetched.userLabels ?? {}).some((key) =>
83 key.startsWith("alchemy-"),
84 ),
85 ).toEqual(true);
86
87 const updated = yield* stack.deploy(
88 Effect.gen(function* () {
89 const checkout = yield* GCP.Monitoring.Service("Checkout", {
90 serviceId: created.checkout.serviceId,
91 displayName: "Checkout",
92 labels: { env: "test" },
93 });
94 const slo = yield* GCP.Monitoring.ServicesServiceLevelObjective(
95 "Latency",
96 {
97 service: checkout.name,
98 serviceLevelObjectiveId: created.slo.serviceLevelObjectiveId,
99 displayName: "95% latency",
100 goal: 0.95,
101 rollingPeriod: "604800s",
102 serviceLevelIndicator: {
103 requestBased: {
104 distributionCut: {
105 distributionFilter: latencyFilter,
106 range: { max: 1000 },
107 },
108 },
109 },
110 labels: { env: "prod" },
111 },
112 );
113 return { checkout, slo };
114 }),
115 );
116
117 expect(updated.slo.name).toEqual(created.slo.name);
118 expect(updated.slo.displayName).toEqual("95% latency");
119 expect(updated.slo.goal).toEqual(0.95);
120 expect(updated.slo.rollingPeriod).toEqual("604800s");
121 expect(
122 updated.slo.serviceLevelIndicator?.requestBased?.distributionCut?.range
123 ?.max,
124 ).toEqual(1000);
125 expect(updated.slo.labels).toMatchObject({ env: "prod" });
126
127 const fetchedUpdate = yield* monitoring.getServicesServiceLevelObjectives(
128 {
129 name: updated.slo.name,
130 },
131 );
132 expect(fetchedUpdate.goal).toEqual(0.95);
133 expect(fetchedUpdate.displayName).toEqual("95% latency");
134 expect(fetchedUpdate.userLabels?.env).toEqual("prod");
135
136 yield* stack.destroy();
137
138 const gone = yield* waitUntilGone(created.slo.name);
139 expect(gone).toEqual("gone");
140 }).pipe(logLevel),
141 {
142 tags: ["provider:gcp", "provider:gcp:monitoring", "live"],
143 timeout: 90_000,
144 },
145);