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

Move testing outside Docker, simplify Dockerfile #508

Merged
merged 6 commits into from
Jan 12, 2023
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
26 changes: 9 additions & 17 deletions .github/workflows/test-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,17 @@ jobs:
--health-retries 5

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Build test image
uses: docker/build-push-action@v2
- uses: actions/checkout@v3
- name: Install poetry
run: pipx install poetry
- uses: actions/setup-python@v4
with:
context: .
file: docker/Dockerfile
push: false
target: "development"
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

python-version: "3.10"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will dependabot notice and be able to update this python-version; or is that something for us to keep in mind?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is something we'll just have to remember, unfortunately. But it would definitely be nice if we could keep these things in sync. Maybe there's a way 🤔

cache: "poetry"
- name: Install dependencies
run: poetry install
- name: Run tests
run: |-
docker run --rm \
-e CTMS_DB_URL \
-e CTMS_SECRET_KEY \
ghcr.io/${{ github.repository }}:${{ github.sha }} \
./docker/test.sh
run: bin/test.sh
env:
CTMS_DB_URL: postgresql://postgres:postgres@172.17.0.1:5432/postgres
CTMS_SECRET_KEY: secret_${{ github.sha }}
Expand Down
8 changes: 4 additions & 4 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,14 @@
"filename": ".github/workflows/test-build.yaml",
"hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3",
"is_verified": false,
"line_number": 46
"line_number": 38
},
{
"type": "Secret Keyword",
"filename": ".github/workflows/test-build.yaml",
"hashed_secret": "a2490b1a07aa4a72606afe91f2de20f8c524d779",
"is_verified": false,
"line_number": 47
"line_number": 39
}
],
"bin/lint.sh": [
Expand All @@ -165,7 +165,7 @@
"filename": "docs/developer_setup.md",
"hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3",
"is_verified": false,
"line_number": 208
"line_number": 203
}
],
"tests/unit/test_acoustic_service.py": [
Expand Down Expand Up @@ -203,5 +203,5 @@
}
]
},
"generated_at": "2022-11-29T20:27:29Z"
"generated_at": "2023-01-11T16:11:56Z"
}
26 changes: 15 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ help:
@echo ""
@echo "CTMS make rules:"
@echo ""
@echo " build - build docker containers"
@echo " lint - lint check for code"
@echo " setup - (re)create the database"
@echo " start - run the API service"
@echo ""
@echo " test - run test suite"
@echo " shell - open a shell in the web container"
@echo " db-only - run PostgreSQL server"
@echo " build - build docker containers"
@echo " db-only - run PostgreSQL server"
@echo " lint - lint check for code"
@echo " setup - (re)create the database"
@echo " shell - open a shell in the web container"
@echo " start - run the API service"
@echo " test - run test suite"
@echo " update-secrets - scan repository for secrets and update baseline file, if necessary"
grahamalama marked this conversation as resolved.
Show resolved Hide resolved
@echo ""
@echo " help - see this text"

Expand Down Expand Up @@ -72,11 +72,15 @@ start: .env
docker-compose up

.PHONY: test
test: .env
docker-compose run --rm ${MK_WITH_SERVICE_PORTS} tests
ifneq (1, ${MK_KEEP_DOCKER_UP})
test: .env $(INSTALL_STAMP)
docker-compose up --wait postgres
bin/test.sh
ifneq (1, ${MK_KEEP_DOCKER_UP})
# Due to https://github.com/docker/compose/issues/2791 we have to explicitly
# rm all running containers
docker-compose down
endif

.PHONY: update-secrets
update-secrets:
bin/update_baseline.sh
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ for this project to ensure correct environment setup.

The project is structured with the following in mind:

