Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 189 additions & 55 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
name: CI/CD Pipeline

on:
'on':
push:
branches:
- main
Expand All @@ -9,104 +8,239 @@ on:
branches:
- main
- master

jobs:
test:
name: Run Django Tests
runs-on: ubuntu-latest

services:
postgres:
image: 'postgres:14'
env:
POSTGRES_DB: '${{ secrets.DB_NAME }}'
POSTGRES_USER: '${{ secrets.DB_USER }}'
POSTGRES_PASSWORD: '${{ secrets.DB_PASSWORD }}'
ports:
- '5432:5432'
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
--health-retries 5
steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Set up Python 3.8
uses: actions/setup-python@v4
with:
python-version: "3.8"

python-version: '3.8'
- name: Install Dependencies
run: |
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

- name: Decode and set environment variables
run: |
echo "AT_USERNAME=$(echo '${{ secrets.AT_USERNAME }}' | base64 -d)" >> $GITHUB_ENV
echo "AT_SHORT_CODE=$(echo '${{ secrets.AT_SHORT_CODE }}' | base64 -d)" >> $GITHUB_ENV
echo "AT_API_KEY=$(echo '${{ secrets.AT_API_KEY }}' | base64 -d)" >> $GITHUB_ENV
run: >
echo "AT_USERNAME=$(echo '${{ secrets.AT_USERNAME }}' | base64 -d)" >>
$GITHUB_ENV

echo "AT_SHORT_CODE=$(echo '${{ secrets.AT_SHORT_CODE }}' | base64
-d)" >> $GITHUB_ENV

