1import * as AWS from "@/AWS";
2import { TelemetryRule } from "@/AWS/ObservabilityAdmin";
3import * as Test from "@/Test/Alchemy";
4import * as obs from "@distilled.cloud/aws/observabilityadmin";
5import { describe, expect } from "alchemy-test";
6import type * as Duration from "effect/Duration";
7import * as Effect from "effect/Effect";
8import * as Schedule from "effect/Schedule";
9import { makeObservabilityAdminTestLease } from "./TestLease.ts";
10
11const { test, beforeAll, afterAll } = Test.make({
12 providers: AWS.providers(),
13});
14const testLease = makeObservabilityAdminTestLease();
15
16beforeAll(testLease.acquire, { timeout: 3_600_000 });
17afterAll(testLease.release);
18
19const readStatus = obs
20 .getTelemetryEvaluationStatus({})
21 .pipe(Effect.map((r) => r.Status ?? "NOT_STARTED"));
22
23// Bounded wait for the account-wide telemetry config toggle to settle.
24const awaitSettled = readStatus.pipe(
25 Effect.repeat({
26 schedule: Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]),
27 until: (status): boolean => status !== "STARTING" && status !== "STOPPING",
28 }),
29);
30
31const isOn = (status: string): boolean =>
32 status === "RUNNING" || status === "STARTING";
33
34/** Restore the account's telemetry config onboarding to its pre-test state. */
35const restoreTo = (prior: string) =>
36 Effect.gen(function* () {
37 const settled = yield* awaitSettled;
38 if (isOn(prior) && !isOn(settled)) {
39 yield* obs.startTelemetryEvaluation({});
40 yield* awaitSettled;
41 }
42 if (!isOn(prior) && isOn(settled)) {
43 yield* obs.stopTelemetryEvaluation({});
44 yield* awaitSettled;
45 }
46 });
47
48// Telemetry rules require the account-wide telemetry config feature, so run
49// sequentially and capture-and-restore the onboarding state.
50describe.sequential(
51 "AWS.ObservabilityAdmin.TelemetryRule",
52 { tags: ["provider:aws", "provider:aws:observabilityadmin", "live"] },
53 () => {
54 test.provider(
55 "creates, updates in place, and deletes a telemetry rule",
56 (stack) =>
57 Effect.gen(function* () {
58 // Capture the account's pre-test onboarding state; rules need the
59 // feature RUNNING, and destroy must restore whatever was there.
60 const before = yield* awaitSettled;
61
62 yield* Effect.gen(function* () {
63 if (!isOn(before)) {
64 yield* obs.startTelemetryEvaluation({});
65 yield* awaitSettled;
66 }
67
68 yield* stack.destroy();
69
70 const deployRule = (retention: Duration.Input) =>
71 stack.deploy(
72 Effect.gen(function* () {
73 // The rule exists for well under the ~24h AWS Config
74 // discovery window, so it never actually configures flow
75 // logs (same shape as the terraform-provider-aws
76 // acceptance tests).
77 const rule = yield* TelemetryRule("Rule", {
78 resourceType: "AWS::EC2::VPC",
79 telemetryType: "Logs",
80 telemetrySourceTypes: ["VPC_FLOW_LOGS"],
81 destinationConfiguration: {
82 DestinationType: "cloud-watch-logs",
83 Retention: retention,
84 },
85 });
86 return {
87 ruleName: rule.ruleName,
88 ruleArn: rule.ruleArn,
89 telemetryType: rule.telemetryType,
90 resourceType: rule.resourceType,
91 };
92 }),
93 );
94
95 // Create.
96 const created = yield* deployRule("30 days");
97 expect(created.ruleName).toBeTruthy();
98 expect(created.ruleArn).toContain("telemetry-rule");
99 expect(created.telemetryType).toBe("Logs");
100 expect(created.resourceType).toBe("AWS::EC2::VPC");
101 const got = yield* obs.getTelemetryRule({
102 RuleIdentifier: created.ruleName,
103 });
104 expect(
105 got.TelemetryRule?.DestinationConfiguration?.RetentionInDays,
106 ).toBe(30);
107
108 // Update in place — the Duration prop lands as whole wire days and
109 // the ARN is stable (no replacement).
110 const updated = yield* deployRule("60 days");
111 expect(updated.ruleArn).toBe(created.ruleArn);
112 const got2 = yield* obs.getTelemetryRule({
113 RuleIdentifier: created.ruleName,
114 });
115 expect(
116 got2.TelemetryRule?.DestinationConfiguration?.RetentionInDays,
117 ).toBe(60);
118
119 // Destroy — the rule is gone (typed check, not a catch-all).
120 yield* stack.destroy();
121 const gone = yield* obs
122 .getTelemetryRule({ RuleIdentifier: created.ruleName })
123 .pipe(
124 Effect.map(() => false),
125 Effect.catchTag("ResourceNotFoundException", () =>
126 Effect.succeed(true),
127 ),
128 );
129 expect(gone).toBe(true);
130 }).pipe(
131 // Always restore the account's onboarding state, even on failure.
132 Effect.ensuring(restoreTo(before).pipe(Effect.ignore)),
133 );
134 }),
135 { timeout: 180_000 },
136 );
137 },
138);