Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1ec2e85
Point manifests to my ACR and current backend IPs
chaitanyakotagiri27 Sep 16, 2025
da9cd08
9.2C: backend CI - PR triggers, SHA+latest tags, login v2, ACR login fix
chaitanyakotagiri27 Sep 16, 2025
b13d60f
9.2C: frontend CI -> login v2, SHA+latest tags, correct ACR login
chaitanyakotagiri27 Sep 16, 2025
9fa8944
CD: use azure/login@v2 for backend-cd
chaitanyakotagiri27 Sep 16, 2025
67ca86d
Update frontend-cd.yml
chaitanyakotagiri27 Sep 16, 2025
dd5db19
Update backend_ci.yml
chaitanyakotagiri27 Sep 16, 2025
24eb556
Update frontend_ci.yml
chaitanyakotagiri27 Sep 16, 2025
d20dc86
Update backend-cd.yml
chaitanyakotagiri27 Sep 16, 2025
69a2cd7
Update frontend-cd.yml
chaitanyakotagiri27 Sep 16, 2025
a6f4c80
Trigger CI to push SHA tags
chaitanyakotagiri27 Sep 16, 2025
5bc7db4
Update frontend_ci.yml
chaitanyakotagiri27 Sep 16, 2025
ff48566
Update frontend_ci.yml
chaitanyakotagiri27 Sep 16, 2025
13221c2
fix(cd): use repo vars + namespace; idempotent ACR attach
chaitanyakotagiri27 Sep 17, 2025
888c8f0
ci: trigger Backend CI via push
chaitanyakotagiri27 Sep 17, 2025
5a8319a
chore: k8s updates
chaitanyakotagiri27 Sep 17, 2025
d1366c1
ci: trigger backend build & push
chaitanyakotagiri27 Sep 17, 2025
f84c508
cd: ignore attach-acr RBAC; kubelet already has AcrPull
chaitanyakotagiri27 Sep 17, 2025
46d3409
cd: tolerate attach-acr; auto-detect service names; wait for IPs
chaitanyakotagiri27 Sep 17, 2025
8e6fc2b
cd: remove environment gate; tolerate attach-acr; auto-detect svc nam…
chaitanyakotagiri27 Sep 17, 2025
45aa535
cd: auto-detect service names + tolerant ACR attach
chaitanyakotagiri27 Sep 17, 2025
5e5bb6e
cd: robust ACR attach + svc detect + longer LB wait
chaitanyakotagiri27 Sep 17, 2025
d2a3fcf
cd: force LoadBalancer type + longer wait + debug
chaitanyakotagiri27 Sep 17, 2025
b6aefff
fix(product svc): external port 80 (targetPort 8000)
chaitanyakotagiri27 Sep 17, 2025
e2ea78c
fix(product): correct ACR image and service port/name; order svc port…
chaitanyakotagiri27 Sep 17, 2025
33e4873
fix(product): use correct ACR and expose service on port 80
chaitanyakotagiri27 Sep 17, 2025
4cc925b
config: PRODUCT_SERVICE_URL -> service DNS (port 80); keep ORDER on :…
chaitanyakotagiri27 Sep 17, 2025
a1b505a
frontend: point to AKS product/order services
chaitanyakotagiri27 Sep 17, 2025
496b411
fix(frontend): ACR image + LB service on port 80
chaitanyakotagiri27 Sep 17, 2025
35f4075
cd(frontend): update workflow + labels; deploy to week08
chaitanyakotagiri27 Sep 17, 2025
4d8cf5d
cd(frontend): target week08 namespace; direct name lookups; correct A…
chaitanyakotagiri27 Sep 17, 2025
9c48c89
frontend: point to AKS Product 20.53.102.217 (80) and Order 20.53.103…
chaitanyakotagiri27 Sep 17, 2025
caaa3c4
frontend: Order on port 80 (no :8001)
chaitanyakotagiri27 Sep 17, 2025
0802b5c
frontend: point to AKS product (80) and order (8001)
chaitanyakotagiri27 Sep 17, 2025
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
165 changes: 92 additions & 73 deletions .github/workflows/backend-cd.yml
Original file line number Diff line number Diff line change
@@ -1,101 +1,120 @@
name: CD - Deploy Backend Services to AKS

on:
workflow_dispatch:
inputs:
aks_cluster_name:
description: 'Name of the AKS Cluster to deploy to'
required: true
default: '<aks_cluster_name>'
aks_resource_group:
description: 'Resource Group of the AKS Cluster'
required: true
default: '<resource_group_name>'
aks_acr_name:
description: 'Name of ACR'
required: true
default: '<acr_name>'
workflow_dispatch: {}

