Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refact: remove bento base image #22

Merged
merged 5 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
FROM ghcr.io/bento-platform/bento_base_image:python-debian-2024.07.09
ARG PYTHON_VERSION=3.12
ARG DEBIAN_VERSION=slim-bookworm

RUN pip install --no-cache-dir "uvicorn[standard]==0.30.1"
FROM python:${PYTHON_VERSION}-${DEBIAN_VERSION}

LABEL Maintainer="Bento Project"

RUN apt-get update -y; \
apt-get upgrade -y; \
apt-get install -y \
bash \
build-essential \
curl \
git \
gosu \
jq \
libpq-dev \
perl \
procps \
vim; \
rm -rf /var/lib/apt/lists/*;

RUN pip install --no-cache-dir -U pip; \
pip install --no-cache-dir poetry==1.8.5; \
pip install --no-cache-dir 'uvicorn[standard]>=0.34.0,<0.35'

RUN mkdir /tds /run/secrets
WORKDIR /tds

COPY pyproject.toml .
Expand All @@ -11,13 +34,15 @@ RUN poetry config virtualenvs.create false && \
poetry --no-cache install --without dev --no-root

COPY transcriptomics_data_service transcriptomics_data_service
COPY entrypoint.bash .
COPY gosu_entrypoint.bash .
COPY run.bash .
COPY LICENSE .
COPY README.md .

# Install the module itself, locally (similar to `pip install -e .`)
RUN poetry install --without dev

ENTRYPOINT [ "bash", "./entrypoint.bash" ]
RUN chmod +x ./*.bash

ENTRYPOINT [ "bash", "./gosu_entrypoint.bash" ]
CMD [ "bash", "./run.bash" ]
62 changes: 40 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,46 @@ For testing purposes, this repository includes an RCM and gene lenghts file:
- [rcm_file.csv](tests/data/rcm_file.csv)
- [gene_lengths.csv](tests/data/gene_lengths.csv)

### Environment variables

The following environment variables should be set when running a TDS container:

| Name | Description | Default |
| ------------------ | ---------------------------------------------------- | ---------- |
| `TDS_USER_NAME` | Non-root container user name running the TDS process | `Null` |
| `TDS_UID` | UID of TDS_USER_NAME | `1000` |
| `DB_HOST` | IP or hostname of the database | `tds-db` |
| `DB_PORT` | Database port | `5432` |
| `DB_USER` | Database username | `tds_user` |
| `DB_NAME` | Database name | `tds` |
| `DB_PASSWORD` | DB_USER's Database password | `Null` |
| `DB_PASSWORD_FILE` | Docker secret file for DB_USER's Database password | `Null` |
| `CORS_ORIGINS` | List of allowed CORS origins | `Null` |

**Note:** Only use `DB_PASSWORD` or `DB_PASSWORK_FILE`, not both, since they serve the same purpose in a different fashion.

## Using Docker Secrets for the PostgreSQL credential

The TDS [`Config`](./transcriptomics_data_service/config.py) object has its values populated from environment variables and secrets at startup.

The `Config.db_password` value is populated by either:
- `DB_PASSWORD=<a secure password>` if using an environment variable
- As seen in [docker-compose.dev.yaml](./docker-compose.dev.yaml)
- `DB_PASSWORD_FILE=/run/secrets/db_password` if using a Docker secret (recommended)
- As seen in [docker-compose.secrets.dev.yaml](./docker-compose.secrets.dev.yaml)

Using a Docker secret is recommended for security, as environment variables are more prone to be leaked.

`DB_PASSWORD` should only be considered for local development, or if the database is secured and isolated from public access in a private network.

## Authorization plugin

The Transcriptomics Data Service is meant to be a reusable microservice that can be integrated in existing
stacks. Since authorization schemes vary across projects, TDS allows adopters to code their own authorization plugin,
enabling adopters to leverage their existing access control code, tools and policies.

See the [authorization docs](./docs/authz.md) for more information on how to create and use the authz plugin with TDS.

## Starting a standalone TDS

Start the TDS server with a local PostgreSQL database for testing by running the following command.
Expand Down Expand Up @@ -64,28 +104,6 @@ docker compose -f ./docker-compose.dev.yaml down

You can then attach VS Code to the `tds` container, and use the preconfigured `Python Debugger (TDS)` for interactive debugging.

## Using Docker Secrets for the PostgreSQL credential

The TDS [`Config`](./transcriptomics_data_service/config.py) object has its values populated from environment variables and secrets at startup.

The `Config.db_password` value is populated by either:
- `DB_PASSWORD=<a secure password>` if using an environment variable
- As seen in [docker-compose.dev.yaml](./docker-compose.dev.yaml)
- `DB_PASSWORD_FILE=/run/secrets/db_password` if using a Docker secret (recommended)
- As seen in [docker-compose.secrets.dev.yaml](./docker-compose.secrets.dev.yaml)

Using a Docker secret is recommended for security, as environment variables are more prone to be leaked.

`DB_PASSWORD` should only be considered for local development, or if the database is secured and isolated from public access in a private network.

## Authorization plugin

The Transcriptomics Data Service is meant to be a reusable microservice that can be integrated in existing
stacks. Since authorization schemes vary across projects, TDS allows adopters to code their own authorization plugin,
enabling adopters to leverage their existing access control code, tools and policies.

See the [authorization docs](./docs/authz.md) for more information on how to create and use the authz plugin with TDS.

## Endpoints

TODO: replace this with Swagger UI docs generated from CI workflows.
Expand Down
40 changes: 32 additions & 8 deletions dev.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
FROM ghcr.io/bento-platform/bento_base_image:python-debian-2024.07.09
ARG PYTHON_VERSION=3.12
ARG DEBIAN_VERSION=slim-bookworm

FROM python:${PYTHON_VERSION}-${DEBIAN_VERSION}

# LABELS
LABEL Maintainer="Bento Project"
LABEL org.opencontainers.image.description="Local development image for the Transcriptomics Data Service."
LABEL devcontainer.metadata='[{ \
"remoteUser": "bento_user", \
"customizations": { \
"vscode": { \
"extensions": ["ms-python.python", "eamodio.gitlens", "ms-python.black-formatter"], \
"settings": {"workspaceFolder": "/tds"} \
} \
} \
}, \
"remoteUser": "tds" \
}]'

# FastAPI uses uvicorn for a development server as well
RUN pip install --upgrade pip && pip install --no-cache-dir "uvicorn[standard]==0.30.1"
RUN apt-get update -y; \
apt-get upgrade -y; \
apt-get install -y \
bash \
build-essential \
curl \
git \
gosu \
jq \
libpq-dev \
perl \
procps \
vim; \
rm -rf /var/lib/apt/lists/*;

RUN pip install --no-cache-dir -U pip; \
pip install --no-cache-dir poetry==1.8.5; \
pip install --no-cache-dir 'uvicorn[standard]>=0.34.0,<0.35'

# INIT DIRECTORIES
RUN mkdir /tds /run/secrets
WORKDIR /tds

COPY pyproject.toml .
COPY poetry.lock .

COPY gosu_entrypoint.bash .
COPY run.dev.bash .

# Install production + development dependencies
Expand All @@ -26,8 +50,8 @@ COPY run.dev.bash .
RUN poetry config virtualenvs.create false && \
poetry --no-cache install --no-root

# Tell the service that we're running a local development container
ENV BENTO_CONTAINER_LOCAL=true
RUN chmod +x ./*.bash

# Don't copy in actual code, since it'll be mounted in via volume for development
ENTRYPOINT [ "bash", "./gosu_entrypoint.bash" ]
CMD [ "bash", "./run.dev.bash" ]
3 changes: 2 additions & 1 deletion docker-compose.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ services:
depends_on:
- tds-db
environment:
- BENTO_UID=${UID}
- TDS_USER_NAME=tds # set to match the expected 'devcontainer' remoteUser of the dev.Dockerfile
- TDS_UID=${UID}
- DB_HOST=tds-db
- DB_PORT=5432
- DB_USER=tds_user
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.secrets.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ services:
depends_on:
- tds-db
environment:
- BENTO_UID=${UID}
- TDS_USER_NAME=tds
- TDS_UID=${UID}
- DB_HOST=tds-db
- DB_PORT=5432
- DB_USER=tds_user
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ services:
depends_on:
- tds-db
environment:
- BENTO_UID=${UID}
# - DATABASE_URI=postgres://tds_user:tds_password@tds-db:5432/tds_db
- TDS_USER_NAME=tds
- TDS_UID=${UID}
- DB_HOST=tds-db
- DB_PORT=5432
- DB_USER=tds_user
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ services:
depends_on:
- tds-db
environment:
- TDS_USER_NAME=tds
- TDS_UID=${UID}
- DB_HOST=tds-db
- DB_PORT=5432
- DB_USER=tds_user
Expand Down
16 changes: 16 additions & 0 deletions gosu_entrypoint.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

# Setup non-root user
USER_NAME=${TDS_USER_NAME}
USER_ID=${TDS_UID:-1000}
GROUP_ID=${TDS_GID:-$USER_ID}
groupadd -g "${GROUP_ID}" -r ${USER_NAME}
useradd --shell /bin/bash -u "${USER_ID}" -r -g "${GROUP_ID}" --non-unique -c "TDS container user" -m ${USER_NAME}
export HOME=/home/${USER_NAME}
echo "PATH=/home/${USER_NAME}/.local/bin/:\$PATH" >> "/home/${USER_NAME}/.bashrc"

chown -R ${USER_NAME}:${USER_NAME} /tds
chown -R ${USER_NAME}:${USER_NAME} /run/secrets

# Drop into ${USER_NAME} from root and execute the CMD specified for the image
exec gosu "${USER_NAME}" "$@"
5 changes: 4 additions & 1 deletion run.dev.bash
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/bin/bash

# Update dependencies and install module locally
/poetry_user_install_dev.bash
poetry export -f requirements.txt --with dev --output requirements.txt
pip install --no-cache-dir --user -r requirements.txt
rm requirements.txt
pip install -e .

# Extra dependencies installation for authz plugin
if [ -f /tds/lib/requirements.txt ]; then
Expand Down
7 changes: 5 additions & 2 deletions test-docker.bash
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ export UID=$(id -u)
docker compose -f docker-compose.test.yaml down
docker compose -f docker-compose.test.yaml up -d --build --wait

docker exec tds /bin/bash -c "
docker exec --user "${UID}" tds /bin/bash -c "
cd /tds &&
/poetry_user_install_dev.bash &&
poetry export -f requirements.txt --with dev --output requirements.txt
pip install --no-cache-dir --user -r requirements.txt
rm requirements.txt
pip install -e .
pytest -svv --cov=transcriptomics_data_service --cov-branch &&
coverage html
"
Expand Down