1import * as GCP from "@/GCP";
2import { GcpEnvironment } from "@/GCP/Environment";
3import * as Effect from "effect/Effect";
4import * as Option from "effect/Option";
5import { serveProbes } from "../../bindingHost.ts";
6import { DEFAULT_NETWORK, defaultNetworkSelfLink } from "../../networkQuota.ts";
7
8/**
9 * Smallest VM the bindings act on. It sits on the project's `default`
10 * network, so it takes no VPC quota slot.
11 */
12export const Vm = Effect.gen(function* () {
13 // GcpEnvironment exists at deploy time only; the deployed host never
14 // reconciles props, so the relative form is an equivalent stand-in there.
15 const env = yield* Effect.serviceOption(GcpEnvironment);
16 const relative = `global/networks/${DEFAULT_NETWORK}`;
17 const network = Option.isSome(env)
18 ? yield* env.value.pipe(
19 Effect.map(({ project }) => defaultNetworkSelfLink(project)),
20 Effect.catchTag("GCP.ProjectMissing", () => Effect.succeed(relative)),
21 )
22 : relative;
23 return yield* GCP.Compute.Instance("Vm", {
24 zone: "us-central1-a",
25 machineType: "e2-micro",
26 network,
27 associatePublicIp: false,
28 });
29});
30
31/**
32 * Effect-native Cloud Run service exercising every Compute instance binding
33 * as its own runtime service account. Deployed from {@link ../Bindings.test.ts}.
34 */
35export default class ComputeBindingsHost extends GCP.Function<ComputeBindingsHost>()(
36 "ComputeBindingsHost",
37 { main: import.meta.url, invokerIamDisabled: true },
38 Effect.gen(function* () {
39 const vm = yield* Vm;
40 const getInstance = yield* GCP.Compute.GetInstance(vm);
41 const stopInstance = yield* GCP.Compute.StopInstance(vm);
42 const startInstance = yield* GCP.Compute.StartInstance(vm);
43
44 return {
45 fetch: serveProbes({
46 getInstance: getInstance().pipe(
47 Effect.map((live) => ({
48 name: live.name,
49 id: live.id,
50 status: live.status,
51 })),
52 ),
53 stopInstance: stopInstance().pipe(
54 Effect.map((op) => ({
55 operationType: op.operationType,
56 targetLink: op.targetLink,
57 })),
58 ),
59 startInstance: startInstance().pipe(
60 Effect.map((op) => ({
61 operationType: op.operationType,
62 targetLink: op.targetLink,
63 })),
64 ),
65 }),
66 };
67 }).pipe(
68 Effect.provide(GCP.Compute.GetInstanceHttp),
69 Effect.provide(GCP.Compute.StopInstanceHttp),
70 Effect.provide(GCP.Compute.StartInstanceHttp),
71 ),
72) {}