1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as networksecurity from "@distilled.cloud/gcp/networksecurity_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import { GcpEnvironment } from "@/GCP/Environment";
9import { withNetworkSlot } from "../networkQuota.ts";
10
11const { test } = Test.make({ providers: GCP.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18const waitUntilGone = (name: string) =>
19 networksecurity.getProjectsLocationsMirroringDeployments({ name }).pipe(
20 Effect.as("found" as const),
21 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
22 Effect.repeat({
23 schedule: Schedule.spaced("2 seconds"),
24 until: (status) => status === "gone",
25 times: 10,
26 }),
27 );
28
29test.provider(
30 "getProjectsLocationsMirroringDeployments on a missing deployment fails with a typed tag",
31 (stack) =>
32 Effect.gen(function* () {
33 yield* stack.destroy();
34
35 const { project } = yield* GcpEnvironment.current;
36 const error = yield* Effect.flip(
37 networksecurity.getProjectsLocationsMirroringDeployments({
38 name: `projects/${project}/locations/us-central1-a/mirroringDeployments/alchemy-missing`,
39 }),
40 );
41 expect(error._tag).toEqual("NotFound");
42
43 yield* stack.destroy();
44 }).pipe(logLevel),
45 {
46 tags: ["provider:gcp", "provider:gcp:networksecurity", "live"],
47 timeout: 90_000,
48 },
49);
50
51test.provider.skipIf(!!process.env.FAST)(
52 "create, update, and delete a mirroring deployment",
53 (stack) =>
54 Effect.gen(function* () {
55 yield* stack.destroy();
56
57 const created = yield* stack.deploy(
58 Effect.gen(function* () {
59 const vpc = yield* GCP.Compute.Network("Vpc", {
60 autoCreateSubnetworks: false,
61 description: "mirroring deployment vpc",
62 });
63 const subnet = yield* GCP.Compute.Subnetwork("Subnet", {
64 region: "us-central1",
65 network: vpc.selfLink.as<string>(),
66 ipCidrRange: "10.10.0.0/24",
67 });
68 const health = yield* GCP.Compute.HealthCheck("Tcp", {
69 type: "TCP",
70 tcpHealthCheck: { port: 80 },
71 });
72 const backend = yield* GCP.Compute.RegionBackendService("Collector", {
73 region: "us-central1",
74 protocol: "UDP",
75 loadBalancingScheme: "INTERNAL",
76 healthChecks: [health.selfLink.as<string>()],
77 });
78 const rule = yield* GCP.Compute.ForwardingRule("CollectorRule", {
79 region: "us-central1",
80 loadBalancingScheme: "INTERNAL",
81 ipProtocol: "UDP",
82 allPorts: true,
83 network: vpc.selfLink.as<string>(),
84 subnetwork: subnet.selfLink.as<string>(),
85 backendService: backend.selfLink.as<string>(),
86 isMirroringCollector: true,
87 });
88 const collectors =
89 yield* GCP.NetworkSecurity.MirroringDeploymentGroup("Collectors", {
90 network: vpc.selfLink.as<string>(),
91 });
92 const deployment = yield* GCP.NetworkSecurity.MirroringDeployment(
93 "ZoneA",
94 {
95 location: "us-central1-a",
96 mirroringDeploymentGroup: collectors.name,
97 forwardingRule: rule.selfLink.as<string>(),
98 description: "mirroring dep a",
99 labels: { env: "test" },
100 },
101 );
102 return { vpc, subnet, health, backend, rule, collectors, deployment };
103 }),
104 );
105
106 expect(created.deployment.name).toContain("/mirroringDeployments/");
107 expect(created.deployment.location).toEqual("us-central1-a");
108 expect(created.deployment.description).toEqual("mirroring dep a");
109 expect(created.deployment.labels).toMatchObject({ env: "test" });
110 expect(created.deployment.mirroringDeploymentGroup).toEqual(
111 created.collectors.name,
112 );
113
114 const fetched =
115 yield* networksecurity.getProjectsLocationsMirroringDeployments({
116 name: created.deployment.name,
117 });
118 expect(fetched.name).toEqual(created.deployment.name);
119 expect(fetched.description).toEqual("mirroring dep a");
120 expect(fetched.labels?.env).toEqual("test");
121 expect(
122 Object.keys(fetched.labels ?? {}).some((key) =>
123 key.startsWith("alchemy-"),
124 ),
125 ).toEqual(true);
126
127 const updated = yield* stack.deploy(
128 Effect.gen(function* () {
129 const vpc = yield* GCP.Compute.Network("Vpc", {
130 networkName: created.vpc.networkName,
131 autoCreateSubnetworks: false,
132 description: "mirroring deployment vpc",
133 });
134 const subnet = yield* GCP.Compute.Subnetwork("Subnet", {
135 subnetworkName: created.subnet.subnetworkName,
136 region: "us-central1",
137 network: vpc.selfLink.as<string>(),
138 ipCidrRange: "10.10.0.0/24",
139 });
140 const health = yield* GCP.Compute.HealthCheck("Tcp", {
141 healthCheckName: created.health.healthCheckName,
142 type: "TCP",
143 tcpHealthCheck: { port: 80 },
144 });
145 const backend = yield* GCP.Compute.RegionBackendService("Collector", {
146 name: created.backend.name,
147 region: "us-central1",
148 protocol: "UDP",
149 loadBalancingScheme: "INTERNAL",
150 healthChecks: [health.selfLink.as<string>()],
151 });
152 const rule = yield* GCP.Compute.ForwardingRule("CollectorRule", {
153 forwardingRuleName: created.rule.forwardingRuleName,
154 region: "us-central1",
155 loadBalancingScheme: "INTERNAL",
156 ipProtocol: "UDP",
157 allPorts: true,
158 network: vpc.selfLink.as<string>(),
159 subnetwork: subnet.selfLink.as<string>(),
160 backendService: backend.selfLink.as<string>(),
161 isMirroringCollector: true,
162 });
163 const collectors =
164 yield* GCP.NetworkSecurity.MirroringDeploymentGroup("Collectors", {
165 mirroringDeploymentGroupId:
166 created.collectors.mirroringDeploymentGroupId,
167 network: vpc.selfLink.as<string>(),
168 });
169 const deployment = yield* GCP.NetworkSecurity.MirroringDeployment(
170 "ZoneA",
171 {
172 mirroringDeploymentId: created.deployment.mirroringDeploymentId,
173 location: "us-central1-a",
174 mirroringDeploymentGroup: collectors.name,
175 forwardingRule: rule.selfLink.as<string>(),
176 description: "mirroring dep b",
177 labels: { env: "prod", role: "nsi" },
178 },
179 );
180 return { vpc, subnet, health, backend, rule, collectors, deployment };
181 }),
182 );
183
184 expect(updated.deployment.name).toEqual(created.deployment.name);
185 expect(updated.deployment.description).toEqual("mirroring dep b");
186 expect(updated.deployment.labels).toMatchObject({
187 env: "prod",
188 role: "nsi",
189 });
190
191 yield* stack.destroy();
192
193 const gone = yield* waitUntilGone(created.deployment.name);
194 expect(gone).toEqual("gone");
195 }).pipe(logLevel, withNetworkSlot),
196 {
197 tags: ["provider:gcp", "provider:gcp:networksecurity", "live"],
198 timeout: 900_000,
199 },
200);