Skip to content

Commit 4273f49

Browse files
authored
Merge pull request #884 from microbiomedata/877-implement-mongodb-transaction-for-api-endpoint
Configure MongoDB Replica Set and implement example MongoDB transaction
2 parents 25a6cbd + d6563c2 commit 4273f49

File tree

6 files changed

+288
-80
lines changed

6 files changed

+288
-80
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#! /bin/sh
2+
3+
###############################################################################
4+
# Overview
5+
# --------
6+
# This shell script uses `mongosh` to: (a) check whether the specified MongoDB
7+
# instance is a member of a replica set and, if is isn't, (b) creates a replica
8+
# set of which that MongoDB instance is the sole member.
9+
#
10+
# This shell script was designed to be run within a Docker container based upon
11+
# the `mongo:8.0.4` container image (i.e., https://hub.docker.com/_/mongo).
12+
#
13+
# Positional parameters
14+
# ---------------------
15+
# 1. MongoDB hostname (e.g., "mongo")
16+
# 2. MongoDB port (e.g., "27017")
17+
# 3. MongoDB username (e.g., "admin")
18+
# 4. MongoDB password (e.g., "root")
19+
#
20+
# References
21+
# ----------
22+
# 1. https://www.mongodb.com/docs/manual/reference/method/rs.status/
23+
# 2. https://www.mongodb.com/docs/mongodb-shell/reference/options/
24+
# 3. https://www.warp.dev/terminus/docker-compose-health-check
25+
###############################################################################
26+
27+
echo 'Setting up replica set.'
28+
29+
echo '
30+
try {
31+
rs.status();
32+
} catch (e) {
33+
rs.initiate({ _id: "rs0", members: [{ _id: 0, host: "localhost" }] });
34+
}
35+
' | mongosh \
36+
--host "${1}" \
37+
--port "${2}" \
38+
--username "${3}" \
39+
--password "${4}"
40+
41+
echo 'Finished setting up replica set.'
42+
43+
# Exit with a status code of 0.
44+
exit 0

.github/workflows/python-app.yml

+37-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3-
1+
# This GitHub Actions workflow spins up the "test" Docker Compose stack and runs automated tests within it.
2+
# Reference: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
43
name: Python application
54

65
# Note: The following events will trigger this workflow:
@@ -35,13 +34,46 @@ on:
3534
# Reference: https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow
3635
workflow_dispatch: { }
3736

38-
3937
jobs:
4038
build:
4139
runs-on: ubuntu-latest
4240

4341
steps:
4442
- uses: actions/checkout@v4 # update version to maintain consistency across workflows
43+
# Prepare the MongoDB keyfile to be mounted by the `mongo` container.
44+
#
45+
# Note: This is to prevent MongoDB from reporting the error:
46+
# > "permissions on /path/to/keyfile are too open"
47+
#
48+
# Note: This involves making the file be readable _only_ by the OS user that MongoDB runs as.
49+
#
50+
# In containers using the `mongo` image, that user is named `mongodb` and has a
51+
# UID of `999`. That user belongs to a group named `mongodb`, whose GID is `999`.
52+
# You can verify this by looking at the Dockerfile layers on Docker Hub.
53+
# Reference: https://hub.docker.com/layers/library/mongo/8.0.5/images/sha256-90bf5066fed8a3cd59345d963922bc5cb557d4b4b2a0e38dfd9ee299c405741b
54+
#
55+
# The GHA Runner will allow me to `chmod 600` the file, but not `chown 999:999` it.
56+
# Since the GHA Runner will not allow me to `chown` the file, I use a Docker container
57+
# to (effectively) accomplish that. Since—after I use the Docker container to change
58+
# the file's owner—the GHA Runner will not allow me to then `chmod` the file, I opt to
59+
# accomplish that within the Docker container as well.
60+
#
61+
# The reason—within the Docker container—I do not `chmod` or `chown` the original file
62+
# directly, is that I am under the impression that ownership/permission changes made
63+
# within a container to mounted files that already exist on the host will not be seen
64+
# by the host. I have not found official documentation supporting this yet.
65+
# TODO: Include a reference about changing mounted file's permission within container.
66+
#
67+
- name: Restrict access to MongoDB keyfile
68+
run: |
69+
mkdir _tmp
70+
docker run --rm \
71+
-v ./mongoKeyFile:/mongoKeyFile \
72+
-v ./_tmp:/out \
73+
alpine \
74+
sh -c 'cp /mongoKeyFile /out/mongoKeyFile && chmod 600 /out/mongoKeyFile && chown 999:999 /out/mongoKeyFile'
75+
mv _tmp/mongoKeyFile ./mongoKeyFile
76+
rmdir _tmp
4577
- name: Set up Python 3.10
4678
uses: actions/setup-python@v4
4779
with:
@@ -53,7 +85,7 @@ jobs:
5385
# make lint
5486
- name: Build and run containers upon which test runner depends
5587
run: make up-test
56-
- name: Build test runner container image
88+
- name: Build container image for test runner
5789
run: make test-build
5890
- name: Run tests
5991
run: make test-run