env:
RG: ${{ vars.AKS_RESOURCE_GROUP }}
AKS: ${{ vars.AKS_CLUSTER_NAME }}
NS: ${{ vars.AKS_NAMESPACE }}
ACR_NAME: ${{ vars.ACR_NAME }}

jobs:
deploy_backend:
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 }}
PRODUCT_API_IP: ${{ steps.out_prod_ip.outputs.external_ip }}
ORDER_API_IP: ${{ steps.out_order_ip.outputs.external_ip }}

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

- name: Log in to Azure
uses: azure/login@v1
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
enable-AzPSSession: true

- name: Set Kubernetes context (get AKS credentials)
- name: Get AKS credentials
run: az aks get-credentials --resource-group "$RG" --name "$AKS" --overwrite-existing

- name: Ensure namespace exists
run: kubectl get ns "$NS" || kubectl create ns "$NS"

# Never fail on ACR attach; skip if you don't have Owner.
- name: Attach ACR (idempotent; ignore if no rights)
shell: bash
run: |
set +e
az aks update --name "$AKS" --resource-group "$RG" --attach-acr "$ACR_NAME"
[ $? -ne 0 ] && echo "Skipping ACR attach (no Owner rights)" || true
exit 0

- name: Apply infra (ConfigMaps, Secrets, DBs)
working-directory: k8s
run: |
az aks get-credentials --resource-group ${{ github.event.inputs.aks_resource_group }} --name ${{ github.event.inputs.aks_cluster_name }} --overwrite-existing
kubectl -n "$NS" apply -f configmaps.yaml
kubectl -n "$NS" apply -f secrets.yaml
kubectl -n "$NS" apply -f product-db.yaml
kubectl -n "$NS" apply -f order-db.yaml

- name: Attach ACR
- name: Deploy product & order services
working-directory: k8s
run: |
az aks update --name ${{ github.event.inputs.aks_cluster_name }} --resource-group ${{ github.event.inputs.aks_resource_group }} --attach-acr ${{ github.event.inputs.aks_acr_name }}
kubectl -n "$NS" apply -f product-service.yaml
kubectl -n "$NS" apply -f order-service.yaml
echo "--- services after apply ---"
kubectl -n "$NS" get svc -o wide

- name: Deploy Backend Infrastructure (Namespace, ConfigMaps, Secrets, Databases)
- name: Detect service names (product/order)
id: detect_svcs
shell: bash
run: |
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 (Product, Order)
set -e
PROD_NAME=$(kubectl -n "$NS" get svc -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep -E '^product' | head -1 || true)
ORD_NAME=$(kubectl -n "$NS" get svc -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep -E '^order' | head -1 || true)
[ -z "$PROD_NAME" ] && PROD_NAME="product-service"
[ -z "$ORD_NAME" ] && ORD_NAME="order-service"
echo "PRODUCT_SVC=$PROD_NAME" >> $GITHUB_ENV
echo "ORDER_SVC=$ORD_NAME" >> $GITHUB_ENV
echo "Detected PRODUCT_SVC=$PROD_NAME ORDER_SVC=$ORD_NAME"

# 🔧 Force both services to LoadBalancer so they can get public IPs
- name: Ensure services are type LoadBalancer
shell: bash
run: |
echo "Deploying backend microservices..."
cd k8s/
kubectl apply -f product-service.yaml
kubectl apply -f order-service.yaml

- name: Wait for Backend LoadBalancer IPs
for SVC in "$PRODUCT_SVC" "$ORDER_SVC"; do
TYPE=$(kubectl -n "$NS" get svc "$SVC" -o jsonpath='{.spec.type}' 2>/dev/null || echo "")
if [ "$TYPE" != "LoadBalancer" ]; then
echo "Patching $SVC to type LoadBalancer (was '$TYPE')"
kubectl -n "$NS" patch svc "$SVC" --type merge -p '{"spec":{"type":"LoadBalancer"}}'
else
echo "$SVC already LoadBalancer"
fi
done
echo "--- services after patch ---"
kubectl -n "$NS" get svc -o wide

