@alchemy.run/effect-cluster 0.0.0-stage → 2.0.0-beta.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +105 -2
- package/lib/AWS/Database.d.ts +54 -0
- package/lib/AWS/Database.d.ts.map +1 -0
- package/lib/AWS/Database.js +96 -0
- package/lib/AWS/Database.js.map +1 -0
- package/lib/AWS/EcsPool.d.ts +56 -0
- package/lib/AWS/EcsPool.d.ts.map +1 -0
- package/lib/AWS/EcsPool.js +87 -0
- package/lib/AWS/EcsPool.js.map +1 -0
- package/lib/AWS/index.d.ts +7 -0
- package/lib/AWS/index.d.ts.map +1 -0
- package/lib/AWS/index.js +7 -0
- package/lib/AWS/index.js.map +1 -0
- package/lib/AWS/internal/Gateway.d.ts +23 -0
- package/lib/AWS/internal/Gateway.d.ts.map +1 -0
- package/lib/AWS/internal/Gateway.js +70 -0
- package/lib/AWS/internal/Gateway.js.map +1 -0
- package/lib/Cloudflare/DurableObjectPool.d.ts +42 -0
- package/lib/Cloudflare/DurableObjectPool.d.ts.map +1 -0
- package/lib/Cloudflare/DurableObjectPool.js +136 -0
- package/lib/Cloudflare/DurableObjectPool.js.map +1 -0
- package/lib/Cloudflare/PoolWorker.d.ts +186 -0
- package/lib/Cloudflare/PoolWorker.d.ts.map +1 -0
- package/lib/Cloudflare/PoolWorker.js +75 -0
- package/lib/Cloudflare/PoolWorker.js.map +1 -0
- package/lib/Cloudflare/index.d.ts +7 -0
- package/lib/Cloudflare/index.d.ts.map +1 -0
- package/lib/Cloudflare/index.js +7 -0
- package/lib/Cloudflare/index.js.map +1 -0
- package/lib/Cloudflare/internal/Objects.d.ts +38 -0
- package/lib/Cloudflare/internal/Objects.d.ts.map +1 -0
- package/lib/Cloudflare/internal/Objects.js +102 -0
- package/lib/Cloudflare/internal/Objects.js.map +1 -0
- package/lib/Cron.d.ts +143 -0
- package/lib/Cron.d.ts.map +1 -0
- package/lib/Cron.js +83 -0
- package/lib/Cron.js.map +1 -0
- package/lib/Database.d.ts +95 -0
- package/lib/Database.d.ts.map +1 -0
- package/lib/Database.js +38 -0
- package/lib/Database.js.map +1 -0
- package/lib/Docker/Database.d.ts +42 -0
- package/lib/Docker/Database.d.ts.map +1 -0
- package/lib/Docker/Database.js +53 -0
- package/lib/Docker/Database.js.map +1 -0
- package/lib/Docker/SwarmPool.d.ts +44 -0
- package/lib/Docker/SwarmPool.d.ts.map +1 -0
- package/lib/Docker/SwarmPool.js +56 -0
- package/lib/Docker/SwarmPool.js.map +1 -0
- package/lib/Docker/index.d.ts +7 -0
- package/lib/Docker/index.d.ts.map +1 -0
- package/lib/Docker/index.js +7 -0
- package/lib/Docker/index.js.map +1 -0
- package/lib/Entity.d.ts +348 -0
- package/lib/Entity.d.ts.map +1 -0
- package/lib/Entity.js +308 -0
- package/lib/Entity.js.map +1 -0
- package/lib/Fly/Database.d.ts +50 -0
- package/lib/Fly/Database.d.ts.map +1 -0
- package/lib/Fly/Database.js +56 -0
- package/lib/Fly/Database.js.map +1 -0
- package/lib/Fly/MachinePool.d.ts +39 -0
- package/lib/Fly/MachinePool.d.ts.map +1 -0
- package/lib/Fly/MachinePool.js +51 -0
- package/lib/Fly/MachinePool.js.map +1 -0
- package/lib/Fly/index.d.ts +7 -0
- package/lib/Fly/index.d.ts.map +1 -0
- package/lib/Fly/index.js +7 -0
- package/lib/Fly/index.js.map +1 -0
- package/lib/GCP/ComputeEnginePool.d.ts +41 -0
- package/lib/GCP/ComputeEnginePool.d.ts.map +1 -0
- package/lib/GCP/ComputeEnginePool.js +172 -0
- package/lib/GCP/ComputeEnginePool.js.map +1 -0
- package/lib/GCP/Database.d.ts +48 -0
- package/lib/GCP/Database.d.ts.map +1 -0
- package/lib/GCP/Database.js +115 -0
- package/lib/GCP/Database.js.map +1 -0
- package/lib/GCP/index.d.ts +7 -0
- package/lib/GCP/index.d.ts.map +1 -0
- package/lib/GCP/index.js +7 -0
- package/lib/GCP/index.js.map +1 -0
- package/lib/Hetzner/Database.d.ts +48 -0
- package/lib/Hetzner/Database.d.ts.map +1 -0
- package/lib/Hetzner/Database.js +91 -0
- package/lib/Hetzner/Database.js.map +1 -0
- package/lib/Hetzner/ServerPool.d.ts +35 -0
- package/lib/Hetzner/ServerPool.d.ts.map +1 -0
- package/lib/Hetzner/ServerPool.js +98 -0
- package/lib/Hetzner/ServerPool.js.map +1 -0
- package/lib/Hetzner/index.d.ts +7 -0
- package/lib/Hetzner/index.d.ts.map +1 -0
- package/lib/Hetzner/index.js +7 -0
- package/lib/Hetzner/index.js.map +1 -0
- package/lib/Host.d.ts +60 -0
- package/lib/Host.d.ts.map +1 -0
- package/lib/Host.js +31 -0
- package/lib/Host.js.map +1 -0
- package/lib/Kubernetes/Database.d.ts +40 -0
- package/lib/Kubernetes/Database.d.ts.map +1 -0
- package/lib/Kubernetes/Database.js +56 -0
- package/lib/Kubernetes/Database.js.map +1 -0
- package/lib/Kubernetes/DeploymentPool.d.ts +54 -0
- package/lib/Kubernetes/DeploymentPool.d.ts.map +1 -0
- package/lib/Kubernetes/DeploymentPool.js +49 -0
- package/lib/Kubernetes/DeploymentPool.js.map +1 -0
- package/lib/Kubernetes/index.d.ts +7 -0
- package/lib/Kubernetes/index.d.ts.map +1 -0
- package/lib/Kubernetes/index.js +7 -0
- package/lib/Kubernetes/index.js.map +1 -0
- package/lib/Pool.d.ts +66 -0
- package/lib/Pool.d.ts.map +1 -0
- package/lib/Pool.js +47 -0
- package/lib/Pool.js.map +1 -0
- package/lib/Providers.d.ts +19 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +31 -0
- package/lib/Providers.js.map +1 -0
- package/lib/ShardGroups.d.ts +24 -0
- package/lib/ShardGroups.d.ts.map +1 -0
- package/lib/ShardGroups.js +19 -0
- package/lib/ShardGroups.js.map +1 -0
- package/lib/Singleton.d.ts +110 -0
- package/lib/Singleton.d.ts.map +1 -0
- package/lib/Singleton.js +60 -0
- package/lib/Singleton.js.map +1 -0
- package/lib/Workflow.d.ts +251 -0
- package/lib/Workflow.d.ts.map +1 -0
- package/lib/Workflow.js +288 -0
- package/lib/Workflow.js.map +1 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -0
- package/lib/internal/Bootstrap.d.ts +28 -0
- package/lib/internal/Bootstrap.d.ts.map +1 -0
- package/lib/internal/Bootstrap.js +257 -0
- package/lib/internal/Bootstrap.js.map +1 -0
- package/lib/internal/EntityRuntime.d.ts +40 -0
- package/lib/internal/EntityRuntime.d.ts.map +1 -0
- package/lib/internal/EntityRuntime.js +698 -0
- package/lib/internal/EntityRuntime.js.map +1 -0
- package/lib/internal/Gateway.d.ts +82 -0
- package/lib/internal/Gateway.d.ts.map +1 -0
- package/lib/internal/Gateway.js +218 -0
- package/lib/internal/Gateway.js.map +1 -0
- package/lib/internal/HostedPlatform.d.ts +36 -0
- package/lib/internal/HostedPlatform.d.ts.map +1 -0
- package/lib/internal/HostedPlatform.js +36 -0
- package/lib/internal/HostedPlatform.js.map +1 -0
- package/lib/internal/HostedRuntime.d.ts +58 -0
- package/lib/internal/HostedRuntime.d.ts.map +1 -0
- package/lib/internal/HostedRuntime.js +72 -0
- package/lib/internal/HostedRuntime.js.map +1 -0
- package/lib/internal/Naming.d.ts +12 -0
- package/lib/internal/Naming.d.ts.map +1 -0
- package/lib/internal/Naming.js +40 -0
- package/lib/internal/Naming.js.map +1 -0
- package/lib/internal/ObjectSharding.d.ts +141 -0
- package/lib/internal/ObjectSharding.d.ts.map +1 -0
- package/lib/internal/ObjectSharding.js +337 -0
- package/lib/internal/ObjectSharding.js.map +1 -0
- package/lib/internal/PoolHost.d.ts +80 -0
- package/lib/internal/PoolHost.d.ts.map +1 -0
- package/lib/internal/PoolHost.js +203 -0
- package/lib/internal/PoolHost.js.map +1 -0
- package/lib/internal/PoolHostService.d.ts +25 -0
- package/lib/internal/PoolHostService.d.ts.map +1 -0
- package/lib/internal/PoolHostService.js +10 -0
- package/lib/internal/PoolHostService.js.map +1 -0
- package/lib/internal/Wire.d.ts +60 -0
- package/lib/internal/Wire.d.ts.map +1 -0
- package/lib/internal/Wire.js +36 -0
- package/lib/internal/Wire.js.map +1 -0
- package/lib/internal/WorkflowGateway.d.ts +36 -0
- package/lib/internal/WorkflowGateway.d.ts.map +1 -0
- package/lib/internal/WorkflowGateway.js +124 -0
- package/lib/internal/WorkflowGateway.js.map +1 -0
- package/lib/internal/external-runner.d.ts +2 -0
- package/lib/internal/external-runner.d.ts.map +1 -0
- package/lib/internal/external-runner.js +7 -0
- package/lib/internal/external-runner.js.map +1 -0
- package/lib/internal/runner.d.ts +2 -0
- package/lib/internal/runner.d.ts.map +1 -0
- package/lib/internal/runner.js +5 -0
- package/lib/internal/runner.js.map +1 -0
- package/package.json +143 -3
- package/src/AWS/Database.ts +139 -0
- package/src/AWS/EcsPool.ts +118 -0
- package/src/AWS/index.ts +6 -0
- package/src/AWS/internal/Gateway.ts +82 -0
- package/src/Cloudflare/DurableObjectPool.ts +198 -0
- package/src/Cloudflare/PoolWorker.ts +124 -0
- package/src/Cloudflare/index.ts +6 -0
- package/src/Cloudflare/internal/Objects.ts +160 -0
- package/src/Cron.ts +202 -0
- package/src/Database.ts +116 -0
- package/src/Docker/Database.ts +94 -0
- package/src/Docker/SwarmPool.ts +83 -0
- package/src/Docker/index.ts +6 -0
- package/src/Entity.ts +621 -0
- package/src/Fly/Database.ts +102 -0
- package/src/Fly/MachinePool.ts +72 -0
- package/src/Fly/index.ts +6 -0
- package/src/GCP/ComputeEnginePool.ts +203 -0
- package/src/GCP/Database.ts +168 -0
- package/src/GCP/index.ts +6 -0
- package/src/Hetzner/Database.ts +142 -0
- package/src/Hetzner/ServerPool.ts +116 -0
- package/src/Hetzner/index.ts +6 -0
- package/src/Host.ts +68 -0
- package/src/Kubernetes/Database.ts +89 -0
- package/src/Kubernetes/DeploymentPool.ts +77 -0
- package/src/Kubernetes/index.ts +6 -0
- package/src/Pool.ts +104 -0
- package/src/Providers.ts +49 -0
- package/src/ShardGroups.ts +41 -0
- package/src/Singleton.ts +153 -0
- package/src/Workflow.ts +556 -0
- package/src/index.ts +11 -0
- package/src/internal/Bootstrap.ts +368 -0
- package/src/internal/EntityRuntime.ts +996 -0
- package/src/internal/Gateway.ts +366 -0
- package/src/internal/HostedPlatform.ts +83 -0
- package/src/internal/HostedRuntime.ts +118 -0
- package/src/internal/Naming.ts +44 -0
- package/src/internal/ObjectSharding.ts +578 -0
- package/src/internal/PoolHost.ts +330 -0
- package/src/internal/PoolHostService.ts +25 -0
- package/src/internal/Wire.ts +83 -0
- package/src/internal/WorkflowGateway.ts +188 -0
- package/src/internal/external-runner.ts +10 -0
- package/src/internal/runner.ts +5 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as Compute from "@alchemy.run/gcp/Compute";
|
|
2
|
+
import * as IAM from "@alchemy.run/gcp/IAM";
|
|
3
|
+
import * as Storage from "@alchemy.run/gcp/Storage";
|
|
4
|
+
import * as Output from "alchemy/Output";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import { GATEWAY_PORT, RUNNER_PORT } from "../internal/Gateway.js";
|
|
7
|
+
import { makePoolLayer } from "../internal/PoolHost.js";
|
|
8
|
+
import { COS_IMAGE } from "./Database.js";
|
|
9
|
+
const NODE_IMAGE = "node:26-slim";
|
|
10
|
+
const APP_PATH = "/var/lib/cluster-runner";
|
|
11
|
+
const ENV_KEY = "cluster-runner-env";
|
|
12
|
+
const HASH_KEY = "cluster-runner-hash";
|
|
13
|
+
/**
|
|
14
|
+
* Fetch the runner program from the bucket and run it on the host network.
|
|
15
|
+
* When the instance's metadata changes (a new program, new environment), run
|
|
16
|
+
* this script again, so every runner picks the change up.
|
|
17
|
+
*/
|
|
18
|
+
const startupScript = (options) => `#!/bin/bash
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
MD=http://metadata.google.internal/computeMetadata/v1
|
|
21
|
+
mkdir -p ${APP_PATH}
|
|
22
|
+
# Container-Optimized OS drops inbound traffic except SSH: open the runner's ports.
|
|
23
|
+
for PORT in ${RUNNER_PORT} ${GATEWAY_PORT}; do
|
|
24
|
+
iptables -C INPUT -p tcp --dport $PORT -j ACCEPT 2>/dev/null || iptables -A INPUT -p tcp --dport $PORT -j ACCEPT
|
|
25
|
+
done
|
|
26
|
+
fetch() {
|
|
27
|
+
TOKEN="$(curl -sf -H 'Metadata-Flavor: Google' "$MD/instance/service-accounts/default/token" | sed -E 's/.*"access_token":"([^"]+)".*/\\1/')"
|
|
28
|
+
curl -sf -H "Authorization: Bearer $TOKEN" -o ${APP_PATH}/$1.next \\
|
|
29
|
+
"https://storage.googleapis.com/storage/v1/b/${options.bucket}/o/$1?alt=media" \\
|
|
30
|
+
&& mv ${APP_PATH}/$1.next ${APP_PATH}/$1
|
|
31
|
+
}
|
|
32
|
+
start() {
|
|
33
|
+
fetch runner.mjs
|
|
34
|
+
curl -sf -H 'Metadata-Flavor: Google' "$MD/instance/attributes/${ENV_KEY}" > ${APP_PATH}/env.list
|
|
35
|
+
docker rm -f cluster-runner >/dev/null 2>&1 || true
|
|
36
|
+
docker run -d --name cluster-runner --restart always --network host \\
|
|
37
|
+
--env-file ${APP_PATH}/env.list -v ${APP_PATH}:/app:ro -w /app ${NODE_IMAGE} node /app/runner.mjs
|
|
38
|
+
}
|
|
39
|
+
etag() {
|
|
40
|
+
curl -sf -H 'Metadata-Flavor: Google' -D - -o /dev/null "$MD/instance/attributes/?recursive=true$1" \\
|
|
41
|
+
| grep -i '^etag:' | awk '{print $2}' | tr -d '\\r'
|
|
42
|
+
}
|
|
43
|
+
start
|
|
44
|
+
(
|
|
45
|
+
ETAG="$(etag "")"
|
|
46
|
+
while true; do
|
|
47
|
+
NEXT="$(etag "&wait_for_change=true&timeout_sec=3600&last_etag=$ETAG")"
|
|
48
|
+
if [ -n "$NEXT" ] && [ "$NEXT" != "$ETAG" ]; then
|
|
49
|
+
curl -sf -H 'Metadata-Flavor: Google' "$MD/instance/attributes/startup-script" > ${APP_PATH}/startup.sh \\
|
|
50
|
+
&& exec setsid bash ${APP_PATH}/startup.sh
|
|
51
|
+
fi
|
|
52
|
+
sleep 5
|
|
53
|
+
done
|
|
54
|
+
) >/dev/null 2>&1 < /dev/null &
|
|
55
|
+
# The runner's logs on the serial console (\`gcloud compute instances get-serial-port-output\`).
|
|
56
|
+
pgrep -f "docker logs -f cluster-runner" >/dev/null || setsid sh -c 'while true; do docker logs -f cluster-runner 2>&1 | sed -u "s/^/cluster-runner: /"; sleep 2; done > /dev/ttyS0' >/dev/null 2>&1 < /dev/null &
|
|
57
|
+
`;
|
|
58
|
+
/**
|
|
59
|
+
* Run a pool of Effect Cluster runners on Compute Engine: instances in the
|
|
60
|
+
* database's VPC, spread across zones, each running every class given these
|
|
61
|
+
* runners from a Cloud Storage bucket, behind a regional load balancer on a
|
|
62
|
+
* static address that serves their gateway to callers anywhere. Runners
|
|
63
|
+
* reach each other and the database through the cluster's network tag.
|
|
64
|
+
* Requires a database on a GCP network — `ClusterGCP.Database`.
|
|
65
|
+
*
|
|
66
|
+
* Cloud Run can't host runners: runners take connections from each other.
|
|
67
|
+
*
|
|
68
|
+
* ### Running on Compute Engine
|
|
69
|
+
* **Example:** Two runners
|
|
70
|
+
* ```typescript
|
|
71
|
+
* export const Database = ClusterGCP.Database("Cluster");
|
|
72
|
+
* export const Default = ClusterGCP.ComputeEnginePool("Default", { replicas: 2 }).pipe(
|
|
73
|
+
* Layer.provide(Database),
|
|
74
|
+
* );
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @primitive
|
|
78
|
+
*/
|
|
79
|
+
export const ComputeEnginePool = (id, options = {}) => makePoolLayer({
|
|
80
|
+
label: "ClusterGCP.ComputeEnginePool",
|
|
81
|
+
id,
|
|
82
|
+
settings: options,
|
|
83
|
+
expected: "a GCP network (ClusterGCP.Database)",
|
|
84
|
+
network: (database) => (database.network?.kind === "gcp" ? database.network : undefined),
|
|
85
|
+
// A static address, before the instances, so the URL never depends on it.
|
|
86
|
+
gateway: (_name, network) => Effect.gen(function* () {
|
|
87
|
+
const address = yield* Compute.Address(`${id}GatewayAddress`, { region: network.region });
|
|
88
|
+
return {
|
|
89
|
+
url: Output.interpolate `http://${address.address}:${GATEWAY_PORT}`,
|
|
90
|
+
address: address.address,
|
|
91
|
+
};
|
|
92
|
+
}),
|
|
93
|
+
nodes: ({ network, gateway, program, environment }) => Effect.gen(function* () {
|
|
94
|
+
const zones = options.zones ?? [
|
|
95
|
+
`${network.region}-a`,
|
|
96
|
+
`${network.region}-b`,
|
|
97
|
+
`${network.region}-c`,
|
|
98
|
+
];
|
|
99
|
+
// The gateway checks every call's token; the passthrough load
|
|
100
|
+
// balancer keeps the caller's address.
|
|
101
|
+
yield* Compute.Firewall(`${id}GatewayFirewall`, {
|
|
102
|
+
network: network.network,
|
|
103
|
+
direction: "INGRESS",
|
|
104
|
+
allowed: [{ protocol: "tcp", ports: [String(GATEWAY_PORT)] }],
|
|
105
|
+
sourceRanges: ["0.0.0.0/0"],
|
|
106
|
+
targetTags: [network.tag],
|
|
107
|
+
});
|
|
108
|
+
const bucket = yield* Storage.Bucket(`${id}Program`, {
|
|
109
|
+
location: network.region,
|
|
110
|
+
forceDestroy: true,
|
|
111
|
+
uniformBucketLevelAccess: true,
|
|
112
|
+
});
|
|
113
|
+
const runner = yield* Storage.Object(`${id}RunnerObject`, {
|
|
114
|
+
bucketName: bucket.bucketName,
|
|
115
|
+
key: "runner.mjs",
|
|
116
|
+
file: program.pipe(Output.map((p) => `${p.dir}/runner.mjs`)),
|
|
117
|
+
contentType: "text/javascript",
|
|
118
|
+
});
|
|
119
|
+
const account = yield* IAM.ServiceAccount(`${id}RunnerAccount`, {
|
|
120
|
+
displayName: "Effect Cluster runners",
|
|
121
|
+
});
|
|
122
|
+
const grant = yield* IAM.Member(`${id}ProgramGrant`, {
|
|
123
|
+
kind: "storage.bucket",
|
|
124
|
+
name: bucket.bucketName,
|
|
125
|
+
role: "roles/storage.objectViewer",
|
|
126
|
+
member: Output.interpolate `serviceAccount:${account.email}`,
|
|
127
|
+
});
|
|
128
|
+
// A Docker env file: one `NAME=value` per line. Each runner finds its
|
|
129
|
+
// own address in the subnetwork's range.
|
|
130
|
+
const envFile = environment.pipe(Output.map((env) => Object.entries({ ...env, CLUSTER_RUNNER_NETWORK: network.cidr })
|
|
131
|
+
.map(([name, value]) => `${name}=${String(value).replaceAll("\n", " ")}`)
|
|
132
|
+
.join("\n")));
|
|
133
|
+
const metadata = Output.all(bucket.bucketName, runner.md5Hash, envFile, grant.role).pipe(Output.map(([bucketName, hash, env]) => ({
|
|
134
|
+
"startup-script": startupScript({ bucket: bucketName }),
|
|
135
|
+
[HASH_KEY]: hash ?? "",
|
|
136
|
+
[ENV_KEY]: env,
|
|
137
|
+
})));
|
|
138
|
+
const instances = [];
|
|
139
|
+
for (let i = 0; i < (options.replicas ?? 2); i++) {
|
|
140
|
+
instances.push(yield* Compute.Instance(`${id}Runner${i}`, {
|
|
141
|
+
zone: zones[i % zones.length],
|
|
142
|
+
machineType: options.machineType ?? "e2-small",
|
|
143
|
+
sourceImage: COS_IMAGE,
|
|
144
|
+
network: Output.interpolate `global/networks/${network.network}`,
|
|
145
|
+
subnetwork: Output.interpolate `regions/${network.region}/subnetworks/${network.subnetwork}`,
|
|
146
|
+
associatePublicIp: true,
|
|
147
|
+
tags: [network.tag],
|
|
148
|
+
serviceAccount: account.email,
|
|
149
|
+
oauthScopes: ["https://www.googleapis.com/auth/cloud-platform"],
|
|
150
|
+
metadata: metadata,
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
const health = yield* Compute.HttpHealthCheck(`${id}GatewayHealth`, {
|
|
154
|
+
port: GATEWAY_PORT,
|
|
155
|
+
requestPath: "/health",
|
|
156
|
+
});
|
|
157
|
+
const pool = yield* Compute.TargetPool(`${id}Gateway`, {
|
|
158
|
+
region: network.region,
|
|
159
|
+
healthChecks: [health.selfLink],
|
|
160
|
+
instances: instances.map((instance) => instance.selfLink),
|
|
161
|
+
});
|
|
162
|
+
yield* Compute.ForwardingRule(`${id}GatewayRule`, {
|
|
163
|
+
region: network.region,
|
|
164
|
+
ipAddress: gateway.address,
|
|
165
|
+
ipProtocol: "TCP",
|
|
166
|
+
portRange: String(GATEWAY_PORT),
|
|
167
|
+
target: pool.selfLink,
|
|
168
|
+
loadBalancingScheme: "EXTERNAL",
|
|
169
|
+
});
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=ComputeEnginePool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComputeEnginePool.js","sources":["../../src/GCP/ComputeEnginePool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { GcpEnvironment } from "@alchemy.run/gcp/Environment";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { Database as ClusterDatabase } from "../Database.ts";
|
|
4
|
+
import type { Providers } from "../Providers.ts";
|
|
5
|
+
export declare const COS_IMAGE = "projects/cos-cloud/global/images/family/cos-stable";
|
|
6
|
+
export interface DatabaseProps {
|
|
7
|
+
/**
|
|
8
|
+
* Zone of the database instance.
|
|
9
|
+
* @default the region's `-a` zone
|
|
10
|
+
*/
|
|
11
|
+
zone?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Machine type of the database instance.
|
|
14
|
+
* @default "e2-small"
|
|
15
|
+
*/
|
|
16
|
+
machineType?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Size of the database's disk, in GB.
|
|
19
|
+
* @default 20
|
|
20
|
+
*/
|
|
21
|
+
diskSize?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Postgres image.
|
|
24
|
+
* @default "postgres:17-alpine"
|
|
25
|
+
*/
|
|
26
|
+
image?: string;
|
|
27
|
+
/**
|
|
28
|
+
* IPv4 range of the cluster's subnetwork.
|
|
29
|
+
* @default "10.47.0.0/20"
|
|
30
|
+
*/
|
|
31
|
+
cidrBlock?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The database an Effect Cluster coordinates through, on Compute Engine,
|
|
35
|
+
* as a `Cluster.Database` Layer: Postgres on an instance with a persistent
|
|
36
|
+
* disk, in a VPC of its own that the pool's runners join. Only the runners reach
|
|
37
|
+
* it; runners reach each other on the runner port.
|
|
38
|
+
*
|
|
39
|
+
* ### Running the database on GCP
|
|
40
|
+
* **Example:** Postgres on Compute Engine
|
|
41
|
+
* ```typescript
|
|
42
|
+
* export const Database = ClusterGCP.Database("Cluster");
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @primitive
|
|
46
|
+
*/
|
|
47
|
+
export declare const Database: (id: string, props?: DatabaseProps) => Layer.Layer<ClusterDatabase, never, GcpEnvironment | Providers | import("@alchemy.run/gcp/Providers").Providers | import("alchemy").Stack>;
|
|
48
|
+
//# sourceMappingURL=Database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Database.d.ts","sources":["../../src/GCP/Database.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAsB,QAAQ,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,eAAO,MAAM,SAAS,uDAAuD,CAAC;AAI9E,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,QAAQ,OAAQ,MAAM,UAAS,aAAa,+IAyFtD,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as Compute from "@alchemy.run/gcp/Compute";
|
|
2
|
+
import { GcpEnvironment } from "@alchemy.run/gcp/Environment";
|
|
3
|
+
import * as Output from "alchemy/Output";
|
|
4
|
+
import { Random } from "alchemy/Random";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Redacted from "effect/Redacted";
|
|
8
|
+
import { clusterShardGroups, Database as ClusterDatabase } from "../Database.js";
|
|
9
|
+
import { RUNNER_PORT } from "../internal/Gateway.js";
|
|
10
|
+
import { stackDnsLabel } from "../internal/Naming.js";
|
|
11
|
+
export const COS_IMAGE = "projects/cos-cloud/global/images/family/cos-stable";
|
|
12
|
+
const DISK_DEVICE = "cluster-data";
|
|
13
|
+
const MOUNT_PATH = "/mnt/disks/postgres";
|
|
14
|
+
const shellQuote = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
15
|
+
/** Mount the data disk, then run Postgres on the host network. */
|
|
16
|
+
const startupScript = (options) => `#!/bin/bash
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
DEVICE=/dev/disk/by-id/google-${DISK_DEVICE}
|
|
19
|
+
blkid "$DEVICE" >/dev/null || mkfs.ext4 -F "$DEVICE"
|
|
20
|
+
mkdir -p ${MOUNT_PATH}
|
|
21
|
+
mountpoint -q ${MOUNT_PATH} || mount "$DEVICE" ${MOUNT_PATH}
|
|
22
|
+
# Container-Optimized OS drops inbound traffic except SSH: open Postgres.
|
|
23
|
+
iptables -C INPUT -p tcp --dport 5432 -j ACCEPT 2>/dev/null || iptables -A INPUT -p tcp --dport 5432 -j ACCEPT
|
|
24
|
+
docker rm -f postgres >/dev/null 2>&1 || true
|
|
25
|
+
docker run -d --name postgres --restart always --network host \\
|
|
26
|
+
-v ${MOUNT_PATH}:/var/lib/postgresql/data \\
|
|
27
|
+
-e PGDATA=/var/lib/postgresql/data/pgdata \\
|
|
28
|
+
-e POSTGRES_DB=cluster \\
|
|
29
|
+
-e POSTGRES_PASSWORD=${shellQuote(options.password)} \\
|
|
30
|
+
${shellQuote(options.image)}
|
|
31
|
+
`;
|
|
32
|
+
/**
|
|
33
|
+
* The database an Effect Cluster coordinates through, on Compute Engine,
|
|
34
|
+
* as a `Cluster.Database` Layer: Postgres on an instance with a persistent
|
|
35
|
+
* disk, in a VPC of its own that the pool's runners join. Only the runners reach
|
|
36
|
+
* it; runners reach each other on the runner port.
|
|
37
|
+
*
|
|
38
|
+
* ### Running the database on GCP
|
|
39
|
+
* **Example:** Postgres on Compute Engine
|
|
40
|
+
* ```typescript
|
|
41
|
+
* export const Database = ClusterGCP.Database("Cluster");
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @primitive
|
|
45
|
+
*/
|
|
46
|
+
export const Database = (id, props = {}) => Layer.effect(ClusterDatabase, Effect.gen(function* () {
|
|
47
|
+
if (globalThis.__ALCHEMY_RUNTIME__) {
|
|
48
|
+
// Runners read their database from their own environment at runtime.
|
|
49
|
+
return { url: Redacted.make(""), shardGroups: undefined };
|
|
50
|
+
}
|
|
51
|
+
const region = yield* GcpEnvironment.current.pipe(Effect.map((env) => env.region), Effect.orDie);
|
|
52
|
+
const zone = props.zone ?? `${region}-a`;
|
|
53
|
+
const cidr = props.cidrBlock ?? "10.47.0.0/20";
|
|
54
|
+
const poolTag = yield* stackDnsLabel(`${id}-pool`, 63);
|
|
55
|
+
const databaseTag = yield* stackDnsLabel(`${id}-db`, 63);
|
|
56
|
+
const network = yield* Compute.Network(`${id}Network`, { autoCreateSubnetworks: false });
|
|
57
|
+
const subnet = yield* Compute.Subnetwork(`${id}Subnet`, {
|
|
58
|
+
network: network.networkName,
|
|
59
|
+
region,
|
|
60
|
+
ipCidrRange: cidr,
|
|
61
|
+
});
|
|
62
|
+
yield* Compute.Firewall(`${id}DatabaseFirewall`, {
|
|
63
|
+
network: network.networkName,
|
|
64
|
+
direction: "INGRESS",
|
|
65
|
+
allowed: [{ protocol: "tcp", ports: ["5432"] }],
|
|
66
|
+
sourceTags: [poolTag],
|
|
67
|
+
targetTags: [databaseTag],
|
|
68
|
+
});
|
|
69
|
+
// Runners send each other messages for the shards they own.
|
|
70
|
+
yield* Compute.Firewall(`${id}RunnerFirewall`, {
|
|
71
|
+
network: network.networkName,
|
|
72
|
+
direction: "INGRESS",
|
|
73
|
+
allowed: [{ protocol: "tcp", ports: [String(RUNNER_PORT)] }],
|
|
74
|
+
sourceTags: [poolTag],
|
|
75
|
+
targetTags: [poolTag],
|
|
76
|
+
});
|
|
77
|
+
const password = yield* Random(`${id}Password`, { bytes: 24 });
|
|
78
|
+
const disk = yield* Compute.Disk(`${id}Disk`, {
|
|
79
|
+
zone,
|
|
80
|
+
type: "pd-balanced",
|
|
81
|
+
sizeGb: props.diskSize ?? 20,
|
|
82
|
+
});
|
|
83
|
+
const instance = yield* Compute.Instance(`${id}Postgres`, {
|
|
84
|
+
zone,
|
|
85
|
+
machineType: props.machineType ?? "e2-small",
|
|
86
|
+
sourceImage: COS_IMAGE,
|
|
87
|
+
network: Output.interpolate `global/networks/${network.networkName}`,
|
|
88
|
+
subnetwork: Output.interpolate `regions/${region}/subnetworks/${subnet.subnetworkName}`,
|
|
89
|
+
// Pulls the Postgres image.
|
|
90
|
+
associatePublicIp: true,
|
|
91
|
+
tags: [databaseTag],
|
|
92
|
+
attachedDisks: [
|
|
93
|
+
{ source: disk.selfLink, deviceName: DISK_DEVICE },
|
|
94
|
+
],
|
|
95
|
+
metadata: {
|
|
96
|
+
"startup-script": password.text.pipe(Output.map((text) => startupScript({
|
|
97
|
+
image: props.image ?? "postgres:17-alpine",
|
|
98
|
+
password: Redacted.value(text),
|
|
99
|
+
}))),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
url: Output.all(instance.networkIP, password.text).pipe(Output.map(([ip, text]) => Redacted.make(`postgres://postgres:${encodeURIComponent(text.pipe(Redacted.value))}@${ip}:5432/cluster`))),
|
|
104
|
+
network: {
|
|
105
|
+
kind: "gcp",
|
|
106
|
+
network: network.networkName,
|
|
107
|
+
subnetwork: subnet.subnetworkName,
|
|
108
|
+
region,
|
|
109
|
+
cidr,
|
|
110
|
+
tag: poolTag,
|
|
111
|
+
},
|
|
112
|
+
shardGroups: yield* clusterShardGroups(id),
|
|
113
|
+
};
|
|
114
|
+
}));
|
|
115
|
+
//# sourceMappingURL=Database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Database.js","sources":["../../src/GCP/Database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sources":["../../src/GCP/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC"}
|
package/lib/GCP/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/GCP/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as Hetzner from "@alchemy.run/hetzner";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { Database as ClusterDatabase } from "../Database.ts";
|
|
4
|
+
import type { Providers } from "../Providers.ts";
|
|
5
|
+
export interface DatabaseProps {
|
|
6
|
+
/**
|
|
7
|
+
* Hetzner location of the database and the cluster's Network.
|
|
8
|
+
* @default "nbg1"
|
|
9
|
+
*/
|
|
10
|
+
location?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Server type of the database.
|
|
13
|
+
* @default "cx23"
|
|
14
|
+
*/
|
|
15
|
+
serverType?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Size of the database's Volume, in GB (at least 10).
|
|
18
|
+
* @default 10
|
|
19
|
+
*/
|
|
20
|
+
diskSize?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Postgres image.
|
|
23
|
+
* @default "postgres:17-alpine"
|
|
24
|
+
*/
|
|
25
|
+
image?: string;
|
|
26
|
+
/**
|
|
27
|
+
* IPv4 range of the cluster's private Network.
|
|
28
|
+
* @default "10.46.0.0/16"
|
|
29
|
+
*/
|
|
30
|
+
ipRange?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The database an Effect Cluster coordinates through, on Hetzner Cloud, as
|
|
34
|
+
* a `Cluster.Database` Layer: Postgres on a Server with a Volume, on a
|
|
35
|
+
* private Network that the pool's Servers attach to. A firewall drops every
|
|
36
|
+
* inbound connection on the Server's public address; runners reach it
|
|
37
|
+
* over the Network.
|
|
38
|
+
*
|
|
39
|
+
* ### Running the database on Hetzner
|
|
40
|
+
* **Example:** Postgres in Nuremberg
|
|
41
|
+
* ```typescript
|
|
42
|
+
* export const Database = ClusterHetzner.Database("Cluster");
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @primitive
|
|
46
|
+
*/
|
|
47
|
+
export declare const Database: (id: string, props?: DatabaseProps) => Layer.Layer<ClusterDatabase, never, Providers | Hetzner.Providers>;
|
|
48
|
+
//# sourceMappingURL=Database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Database.d.ts","sources":["../../src/Hetzner/Database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,sBAAsB,CAAC;AAIhD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAsB,QAAQ,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiCD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,OAAQ,MAAM,UAAS,aAAa,uEA0DtD,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as Hetzner from "@alchemy.run/hetzner";
|
|
2
|
+
import * as Output from "alchemy/Output";
|
|
3
|
+
import { Random } from "alchemy/Random";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import * as Redacted from "effect/Redacted";
|
|
7
|
+
import { clusterShardGroups, Database as ClusterDatabase } from "../Database.js";
|
|
8
|
+
/** The network zone a location belongs to. */
|
|
9
|
+
const networkZoneOf = (location) => location.startsWith("ash")
|
|
10
|
+
? "us-east"
|
|
11
|
+
: location.startsWith("hil")
|
|
12
|
+
? "us-west"
|
|
13
|
+
: location.startsWith("sin")
|
|
14
|
+
? "ap-southeast"
|
|
15
|
+
: "eu-central";
|
|
16
|
+
const shellQuote = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
17
|
+
/** Mount the Volume and run Postgres on it, on the Server's own network. */
|
|
18
|
+
const userData = (options) => `#!/bin/bash
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
DEVICE=/dev/disk/by-id/scsi-0HC_Volume_${options.volumeId}
|
|
21
|
+
for _ in $(seq 1 60); do [ -e "$DEVICE" ] && break; sleep 2; done
|
|
22
|
+
blkid "$DEVICE" >/dev/null || mkfs.ext4 -F "$DEVICE"
|
|
23
|
+
mkdir -p /mnt/postgres
|
|
24
|
+
grep -q " /mnt/postgres " /etc/fstab || echo "$DEVICE /mnt/postgres ext4 discard,nofail,defaults 0 0" >> /etc/fstab
|
|
25
|
+
mountpoint -q /mnt/postgres || mount /mnt/postgres
|
|
26
|
+
until docker info >/dev/null 2>&1; do sleep 2; done
|
|
27
|
+
docker rm -f postgres >/dev/null 2>&1 || true
|
|
28
|
+
docker run -d --name postgres --restart always --network host \\
|
|
29
|
+
-v /mnt/postgres:/var/lib/postgresql/data \\
|
|
30
|
+
-e PGDATA=/var/lib/postgresql/data/pgdata \\
|
|
31
|
+
-e POSTGRES_DB=cluster \\
|
|
32
|
+
-e POSTGRES_PASSWORD=${shellQuote(options.password)} \\
|
|
33
|
+
${shellQuote(options.image)}
|
|
34
|
+
`;
|
|
35
|
+
/**
|
|
36
|
+
* The database an Effect Cluster coordinates through, on Hetzner Cloud, as
|
|
37
|
+
* a `Cluster.Database` Layer: Postgres on a Server with a Volume, on a
|
|
38
|
+
* private Network that the pool's Servers attach to. A firewall drops every
|
|
39
|
+
* inbound connection on the Server's public address; runners reach it
|
|
40
|
+
* over the Network.
|
|
41
|
+
*
|
|
42
|
+
* ### Running the database on Hetzner
|
|
43
|
+
* **Example:** Postgres in Nuremberg
|
|
44
|
+
* ```typescript
|
|
45
|
+
* export const Database = ClusterHetzner.Database("Cluster");
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @primitive
|
|
49
|
+
*/
|
|
50
|
+
export const Database = (id, props = {}) => Layer.effect(ClusterDatabase, Effect.gen(function* () {
|
|
51
|
+
if (globalThis.__ALCHEMY_RUNTIME__) {
|
|
52
|
+
// Runners read their database from their own environment at runtime.
|
|
53
|
+
return { url: Redacted.make(""), shardGroups: undefined };
|
|
54
|
+
}
|
|
55
|
+
const location = props.location ?? "nbg1";
|
|
56
|
+
const ipRange = props.ipRange ?? "10.46.0.0/16";
|
|
57
|
+
const password = yield* Random(`${id}Password`, { bytes: 24 });
|
|
58
|
+
const network = yield* Hetzner.Network(`${id}Network`, {
|
|
59
|
+
ipRange,
|
|
60
|
+
subnets: [{ type: "cloud", networkZone: networkZoneOf(location) }],
|
|
61
|
+
});
|
|
62
|
+
// No inbound rules: only the Network reaches Postgres.
|
|
63
|
+
const firewall = yield* Hetzner.Firewall(`${id}Firewall`, {});
|
|
64
|
+
const volume = yield* Hetzner.Volume(`${id}Data`, {
|
|
65
|
+
size: props.diskSize ?? 10,
|
|
66
|
+
format: "ext4",
|
|
67
|
+
location,
|
|
68
|
+
});
|
|
69
|
+
const server = yield* Hetzner.Server(`${id}Postgres`, {
|
|
70
|
+
serverType: props.serverType ?? "cx23",
|
|
71
|
+
// Hetzner's Ubuntu image with Docker preinstalled.
|
|
72
|
+
image: "docker-ce",
|
|
73
|
+
location,
|
|
74
|
+
// IPv4 pulls the Postgres image; IPv6 would only spend an address.
|
|
75
|
+
enableIpv6: false,
|
|
76
|
+
networks: [network],
|
|
77
|
+
firewalls: [firewall],
|
|
78
|
+
volumes: [volume],
|
|
79
|
+
userData: Output.all(volume.id, password.text).pipe(Output.map(([volumeId, text]) => userData({
|
|
80
|
+
volumeId,
|
|
81
|
+
image: props.image ?? "postgres:17-alpine",
|
|
82
|
+
password: Redacted.value(text),
|
|
83
|
+
}))),
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
url: Output.all(server.privateIps, password.text).pipe(Output.map(([ips, text]) => Redacted.make(`postgres://postgres:${encodeURIComponent(text.pipe(Redacted.value))}@${ips[0]}:5432/cluster`))),
|
|
87
|
+
network: { kind: "hetzner", networkId: network.networkId, ipRange, location },
|
|
88
|
+
shardGroups: yield* clusterShardGroups(id),
|
|
89
|
+
};
|
|
90
|
+
}));
|
|
91
|
+
//# sourceMappingURL=Database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Database.js","sources":["../../src/Hetzner/Database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as Hetzner from "@alchemy.run/hetzner";
|
|
2
|
+
import { type PoolSettings } from "../internal/PoolHost.ts";
|
|
3
|
+
export interface ServerPoolOptions extends PoolSettings {
|
|
4
|
+
/**
|
|
5
|
+
* Number of runners, one Server each.
|
|
6
|
+
* @default 2
|
|
7
|
+
*/
|
|
8
|
+
replicas?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Server type of each runner.
|
|
11
|
+
* @default "cx23"
|
|
12
|
+
*/
|
|
13
|
+
serverType?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run a pool of Effect Cluster runners on Hetzner Cloud: Servers on the
|
|
17
|
+
* database's private Network, each running every class given these
|
|
18
|
+
* runners as a `Hetzner.Service`, behind a Load Balancer that serves their
|
|
19
|
+
* gateway to callers anywhere. Runners talk to each other and the database
|
|
20
|
+
* over the Network; their firewall only lets SSH in, which deploys them.
|
|
21
|
+
* Requires a database on a Hetzner Network — `ClusterHetzner.Database`.
|
|
22
|
+
*
|
|
23
|
+
* ### Running on Hetzner
|
|
24
|
+
* **Example:** Two runners
|
|
25
|
+
* ```typescript
|
|
26
|
+
* export const Database = ClusterHetzner.Database("Cluster");
|
|
27
|
+
* export const Default = ClusterHetzner.ServerPool("Default", { replicas: 2 }).pipe(
|
|
28
|
+
* Layer.provide(Database),
|
|
29
|
+
* );
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @primitive
|
|
33
|
+
*/
|
|
34
|
+
export declare const ServerPool: (id: string, options?: ServerPoolOptions) => import("effect/Layer").Layer<import("../internal/PoolHostService.ts").PoolHost, never, import("../Database.ts").Database | import("../Providers.ts").Providers | Hetzner.Providers | import("alchemy").Stack>;
|
|
35
|
+
//# sourceMappingURL=ServerPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ServerPool.d.ts","sources":["../../src/Hetzner/ServerPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,sBAAsB,CAAC;AAIhD,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,OAAQ,MAAM,YAAW,iBAAiB,kNA0E7D,CAAC"}
|