diff --git a/deployment-service/snakeapi_service/dockerfile b/deployment-service/snakeapi_service/dockerfile index b9cbff5..9ba254e 100644 --- a/deployment-service/snakeapi_service/dockerfile +++ b/deployment-service/snakeapi_service/dockerfile @@ -1,19 +1,15 @@ FROM python:3.11-slim WORKDIR /app -# Install Jupyter, Flask, AWS CLI -RUN pip install --no-cache-dir jupyter flask awscli +RUN pip install --no-cache-dir jupyter flask awscli flask_cors nbconvert nbformat -# Copy runner scripts and notebook (relative to this Dockerfile) COPY entrypoint.sh . COPY battlesnake_server.py . COPY notebook.ipynb . RUN chmod +x entrypoint.sh -# Set and expose the port ENV PORT=3006 EXPOSE 3006 -# Start the notebook-to-Flask loop CMD ["./entrypoint.sh"] diff --git a/deployment-service/snakeapi_service/entrypoint.sh b/deployment-service/snakeapi_service/entrypoint.sh index 3ea8284..6f48c9b 100644 --- a/deployment-service/snakeapi_service/entrypoint.sh +++ b/deployment-service/snakeapi_service/entrypoint.sh @@ -1,18 +1,22 @@ #!/usr/bin/env bash +last_mod=0 + while true; do - # Fetch the latest notebook from S3 (common bucket + prefix) aws --endpoint-url "$AWS_ENDPOINT_URL_S3" --region "$AWS_REGION" \ - s3 cp "s3://$BUCKET_NAME/$INSTANCE_PREFIX/notebook.ipynb" notebook.ipynb + s3 sync "s3://$BUCKET_NAME/$INSTANCE_PREFIX" . --exclude "*" --include "notebook.ipynb" - # Execute all cells, including run_server(...) in the last cell - jupyter nbconvert \ - --to notebook \ - --execute \ - --inplace \ - --ExecutePreprocessor.timeout=0 \ - notebook.ipynb + if [ -f "notebook.ipynb" ]; then + new_mod=$(stat -c %Y notebook.ipynb) + else + new_mod=0 + fi + + if [ "$new_mod" -ne "$last_mod" ]; then + last_mod=$new_mod + jupyter nbconvert --to notebook --execute --inplace --ExecutePreprocessor.timeout=0 notebook.ipynb + echo "Notebook executed; restarting..." + fi - echo "Notebook executed; restarting..." sleep 1 done diff --git a/deployment-service/snakeapi_service/fly.toml b/deployment-service/snakeapi_service/fly.toml new file mode 100644 index 0000000..47bb81d --- /dev/null +++ b/deployment-service/snakeapi_service/fly.toml @@ -0,0 +1,17 @@ +app = "" +kill_signal = "SIGINT" +kill_timeout = 5 + +[build] +dockerfile = "Dockerfile" + +[env] + PORT = "3006" + +[[services]] +internal_port = 3006 +protocol = "tcp" + +[[services.ports]] +handlers = ["http"] +port = 80 diff --git a/deployment-service/snakeapi_service/snakeapi_server.py b/deployment-service/snakeapi_service/snakeapi_server.py index b46eeb4..6a10bd9 100644 --- a/deployment-service/snakeapi_service/snakeapi_server.py +++ b/deployment-service/snakeapi_service/snakeapi_server.py @@ -1,7 +1,11 @@ import os import logging import typing -from flask import Flask, request +import json +import nbformat +from nbconvert import PythonExporter +import subprocess +from flask import Flask, request, jsonify from flask_cors import CORS def run_server(handlers: typing.Dict): @@ -26,10 +30,18 @@ def run_server(handlers: typing.Dict): handlers["end"](request.get_json()) return "ok" - @app.after_request - def identify_server(response): - response.headers["server"] = "battlesnake/github/starter-snake-python" - return response + @app.get("/notebook") + def get_notebook(): + with open("notebook.ipynb", "r", encoding="utf-8") as f: + content = f.read() + return content, 200, {"Content-Type": "application/json"} + + @app.post("/notebook") + def update_notebook(): + notebook_json = request.get_json() + with open("notebook.ipynb", "w", encoding="utf-8") as f: + json.dump(notebook_json, f) + return {"status": "saved"}, 200 port = int(os.environ.get("PORT", "3006")) logging.getLogger("werkzeug").setLevel(logging.ERROR)