Skip to content

Commit 580234e

Browse files
Merge pull request #55 from druling/dev
Release 4 Feb 2025
2 parents c9a73fe + 8d4cd8d commit 580234e

File tree

31 files changed

+408
-55
lines changed

31 files changed

+408
-55
lines changed

.env.example

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
PORT=3000
2-
DEV=True
2+
ENV=dev
3+
DEBUG=True
34
ALLOWED_HOSTS=localhost,127.0.0.1,
45

56
# Google OAuth Credentials
@@ -16,13 +17,12 @@ DATABASE_PORT=5432
1617
# Redis Credentials
1718
REDIS_HOST=localhost
1819
REDIS_PORT=6379
20+
REDIS_PASSWORD=your-redis-password
1921

2022
# JWT Token Lifetime
2123
ACCESS_TOKEN_LIFETIME=3600
2224

2325
# AWS Credentials
24-
AWS_ACCESS_KEY_ID=your-access-key-id
25-
AWS_SECRET_ACCESS_KEY=your-secret-access-key
2626
AWS_DEFAULT_REGION=ap-south-1
2727

2828
# AWS Credentials for Localstack

.github/workflows/build.yml

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
name: Build and Push
1+
name: Build, Push, and Deploy
22

33
on:
44
push:
55
branches:
6-
- gracefull_shutdown
6+
- handle_bounce_email
77

88
jobs:
99
build-and-push:
@@ -46,4 +46,39 @@ jobs:
4646
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
4747
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
4848
49-
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
49+
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
50+
51+
update-ecs-service:
52+
name: Update ECS Service
53+
runs-on: ubuntu-latest
54+
needs: build-and-push
55+
environment: dev
56+
57+
permissions:
58+
id-token: write
59+
contents: read
60+
61+
env:
62+
AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
63+
ECS_CLUSTER: ${{ secrets.ECS_NAME }}
64+
ECS_SERVICE: ${{ secrets.ECS_NAME }}
65+
IMAGE: ${{ needs.build-and-push.outputs.image }}
66+
67+
steps:
68+
- name: Configure AWS credentials
69+
uses: aws-actions/configure-aws-credentials@v4
70+
with:
71+
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
72+
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
73+
74+
- name: Update ECS service
75+
run: |
76+
aws ecs update-service \
77+
--cluster $ECS_CLUSTER \
78+
--service $ECS_SERVICE \
79+
--force-new-deployment \
80+
--region $AWS_REGION \
81+
--no-cli-pager \
82+
> /dev/null 2>&1
83+
84+
echo "Started Deployment"

build/docker/Dockerfile

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ ENV PYTHONDONTWRITEBYTECODE 1 # Prevent Python from writing .pyc files
66
ENV PYTHONUNBUFFERED 1 # Force the stdout and stderr streams to be unbuffered
77

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

1214
# Set the working directory in the container
1315
WORKDIR /app
1416

17+
# Ensure required directories exist
18+
RUN mkdir -p /app/static /app/media
19+
1520
# Copy the requirements file to the container
1621
COPY ../../requirements.txt /app/
1722

@@ -21,17 +26,14 @@ RUN pip install --no-cache-dir -r requirements.txt
2126
# Copy the rest of the application code to the container
2227
COPY ../../ /app/
2328

24-
# Copy the supervisord configuration file
25-
COPY /build/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
26-
2729
# Copy the entrypoint configuration file
2830
COPY /build/docker/entrypoint.sh /app/entrypoint.sh
2931

30-
# Make the entrypoint script executable
31-
RUN chmod +x /app/entrypoint.sh
32-
3332
# Expose port 8000 for the Django server
3433
EXPOSE 8000
3534

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

build/docker/entrypoint.sh

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +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

9-
# Start supervisord
10-
echo "Starting supervisord..."
11-
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf
29+
# Set up email templates with the --force option
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
49+
50+
# Wait for Gunicorn to exit
51+
wait $GUNICORN_PID

build/docker/supervisord.conf

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

build/stack/web/Dockerfile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Use an official Python runtime as a base image
2+
FROM python:3.10-slim
3+
4+
# Set environment variables
5+
ENV PYTHONDONTWRITEBYTECODE 1 # Prevent Python from writing .pyc files
6+
ENV PYTHONUNBUFFERED 1 # Force the stdout and stderr streams to be unbuffered
7+
8+
# Install system dependencies
9+
RUN apt-get update && apt-get install -y --no-install-recommends \
10+
postgresql-client \
11+
supervisor && \
12+
apt-get clean && rm -rf /var/lib/apt/lists/*
13+
14+
# Set the working directory in the container
15+
WORKDIR /app
16+
17+
# Copy the requirements file to the container
18+
COPY ../../requirements.txt /app/
19+
20+
# Install dependencies
21+
RUN pip install --no-cache-dir -r requirements.txt
22+
23+
# Copy the rest of the application code to the container
24+
COPY ../../ /app/
25+
26+
# Expose port 8000 for the Django server
27+
EXPOSE 3000
28+
29+
# Use the entrypoint script
30+
CMD ["python", "manage.py", "runserver", "0.0.0.0:3000"]

commons/clients/boto_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.conf import settings
44

55

6-
def boto_client(service=None):
6+
def boto_client(service=None, config=None):
77
if service is None:
88
raise ValueError("Service name is required")
99

@@ -15,4 +15,6 @@ def boto_client(service=None):
1515
)
1616
else:
1717
# Production AWS configuration
18-
return boto3.client(service, region_name=settings.AWS_DEFAULT_REGION)
18+
return boto3.client(
19+
service, region_name=settings.AWS_DEFAULT_REGION, config=config
20+
)

commons/clients/redis_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
redis_host = os.getenv("REDIS_HOST", "localhost")
66
redis_port = os.getenv("REDIS_PORT", 6379)
7+
redis_pass = os.getenv("REDIS_PASSWORD", None)
78

89

910
class RedisClient:
1011
def __init__(self):
1112
self.client = redis.StrictRedis(
1213
host=redis_host,
1314
port=redis_port,
15+
password=redis_pass,
1416
decode_responses=True, # Automatically decode byte strings to regular strings
1517
)
1618

commons/enums/BaseResourceEnum.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from enum import Enum
2+
from django.conf import settings
3+
4+
5+
env = settings.ENV
6+
7+
8+
class BaseResourceEnum(Enum):
9+
def __str__(self):
10+
return f"{env}-{self.value}"
11+
12+
@property
13+
def value(self) -> str:
14+
return f"{env}-{super().value}"
15+
16+
@classmethod
17+
def choices(cls):
18+
"""Generate choices for use in Django models."""
19+
return [(item.value, item.name) for item in cls]

commons/utils/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)