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.getProjectsLocationsInterceptDeployments({ 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 "getProjectsLocationsInterceptDeployments 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.getProjectsLocationsInterceptDeployments({
38 name: `projects/${project}/locations/us-central1-a/interceptDeployments/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 an intercept 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: "intercept deployment vpc",
62 });
63 const subnet = yield* GCP.Compute.Subnetwork("Subnet", {
64 region: "us-central1",
65 network: vpc.networkName,
66 ipCidrRange: "10.90.0.0/24",
67 description: "intercept deployment subnet",
68 });
69 const healthCheck = yield* GCP.Compute.RegionHealthCheck("Hc", {
70 region: "us-central1",
71 type: "TCP",
72 tcpHealthCheck: { port: 80 },
73 description: "intercept deployment hc",
74 });
75 const backend = yield* GCP.Compute.RegionBackendService("Backend", {
76 region: "us-central1",
77 protocol: "UDP",
78 loadBalancingScheme: "INTERNAL",
79 network: vpc.selfLink,
80 healthChecks: [healthCheck.selfLink.as<string>()],
81 description: "intercept deployment backend",
82 });
83 const rule = yield* GCP.Compute.ForwardingRule("Rule", {
84 region: "us-central1",
85 loadBalancingScheme: "INTERNAL",
86 ipProtocol: "UDP",
87 ports: ["6081"],
88 network: vpc.selfLink,
89 subnetwork: subnet.selfLink,
90 backendService: backend.selfLink,
91 description: "intercept deployment rule",
92 });
93 const group = yield* GCP.NetworkSecurity.InterceptDeploymentGroup(
94 "Inspect",
95 {
96 location: "global",
97 network: vpc.selfLink.as<string>(),
98 description: "intercept deployment group",
99 labels: { env: "test" },
100 },
101 );
102 const deployment = yield* GCP.NetworkSecurity.InterceptDeployment(
103 "ZoneA",
104 {
105 location: "us-central1-a",
106 interceptDeploymentGroup: group.name,
107 forwardingRule: rule.selfLink.as<string>(),
108 description: "intercept deployment a",
109 labels: { env: "test" },
110 },
111 );
112 return { vpc, subnet, healthCheck, backend, rule, group, deployment };
113 }),
114 );
115
116 expect(created.deployment.name).toContain("/interceptDeployments/");
117 expect(created.deployment.location).toEqual("us-central1-a");
118 expect(created.deployment.description).toEqual("intercept deployment a");
119 expect(created.deployment.labels).toMatchObject({ env: "test" });
120 expect(created.deployment.interceptDeploymentGroup).toEqual(
121 created.group.name,
122 );
123
124 const fetched =
125 yield* networksecurity.getProjectsLocationsInterceptDeployments({
126 name: created.deployment.name,
127 });
128 expect(fetched.name).toEqual(created.deployment.name);
129 expect(fetched.description).toEqual("intercept deployment a");
130 expect(fetched.labels?.env).toEqual("test");
131 expect(
132 Object.keys(fetched.labels ?? {}).some((key) =>
133 key.startsWith("alchemy-"),
134 ),
135 ).toEqual(true);
136
137 const updated = yield* stack.deploy(
138 Effect.gen(function* () {
139 const vpc = yield* GCP.Compute.Network("Vpc", {
140 networkName: created.vpc.networkName,
141 autoCreateSubnetworks: false,
142 description: "intercept deployment vpc",
143 });
144 const subnet = yield* GCP.Compute.Subnetwork("Subnet", {
145 subnetworkName: created.subnet.subnetworkName,
146 region: "us-central1",
147 network: vpc.networkName,
148 ipCidrRange: "10.90.0.0/24",
149 description: "intercept deployment subnet",
150 });
151 const healthCheck = yield* GCP.Compute.RegionHealthCheck("Hc", {
152 healthCheckName: created.healthCheck.healthCheckName,
153 region: "us-central1",
154 type: "TCP",
155 tcpHealthCheck: { port: 80 },
156 description: "intercept deployment hc",
157 });
158 const backend = yield* GCP.Compute.RegionBackendService("Backend", {
159 name: created.backend.name,
160 region: "us-central1",
161 protocol: "UDP",
162 loadBalancingScheme: "INTERNAL",
163 network: vpc.selfLink,
164 healthChecks: [healthCheck.selfLink.as<string>()],
165 description: "intercept deployment backend",
166 });
167 const rule = yield* GCP.Compute.ForwardingRule("Rule", {
168 forwardingRuleName: created.rule.forwardingRuleName,
169 region: "us-central1",
170 loadBalancingScheme: "INTERNAL",
171 ipProtocol: "UDP",
172 ports: ["6081"],
173 network: vpc.selfLink,
174 subnetwork: subnet.selfLink,
175 backendService: backend.selfLink,
176 description: "intercept deployment rule",
177 });
178 const group = yield* GCP.NetworkSecurity.InterceptDeploymentGroup(
179 "Inspect",
180 {
181 interceptDeploymentGroupId:
182 created.group.interceptDeploymentGroupId,
183 location: "global",
184 network: vpc.selfLink.as<string>(),
185 description: "intercept deployment group",
186 labels: { env: "test" },
187 },
188 );
189 const deployment = yield* GCP.NetworkSecurity.InterceptDeployment(
190 "ZoneA",
191 {
192 interceptDeploymentId: created.deployment.interceptDeploymentId,
193 location: "us-central1-a",
194 interceptDeploymentGroup: group.name,
195 forwardingRule: rule.selfLink.as<string>(),
196 description: "intercept deployment b",
197 labels: { env: "prod", role: "intercept" },
198 },
199 );
200 return { vpc, subnet, healthCheck, backend, rule, group, deployment };
201 }),
202 );
203
204 expect(updated.deployment.name).toEqual(created.deployment.name);
205 expect(updated.deployment.description).toEqual("intercept deployment b");
206 expect(updated.deployment.labels).toMatchObject({
207 env: "prod",
208 role: "intercept",
209 });
210
211 const refetched =
212 yield* networksecurity.getProjectsLocationsInterceptDeployments({
213 name: created.deployment.name,
214 });
215 expect(refetched.description).toEqual("intercept deployment b");
216 expect(refetched.labels?.env).toEqual("prod");
217 expect(refetched.labels?.role).toEqual("intercept");
218
219 yield* stack.destroy();
220
221 const gone = yield* waitUntilGone(created.deployment.name);
222 expect(gone).toEqual("gone");
223 }).pipe(logLevel, withNetworkSlot),
224 {
225 tags: ["provider:gcp", "provider:gcp:networksecurity", "live"],
226 timeout: 600_000,
227 },
228);