-
Notifications
You must be signed in to change notification settings - Fork 35
Add docker support for local development #2878
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| .dockerignore | ||
| .editorconfig | ||
| .env | ||
| .git | ||
| .github | ||
| .gitignore | ||
| README.md | ||
|
|
||
| docker/Docker* | ||
| docker/docker-compose* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| FROM python:3.11 | ||
|
|
||
| # use root to install pip libs | ||
| USER root | ||
|
|
||
| WORKDIR /code | ||
|
|
||
| COPY . /code/ | ||
|
|
||
| ENV PYTHONDONTWRITEBYTECODE 1 | ||
| ENV PYTHONUNBUFFERED 1 | ||
|
|
||
| # can't use make install because we ignore .github/ via .dockerignore | ||
| # and env is set via docker-compose.yml or injection at container run time | ||
| RUN pip install -e ".[dev]" --config-settings editable_mode=compat | ||
|
|
||
| # switch to app user | ||
| RUN groupadd policyapi && \ | ||
| useradd -g policyapi policyapi && \ | ||
| apt-get purge -y --auto-remove build-essential && \ | ||
| apt-get -y install make && \ | ||
| chown -R policyapi:policyapi /code | ||
|
|
||
| # TODO cannot switch because policyengine_core writes to data/storage | ||
| RUN mkdir /usr/local/lib/python3.11/site-packages/policyengine_core/country_template/data/storage | ||
|
|
||
| # USER policyapi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| services: | ||
| redis: | ||
| image: "redis:alpine" | ||
| expose: | ||
| - "6379" | ||
| volumes: | ||
| - redis-data:/data | ||
| networks: | ||
| - my_network | ||
|
|
||
| policyengine: | ||
| build: | ||
| context: ../ | ||
| dockerfile: docker/Dockerfile.api | ||
| command: ["/bin/bash", "/code/docker/start.sh"] | ||
| image: policyengine:policyengine-api | ||
| depends_on: | ||
| - redis | ||
| env_file: | ||
| - ../.env | ||
| expose: | ||
| - 8080 | ||
| ports: | ||
| - "8080:8080" | ||
| networks: | ||
| - my_network | ||
|
|
||
| volumes: | ||
| redis-data: | ||
|
|
||
| networks: | ||
| my_network: | ||
| name: policyengine-api_default | ||
| external: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #!/bin/bash | ||
| # Environment variables | ||
| PORT="${PORT:-8080}" | ||
| WORKER_COUNT="${WORKER_COUNT:-3}" | ||
| REDIS_PORT="${REDIS_PORT:-6379}" | ||
|
|
||
| # Start the API | ||
| gunicorn -b :"$PORT" policyengine_api.api --timeout 300 --workers 5 --preload & | ||
|
|
||
| # Start multiple workers using POSIX-compliant loop | ||
| # TODO worker.py does not seem to exist? | ||
| #i=1 | ||
| #while [ $i -le "$WORKER_COUNT" ] | ||
| #do | ||
| # echo "Starting worker $i..." | ||
| # python3 policyengine_api/worker.py & | ||
| # i=$((i + 1)) | ||
| #done | ||
|
|
||
| # Keep the script running and handle shutdown gracefully | ||
| trap "pkill -P $$; exit 1" INT TERM | ||
|
|
||
| wait | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,26 @@ | ||
| import os | ||
| import logging | ||
| from google.cloud.logging import Client | ||
|
|
||
| logger = Client().logger("policyengine-api") | ||
| if os.environ.get("FLASK_DEBUG") == "1": | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
| # shims to make default logger act like google's | ||
| levels = { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. accolade: Like the shims! |
||
| "DEBUG": logging.DEBUG, | ||
| "INFO": logging.INFO, | ||
| "WARNING": logging.WARNING, | ||
| "ERROR": logging.ERROR, | ||
| "CRITICAL": logging.CRITICAL, | ||
| } | ||
|
|
||
| def log_struct(msg, severity="INFO"): | ||
| logger.log(levels.get(severity, "INFO"), msg) | ||
|
|
||
| def log_text(msg, severity="INFO"): | ||
| logger.log(levels.get(severity, "INFO"), msg) | ||
|
|
||
| logger.log_struct = log_struct | ||
| logger.log_text = log_text | ||
| else: | ||
| logger = Client().logger("policyengine-api") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,9 @@ def __init__(self): | |
| "GOOGLE_APPLICATION_CREDENTIALS not set; unable to run simulation API.", | ||
| severity="ERROR", | ||
| ) | ||
| raise ValueError( | ||
| "GOOGLE_APPLICATION_CREDENTIALS not set; unable to run simulation API." | ||
| ) | ||
|
|
||
| return | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue, unclear impact: We call an external service using this GAC; without it, the app will not return a society-wide simulation result. I'm unclear on how we might handle this; do you have any ideas?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the issue here is along the lines of "how should the app behave if it is missing an expected configuration?" Currently the code both logs an error and raises an exception during app start-up time, as the In my case, I was using the MyFriendBen benefits API against a local instance of the PolicyEngineAPI and I didn't need the full society-wide simulation service at all. So if the config check were somehow deferred till it was actually needed, that would be ideal from my pov. Perhaps |
||
|
|
||
| self.project = "prod-api-v2-c4d5" | ||
| self.location = "us-central1" | ||
| self.workflow = "simulation-workflow" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both the
README.mdand./gcp/policyengine_api/start.shmentionworker.pybut I found no file with that name in the repo. Are the docs just out of date?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, the docs are out of date. We instead delegate to an external service for what we used to use these workers for.