- name: Wait for LoadBalancer IPs (<=10 min)
shell: bash
run: |
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
set -e
for i in {1..120}; do
PROD_IP=$(kubectl -n "$NS" get svc "$PRODUCT_SVC" -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || true)
ORD_IP=$(kubectl -n "$NS" get svc "$ORDER_SVC" -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || true)
echo "Attempt $i: product=${PROD_IP:-<none>} order=${ORD_IP:-<none>}"
if [[ -n "$PROD_IP" && -n "$ORD_IP" ]]; then
echo "PRODUCT_IP=$PROD_IP" >> $GITHUB_ENV
echo "ORDER_IP=$ORD_IP" >> $GITHUB_ENV
exit 0
fi
sleep 5 # Wait 5 seconds before next attempt
sleep 5
done

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

# These are environment variables for subsequent steps in the *same job*
# And used to set the job outputs
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
echo 'Timed out waiting for external IPs'
echo '--- describe services ---'
kubectl -n "$NS" describe svc "$PRODUCT_SVC" || true
kubectl -n "$NS" describe svc "$ORDER_SVC" || true
echo '--- recent events ---'
kubectl -n "$NS" get events --sort-by=.lastTimestamp | tail -n 100 || true
exit 1

- id: out_prod_ip
run: echo "external_ip=${{ env.PRODUCT_IP }}" >> $GITHUB_OUTPUT

- name: Capture Order Service IP for Workflow Output
id: get_order_ip

- id: out_order_ip
run: echo "external_ip=${{ env.ORDER_IP }}" >> $GITHUB_OUTPUT

- name: Logout from Azure
- name: Azure Logout
if: always()
run: az logout
154 changes: 58 additions & 96 deletions .github/workflows/backend_ci.yml
Original file line number Diff line number Diff line change
@@ -1,146 +1,108 @@
# week08/.github/workflows/backend_ci.yml
name: Backend CI — test, build & push (ACR)

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
on:
# Manual trigger
workflow_dispatch:

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

# 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 }}
ACR_NAME: ${{ secrets.ACR_NAME }} # <— ACR registry NAME (no .azurecr.io)

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

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
ports: [ "5432:5432" ]
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

# Order DB
--health-interval 10s --health-timeout 5s --health-retries 5

order_db:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: orders
ports:
- 5433:5432
ports: [ "5433:5432" ]
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
--health-interval 10s --health-timeout 5s --health-retries 5

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: Set REGISTRY_SERVER env
run: echo "REGISTRY_SERVER=${{ env.ACR_NAME }}.azurecr.io" >> $GITHUB_ENV

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: "3.10"

# 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
- name: Install deps (all backends)
run: |
pip install --upgrade pip pytest httpx
for req in backend/*/requirements.txt; do
echo "Installing $req"
pip install -r "$req"
echo "Installing $req"; pip install -r "$req";
done
# Install CI tools
pip install pytest httpx

# 5. Run tests for product service
- name: Run product_service tests
- name: Test product_service
working-directory: backend/product_service
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
POSTGRES_DB: products
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
run: |
pytest tests --maxfail=1 --disable-warnings -q

# 6. Run tests for order service
- name: Run order_service tests
run: pytest -q

- name: Test order_service
working-directory: backend/order_service
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5433
POSTGRES_DB: orders
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
run: |
pytest tests --maxfail=1 --disable-warnings -q
run: pytest -q

# Job 2: Build and Push Docker Images (runs only if tests pass)
build_and_push_images:
runs-on: ubuntu-latest
needs: test_and_lint_backends
# Build & push only on non-PR pushes
- name: Azure Login
if: github.event_name == 'push'
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

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

# Logout from Azure for security (runs even if image push fails)
- name: Logout from Azure
run: az logout
if: always()
- name: ACR login (name, not FQDN)
if: github.event_name == 'push'
run: az acr login -n "${{ env.ACR_NAME }}"

- name: Build & push product_service (SHA + latest)
if: github.event_name == 'push'
run: |
docker build -t $REGISTRY_SERVER/product_service:${GITHUB_SHA} ./backend/product_service
docker tag $REGISTRY_SERVER/product_service:${GITHUB_SHA} $REGISTRY_SERVER/product_service:latest
docker push $REGISTRY_SERVER/product_service:${GITHUB_SHA}
docker push $REGISTRY_SERVER/product_service:latest

- name: Build & push order_service (SHA + latest)
if: github.event_name == 'push'
run: |
docker build -t $REGISTRY_SERVER/order_service:${GITHUB_SHA} ./backend/order_service
docker tag $REGISTRY_SERVER/order_service:${GITHUB_SHA} $REGISTRY_SERVER/order_service:latest
docker push $REGISTRY_SERVER/order_service:${GITHUB_SHA}
docker push $REGISTRY_SERVER/order_service:latest

- name: Logout Azure
if: always()
run: az logout
Loading