- [bin/*](bin/)
- Some scripts that have proven useful within the CTMS ecosystem
- [docs/*](docs/)
- Documentation to guide others around the project interactions
- [ctms/*](ctms/)
Expand All @@ -30,8 +32,6 @@ The project is structured with the following in mind:
- Pydantic Models for Data Modeling and Contract Validation
- [migrations/*](migrations/)
- Alembic migrations that act as a changelog or version control system for implementing DB changes in an ordered fashion
- [scripts/*](scripts/)
- Some scripts that have proven useful within the CTMS ecosystem
- [tests/unit/*](test/unit/)
- Test suite using pytest

Expand Down
15 changes: 15 additions & 0 deletions bin/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

set -euo pipefail

POETRY_RUN="poetry run"

CURRENT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
BASE_DIR="$(dirname "$CURRENT_DIR")"

# without this, some tests fail because of off-by-timezone errors.
export TZ=UTC

$POETRY_RUN coverage run --rcfile "${BASE_DIR}/pyproject.toml" -m pytest
$POETRY_RUN coverage report --rcfile "${BASE_DIR}/pyproject.toml" -m --fail-under 80
$POETRY_RUN coverage html --rcfile "${BASE_DIR}/pyproject.toml"
14 changes: 14 additions & 0 deletions bin/update_and_install_system_packages.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
grahamalama marked this conversation as resolved.
Show resolved Hide resolved
set -euo pipefail
# Tell apt-get we're never going to be able to give manual
# feedback:
export DEBIAN_FRONTEND=noninteractive

apt-get update
# Install security updates
apt-get -y upgrade
# Install packages
apt-get -y install --no-install-recommends $@
# Delete cached files we don't need anymore
apt-get clean
rm -rf /var/lib/apt/lists/*
7 changes: 7 additions & 0 deletions bin/update_baseline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

set -euo pipefail

detect-secrets scan . \
--baseline .secrets.baseline \
--exclude-files "(poetry.lock$)|(htmlcov/)"
grahamalama marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 2 additions & 18 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ services:
build:
context: .
dockerfile: docker/Dockerfile
target: development
image: web
volumes:
- type: bind
source: .
target: /app
volumes:
- .:/app
ports:
- ${PORT:-8000}:${PORT:-8000}
# Let the init system handle signals for us.
Expand Down Expand Up @@ -43,16 +40,3 @@ services:
ports:
- 8080:8080

tests:
image: web
depends_on:
web:
condition: service_started
postgres:
condition: service_healthy
command: ["bash", "./docker/test.sh"]
profiles: [test]
init: true
env_file:
- docker/config/local_dev.env
- .env
97 changes: 25 additions & 72 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,91 +1,44 @@
# Dockerfile
# Uses multi-stage builds requiring Docker 17.05 or higher
# See https://docs.docker.com/develop/develop-images/multistage-build/
FROM python:3.10.6 as python-base

# Creating a python base with shared environment variables
FROM python:3.10.6-slim as python-base
ENV PYTHONPATH=/app \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
ENV PIP_DEFAULT_TIMEOUT=100 \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
PIP_NO_CACHE_DIR=off \
POETRY_HOME=/opt/poetry\
POETRY_NO_INTERACTION=1 \
PYSETUP_PATH="/opt/pysetup" \
VENV_PATH="/opt/pysetup/.venv" \
PORT=8000

ENV PATH="$VENV_PATH/bin:$POETRY_HOME/bin:$PATH"

# Set up user and group
ARG userid=10001
ARG groupid=10001
WORKDIR /app
RUN groupadd --gid $groupid app && \
useradd -g app --uid $userid --shell /usr/sbin/nologin --create-home app

RUN mkdir -p $POETRY_HOME && \
chown app:app /opt/poetry && \
mkdir -p $PYSETUP_PATH && \
chown app:app $PYSETUP_PATH && \
mkdir -p /app && \
chown app:app /app

RUN apt-get update && \
apt-get install --assume-yes apt-utils && \
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \
apt-get install --no-install-recommends -y \
libpq5

# builder-base is used to build dependencies
FROM python-base as builder-base
RUN apt-get install --no-install-recommends -y \
build-essential \
libpq-dev
POETRY_VIRTUALENVS_IN_PROJECT=true \
PYSETUP_PATH="/opt/pysetup"

# Install Poetry - respects $POETRY_VERSION & $POETRY_HOME
USER app
RUN python3 -m venv $POETRY_HOME && \
$POETRY_HOME/bin/pip install poetry==1.3.1 && \
$POETRY_HOME/bin/poetry --version
grahamalama marked this conversation as resolved.
Show resolved Hide resolved

# We copy our Python requirements here to cache them
# and install only runtime deps using poetry
WORKDIR $PYSETUP_PATH
COPY --chown=app:app ./poetry.lock ./pyproject.toml ./
RUN poetry install --no-dev --no-root
COPY ./poetry.lock ./pyproject.toml ./
RUN $POETRY_HOME/bin/poetry install --no-dev --no-root

FROM python:3.10.6-slim as production

# 'development' stage installs all dev deps and can be used to develop code.
# For example using docker-compose to mount local volume under /app
FROM python-base as development
ENV FASTAPI_ENV=development
COPY bin/update_and_install_system_packages.sh /opt
RUN opt/update_and_install_system_packages.sh libpq5

# Copying poetry and venv into image
USER app
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
ENV PATH="/opt/pysetup/.venv/bin:$PATH" \
PORT=8000 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH=/app \
VENV_PATH="/opt/pysetup/.venv"

# venv already has runtime deps installed we get a quicker install
WORKDIR $PYSETUP_PATH
RUN poetry install --no-root
COPY --from=python-base $VENV_PATH $VENV_PATH

# Set up user and group
ARG userid=10001
ARG groupid=10001
RUN groupadd --gid $groupid app && \
useradd -g app --uid $userid --shell /usr/sbin/nologin --create-home app
USER app
WORKDIR /app
COPY --chown=app:app . .

EXPOSE $PORT
CMD ["python", "asgi.py"]

# 'production' stage uses the clean 'python-base' stage and copyies
# in only our runtime deps that were installed in the 'builder-base'
FROM python-base as production
ENV FASTAPI_ENV=production

COPY --from=builder-base $VENV_PATH $VENV_PATH

WORKDIR /app
COPY --chown=app:app . .

EXPOSE $PORT
Expand Down
10 changes: 0 additions & 10 deletions docker/test.sh

This file was deleted.

9 changes: 2 additions & 7 deletions docs/developer_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@ curl -sSL https://install.python-poetry.org | python3 -
```

## Setup Dependencies
Install prod dependencies in pyproject.toml through poetry:
To set up dependencies necessary for local development, run:

```sh
poetry install --no-dev
```

Install ALL deps including DEV dependencies:
```sh
poetry install
make install
```

If these fail with an error like:
Expand Down
Loading