diff --git a/README.md b/README.md index bca00fc..d4db22c 100644 --- a/README.md +++ b/README.md @@ -367,9 +367,36 @@ It uses the environment variables declared above to set all the configurations. You can override it by including a file in: -* `/app/gunicorn_conf.py`, -* `/app/app/gunicorn_conf.py` or replacing the one in -* `/gunicorn_conf.py` or replacing the one in +* `/app/gunicorn_conf.py` +* `/app/app/gunicorn_conf.py` +* `/gunicorn_conf.py` + + +### Custom `/app/prestart.sh` + +If you need to run anything before starting the app, you can add a file `prestart.sh` to the directory `/app`. The image will automatically detect and run it before starting everything. + +For example, if you want to add Alembic SQL migrations (with SQLALchemy), you could create a `./app/prestart.sh` file in your code directory (that will be copied by your `Dockerfile`) with: + +```bash +#! /usr/bin/env bash + +# Let the DB start +sleep 10; +# Run migrations +alembic upgrade head +``` + +and it would wait 10 seconds to give the database some time to start and then run that `alembic` command. + +If you need to run a Python script before starting the app, you could make the `/app/prestart.sh` file run your Python script, with something like: + +```bash +#! /usr/bin/env bash + +# Run custom Python script before starting +python /app/my_custom_prestart_script.py +``` ## Tests @@ -377,6 +404,13 @@ You can override it by including a file in: All the image tags, configurations, environment variables and application options are tested. +## Release Notes + +### 0.1.0 + +* Add support for `/app/prestart.sh`. + + ## License This project is licensed under the terms of the MIT license. diff --git a/tests/test_01_main/test_defaults.py b/tests/test_01_main/test_defaults.py index 2e27bd5..b602a0b 100644 --- a/tests/test_01_main/test_defaults.py +++ b/tests/test_01_main/test_defaults.py @@ -4,11 +4,30 @@ import pytest import requests -from ..utils import CONTAINER_NAME, get_config, stop_previous_container +from ..utils import CONTAINER_NAME, get_config, get_logs, remove_previous_container client = docker.from_env() +def verify_container(container, response_text): + config_data = get_config(container) + assert config_data["workers_per_core"] == 2 + assert config_data["host"] == "0.0.0.0" + assert config_data["port"] == "80" + assert config_data["loglevel"] == "info" + assert config_data["workers"] > 2 + assert config_data["bind"] == "0.0.0.0:80" + logs = get_logs(container) + assert "Checking for script in /app/prestart.sh" in logs + assert "Running script /app/prestart.sh" in logs + assert ( + "Running inside /app/prestart.sh, you could add migrations to this file" in logs + ) + response = requests.get("http://127.0.0.1:8000") + data = response.json() + assert data["message"] == response_text + + @pytest.mark.parametrize( "image,response_text", [ @@ -35,20 +54,16 @@ ], ) def test_defaults(image, response_text): - stop_previous_container(client) + remove_previous_container(client) container = client.containers.run( image, name=CONTAINER_NAME, ports={"80": "8000"}, detach=True ) time.sleep(1) - config_data = get_config(container) - assert config_data["workers_per_core"] == 2 - assert config_data["host"] == "0.0.0.0" - assert config_data["port"] == "80" - assert config_data["loglevel"] == "info" - assert config_data["workers"] > 2 - assert config_data["bind"] == "0.0.0.0:80" - response = requests.get("http://127.0.0.1:8000") - data = response.json() - assert data["message"] == response_text + verify_container(container, response_text) + container.stop() + # Test that everything works after restarting too + container.start() + time.sleep(1) + verify_container(container, response_text) container.stop() container.remove() diff --git a/tests/utils.py b/tests/utils.py index 54bf166..f8b3b9d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -26,10 +26,15 @@ def get_config(container): return json.loads(result.output.decode()) -def stop_previous_container(client): +def remove_previous_container(client): try: previous = client.containers.get(CONTAINER_NAME) previous.stop() previous.remove() except NotFound: return None + + +def get_logs(container): + logs: str = container.logs() + return logs.decode("utf-8")