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 { defaultComputeServiceAccount } from "../serviceAccount.ts";
7import { CAPACITY_REGION } from "../../zones.ts";
8
9/** Environment GetEnvironment / ExecuteAirflowCommand are bound to. */
10export const Airflow = Effect.gen(function* () {
11 // GcpEnvironment exists at deploy time only; the deployed host never
12 // reconciles props, so the placeholder is an equivalent stand-in there.
13 const env = yield* Effect.serviceOption(GcpEnvironment);
14 const serviceAccount = Option.isSome(env)
15 ? // A missing project or lookup failure aborts the test deploy itself.
16 yield* defaultComputeServiceAccount.pipe(Effect.orDie)
17 : "runtime-placeholder";
18 return yield* GCP.Composer.Environment("Airflow", {
19 location: CAPACITY_REGION,
20 config: {
21 environmentSize: "ENVIRONMENT_SIZE_SMALL",
22 nodeConfig: { serviceAccount },
23 softwareConfig: { imageVersion: "composer-3-airflow-2" },
24 },
25 });
26});
27
28/** User-workloads ConfigMap GetUserWorkloadsConfigMap reads. */
29export const TaskConfig = Effect.gen(function* () {
30 const environment = yield* Airflow;
31 return yield* GCP.Composer.EnvironmentsUserWorkloadsConfigMap("TaskConfig", {
32 environmentName: environment.name,
33 data: { LOG_LEVEL: "INFO" },
34 });
35});
36
37/** User-workloads Secret GetUserWorkloadsSecret reads. */
38export const TaskSecret = Effect.gen(function* () {
39 const environment = yield* Airflow;
40 return yield* GCP.Composer.EnvironmentsUserWorkloadsSecret("TaskSecret", {
41 environmentName: environment.name,
42 data: { password: btoa("s3cret") },
43 });
44});
45
46/**
47 * Effect-native Cloud Run service exercising every Cloud Composer binding
48 * as its own runtime service account. Deployed from
49 * {@link ../Bindings.test.ts}.
50 */
51export default class ComposerBindingsHost extends GCP.Function<ComposerBindingsHost>()(
52 "ComposerBindingsHost",
53 { main: import.meta.url, invokerIamDisabled: true },
54 Effect.gen(function* () {
55 const environment = yield* Airflow;
56 const getEnvironment = yield* GCP.Composer.GetEnvironment(environment);
57 const execute = yield* GCP.Composer.ExecuteAirflowCommand(environment);
58 const getConfigMap = yield* GCP.Composer.GetUserWorkloadsConfigMap(
59 yield* TaskConfig,
60 );
61 const getSecret = yield* GCP.Composer.GetUserWorkloadsSecret(
62 yield* TaskSecret,
63 );
64
65 return {
66 fetch: serveProbes({
67 getEnvironment: getEnvironment(),
68 executeAirflowCommand: execute({ body: { command: "version" } }),
69 getUserWorkloadsConfigMap: getConfigMap(),
70 getUserWorkloadsSecret: getSecret(),
71 }),
72 };
73 }).pipe(
74 Effect.provide(GCP.Composer.GetEnvironmentHttp),
75 Effect.provide(GCP.Composer.ExecuteAirflowCommandHttp),
76 Effect.provide(GCP.Composer.GetUserWorkloadsConfigMapHttp),
77 Effect.provide(GCP.Composer.GetUserWorkloadsSecretHttp),
78 ),
79) {}