pyrunner / entrypoint.sh
Perspicacious's picture
Deploy PyRunner on Hugging Face
2529305
Raw
History Blame Contribute Delete
3.96 kB
#!/bin/bash
set -e
echo "=========================================="
echo " PyRunner - Starting up..."
echo "=========================================="
# Validate required environment variables
if [ -z "$SECRET_KEY" ]; then
echo ""
echo "ERROR: SECRET_KEY is required but not set."
echo ""
echo "Generate one with:"
echo " python -c \"from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())\""
echo ""
exit 1
fi
if [ -z "$ENCRYPTION_KEY" ]; then
echo ""
echo "ERROR: ENCRYPTION_KEY is required but not set."
echo ""
echo "Generate one with:"
echo " python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
echo ""
exit 1
fi
# Run setup (migrations + default environment)
echo "[*] Running setup..."
python manage.py setup
echo ""
echo "[*] Starting services..."
# PID file location
QCLUSTER_PID_FILE="/tmp/qcluster.pid"
# Function to start qcluster worker
start_qcluster() {
echo " - Starting django-q2 worker..."
python manage.py qcluster &
QCLUSTER_PID=$!
echo $QCLUSTER_PID > "$QCLUSTER_PID_FILE"
echo " - Worker started with PID $QCLUSTER_PID"
}
# Function to stop qcluster gracefully
stop_qcluster() {
if [ -f "$QCLUSTER_PID_FILE" ]; then
local pid=$(cat "$QCLUSTER_PID_FILE")
if kill -0 $pid 2>/dev/null; then
echo "[*] Stopping worker (PID $pid)..."
kill -TERM $pid 2>/dev/null || true
# Wait up to 30 seconds for graceful shutdown
local count=0
while kill -0 $pid 2>/dev/null && [ $count -lt 30 ]; do
sleep 1
count=$((count + 1))
done
# Force kill if still running
if kill -0 $pid 2>/dev/null; then
echo "[!] Worker did not stop gracefully, force killing..."
kill -9 $pid 2>/dev/null || true
fi
fi
rm -f "$QCLUSTER_PID_FILE"
fi
}
# Signal handler for restart request (SIGUSR1)
handle_restart() {
echo ""
echo "[*] Restart signal received, restarting workers..."
stop_qcluster
start_qcluster
echo "[*] Workers restarted successfully"
}
trap handle_restart SIGUSR1
# Handle graceful shutdown
cleanup() {
echo ""
echo "[*] Shutting down..."
stop_qcluster
# Kill monitor if running
if [ -n "$MONITOR_PID" ]; then
kill $MONITOR_PID 2>/dev/null || true
fi
exit 0
}
trap cleanup SIGTERM SIGINT
# Start workers initially
start_qcluster
# Worker monitoring loop (runs in background)
monitor_workers() {
local backoff=1
local max_backoff=60
while true; do
sleep 5
# Check if worker is still running
if [ -f "$QCLUSTER_PID_FILE" ]; then
local pid=$(cat "$QCLUSTER_PID_FILE")
if ! kill -0 $pid 2>/dev/null; then
echo "[!] Worker (PID $pid) died unexpectedly, restarting in ${backoff}s..."
sleep $backoff
start_qcluster
# Increase backoff (exponential with max)
backoff=$((backoff * 2))
if [ $backoff -gt $max_backoff ]; then
backoff=$max_backoff
fi
else
# Worker is running, reset backoff
backoff=1
fi
fi
done
}
monitor_workers &
MONITOR_PID=$!
# Start gunicorn web server
echo " - Starting web server on port ${PORT:-8000}..."
echo ""
echo "=========================================="
echo " PyRunner is ready!"
echo " Open http://localhost:${PORT:-8000}"
echo "=========================================="
echo ""
exec gunicorn pyrunner.wsgi:application \
--bind 0.0.0.0:${PORT:-8000} \
--workers ${GUNICORN_WORKERS:-2} \
--threads ${GUNICORN_THREADS:-4} \
--timeout ${GUNICORN_TIMEOUT:-120} \
--access-logfile - \
--error-logfile -