Skip to content

Commit 8d4cd8d

Browse files
Merge pull request #54 from druling/handle_bounce_email
adding blocked email module
2 parents 023ef79 + b31226b commit 8d4cd8d

File tree

23 files changed

+287
-37
lines changed

23 files changed

+287
-37
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Build, Push, and Deploy
33
on:
44
push:
55
branches:
6-
- cors_fix
6+
- handle_bounce_email
77

88
jobs:
99
build-and-push:

build/docker/Dockerfile

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ ENV PYTHONUNBUFFERED 1 # Force the stdout and stderr streams to be unbuff
77

88
# Install system dependencies
99
RUN apt-get update && apt-get install -y --no-install-recommends \
10-
postgresql-client \
11-
supervisor && \
10+
curl \
11+
postgresql-client && \
1212
apt-get clean && rm -rf /var/lib/apt/lists/*
1313

1414
# Set the working directory in the container
@@ -26,17 +26,14 @@ RUN pip install --no-cache-dir -r requirements.txt
2626
# Copy the rest of the application code to the container
2727
COPY ../../ /app/
2828

29-
# Copy the supervisord configuration file
30-
COPY /build/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
31-
3229
# Copy the entrypoint configuration file
3330
COPY /build/docker/entrypoint.sh /app/entrypoint.sh
3431

35-
# Make the entrypoint script executable
36-
RUN chmod +x /app/entrypoint.sh
37-
3832
# Expose port 8000 for the Django server
3933
EXPOSE 8000
4034

41-
# Use the entrypoint script
35+
# Make the entrypoint script executable
36+
RUN chmod +x /app/entrypoint.sh
37+
38+
# Set the entrypoint script
4239
ENTRYPOINT ["/app/entrypoint.sh"]

build/docker/entrypoint.sh

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
11
#!/bin/bash
2-
32
set -e
43

4+
# Function to gracefully shut down Gunicorn
5+
shutdown() {
6+
echo "Shutdown signal received. Stopping Gunicorn gracefully..."
7+
8+
# Gracefully stop Gunicorn workers (stops accepting new requests)
9+
kill -SIGTERM $GUNICORN_PID
10+
11+
# Allow some time for in-progress requests to complete
12+
echo "Waiting for ongoing requests to finish..."
13+
sleep 10 # Adjust time as needed
14+
15+
# Force kill Gunicorn if it's still running after the grace period
16+
kill -9 $GUNICORN_PID 2>/dev/null || true
17+
18+
echo "Shutdown complete."
19+
exit 0
20+
}
21+
22+
# Trap SIGTERM and SIGINT signals (container stop or AWS shutdown notice)
23+
trap shutdown SIGTERM SIGINT
24+
525
# Apply database migrations
626
echo "Applying database migrations..."
727
python manage.py migrate
828

929
# Set up email templates with the --force option
10-
python manage.py setup_email_templates
30+
echo "Setting up email templates..."
31+
python manage.py setup_email_templates --force
32+
33+
# Start Gunicorn in the background with graceful timeout
34+
echo "Starting Gunicorn..."
35+
gunicorn --bind 0.0.0.0:8000 setup.wsgi:application --workers 4 --timeout 90 &
36+
37+
# Store the PID of the Gunicorn process
38+
GUNICORN_PID=$!
39+
40+
# Poll AWS metadata service for spot termination notice
41+
while true; do
42+
TERMINATION_INFO=$(curl -s http://169.254.169.254/latest/meta-data/spot/instance-action || true)
43+
if [ -n "$TERMINATION_INFO" ]; then
44+
echo "AWS Spot termination notice detected! Gracefully shutting down..."
45+
shutdown
46+
fi
47+
sleep 5
48+
done
1149

12-
# Start supervisord
13-
echo "Starting supervisord..."
14-
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf
50+
# Wait for Gunicorn to exit
51+
wait $GUNICORN_PID

build/docker/supervisord.conf

Lines changed: 0 additions & 18 deletions
This file was deleted.

commons/utils/__init__.py

Whitespace-only changes.

commons/utils/parse_plain_text.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import json
2+
3+
from rest_framework.parsers import BaseParser
4+
5+
6+
class PlainTextParser(BaseParser):
7+
"""
8+
Plain text parser that handles SNS messages sent as text/plain
9+
"""
10+
11+
media_type = "text/plain"
12+
13+
def parse(self, stream, media_type=None, parser_context=None):
14+
"""
15+
Simply return a string representing the body of the request.
16+
"""
17+
try:
18+
return json.loads(stream.read().decode("utf-8"))
19+
except json.JSONDecodeError as e:
20+
raise e

email_config/__init__.py

Whitespace-only changes.

email_config/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class EmailConfig(AppConfig):
5+
name = "email_config"

email_config/enums/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .blocked_type import BlockedType
2+
3+
__all__ = [BlockedType]

email_config/enums/blocked_type.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from commons.enums.BaseEnum import BaseEnum
2+
3+
4+
class BlockedType(BaseEnum):
5+
PERMANENT = "permanent"
6+
TEMPORARY = "temporary"
7+
UNBLOCKED = "unblocked"

0 commit comments

Comments
 (0)