Skip to content
Open
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
47 changes: 36 additions & 11 deletions .github/workflows/backend-cd.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
name: CD - Deploy Backend Services to AKS
deploy_backend_to_aks:
name: CD - Deploy Backend Services to AKS
runs-on: ubuntu-latest
environment: Production

on:
workflow_dispatch:
workflow_call:
inputs:
aks_cluster_name:
description: 'Name of the AKS Cluster to deploy to'
required: true
default: '<aks_cluster_name>'
required: true
type: string
aks_resource_group:
description: 'Resource Group of the AKS Cluster'
required: true
default: '<resource_group_name>'
required: true
type: string
aks_acr_name:
description: 'Name of ACR'
required: true
default: '<acr_name>'
required: true
type: string
outputs:
PRODUCT_API_IP:
description: 'External IP of Product Service'
value: ${{ jobs.deploy_backend.outputs.PRODUCT_API_IP }}
ORDER_API_IP:
description: 'External IP of Order Service'
value: ${{ jobs.deploy_backend.outputs.ORDER_API_IP }}

# workflow_dispatch:
# inputs:
# aks_cluster_name:
# description: 'Name of the AKS Cluster to deploy to'
# required: true
# default: 'parvathy'
# aks_resource_group:
# description: 'Resource Group of the AKS Cluster'
# required: true
# default: 'week8'
# aks_acr_name:
# description: 'Name of ACR'
# required: true
# default: 'parvathy'
push:
branches:
- main

jobs:
deploy_backend:
Expand Down
213 changes: 88 additions & 125 deletions .github/workflows/backend_ci.yml
Original file line number Diff line number Diff line change
@@ -1,146 +1,109 @@
# week08/.github/workflows/backend_ci.yml
name: Backend CD - Deploy to AKS

name: Backend CI - Test, Build and Push Images to ACR

# Trigger the workflow on pushes to the 'main' branch
# You can also add 'pull_request:' to run on PRs
# Only deploy when code is pushed to main branch
on:
# Manual trigger
workflow_dispatch:

# Automatically on pushes to main branch
push:
branches:
- main
paths: # Only trigger if changes are in backend directories
branches: [ main ]
paths:
- 'backend/**'
- '.github/workflows/backend_ci.yml' # Trigger if this workflow file changes

# Define global environment variables that can be used across jobs
env:
# ACR Login Server (e.g., myregistry.azurecr.io)
# This needs to be set as a GitHub Repository Secret
ACR_LOGIN_SERVER: ${{ secrets.AZURE_CONTAINER_REGISTRY }}
# Dynamically generate image tags based on Git SHA and GitHub Run ID
# This provides unique, traceable tags for each image build
IMAGE_TAG: ${{ github.sha }}-${{ github.run_id }}
- 'k8s/**'
- '.github/workflows/backend-cd.yml'

jobs:
# Job 1: Run tests and linting for all backend services
test_and_lint_backends:
runs-on: ubuntu-latest # Use a GitHub-hosted runner

services:
# Product DB container
product_db:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: products
# Make pg_isready available so the service is healthy before tests run
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

# Order DB
order_db:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: orders
ports:
- 5433:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
deploy_backend_to_aks:
runs-on: ubuntu-latest
environment: Production

outputs:
PRODUCT_API_IP: ${{ steps.get_product_ip.outputs.external_ip }}
ORDER_API_IP: ${{ steps.get_order_ip.outputs.external_ip }}

steps:
# 1. Checkout the repository code to the runner
- name: Checkout repository
uses: actions/checkout@v4 # Action to check out your repository code
uses: actions/checkout@v4

# 2. Set up Python environment
- name: Set up Python 3.10
uses: actions/setup-python@v5 # Action to set up Python environment
- name: Log in to Azure
uses: azure/login@v1
with:
python-version: '3.10'
creds: ${{ secrets.AZURE_CREDENTIALS }}
enable-AzPSSession: true