docker-compose.test.yml

+63-30
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
services:
2-
# This service runs the postgres DB used by dagster for run storage, schedule storage,
3-
# and event log storage.
4-
# Tests use `postgres:11` image.
5-
# https://github.com/dagster-io/dagster/blob/0.11.9/python_modules/libraries/dagster-postgres/dagster_postgres_tests/docker-compose.yml
2+
3+
# Postgres server used by Dagster.
4+
#
5+
# > This service runs the postgres DB used by dagster for run storage, schedule storage,
6+
# > and event log storage.
7+
# > Tests use `postgres:11` image.
8+
# > https://github.com/dagster-io/dagster/blob/0.11.9/python_modules/libraries/dagster-postgres/dagster_postgres_tests/docker-compose.yml
9+
#
610
dagster-postgresql:
711
image: postgres:11
812
container_name: dagster-postgresql
9-
volumes:
10-
- nmdc_runtime_test_postgres_data:/var/lib/postgresql/data
1113
environment:
1214
POSTGRES_USER: "postgres_user"
1315
POSTGRES_PASSWORD: "postgres_password"
1416
POSTGRES_DB: "postgres_db"
17+
volumes:
18+
- nmdc_runtime_test_postgres_data:/var/lib/postgresql/data
1519

16-
# This service runs dagit.
17-
# Since our instance uses the QueuedRunCoordinator, any runs submitted from dagit will be put on
18-
# a queue and later dequeued and launched by dagster-daemon.
20+
# Web server that hosts the Dagster web UI.
21+
#
22+
# > This service runs dagit.
23+
# > Since our instance uses the QueuedRunCoordinator, any runs submitted from dagit will be put on
24+
# > a queue and later dequeued and launched by dagster-daemon.
25+
#
1926
dagster-dagit:
2027
build:
2128
context: .
@@ -27,8 +34,6 @@ services:
2734
- "3000"
2835
ports:
2936
- "3000:3000"
30-
env_file:
31-
- .env.test
3237
environment:
3338
DAGSTER_POSTGRES_USER: "postgres_user"
3439
DAGSTER_POSTGRES_PASSWORD: "postgres_password"
@@ -38,9 +43,13 @@ services:
3843
restart: on-failure
3944
volumes:
4045
- ./:/opt/dagster/lib
46+
env_file: .env.test
4147

42-
# This service runs the dagster-daemon process, which is responsible for taking runs
43-
# off of the queue and launching them, as well as creating runs from schedules or sensors.
48+
# Dagster daemon.
49+
#
50+
# > This service runs the dagster-daemon process, which is responsible for taking runs
51+
# > off of the queue and launching them, as well as creating runs from schedules or sensors.
52+
#
4453
dagster-daemon:
4554
build:
4655
context: .
@@ -49,17 +58,20 @@ services:
4958
container_name: dagster-daemon
5059
entrypoint: ["tini", "--", "../lib/nmdc_runtime/site/entrypoint-daemon.sh"]
5160
restart: on-failure
52-
env_file:
53-
- .env.test
5461
environment:
5562
DAGSTER_POSTGRES_USER: "postgres_user"
5663
DAGSTER_POSTGRES_PASSWORD: "postgres_password"
5764
DAGSTER_POSTGRES_DB: "postgres_db"
5865
depends_on:
59-
- dagster-postgresql
66+
dagster-postgresql: { condition: service_started }
67+
# Wait until the MongoDB replica set has been set up by the "init container"
68+
# before starting this Dagster daemon container.
69+
mongo-init: { condition: service_completed_successfully }
6070
volumes:
6171
- ./:/opt/dagster/lib
72+
env_file: .env.test
6273

