1import * as AWS from "@/AWS";
2import { Vpc, VpcPeeringConnection } from "@/AWS/EC2";
3import * as Provider from "@/Provider";
4import * as Test from "./VpcTest.ts";
5import * as EC2 from "@distilled.cloud/aws/ec2";
6import { expect } from "alchemy-test";
7import * as Data from "effect/Data";
8import * as Effect from "effect/Effect";
9import { MinimumLogLevel } from "effect/References";
10import * as Schedule from "effect/Schedule";
11
12const { test } = Test.make({ providers: AWS.providers() }, 2);
13
14const logLevel = Effect.provideService(
15 MinimumLogLevel,
16 process.env.DEBUG ? "Debug" : "Info",
17);
18
19const DEAD = new Set(["deleted", "deleting", "rejected", "failed", "expired"]);
20
21class PeeringStillLive extends Data.TaggedError("PeeringStillLive") {}
22
23const assertDeleted = Effect.fn(function* (pcxId: string) {
24 yield* EC2.describeVpcPeeringConnections({
25 VpcPeeringConnectionIds: [pcxId],
26 }).pipe(
27 Effect.flatMap((r) => {
28 const code = r.VpcPeeringConnections?.[0]?.Status?.Code;
29 return code === undefined || DEAD.has(code)
30 ? Effect.void
31 : Effect.fail(new PeeringStillLive());
32 }),
33 Effect.retry({
34 while: (e) => e instanceof PeeringStillLive,
35 schedule: Schedule.max([Schedule.exponential(300), Schedule.recurs(8)]),
36 }),
37 Effect.catchTag(
38 "InvalidVpcPeeringConnectionID.NotFound",
39 () => Effect.void,
40 ),
41 Effect.catchTag(
42 "InvalidVpcPeeringConnectionId.NotFound",
43 () => Effect.void,
44 ),
45 );
46});
47
48test.provider(
49 "create, auto-accept, delete same-account peering connection",
50 (stack) =>
51 Effect.gen(function* () {
52 yield* stack.destroy();
53
54 const { peering, vpcA, vpcB } = yield* stack.deploy(
55 Effect.gen(function* () {
56 const vpcA = yield* Vpc("PeeringVpcA", {
57 cidrBlock: "10.10.0.0/16",
58 });
59 const vpcB = yield* Vpc("PeeringVpcB", {
60 cidrBlock: "10.20.0.0/16",
61 });
62 const peering = yield* VpcPeeringConnection("Peering", {
63 vpcId: vpcA.vpcId,
64 peerVpcId: vpcB.vpcId,
65 });
66 return { peering, vpcA, vpcB };
67 }),
68 );
69
70 expect(peering.vpcPeeringConnectionId).toMatch(/^pcx-/);
71 expect(peering.status).toEqual("active");
72 expect(peering.requesterVpcId).toEqual(vpcA.vpcId);
73 expect(peering.accepterVpcId).toEqual(vpcB.vpcId);
74
75 // Verify out-of-band that it is ACTIVE.
76 const described = yield* EC2.describeVpcPeeringConnections({
77 VpcPeeringConnectionIds: [peering.vpcPeeringConnectionId],
78 });
79 const pcx = described.VpcPeeringConnections?.[0];
80 expect(pcx?.Status?.Code).toEqual("active");
81 expect(pcx?.RequesterVpcInfo?.VpcId).toEqual(vpcA.vpcId);
82 expect(pcx?.AccepterVpcInfo?.VpcId).toEqual(vpcB.vpcId);
83
84 yield* stack.destroy();
85 yield* assertDeleted(peering.vpcPeeringConnectionId);
86 }).pipe(logLevel),
87 { tags: ["provider:aws", "provider:aws:ec2", "live"], timeout: 240_000 },
88);
89
90test.provider(
91 "list enumerates the deployed peering connection",
92 (stack) =>
93 Effect.gen(function* () {
94 yield* stack.destroy();
95
96 const { peering } = yield* stack.deploy(
97 Effect.gen(function* () {
98 const vpcA = yield* Vpc("ListPeeringVpcA", {
99 cidrBlock: "10.30.0.0/16",
100 });
101 const vpcB = yield* Vpc("ListPeeringVpcB", {
102 cidrBlock: "10.40.0.0/16",
103 });
104 const peering = yield* VpcPeeringConnection("ListPeering", {
105 vpcId: vpcA.vpcId,
106 peerVpcId: vpcB.vpcId,
107 });
108 return { peering };
109 }),
110 );
111
112 const provider = yield* Provider.findProvider(VpcPeeringConnection);
113 const all = yield* provider.list();
114 expect(
115 all.some(
116 (x) => x.vpcPeeringConnectionId === peering.vpcPeeringConnectionId,
117 ),
118 ).toBe(true);
119
120 yield* stack.destroy();
121 yield* assertDeleted(peering.vpcPeeringConnectionId);
122 }).pipe(logLevel),
123 { tags: ["provider:aws", "provider:aws:ec2", "live"], timeout: 240_000 },
124);