-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.test.yml
More file actions
452 lines (440 loc) · 18.5 KB
/
Copy pathdocker-compose.test.yml
File metadata and controls
452 lines (440 loc) · 18.5 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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
services:
postgres:
image: postgres:18.6-alpine3.24
shm_size: "1gb"
# The test suite creates a per-test schema + connection pool and applies
# the full migration set concurrently; stock max_connections=100 is below
# the suite's measured peak (~104), producing pool-acquire timeout flakes
# (MC-10 close-out evidence). 500 aligns capacity with the real profile.
command: ["postgres", "-c", "max_connections=500"]
environment:
POSTGRES_USER: mega2
POSTGRES_PASSWORD: mega2_test_password
POSTGRES_DB: mega2
ports:
- "127.0.0.1:15432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mega2 -d mega2"]
interval: 2s
timeout: 5s
retries: 20
redis:
image: redis:8.10.1-alpine3.23
ports:
- "127.0.0.1:16379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 2s
timeout: 5s
retries: 20
# S3-compatible object storage for IT smoke (RustFS). Public test credentials
# are `rustfs` / `rustfs_secret` (see `.env.test.example`).
rustfs:
image: rustfs/rustfs:1.0.0@sha256:8cc9801755448b71a786705ce76692c77e14936cccd87cf2fc31842e58f4d1ff
environment:
RUSTFS_VOLUMES: /data
RUSTFS_ADDRESS: 0.0.0.0:9000
RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001
RUSTFS_CONSOLE_ENABLE: "true"
RUSTFS_ACCESS_KEY: rustfs
RUSTFS_SECRET_KEY: rustfs_secret
# Single-volume local smoke; required when not using multi-disk topology.
RUSTFS_UNSAFE_BYPASS_DISK_CHECK: "true"
ports:
- "127.0.0.1:19000:9000"
- "127.0.0.1:19001:9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/health"]
interval: 2s
timeout: 5s
retries: 30
start_period: 10s
# Bucket initialization via RustFS CLI (`rc`). Stays up (sleep infinity) with
# a healthcheck so default `up -d --wait` can wait on it — a pure one-shot
# exit breaks --wait. Recreate this service (or `down` + `up`) after wiping
# rustfs if the bucket disappears; `mb --ignore-existing` is idempotent on
# fresh starts. `rc` is the official S3 client; MinIO `mc` is not required.
rustfs-init:
image: rustfs/rc:v0.1.36@sha256:ab024bfebee49a750ce886b4c70963ccd9ddaa03f491704a90710641d7a26699
depends_on:
rustfs:
condition: service_healthy
entrypoint: >
/bin/sh -c "
/usr/bin/rc alias set local http://rustfs:9000 rustfs rustfs_secret &&
/usr/bin/rc mb --ignore-existing local/mega2 &&
/usr/bin/rc anonymous set download local/mega2 &&
/usr/bin/rc mb --ignore-existing local/monoui &&
/usr/bin/rc anonymous set download local/monoui &&
exec sleep infinity
"
healthcheck:
test: ["CMD-SHELL", "/usr/bin/rc ls local/mega2 && /usr/bin/rc ls local/monoui"]
interval: 2s
timeout: 5s
retries: 20
start_period: 3s
# Fixed-version Git CLI runner for cargo-native black-box tests (ADR-IT-01).
# Joins networks.default and reaches per-case mega2 on the host via
# host.docker.internal (extra_hosts below). Cross-platform on Linux and macOS
# Docker Desktop; harness maps remote URLs accordingly.
#
# Prefer pre-creating the shared workdir as the test UID before `up`
# (`mkdir -p … && chmod 1777 …`). If the path is missing, Docker's bind
# auto-create may make it root:root and the unprivileged cargo process will
# then hit EACCES allocating per-case subdirs — CI/startup scripts should
# always mkdir first (IT-04 will wire this into workflows).
#
# Run as a fixed numeric UID/GID so Compose can resolve `user:` without
# relying on Bash's unexported `$UID`. Default 1000:1000 matches common
# CI runners (ubuntu-latest) and local desktop users; override with
# `export MEGA2_IT_GIT_UID=$(id -u) MEGA2_IT_GIT_GID=$(id -g)`
# when the test process is not 1000.
git-cli:
image: mega2-git-cli:3.7.1
build:
context: ..
dockerfile: Dockerfile.git-cli
# Opt-in profile: keeping git-cli off the default `up -d --wait` set avoids
# pulling the runner into data-plane-only dev loops. Enable with
# `--profile git` for integration_git_cli / cargo test --all IT gates.
profiles: ["git"]
# Reap orphaned git/ssh children from the docker-exec timeout wrapper
# (setsid + kill). Without init, PID 1 is `sleep infinity` and zombies
# accumulate until later SSH clones fail with protocol `error: …`.
init: true
networks:
- default
extra_hosts:
- "host.docker.internal:host-gateway"
user: "${MEGA2_IT_GIT_UID:-1000}:${MEGA2_IT_GIT_GID:-1000}"
volumes:
- ${MEGA2_IT_GIT_WORKDIR:-/tmp/mega2-git}:/work
working_dir: /work
# Override the image's `git` entrypoint so the container stays up for `exec`.
entrypoint: ["sleep", "infinity"]
healthcheck:
test: ["CMD-SHELL", "git --version >/dev/null && git lfs version | grep -q '^git-lfs/3[.]7[.]1 '"]
interval: 2s
timeout: 5s
retries: 20
# Linked git protocol smoke runner (profile `smoke`).
#
# Unlike `git-cli` (bridge + host.docker.internal, for the cargo-native harness
# that spawns its own service on the host), this service talks
# to the compose-hosted `mega2` over the internal bridge
# (http://mega2:8000/). It runs `scripts/git_protocol_smoke.sh` against
# the linked stack: ls-remote / clone / fetch / push / tag-reject / LFS.
#
# It depends on `mega2` (profile `app`) being up, so bring the whole
# linked stack up together:
# docker compose -p mega2-it -f docker/docker-compose.test.yml \
# --profile app --profile smoke up -d --wait
#
# Then drive the smoke from the host:
# docker compose -p mega2-it -f docker/docker-compose.test.yml \
# --profile smoke exec -T git-smoke \
# bash scripts/git_protocol_smoke.sh
# (with MEGA2_HTTP_REPO_URL=http://mega2:8000/ and a seeded token).
#
# NOTE: git-smoke lives in its own `smoke` profile (not `git`) so that
# `--profile git` invocations (git-cli + data plane) never activate it. Its
# `depends_on: mega2` (profile `app`) would otherwise make the compose
# project invalid whenever the `git` profile is active but `app` is not
# ("service git-smoke depends on undefined service mega2").
git-smoke:
image: mega2-git-smoke:3.7.1
build:
context: ..
dockerfile: Dockerfile.git-smoke
profiles: ["smoke"]
init: true
depends_on:
mega2:
condition: service_healthy
environment:
# Default repo URL points at the linked compose-hosted mega2.
MEGA2_HTTP_REPO_URL: http://mega2:8000/
# Optional SSH URL (mega2 SSH service, if enabled in the stack).
# MEGA2_SSH_REPO_URL: ssh://git@mega2:2222/
# Push/tag/LFS smoke are opt-in via these flags.
MEGA2_GIT_SMOKE_PUSH: "1"
MEGA2_GIT_SMOKE_LFS: "0"
# Seed token for receive-pack Basic Auth (overridden by the exec command).
MEGA2_IT_SEED_TOKEN: mega2-it-git-smoke-token-0001
volumes:
- ${MEGA2_IT_GIT_WORKDIR:-/tmp/mega2-git}:/work
# Mount the mega2 repo so the smoke script is available inside the
# linked container (scripts/git_protocol_smoke.sh).
- ..:/repo:ro
working_dir: /work
healthcheck:
test: ["CMD-SHELL", "git --version >/dev/null && git lfs version | grep -q '^git-lfs/3[.]7[.]1 '"]
interval: 2s
timeout: 5s
retries: 20
# Standing mega2 HTTP service for stack-level IT (profile `app`).
# Per-case cargo black-box tests still spawn via CARGO_BIN_EXE for isolation;
# this service is the compose-hosted process under test for stack smoke.
# Build context is the parent of `mega2/` + `orbit/` (see Dockerfile).
# CI may pre-tag `mega2:local` from Dockerfile.it-runtime instead of
# rebuilding from source (`pull_policy: never`).
mega2:
profiles: ["app"]
image: mega2:local
pull_policy: never
build:
context: ..
dockerfile: Dockerfile
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
MEGA_BASE_DIR: /var/lib/mega2
MEGA_CONFIG: /etc/mega2/config.toml
MEGA_DATABASE__DB_TYPE: postgres
MEGA_DATABASE__DB_URL: postgres://mega2:mega2_test_password@postgres:5432/mega2
MEGA_REDIS__URL: redis://redis:6379
# Keep the full documented IT CORS allowlist when overriding the sample
# config, and add the website-next host origin for stack session tests.
MEGA_OAUTH__ALLOWED_CORS_ORIGINS: http://local.gitmega.com,https://app.gitmega.com,http://app.gitmono.test,http://127.0.0.1:17001
MEGA_OAUTH__WEBSITE_API_BASE_URL: http://website-next:7001
MEGA_OBJECT_STORAGE__STORAGE_TYPE: local
MEGA_OBJECT_STORAGE__LOCAL__ROOT_DIR: /var/lib/mega2/objects
MEGA_LOG__PRINT_STD: "true"
MEGA_LOG__LEVEL: info
volumes:
- mega2-data:/var/lib/mega2
ports:
- "127.0.0.1:19180:8000"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/api/openapi.json"]
interval: 3s
timeout: 5s
retries: 40
start_period: 20s
# ScorpioFS FUSE workspace daemon linked to the compose-hosted `mega2`
# (profile `scorpio`; registered in docs/refactoring/test-infra.md).
#
# Built from the sibling checkout `../scorpiofs` (same convention as
# `../megaui`). It reads trees/blobs from http://mega2:8000 and mounts the
# monorepo under /var/lib/scorpiofs/mount. FUSE in a container needs the
# host's /dev/fuse plus CAP_SYS_ADMIN (and a relaxed AppArmor profile on hosts
# that enforce one); rootless Docker cannot run it.
#
# The FUSE mounts are made visible on the HOST: the workspace and the Antares
# mount root are bind-mounted from ${MEGA2_IT_SCORPIO_WORKDIR:-/tmp/mega2-scorpiofs}
# with `rshared` propagation, so a mount performed inside the container shows
# up at <workdir>/mount and <workdir>/antares/<mount_id> on the host (ScorpioFS
# mounts with allow_other, so the unprivileged host user can browse it). This
# needs the host source to sit on a `shared` mount (systemd hosts: yes) and a
# rootful Docker whose daemon shares the host mount namespace. Pre-create the
# workdir as the test user before `up` (dev-test.sh does) so Docker does not
# auto-create it root-owned; ScorpioFS refuses a non-empty mountpoint.
#
# The mountpoints are moved to /mnt/scorpiofs/* via SCORPIO_WORKSPACE /
# SCORPIO_ANTARES_MOUNT_ROOT on purpose: nesting rshared binds inside the
# `scorpiofs-data` named volume propagated copies of them into the host at the
# volume's _data path, where they outlived the container and blocked
# `docker volume rm` (observed during bring-up). Store / state / Antares
# upper+cl layers stay in the volume at the image defaults.
#
# Lives in its own profile because it depends on `mega2` (profile `app`):
# docker compose -p mega2-it -f docker/docker-compose.test.yml \
# --profile app --profile scorpio up -d --wait
# bash scripts/scorpiofs_smoke.sh # or: ./scripts/dev-test.sh scorpio-smoke
# ls /tmp/mega2-scorpiofs/mount # the monorepo, on the host
scorpiofs:
profiles: ["scorpio"]
image: scorpiofs:local
pull_policy: never
build:
context: ../../scorpiofs
depends_on:
mega2:
condition: service_healthy
environment:
# Required by the image entrypoint; point at the linked mega2 service.
SCORPIO_BASE_URL: http://mega2:8000
SCORPIO_LFS_URL: http://mega2:8000/api/v1/lfs
# Any scorpio.toml key can be overridden as SCORPIO_<KEY>; e.g. use
# `scorpio=debug` while debugging the mega2 <-> ScorpioFS API contract.
SCORPIO_LOG_LEVEL: ${MEGA2_IT_SCORPIO_LOG_LEVEL:-info}
# FUSE mountpoints outside the named volume (see the service comment).
SCORPIO_WORKSPACE: /mnt/scorpiofs/mount
SCORPIO_ANTARES_MOUNT_ROOT: /mnt/scorpiofs/antares
devices:
- /dev/fuse
cap_add:
# SYS_ADMIN: mount(2) for FUSE. DAC_READ_SEARCH: open_by_handle_at(2) used by
# the passthrough overlay layer; without it ScorpioFS logs an ERROR per mount
# and falls back to fd-backed inodes (works, but hides the real code path).
- SYS_ADMIN
- DAC_READ_SEARCH
security_opt:
- apparmor:unconfined
volumes:
# Store, runtime state file and Antares upper/cl layers.
- scorpiofs-data:/var/lib/scorpiofs
# Host-visible FUSE mountpoints (see the service comment above). Both
# sources must exist and be EMPTY before `up`.
- type: bind
source: ${MEGA2_IT_SCORPIO_WORKDIR:-/tmp/mega2-scorpiofs}/mount
target: /mnt/scorpiofs/mount
bind:
propagation: rshared
- type: bind
source: ${MEGA2_IT_SCORPIO_WORKDIR:-/tmp/mega2-scorpiofs}/antares
target: /mnt/scorpiofs/antares
bind:
propagation: rshared
ports:
# The ScorpioFS HTTP API is unauthenticated: loopback + high port only.
- "127.0.0.1:12725:2725"
# The daemon unmounts on SIGTERM (up to ~20s daemon join + 15s Antares
# cleanup). Give it that budget: a SIGKILL would leave the host-visible
# mountpoints as stale "Transport endpoint is not connected" FUSE mounts.
stop_grace_period: 45s
healthcheck:
# `serve` mounts the FUSE workspace before binding HTTP, so a healthy
# probe also proves the FUSE mount succeeded (and, via rshared, that it
# is visible on the host).
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:2725/health"]
interval: 3s
timeout: 5s
retries: 40
start_period: 20s
# One-shot PostgreSQL schema initialization for the website front end
# (megaui apps/web). Uses the megaui Dockerfile `db-init` target which
# carries drizzle-kit. Account data lives in an isolated `website` database
# on the shared postgres service — never the mega2 business DB (ADR-WA-07).
website-db-init:
profiles: ["web"]
build:
context: ../../megaui
dockerfile: apps/web/Dockerfile
target: db-init
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://mega2:mega2_test_password@postgres:5432/website
# Connects to the maintenance DB only to CREATE DATABASE website.
DATABASE_ADMIN_URL: postgresql://mega2:mega2_test_password@postgres:5432/postgres
working_dir: /app
command:
- /bin/sh
- -ec
- |
node -e "
const { createRequire } = require('module');
const requireFromDb = createRequire('/app/packages/database/package.json');
const { Client } = requireFromDb('pg');
(async () => {
const admin = new Client({ connectionString: process.env.DATABASE_ADMIN_URL });
await admin.connect();
const { rowCount } = await admin.query(
\"SELECT 1 FROM pg_database WHERE datname = 'website'\"
);
if (rowCount === 0) {
await admin.query('CREATE DATABASE website');
console.log('created database website');
} else {
console.log('database website already exists');
}
await admin.end();
})().catch((err) => {
console.error(err);
process.exit(1);
});
"
cd /app/packages/database && pnpm exec drizzle-kit migrate
# Hocuspocus collab server (megaui apps/collab-server). Browser WS on host
# 17002; internal HTTP for website-next bridge on the compose network.
megaui-collab:
profiles: ["web"]
image: megaui-collab:local
pull_policy: never
build:
context: ../../megaui
dockerfile: apps/collab-server/Dockerfile
environment:
MEGA_COLLAB_PORT: "7002"
MEGA_COLLAB_BIND: "0.0.0.0"
MEGA_COLLAB_QUIET: "1"
MEGA_COLLAB_INTERNAL_TOKEN: mega2-it-collab-token-0001
# IT-only: authenticated tickets may join/edit until a web authz adapter
# is wired; production must leave this unset (fail-closed deny-all).
MEGA_COLLAB_DEV_ALLOW_AUTHENTICATED: "1"
ports:
- "127.0.0.1:17002:7002"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:7002/health"]
interval: 3s
timeout: 5s
retries: 40
start_period: 10s
# website Better Auth + Next app (built from sibling megaui checkout).
# Uses isolated Postgres database `website` on the shared postgres service
# (not mega2's business DB). CI must check out ../megaui too.
website-next:
profiles: ["web"]
image: website-next:local
pull_policy: never
build:
context: ../../megaui
dockerfile: apps/web/Dockerfile
target: runner
args:
NEXT_PUBLIC_MEGA_COLLAB_PUBLIC_URL: ws://127.0.0.1:17002
environment:
DATABASE_URL: postgresql://mega2:mega2_test_password@postgres:5432/website
APP_BASE_URL: http://127.0.0.1:17001
BETTER_AUTH_URL: http://127.0.0.1:17001
BETTER_AUTH_TRUSTED_ORIGINS: "http://localhost:17001,http://127.0.0.1:17001"
BETTER_AUTH_SECRET: mega2-it-website-auth-secret-0001
# Consumed by DEP-06's internal notification route. This public value is
# only for the isolated Compose integration stack.
MEGA2_INTERNAL_MAIL_BEARER: mega2-it-website-mail-bearer-0001
# WE-05/WE-06: IT product mail uses the in-memory test provider (no cloud).
EMAIL_PROVIDER: test
EMAIL_DEFAULT_FROM: noreply@mega2-it.test
MEGA2_PUBLIC_BASE_URL: http://mega2:8000
# MC-07: route Code read paths through the mega2 BFF inside compose.
MEGA_CODE_DATA_BACKEND: mega2
# FS-02: uploads via RustFS S3 (stack default bucket name retained).
S3_REGION: us-east-1
S3_ACCESS_KEY_ID: rustfs
S3_ACCESS_KEY_SECRET: rustfs_secret
S3_BUCKET: monoui
S3_ENDPOINT: http://rustfs:9000
S3_PUBLIC_URL: http://127.0.0.1:19000/monoui
S3_FORCE_PATH_STYLE: "true"
MEGA_COLLAB_INTERNAL_URL: http://megaui-collab:7002
MEGA_COLLAB_INTERNAL_TOKEN: mega2-it-collab-token-0001
NEXT_PUBLIC_MEGA_COLLAB_PUBLIC_URL: ws://127.0.0.1:17002
depends_on:
postgres:
condition: service_healthy
website-db-init:
condition: service_completed_successfully
rustfs-init:
condition: service_healthy
megaui-collab:
condition: service_healthy
ports:
- "127.0.0.1:17001:7001"
healthcheck:
test: ["CMD-SHELL", "node -e \"const socket = require('net').connect(7001, '127.0.0.1'); socket.setTimeout(1000); socket.on('connect', () => process.exit(0)); socket.on('error', () => process.exit(1)); socket.on('timeout', () => process.exit(1));\""]
interval: 3s
timeout: 5s
retries: 40
start_period: 20s
volumes:
mega2-data:
scorpiofs-data:
networks:
default:
name: mega2-test-network