echo "AT_API_KEY=$(echo '${{ secrets.AT_API_KEY }}' | base64 -d)" >>
$GITHUB_ENV
- name: Run Tests
env:
DB_NAME: ${{ secrets.DB_NAME }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
DB_HOST: ${{ secrets.DB_HOST }}
DB_PORT: ${{ secrets.DB_PORT }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
DB_NAME: '${{ secrets.DB_NAME }}'
DB_USER: '${{ secrets.DB_USER }}'
DB_PASSWORD: '${{ secrets.DB_PASSWORD }}'
DB_HOST: localhost
DB_PORT: '5432'
SECRET_KEY: '${{ secrets.SECRET_KEY }}'
run: |
source venv/bin/activate
# Debug: Check if variables are set (without exposing values)
echo "Checking database configuration..."
[ -z "$DB_NAME" ] && echo "DB_NAME is not set" || echo "DB_NAME is set"
[ -z "$DB_USER" ] && echo "DB_USER is not set" || echo "DB_USER is set"
[ -z "$DB_HOST" ] && echo "DB_HOST is not set" || echo "DB_HOST is set"
[ -z "$DB_PORT" ] && echo "DB_PORT is not set" || echo "DB_PORT is set"
pytest -v

pytest -v
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

username: '${{ secrets.DOCKER_USERNAME }}'
password: '${{ secrets.DOCKER_PASSWORD }}'
- name: Build and Push Docker Image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest .
docker push ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest
run: >
docker build -t ${{ secrets.DOCKER_USERNAME
}}/e-commerce-service:latest .

docker push ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest
deploy:
name: Deploy to Kubernetes
name: Deploy to Minikube
runs-on: ubuntu-latest
needs: build-and-push

needs: test
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Check Minikube and Kubectl Installation
id: check-tools
run: |
MINIKUBE_INSTALLED=$(which minikube || echo "false")
KUBECTL_INSTALLED=$(which kubectl || echo "false")

echo "Minikube installed: $MINIKUBE_INSTALLED"
echo "Kubectl installed: $KUBECTL_INSTALLED"

echo "minikube_exists=$MINIKUBE_INSTALLED" >> $GITHUB_OUTPUT
echo "kubectl_exists=$KUBECTL_INSTALLED" >> $GITHUB_OUTPUT
- name: Install Minikube (if needed)
if: steps.check-tools.outputs.minikube_exists == 'false'
run: >
curl -LO
https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64

sudo install minikube-linux-amd64 /usr/local/bin/minikube
- name: Install Kubectl (if needed)
if: steps.check-tools.outputs.kubectl_exists == 'false'
run: >
curl -LO "https://dl.k8s.io/release/$(curl -L -s
https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

sudo install -o root -g root -m 0755 kubectl
/usr/local/bin/kubectl

- name: Reset Kubernetes Cluster
run: |
# Stop any running Minikube instance
minikube stop || true

# Delete existing Minikube cluster
minikube delete || true

# Restart Minikube with optimized resources
minikube start \
--driver=docker \
--kubernetes-version=v1.27.1 \
--cpus=2 \
--memory=4g \
--wait=all \
--force

# Wait for cluster to be fully ready
kubectl wait --for=condition=Ready nodes --all --timeout=300s

- name: Set Up Kubectl
uses: azure/setup-kubectl@v3
with:
version: 'latest'
- name: Clean Up Existing Resources
run: |
# Remove all existing resources
kubectl delete all --all || true
kubectl delete pvc --all || true
kubectl delete secrets --all || true

# Wait for resources to be terminated
sleep 15

- name: Validate KUBECONFIG Secret
- name: Configure Kubectl Context
run: |
if [ -z "${{ secrets.KUBECONFIG }}" ]; then
echo "Error: KUBECONFIG secret is missing!"
exit 1
fi
kubectl config use-context minikube
kubectl config set-cluster minikube --insecure-skip-tls-verify=true

- name: Deploy Configuration
run: |
# Apply ConfigMaps
kubectl apply -f configmap.yaml

# Apply Secrets
kubectl apply -f secrets.yaml

# Verify resources
kubectl get configmaps
kubectl get secrets


- name: Verify Minikube Setup
run: >
minikube status

minikube ip

kubectl cluster-info

- name: Configure Kubeconfig
kubectl get nodes

kubectl version || true # Use || true to prevent failure if version
command is incompatible

- name: Create Kubernetes Secrets
env:
DB_NAME: '${{ secrets.DB_NAME }}'
DB_USER: '${{ secrets.DB_USER }}'
DB_PASSWORD: '${{ secrets.DB_PASSWORD }}'
DB_HOST: postgres-service
DB_PORT: '5432'
SECRET_KEY: '${{ secrets.SECRET_KEY }}'
run: |
echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig.yaml
export KUBECONFIG=$(pwd)/kubeconfig.yaml
echo "KUBECONFIG=$(pwd)/kubeconfig.yaml" >> $GITHUB_ENV
# Create secret only if it doesn't exist
kubectl get secret django-secrets || \
kubectl create secret generic django-secrets \
--from-literal=DB_NAME="$DB_NAME" \
--from-literal=DB_USER="$DB_USER" \
--from-literal=DB_PASSWORD="$DB_PASSWORD" \
--from-literal=DB_HOST="$DB_HOST" \
--from-literal=DB_PORT="$DB_PORT" \
--from-literal=SECRET_KEY="$SECRET_KEY"
- name: Log in to Docker Hub
run: >
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{
secrets.DOCKER_PASSWORD }}

- name: Deploy Postgres to Kubernetes
- name: Deploy Postgres to Minikube
run: |
kubectl apply -f k8s/postgres.yaml --validate=false
echo "Checking Postgres Deployment Details:"
kubectl get deployments
kubectl get pods
kubectl describe deployment postgres
kubectl get events --sort-by='.metadata.creationTimestamp'
kubectl rollout status deployment/postgres --timeout=120s

- name: Check Postgres Deployment Logs
run: |
kubectl apply -f k8s/postgres.yaml
kubectl rollout status statefulset/postgres --timeout=120s
sleep 10
POSTGRES_PODS=$(kubectl get pods -l app=postgres -o jsonpath='{.items[*].metadata.name}')
for pod in $POSTGRES_PODS; do
echo "Logs for pod: $pod"
kubectl logs $pod || true
echo "---"
done

- name: Verify Persistent Volume and Claims
run: |
kubectl get pvc
kubectl describe pvc postgres-pvc
kubectl get storageclass

- name: Wait for Postgres to be Ready
run: >
kubectl wait --for=condition=Ready pod -l app=postgres --timeout=180s || {
echo "Postgres pod did not become ready in time"
kubectl get pods
kubectl describe pods
exit 1
}

- name: Deploy Application to Kubernetes
- name: Pull and Deploy Application to Minikube
run: |
eval $(minikube docker-env)
docker pull ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest
minikube image load ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest
minikube image ls | grep e-commerce-service
sed -i 's|image: .*|image: ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest|' k8s/deployment.yaml
kubectl apply -f k8s/deployment.yaml
kubectl set image deployment/ecommerce-app ecommerce-app=${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest
kubectl rollout status deployment/ecommerce-app --timeout=120s

- name: Check Deployment Status
run: |
kubectl get pods
kubectl get services
kubectl describe deployment/ecommerce-app
kubectl describe pods

2 changes: 1 addition & 1 deletion entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh

echo "Waiting for database to be ready..."
while ! nc -z "$DATABASE_HOST" "$DATABASE_PORT"; do
while ! nc -z "$DB_HOST" "$DB_PORT"; do
sleep 2
done

Expand Down
28 changes: 20 additions & 8 deletions k8s/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ metadata:
spec:
replicas: 1
strategy:
type: Recreate
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: ecommerce-app
Expand All @@ -17,11 +20,20 @@ spec:
initContainers:
- name: wait-for-db
image: busybox
command: ['sh', '-c', 'until nc -z postgres-service 5432; do echo waiting for db; sleep 2; done;']
command:
- 'sh'
- '-c'
- |
echo "Checking PostgreSQL connection..."
until nc -z -v -w5 postgres-service 5432; do
echo "Waiting for PostgreSQL to be ready...";
sleep 5;
done
echo "PostgreSQL is ready!"
containers:
- name: ecommerce-app
image: ${{ secrets.DOCKER_USERNAME }}/e-commerce-service:latest
imagePullPolicy: Always
imagePullPolicy: Always
ports:
- containerPort: 8000
envFrom:
Expand All @@ -31,16 +43,16 @@ spec:
name: django-secrets
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "1000m"
readinessProbe:
httpGet:
path: /api/v1/health/
port: 8000
initialDelaySeconds: 30
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 5
Expand Down
Loading