# 3. Install dependencies and run code quality checks
- name: Install dependencies
run: | # Use a multi-line script to install pip dependencies
pip install --upgrade pip
# Loop through each backend service folder
for req in backend/*/requirements.txt; do
echo "Installing $req"
pip install -r "$req"
done
# Install CI tools
pip install pytest httpx
- name: Set Kubernetes context
run: |
az aks get-credentials --resource-group week8 --name parvathy --overwrite-existing

- name: Attach ACR
run: |
az aks update --name parvathy --resource-group week8 --attach-acr parvathy

# 5. Run tests for product service
- name: Run product_service tests
working-directory: backend/product_service
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
POSTGRES_DB: products
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
- name: Deploy Backend Infrastructure
run: |
pytest tests --maxfail=1 --disable-warnings -q
echo "Deploying backend infrastructure..."
cd k8s/
kubectl apply -f configmaps.yaml
kubectl apply -f secrets.yaml
kubectl apply -f product-db.yaml
kubectl apply -f order-db.yaml

- name: Deploy Backend Microservices
run: |
echo "Deploying backend microservices..."
cd k8s/
kubectl apply -f product-service.yaml
kubectl apply -f order-service.yaml

# 6. Run tests for order service
- name: Run order_service tests
working-directory: backend/order_service
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5433
POSTGRES_DB: orders
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
- name: Wait for Backend LoadBalancer IPs
run: |
pytest tests --maxfail=1 --disable-warnings -q
echo "Waiting for Product, Order LoadBalancer IPs to be assigned (up to 5 minutes)..."
PRODUCT_IP=""
ORDER_IP=""

for i in $(seq 1 60); do
echo "Attempt $i/60 to get IPs..."
PRODUCT_IP=$(kubectl get service product-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
ORDER_IP=$(kubectl get service order-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

if [[ -n "$PRODUCT_IP" && -n "$ORDER_IP" ]]; then
echo "All backend LoadBalancer IPs assigned!"
echo "Product Service IP: $PRODUCT_IP"
echo "Order Service IP: $ORDER_IP"
break
fi
sleep 5
done

if [[ -z "$PRODUCT_IP" || -z "$ORDER_IP" ]]; then
echo "Error: One or more LoadBalancer IPs not assigned after timeout."
exit 1
fi

echo "PRODUCT_IP=$PRODUCT_IP" >> $GITHUB_ENV
echo "ORDER_IP=$ORDER_IP" >> $GITHUB_ENV

- name: Capture Product Service IP for Workflow Output
id: get_product_ip
run: echo "external_ip=${{ env.PRODUCT_IP }}" >> $GITHUB_OUTPUT

- name: Capture Order Service IP for Workflow Output
id: get_order_ip
run: echo "external_ip=${{ env.ORDER_IP }}" >> $GITHUB_OUTPUT

# Job 2: Build and Push Docker Images (runs only if tests pass)
build_and_push_images:
- name: Logout from Azure
run: az logout

trigger_frontend_deployment:
name: Trigger Frontend Deployment
runs-on: ubuntu-latest
needs: test_and_lint_backends
needs: deploy_backend_to_aks
environment: Production

steps:
- name: Checkout repository
uses: actions/checkout@v4

# Azure login using a Service Principal secret
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }} # Needs to be set as a GitHub Secret (Service Principal JSON)

# Login to Azure Container Registry (ACR)
- name: Login to Azure Container Registry
run: az acr login --name ${{ env.ACR_LOGIN_SERVER }}

# Build and Push Docker image for Product Service
- name: Build and Push Product Service Image
run: |
docker build -t ${{ env.ACR_LOGIN_SERVER }}/product_service:latest ./backend/product_service/
docker push ${{ env.ACR_LOGIN_SERVER }}/product_service:latest

# Build and Push Docker image for Order Service
- name: Build and Push Order Service Image
run: |
docker build -t ${{ env.ACR_LOGIN_SERVER }}/order_service:latest ./backend/order_service/
docker push ${{ env.ACR_LOGIN_SERVER }}/order_service:latest
- name: Checkout repository
uses: actions/checkout@v4

# Logout from Azure for security (runs even if image push fails)
- name: Logout from Azure
run: az logout
if: always()
- name: Trigger Frontend CD Workflow
uses: benc-uk/workflow-dispatch@v1
with:
workflow: frontend-cd.yml
token: ${{ secrets.GITHUB_TOKEN }}
inputs: '{"product_api_ip": "${{ needs.deploy_backend_to_aks.outputs.PRODUCT_API_IP }}", "order_api_ip": "${{ needs.deploy_backend_to_aks.outputs.ORDER_API_IP }}", "aks_cluster_name": "parvathy", "aks_resource_group": "week8"}'
73 changes: 73 additions & 0 deletions .github/workflows/ci_cd_pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# name: CI/CD Pipeline - Test, Build, Deploy

# on:
# push:
# branches:
# - main
# # Only trigger CD if there are changes to application code
# paths:
# - 'backend/**'
# - 'frontend/**'
# - 'k8s/**'
# - '.github/workflows/backend_ci.yml'
# - '.github/workflows/frontend_ci.yml'
# - '.github/workflows/backend-cd.yml'
# - '.github/workflows/frontend-cd.yml'
# - '.github/workflows/ci_cd_pipeline.yml'

# workflow_dispatch:
# inputs:
# aks_cluster_name:
# description: 'Name of the AKS Cluster to deploy to'
# required: true
# default: 'parvathy' # REPLACE with your default
# aks_resource_group:
# description: 'Resource Group of the AKS Cluster'
# required: true
# default: 'week8' # REPLACE with your default
# aks_acr_name:
# description: 'Name of ACR'
# required: true
# default: 'parvathy' # REPLACE with your default

# jobs:
# # Job 1: Run CI for both backend and frontend. Note: This assumes you've made the CI workflows callable (Step 3A).
# run_ci:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4

# # Run Backend CI as a reusable workflow (assumes you have a commit with the latest code to build)
# - name: Run Backend CI
# uses: ./.github/workflows/backend_ci.yml
# # Note: Since the backend_ci.yml now has workflow_call, this will run it.
# # This will only run build and push if the underlying logic is correct.
# # For a truly linked pipeline, we would call the jobs directly, or rely on the push trigger.
# # Since we changed backend_ci to only run on PR, we'll revert it back to push main
# # OR use a more complex setup. For simplicity, we'll keep the backend/frontend CI push triggers
# # and rely on them to push :latest on merge to main.

# # Job 2: Deploy Backend
# deploy_backend:
# runs-on: ubuntu-latest
# needs: run_ci # Deploy only if CI is successful (CI should have pushed images)
# uses: ./.github/workflows/backend-cd.yml # Call the backend CD workflow as a reusable workflow
# with:
# aks_cluster_name: ${{ github.event.inputs.aks_cluster_name || 'parvathy' }} # Use input or default
# aks_resource_group: ${{ github.event.inputs.aks_resource_group || 'week8' }} # Use input or default
# aks_acr_name: ${{ github.event.inputs.aks_acr_name || 'parvathy' }} # Use input or default
# secrets: inherit # Pass secrets to the called workflow

# # Job 3: Deploy Frontend
# deploy_frontend:
# runs-on: ubuntu-latest
# needs: deploy_backend # Deploy frontend only after backend is deployed and IPs are available
# uses: ./.github/workflows/frontend-cd.yml # Call the frontend CD workflow as a reusable workflow
# with:
# aks_cluster_name: ${{ github.event.inputs.aks_cluster_name || 'parvathy' }}
# aks_resource_group: ${{ github.event.inputs.aks_resource_group || 'week8' }}
# # Pass the dynamic IPs from the output of the deploy_backend job
# product_api_ip: http://${{ needs.deploy_backend.outputs.PRODUCT_API_IP }}:8000
# order_api_ip: http://${{ needs.deploy_backend.outputs.ORDER_API_IP }}:8001
# secrets: inherit # Pass secrets to the called workflow
Loading