| 1 | import * as AWS from "alchemy/AWS"; |
| 2 | import * as Kubernetes from "alchemy/Kubernetes"; |
| 3 | import * as Effect from "effect/Effect"; |
| 4 | import { EntriesTable, GuestbookCluster, GuestbookNamespace } from "./infra.ts"; |
| 5 | |
| 6 | const seedEntries = [ |
| 7 | { id: "ada", message: "First computers, now clusters." }, |
| 8 | { |
| 9 | id: "grace", |
| 10 | message: |
| 11 | "A cluster in port is safe, but that is not what clusters are for.", |
| 12 | }, |
| 13 | { id: "linus", message: "Talk is cheap. Show me the manifest." }, |
| 14 | ]; |
| 15 | |
| 16 | /** |
| 17 | * A one-shot `Kubernetes.Job` in the INLINE EFFECT form: props + an init Effect |
| 18 | * whose impl returns `{ run }` — a one-shot entry that executes to |
| 19 | * completion inside the pod, after which the process exits (the Kubernetes |
| 20 | * analog of `AWS.ECS.Task`). |
| 21 | * |
| 22 | * The Effect program is bundled into a generated image |
| 23 | * (`main: import.meta.url` names this module as the entrypoint). The |
| 24 | * `AWS.DynamoDB.PutItem` binding injects the table's name into the pod and |
| 25 | * grants `dynamodb:PutItem` on the generated pod-identity role. |
| 26 | * |
| 27 | * Applying the batch/v1 Job runs it: the seed executes once on deploy. |
| 28 | * Adding `schedule: "0 3 * * *"` to the props would synthesize a CronJob |
| 29 | * instead. |
| 30 | */ |
| 31 | export default Kubernetes.Job( |
| 32 | "SeedJob", |
| 33 | // Props are themselves an Effect so they can reference shared resources. |
| 34 | Effect.gen(function* () { |
| 35 | const cluster = yield* GuestbookCluster; |
| 36 | const ns = yield* GuestbookNamespace; |
| 37 | return { |
| 38 | cluster, |
| 39 | main: import.meta.url, |
| 40 | // Deploy after the namespace exists (see src/infra.ts). |
| 41 | namespace: ns.name, |
| 42 | backoffLimit: 2, |
| 43 | }; |
| 44 | }), |
| 45 | Effect.gen(function* () { |
| 46 | const table = yield* EntriesTable; |
| 47 | const putItem = yield* AWS.DynamoDB.PutItem(table); |
| 48 | |
| 49 | return { |
| 50 | // One-shot entry: seed the table, log, exit 0. |
| 51 | run: Effect.gen(function* () { |
| 52 | yield* Effect.forEach( |
| 53 | seedEntries, |
| 54 | (entry) => |
| 55 | putItem({ |
| 56 | Item: { |
| 57 | pk: { S: `entry#${entry.id}` }, |
| 58 | author: { S: entry.id }, |
| 59 | message: { S: entry.message }, |
| 60 | }, |
| 61 | }), |
| 62 | { discard: true }, |
| 63 | ); |
| 64 | yield* Effect.log(`seeded ${seedEntries.length} guestbook entries`); |
| 65 | }).pipe(Effect.orDie), |
| 66 | }; |
| 67 | }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)), |
| 68 | ); |