1import * as AWS from "@/AWS";
2import { LogGroup } from "@/AWS/Logs";
3import { AccessLogSubscription, ServiceNetwork } from "@/AWS/VpcLattice";
4import * as Test from "@/Test/Alchemy";
5import * as vpclattice from "@distilled.cloud/aws/vpc-lattice";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11// CloudWatch log-group destination ARNs round-trip through the lattice API
12// with a trailing `:*`.
13const normalizeArn = (arn: string) => arn.replace(/:\*$/, "");
14
15test.provider(
16 "create, update destination, delete an access log subscription",
17 (stack) =>
18 Effect.gen(function* () {
19 yield* stack.destroy();
20
21 const program = (destination: "primary" | "secondary") =>
22 Effect.gen(function* () {
23 const network = yield* ServiceNetwork("LogsNetwork", {});
24 const primary = yield* LogGroup("LatticeLogsPrimary", {
25 retention: "1 days",
26 });
27 const secondary = yield* LogGroup("LatticeLogsSecondary", {
28 retention: "1 days",
29 });
30 const subscription = yield* AccessLogSubscription("NetworkLogs", {
31 resourceIdentifier: network.serviceNetworkId,
32 destinationArn:
33 destination === "primary"
34 ? primary.logGroupArn
35 : secondary.logGroupArn,
36 });
37 return { network, primary, secondary, subscription };
38 });
39
40 const first = yield* stack.deploy(program("primary"));
41
42 expect(first.subscription.accessLogSubscriptionId).toMatch(/^als-/);
43 expect(normalizeArn(first.subscription.destinationArn)).toBe(
44 normalizeArn(first.primary.logGroupArn),
45 );
46
47 const live = yield* vpclattice.getAccessLogSubscription({
48 accessLogSubscriptionIdentifier:
49 first.subscription.accessLogSubscriptionId,
50 });
51 expect(normalizeArn(live.destinationArn)).toBe(
52 normalizeArn(first.primary.logGroupArn),
53 );
54 expect(live.resourceId).toBe(first.network.serviceNetworkId);
55
56 // Update the destination in place (same destination type).
57 const second = yield* stack.deploy(program("secondary"));
58 expect(second.subscription.accessLogSubscriptionId).toBe(
59 first.subscription.accessLogSubscriptionId,
60 );
61 const updated = yield* vpclattice.getAccessLogSubscription({
62 accessLogSubscriptionIdentifier:
63 first.subscription.accessLogSubscriptionId,
64 });
65 expect(normalizeArn(updated.destinationArn)).toBe(
66 normalizeArn(first.secondary.logGroupArn),
67 );
68
69 yield* stack.destroy();
70 const gone = yield* vpclattice
71 .getAccessLogSubscription({
72 accessLogSubscriptionIdentifier:
73 first.subscription.accessLogSubscriptionId,
74 })
75 .pipe(
76 Effect.map(() => false),
77 Effect.catchTag("ResourceNotFoundException", () =>
78 Effect.succeed(true),
79 ),
80 );
81 expect(gone).toBe(true);
82 }).pipe(Effect.ensuring(Effect.ignore(stack.destroy()))),
83 {
84 tags: [
85 "provider:aws",
86 "provider:aws:logs",
87 "provider:aws:vpclattice",
88 "live",
89 ],
90 timeout: 300_000,
91 },
92);