74+
# Uvicorn server hosting the FastAPI application.
6375
fastapi:
6476
build:
6577
context: .
@@ -68,27 +80,48 @@ services:
6880
container_name: fastapi
6981
ports:
7082
- "8000:8000"
71-
env_file:
72-
- .env.test
73-
depends_on:
74-
- mongo
7583
volumes:
7684
- .:/code
85+
# Wait until the MongoDB replica set has been set up by the "init container"
86+
# before starting this FastAPI container.
87+
depends_on:
88+
mongo-init: { condition: service_completed_successfully }
89+
env_file: .env.test
90+
91+
# Short-lived MongoDB server used to initialize the one used by the FastAPI application.
92+
mongo-init:
93+
image: mongo:8.0.4
94+
container_name: mongo-init
95+
volumes:
96+
- ./wait-for-it.sh:/wait-for-it.sh:ro
97+
- .docker/mongo_init/initialize_replica_set.sh:/initialize_replica_set.sh:ro
98+
depends_on:
99+
mongo: { condition: service_started }
100+
entrypoint: /bin/bash /wait-for-it.sh --host=mongo --port=27017 --timeout=20 -- /bin/sh /initialize_replica_set.sh mongo 27017 admin root
77101

102+
# MongoDB server used by the FastAPI application.
78103
mongo:
79104
image: mongo:8.0.4
80105
container_name: mongo
81106
ports:
82107
- "27018:27017"
83-
volumes:
84-
- nmdc_runtime_test_mongo_data:/data/db
85-
- ~/nmdcdb-mongodump:/nmdc_dump:ro
86-
- ./tests/mongorestore-nmdc-testdb.sh:/mongorestore-nmdc-testdb.sh:ro
87108
restart: unless-stopped
88109
environment:
89110
MONGO_INITDB_ROOT_USERNAME: admin
90111
MONGO_INITDB_ROOT_PASSWORD: root
112+
# Configure MongoDB to run in replica set mode, so we can use MongoDB transactions.
113+
#
114+
# Note: Including a KeyFile is necessary when doing the combination of (a) running MongoDB in
115+
# replica set mode and (b) running MongoDB with authentication enabled.
116+
#
117+
command: ["--replSet", "rs0", "--bind_ip_all", "--keyFile", "/keyFile"]
118+
volumes:
119+
- nmdc_runtime_test_mongo_data:/data/db
120+
- ./mongoKeyFile:/keyFile:ro
121+
- ~/nmdcdb-mongodump:/nmdc_dump:ro
122+
- ./tests/mongorestore-nmdc-testdb.sh:/mongorestore-nmdc-testdb.sh:ro
91123

124+
# Test runner.
92125
test:
93126
# Prevent Docker Compose from starting this service automatically.
94127
# Reference: https://stackoverflow.com/a/65957695
@@ -99,13 +132,13 @@ services:
99132
dockerfile: nmdc_runtime/Dockerfile
100133
target: test
101134
container_name: test
102-
env_file:
103-
- .env.test
135+
env_file: .env.test
104136
depends_on:
105-
- mongo
106-
- fastapi
107-
- dagster-daemon
108-
- dagster-dagit
137+
mongo-init: { condition: service_completed_successfully }
138+
mongo: { condition: service_started }
139+
fastapi: { condition: service_started }
140+
dagster-daemon: { condition: service_started }
141+
dagster-dagit: { condition: service_started }
109142
volumes:
110143
- .:/code
111144

0 commit comments

Comments
 (0)