Repository navigation
Expand file tree
/
Copy pathcompose.yaml
More file actions
228 lines (216 loc) · 7.97 KB
/
Copy pathcompose.yaml
File metadata and controls
228 lines (216 loc) · 7.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# The hosted service for local development and tests, with no cloud account and no provider
# calls (ADR 0011, D15): Postgres, a Cloud Storage emulator, Keycloak, the API in owner and in
# sign-in mode, the page fetcher and one worker. Start it with `pnpm server:up`; see
# docs/operations/local-server-stack.md. Every credential here is a fixed local-only value; a
# deployment takes its own from its secrets.
name: draft-loop-server
# What the API and the worker share: the runtime role, the tenant, the emulated bucket and the
# limits per run.
x-hosted-environment: &hosted-environment
DRAFT_LOOP_DATABASE_URL: postgres://draft_loop_app:draft_loop_app@postgres:5432/postgres
DRAFT_LOOP_TENANT_ID: local
DRAFT_LOOP_BLOB_BUCKET: draft-loop-local
DRAFT_LOOP_GCS_ENDPOINT: http://gcs:4443
DRAFT_LOOP_RUN_MAX_ROUNDS: "5"
DRAFT_LOOP_RUN_MAX_COST_USD: "3"
DRAFT_LOOP_RUN_MAX_DURATION_MS: "1800000"
services:
postgres:
# The same image as the CI `Server storage` job.
image: pgvector/pgvector:0.8.0-pg16
environment:
POSTGRES_PASSWORD: postgres
ports:
- "127.0.0.1:55432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 2s
timeout: 5s
retries: 30
gcs:
# A Cloud Storage emulator, the same version as the CI `Server storage` job.
image: fsouza/fake-gcs-server:1.56.1
command: ["-scheme", "http", "-port", "4443", "-backend", "filesystem", "-filesystem-root", "/storage"]
ports:
- "127.0.0.1:4443:4443"
volumes:
- gcs-data:/storage
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:4443/_internal/healthcheck"]
interval: 2s
timeout: 5s
retries: 30
keycloak:
# The identity provider for sign-in on the local stack, in development mode, with a realm of
# fictional users imported at start (compose/keycloak). A deployment uses a hosted Keycloak.
image: keycloak/keycloak:26.7.5
command: ["start-dev", "--import-realm"]
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: local-stack-keycloak-admin-not-a-secret
# Tokens name the address the browser and the smoke use; the API reaches the keys inside
# the Compose network.
KC_HOSTNAME: http://127.0.0.1:58081
KC_HOSTNAME_BACKCHANNEL_DYNAMIC: "true"
KC_HEALTH_ENABLED: "true"
volumes:
- ./compose/keycloak:/opt/keycloak/data/import:ro
ports:
- "127.0.0.1:58081:8080"
healthcheck:
# The image has no curl or wget; bash's /dev/tcp reaches the management port's health check.
test:
- CMD-SHELL
- >-
exec 3<>/dev/tcp/127.0.0.1/9000 && printf 'GET /health/ready HTTP/1.0\r\n\r\n' >&3 &&
grep -q '"UP"' <&3
interval: 5s
timeout: 5s
retries: 60
# Local-only setup a deployment's infrastructure does instead: the runtime role, then the bucket.
postgres-setup:
image: pgvector/pgvector:0.8.0-pg16
depends_on:
postgres:
condition: service_healthy
environment:
PGPASSWORD: postgres
command:
- psql
- --host=postgres
- --username=postgres
- --set=ON_ERROR_STOP=1
- --command=DO $$$$ BEGIN CREATE ROLE draft_loop_app LOGIN PASSWORD 'draft_loop_app'; EXCEPTION WHEN duplicate_object THEN NULL; END $$$$;
restart: "no"
gcs-setup:
# The worker's image, which the worker service builds.
image: draft-loop-worker:local
pull_policy: never
depends_on:
gcs:
condition: service_healthy
# Creates the bucket; the emulator answers 409 when it already exists.
command:
- node
- --eval
- >-
fetch("http://gcs:4443/storage/v1/b?project=draft-loop-local", {method: "POST",
headers: {"content-type": "application/json"}, body: JSON.stringify({name: "draft-loop-local"})})
.then((response) => process.exit(response.ok || response.status === 409 ? 0 : 1))
restart: "no"
migrate:
image: draft-loop-worker:local
pull_policy: never
depends_on:
postgres-setup:
condition: service_completed_successfully
command: ["/app/node_modules/.bin/tsx", "src/migrate.ts"]
environment:
DRAFT_LOOP_MIGRATION_DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
DRAFT_LOOP_RUNTIME_ROLE: draft_loop_app
restart: "no"
# Fetches approved job pages for the API, as in a deployment, where the API has no route to the
# internet. It holds no database and no secret; here it needs no identity token.
page-fetcher:
build:
context: .
dockerfile: apps/page-fetcher/Dockerfile
image: draft-loop-page-fetcher:local
healthcheck:
test:
- CMD
- node
- --eval
- 'fetch("http://127.0.0.1:8080/healthz").then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))'
interval: 2s
timeout: 5s
retries: 30
api:
build:
context: .
dockerfile: apps/api/Dockerfile
image: draft-loop-api:local
depends_on:
migrate:
condition: service_completed_successfully
gcs-setup:
condition: service_completed_successfully
page-fetcher:
condition: service_healthy
environment:
<<: *hosted-environment
DRAFT_LOOP_API_OWNER_TOKEN: local-stack-owner-token-not-a-secret
DRAFT_LOOP_PAGE_FETCHER_URL: http://page-fetcher:8080
ports:
- "127.0.0.1:58080:8080"
healthcheck:
test:
- CMD
- node
- --eval
- 'fetch("http://127.0.0.1:8080/v1/health").then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))'
interval: 2s
timeout: 5s
retries: 30
# The same API in sign-in mode: members sign in through the local Keycloak and act in their own
# organisation; the owner token is the operator's credential for creating organisations.
api-oidc:
image: draft-loop-api:local
pull_policy: never
depends_on:
api:
condition: service_healthy
keycloak:
condition: service_healthy
environment:
<<: *hosted-environment
DRAFT_LOOP_API_OWNER_TOKEN: local-stack-owner-token-not-a-secret
DRAFT_LOOP_PAGE_FETCHER_URL: http://page-fetcher:8080
DRAFT_LOOP_AUTH_MODE: oidc
DRAFT_LOOP_OIDC_ISSUER: http://127.0.0.1:58081/realms/draft-loop-local
DRAFT_LOOP_OIDC_ALLOW_HTTP: "1"
DRAFT_LOOP_OIDC_AUDIENCE: draft-loop-api
DRAFT_LOOP_OIDC_WEB_CLIENT_ID: draft-loop-web
# Tokens name the browser's address for Keycloak; the API fetches the keys inside the network.
DRAFT_LOOP_OIDC_JWKS_URL: http://keycloak:8080/realms/draft-loop-local/protocol/openid-connect/certs
ports:
- "127.0.0.1:58082:8080"
healthcheck:
test:
- CMD
- node
- --eval
- 'fetch("http://127.0.0.1:8080/v1/health").then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))'
interval: 2s
timeout: 5s
retries: 30
worker:
build:
context: .
dockerfile: apps/worker/Dockerfile
image: draft-loop-worker:local
depends_on:
migrate:
condition: service_completed_successfully
gcs-setup:
condition: service_completed_successfully
environment:
<<: *hosted-environment
DRAFT_LOOP_WORKER_READY_FILE: /tmp/draft-loop-worker.ready
# A worker started with DRAFT_LOOP_AUTH_MODE=oidc always checks consent, and refuses to start
# with this set to false. This one worker also serves the owner-mode API, whose runs carry no
# consent, so it runs in owner mode with the check off; api-oidc still refuses runs without
# consent, but a withdrawal is not re-checked at claim on this stack.
DRAFT_LOOP_REQUIRE_CONSENT: "false"
# Cloud Run sends SIGTERM and waits 10 seconds; the worker gives its job 8.
stop_grace_period: 10s
healthcheck:
test: ["CMD", "test", "-f", "/tmp/draft-loop-worker.ready"]
interval: 2s
timeout: 5s
retries: 30
volumes:
postgres-data:
gcs-data: