diff --git a/.github/workflows/backend-cd.yml b/.github/workflows/backend-cd.yml index b3e8b3f4..aa1ae7d7 100644 --- a/.github/workflows/backend-cd.yml +++ b/.github/workflows/backend-cd.yml @@ -1,7 +1,7 @@ name: CD - Deploy Backend Services to AKS on: - workflow_call: # Only callable from backend-ci + workflow_call: inputs: aks_cluster_name: required: true @@ -15,23 +15,26 @@ on: image_tag: required: true type: string + secrets: + AZURE_CREDENTIALS: + required: true 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.get_product_ip.outputs.ip }} + ORDER_API_IP: ${{ steps.get_order_ip.outputs.ip }} steps: - uses: actions/checkout@v4 - - name: Log in to Azure + - name: Azure Login uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} + enable-AzPSSession: true - name: Set Kubernetes context run: | @@ -41,25 +44,32 @@ jobs: - name: Deploy Backend Infrastructure run: | - kubectl apply -f k8s/configmaps.yaml - kubectl apply -f k8s/secrets.yaml - kubectl apply -f k8s/product-db.yaml - kubectl apply -f k8s/order-db.yaml + 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: | - kubectl apply -f k8s/product-service.yaml - kubectl apply -f k8s/order-service.yaml + cd k8s/ + kubectl apply -f product-service.yaml + kubectl apply -f order-service.yaml - - name: Update Backend Images with Correct Tag + - name: Update Backend Images run: | - kubectl set image deployment/product-service \ - product-service=${{ inputs.aks_acr_name }}.azurecr.io/product_service:${{ inputs.image_tag }} -n default - kubectl set image deployment/order-service \ - order-service=${{ inputs.aks_acr_name }}.azurecr.io/order_service:${{ inputs.image_tag }} -n default + kubectl set image deployment/product-service-w08e1 product-service-container=${{ inputs.aks_acr_name }}.azurecr.io/product_service:${{ inputs.image_tag }} + kubectl set image deployment/order-service-w08e1 order-service-container=${{ inputs.aks_acr_name }}.azurecr.io/order_service:${{ inputs.image_tag }} - - id: get_product_ip - run: echo "external_ip=$(kubectl get service product-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" >> $GITHUB_OUTPUT - - id: get_order_ip - run: echo "external_ip=$(kubectl get service order-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" >> $GITHUB_OUTPUT \ No newline at end of file + - name: Get Product Service IP + id: get_product_ip + run: | + ip=$(kubectl get svc product-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo "ip=$ip" >> $GITHUB_OUTPUT + + - name: Get Order Service IP + id: get_order_ip + run: | + ip=$(kubectl get svc order-service-w08e1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo "ip=$ip" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/.github/workflows/backend_ci.yml b/.github/workflows/ci-cd.yml similarity index 50% rename from .github/workflows/backend_ci.yml rename to .github/workflows/ci-cd.yml index c14097fd..36007c0f 100644 --- a/.github/workflows/backend_ci.yml +++ b/.github/workflows/ci-cd.yml @@ -1,34 +1,27 @@ -name: Backend CI - Test, Build and Push Images to ACR +name: Full CI/CD Pipeline - Backend + Frontend on: - # Issue 1 + 3 fix: Run CI on PRs into main (test before production merge) - pull_request: - branches: [ main ] - paths: - - 'backend/**' - - '.github/workflows/backend-ci.yml' - - # Issue 1 fix: Run CI on pushes to development branch push: - branches: [ development ] - paths: - - 'backend/**' - - '.github/workflows/backend-ci.yml' - - workflow_dispatch: + branches: + - development # Run on pushes to development + pull_request: + branches: + - main # Run on PRs into main + workflow_dispatch: # Manual trigger option env: ACR_LOGIN_SERVER: ${{ secrets.AZURE_CONTAINER_REGISTRY }} IMAGE_TAG: ${{ github.sha }} jobs: - test_and_lint_backends: + # ----------------------------- + # 1. Backend - Test & Lint + # ----------------------------- + test_and_lint_backend: runs-on: ubuntu-latest - # Issue 7 fix: Matrix testing strategy: matrix: python-version: ["3.9", "3.10", "3.11"] - os: [ubuntu-latest, windows-latest] services: product_db: image: postgres:15 @@ -49,13 +42,13 @@ jobs: 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 + ports: + - 5433:5432 steps: - uses: actions/checkout@v4 @@ -65,7 +58,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - # Issue 5 fix: Cache dependencies - name: Cache Python packages uses: actions/cache@v4 with: @@ -80,15 +72,30 @@ jobs: - 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 run: pytest tests --maxfail=1 --disable-warnings -q - 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 run: pytest tests --maxfail=1 --disable-warnings -q - build_and_push_images: + # ----------------------------- + # 2. Backend - Build & Push Images + # ----------------------------- + build_backend_images: runs-on: ubuntu-latest - needs: test_and_lint_backends + needs: test_and_lint_backend steps: - uses: actions/checkout@v4 @@ -97,7 +104,6 @@ jobs: with: creds: ${{ secrets.AZURE_CREDENTIALS }} - # Issue 6 fix: Use docker/login-action - name: Docker Login to ACR uses: docker/login-action@v2 with: @@ -105,7 +111,6 @@ jobs: username: ${{ secrets.AZURE_CLIENT_ID }} password: ${{ secrets.AZURE_CLIENT_SECRET }} - # Issue 4 fix: Use SHA-based tags - name: Build and Push Product Service Image run: | docker build -t ${{ env.ACR_LOGIN_SERVER }}/product_service:${{ env.IMAGE_TAG }} ./backend/product_service/ @@ -116,13 +121,68 @@ jobs: docker build -t ${{ env.ACR_LOGIN_SERVER }}/order_service:${{ env.IMAGE_TAG }} ./backend/order_service/ docker push ${{ env.ACR_LOGIN_SERVER }}/order_service:${{ env.IMAGE_TAG }} - # Issue 2 fix: CI calls backend CD + # ----------------------------- + # 3. Backend - Deploy to AKS + # ----------------------------- deploy_backend: - needs: build_and_push_images + needs: build_backend_images uses: ./.github/workflows/backend-cd.yml with: - aks_cluster_name: sit722-aks + aks_cluster_name: sit722wk09aks aks_resource_group: sit722-rg aks_acr_name: sit722acr9 image_tag: ${{ github.sha }} -#test \ No newline at end of file + secrets: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + + # ----------------------------- + # 4. Frontend - Build & Push Image + # ----------------------------- + build_frontend_image: + runs-on: ubuntu-latest + needs: deploy_backend + steps: + - uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Docker Login to ACR + uses: docker/login-action@v2 + with: + registry: ${{ env.ACR_LOGIN_SERVER }} + username: ${{ secrets.AZURE_CLIENT_ID }} + password: ${{ secrets.AZURE_CLIENT_SECRET }} + + - name: Cache Node modules + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('frontend/package-lock.json') }} + + - name: Inject Backend IPs into frontend code + run: | + sed -i "s|_PRODUCT_API_URL_|http://${{ needs.deploy_backend.outputs.PRODUCT_API_IP }}:8000|g" frontend/main.js + sed -i "s|_ORDER_API_URL_|http://${{ needs.deploy_backend.outputs.ORDER_API_IP }}:8001|g" frontend/main.js + + - name: Build and Push Frontend Image + run: | + docker build -t ${{ env.ACR_LOGIN_SERVER }}/frontend:${{ env.IMAGE_TAG }} ./frontend/ + docker push ${{ env.ACR_LOGIN_SERVER }}/frontend:${{ env.IMAGE_TAG }} + + # ----------------------------- + # 5. Frontend - Deploy to AKS + # ----------------------------- + deploy_frontend: + needs: build_frontend_image + uses: ./.github/workflows/frontend-cd.yml + with: + aks_cluster_name: sit722wk09aks # ✅ matches real cluster + aks_resource_group: sit722-rg + aks_acr_name: sit722acr9 # ✅ ACR passed in + image_tag: ${{ github.sha }} + secrets: + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + \ No newline at end of file diff --git a/.github/workflows/frontend-cd.yml b/.github/workflows/frontend-cd.yml index ad63f079..9415d46f 100644 --- a/.github/workflows/frontend-cd.yml +++ b/.github/workflows/frontend-cd.yml @@ -1,23 +1,23 @@ name: CD - Deploy Frontend to AKS on: - workflow_call: # Only callable by frontend-ci + workflow_call: inputs: - product_api_ip: - required: true - type: string - order_api_ip: - required: true - type: string aks_cluster_name: required: true type: string aks_resource_group: required: true type: string + aks_acr_name: + required: true + type: string image_tag: required: true type: string + secrets: + AZURE_CREDENTIALS: + required: true jobs: deploy_frontend: @@ -32,21 +32,14 @@ jobs: with: creds: ${{ secrets.AZURE_CREDENTIALS }} - - name: Inject Backend IPs into Frontend main.js - run: | - sed -i "s|_PRODUCT_API_URL_|${{ inputs.product_api_ip }}|g" frontend/main.js - sed -i "s|_ORDER_API_URL_|${{ inputs.order_api_ip }}|g" frontend/main.js - - - name: Build and Push Frontend Image - run: | - docker build -t ${{ secrets.AZURE_CONTAINER_REGISTRY }}/frontend:${{ inputs.image_tag }} ./frontend/ - docker push ${{ secrets.AZURE_CONTAINER_REGISTRY }}/frontend:${{ inputs.image_tag }} - - name: Set Kubernetes context uses: azure/aks-set-context@v3 with: resource-group: ${{ inputs.aks_resource_group }} cluster-name: ${{ inputs.aks_cluster_name }} - - name: Deploy Frontend to AKS - run: kubectl apply -f k8s/frontend.yaml \ No newline at end of file + - name: Deploy Frontend + run: | + cd k8s/ + kubectl apply -f frontend.yaml + kubectl set image deployment/frontend frontend-container=${{ inputs.aks_acr_name }}.azurecr.io/frontend:${{ inputs.image_tag }} \ No newline at end of file diff --git a/.github/workflows/frontend_ci.yml b/.github/workflows/frontend_ci.yml deleted file mode 100644 index e524971c..00000000 --- a/.github/workflows/frontend_ci.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Frontend CI - Build & Push Image - -on: - pull_request: - branches: [ main ] - paths: - - 'frontend/**' - - '.github/workflows/frontend-ci.yml' - - push: - branches: [ development ] - paths: - - 'frontend/**' - - '.github/workflows/frontend-ci.yml' - - workflow_dispatch: - -env: - ACR_LOGIN_SERVER: ${{ secrets.AZURE_CONTAINER_REGISTRY }} - IMAGE_TAG: ${{ github.sha }} - -jobs: - build_and_push_frontend: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Docker Login to ACR - uses: docker/login-action@v2 - with: - registry: ${{ env.ACR_LOGIN_SERVER }} - username: ${{ secrets.AZURE_CLIENT_ID }} - password: ${{ secrets.AZURE_CLIENT_SECRET }} - - - name: Cache Node modules - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('frontend/package-lock.json') }} - - - name: Build and Push Frontend Image - run: | - docker build -t ${{ env.ACR_LOGIN_SERVER }}/frontend:${{ env.IMAGE_TAG }} ./frontend/ - docker push ${{ env.ACR_LOGIN_SERVER }}/frontend:${{ env.IMAGE_TAG }} - - deploy_frontend: - needs: build_and_push_frontend - uses: ./.github/workflows/frontend-cd.yml - with: - product_api_ip: ${{ needs.deploy_backend.outputs.PRODUCT_API_IP }} - order_api_ip: ${{ needs.deploy_backend.outputs.ORDER_API_IP }} - aks_cluster_name: sit722-aks - aks_resource_group: sit722-rg - image_tag: ${{ github.sha }} \ No newline at end of file diff --git a/frontend/main.js b/frontend/main.js index f321fd91..7226e120 100644 --- a/frontend/main.js +++ b/frontend/main.js @@ -4,8 +4,8 @@ document.addEventListener('DOMContentLoaded', () => { // API endpoints for the Product and Order services. // These ports (30000 for Product, 30001 for Order) are mapped // from the Docker containers to the host machine in docker-compose.yml for Example 2. - const PRODUCT_API_BASE_URL = '_PRODUCT_API_URL_'; - const ORDER_API_BASE_URL = '_ORDER_API_URL_'; + const PRODUCT_API_BASE_URL = 'http://4.147.229.41:8000'; + const ORDER_API_BASE_URL = 'http://4.147.27.56:8001'; // Product Service is named 'product-service-w04e2' and exposes port 8000 internally. //const PRODUCT_API_BASE_URL = 'http://product-service-w04e2:8000'; diff --git a/k8s/frontend.yaml b/k8s/frontend.yaml index e559454b..766e3d04 100644 --- a/k8s/frontend.yaml +++ b/k8s/frontend.yaml @@ -1,5 +1,3 @@ -# week08/k8s/frontend.yaml - apiVersion: apps/v1 kind: Deployment metadata: @@ -18,15 +16,22 @@ spec: spec: containers: - name: frontend-container - image: sit72281pacr.azurecr.io/frontend:latest + development + image: sit722acr9.azurecr.io/frontend:latest # ✅ workflow will override ta + main imagePullPolicy: Always ports: - containerPort: 80 + env: # ✅ added env vars + - name: PRODUCT_API_URL + value: "http://product-service-w08e1:80" + - name: ORDER_API_URL + value: "http://order-service-w08e1:80" --- apiVersion: v1 kind: Service metadata: - name: frontend-w08e1 # Service name matches + name: frontend-w08e1 labels: app: frontend spec: @@ -34,6 +39,6 @@ spec: app: frontend ports: - protocol: TCP - port: 80 # The port the service listens on inside the cluster - targetPort: 80 # The port on the Pod (containerPort where Nginx runs) - type: LoadBalancer # Exposes the service on a port on each Node's IP + port: 80 + targetPort: 80 + type: LoadBalancer \ No newline at end of file