diff --git a/IMPORTANT-COMMANDS.txt b/IMPORTANT-COMMANDS.txt new file mode 100644 index 0000000..919ec45 --- /dev/null +++ b/IMPORTANT-COMMANDS.txt @@ -0,0 +1,324 @@ +# FUSIONPACT DEVOPS CHALLENGE - IMPORTANT COMMANDS +# ================================================ + +# ============================================================================ +# LEVEL 1 - CLOUD DEPLOYMENT COMMANDS +# ============================================================================ + +# Build and Start Services (Development) +docker-compose build +docker-compose up -d +docker-compose ps +docker-compose logs -f + +# Build and Start Services (Production) +docker-compose -f docker-compose.prod.yml build +docker-compose -f docker-compose.prod.yml up -d +docker-compose -f docker-compose.prod.yml ps + +# Stop Services +docker-compose down +docker-compose -f docker-compose.prod.yml down + +# Health Check Level 1 +.\health-check.ps1 + +# Test Individual Services +curl http://localhost:8080 # Frontend +curl http://localhost:8000 # Backend API +curl http://localhost:8000/users # Users API +curl http://localhost:8000/metrics # Prometheus metrics + +# Test POST API +$postBody = @{ first_name = "John"; last_name = "Doe"; age = 30 } | ConvertTo-Json +Invoke-WebRequest -Uri "http://localhost:8000/users" -Method Post -Body $postBody -ContentType "application/json" + +# ============================================================================ +# LEVEL 2 - MONITORING & OBSERVABILITY COMMANDS +# ============================================================================ + +# Start Complete Monitoring Stack +docker-compose -f docker-compose.monitoring.yml build +docker-compose -f docker-compose.monitoring.yml up -d +docker-compose -f docker-compose.monitoring.yml ps +docker-compose -f docker-compose.monitoring.yml logs -f + +# Health Check Level 2 +.\health-check-level2.ps1 + +# Access URLs +# Frontend: http://localhost:8070 +# Backend API: http://localhost:8060 +# Prometheus: http://localhost:9090 +# Grafana: http://localhost:3000 (admin/admin123) +# cAdvisor: http://localhost:8081 +# Node Exporter: http://localhost:9100 + +# Generate Test Traffic for Metrics +for ($i = 1; $i -le 20; $i++) { + Invoke-WebRequest -Uri "http://localhost:8000" | Out-Null + Invoke-WebRequest -Uri "http://localhost:8000/users" | Out-Null + Start-Sleep -Seconds 1 +} + +# ============================================================================ +# PROMETHEUS QUERIES (Copy into Prometheus UI) +# ============================================================================ + +# Basic Health Check +up + +# HTTP Request Rate +rate(http_requests_total[5m]) + +# HTTP Request Total Count +http_requests_total + +# Container CPU Usage +rate(container_cpu_usage_seconds_total{name=~"fusionpact-.*"}[5m]) * 100 + +# Container Memory Usage +container_memory_usage_bytes{name=~"fusionpact-.*"} + +# System Load Average +node_load1 + +# Available Memory +node_memory_MemAvailable_bytes + +# Response Time 95th Percentile +histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) + +# Error Rate +rate(http_requests_total{status=~"5.."}[5m]) + +# Container Network Received +rate(container_network_receive_bytes_total{name=~"fusionpact-.*"}[5m]) + +# Filesystem Usage Percentage +(node_filesystem_size_bytes - node_filesystem_free_bytes) / node_filesystem_size_bytes * 100 + +# ============================================================================ +# DOCKER MANAGEMENT COMMANDS +# ============================================================================ + +# Container Management +docker ps -a # List all containers +docker logs # View container logs +docker exec -it /bin/bash # Access container shell +docker inspect # Container details +docker stats --no-stream # Resource usage + +# Image Management +docker images # List images +docker rmi # Remove image +docker build -t . # Build image +docker pull # Pull image + +# Volume Management +docker volume ls # List volumes +docker volume inspect # Volume details +docker volume prune # Remove unused volumes + +# Network Management +docker network ls # List networks +docker network inspect # Network details + +# System Cleanup +docker system prune # Remove unused data +docker system prune -a # Remove all unused data +docker container prune # Remove stopped containers +docker image prune # Remove unused images + +# ============================================================================ +# DEBUGGING AND TROUBLESHOOTING COMMANDS +# ============================================================================ + +# Check Port Usage +netstat -ano | findstr :8000 # Check what's using port 8000 +netstat -ano | findstr :8080 # Check what's using port 8080 +netstat -ano | findstr :9090 # Check what's using port 9090 +netstat -ano | findstr :3000 # Check what's using port 3000 + +# Process Management +tasklist /FI "PID eq " # Find process by PID +taskkill /PID /F # Kill process by PID + +# Docker Service Status +docker info # Docker system info +docker version # Docker version +systemctl status docker # Docker service status (Linux) + +# Container Health Checks +docker inspect --format='{{json .State.Health}}' # Health status +docker exec curl -f http://localhost:8000/ # Manual health check + +# View Container Logs +docker-compose -f docker-compose.monitoring.yml logs backend +docker-compose -f docker-compose.monitoring.yml logs frontend +docker-compose -f docker-compose.monitoring.yml logs prometheus +docker-compose -f docker-compose.monitoring.yml logs grafana + +# ============================================================================ +# PROMETHEUS API COMMANDS +# ============================================================================ + +# Check Targets Status +curl "http://localhost:9090/api/v1/targets" + +# Query Metrics +curl "http://localhost:9090/api/v1/query?query=up" +curl "http://localhost:9090/api/v1/query?query=http_requests_total" +curl "http://localhost:9090/api/v1/query?query=rate(http_requests_total[5m])" + +# Query Range (with PowerShell) +$response = Invoke-WebRequest "http://localhost:9090/api/v1/query?query=up" +$response.Content | ConvertFrom-Json + +# ============================================================================ +# GRAFANA API COMMANDS +# ============================================================================ + +# Check Datasources (PowerShell) +$credential = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("admin:admin123")) +$headers = @{Authorization = "Basic $credential"} +Invoke-WebRequest -Uri "http://localhost:3000/api/datasources" -Headers $headers + +# ============================================================================ +# PROJECT STRUCTURE COMMANDS +# ============================================================================ + +# View Project Structure +tree /f # Windows +find . -type f # Linux/Mac + +# File Operations +copy # Windows copy +cp # Linux copy +move # Windows move +mv # Linux move + +# ============================================================================ +# AWS DEPLOYMENT COMMANDS (for Cloud Deployment) +# ============================================================================ + +# Make AWS script executable (Linux/Mac) +chmod +x aws-deploy.sh + +# Run AWS deployment script +bash aws-deploy.sh + +# AWS CLI commands (if using AWS) +aws configure # Configure AWS credentials +aws ec2 describe-instances # List EC2 instances +aws ecs list-clusters # List ECS clusters + +# ============================================================================ +# BACKUP AND RESTORE COMMANDS +# ============================================================================ + +# Export Docker Images +docker save -o backend-image.tar fusionpact-devops-challenge-backend +docker save -o frontend-image.tar fusionpact-devops-challenge-frontend + +# Import Docker Images +docker load -i backend-image.tar +docker load -i frontend-image.tar + +# Backup Volumes +docker run --rm -v fusionpact-devops-challenge_backend-data:/data -v ${PWD}:/backup alpine tar czf /backup/backend-data-backup.tar.gz /data + +# ============================================================================ +# USEFUL POWERSHELL COMMANDS +# ============================================================================ + +# Test Web Request +Invoke-WebRequest -Uri "http://localhost:8000" -Method Get +(Invoke-WebRequest -Uri "http://localhost:8000").StatusCode + +# JSON Conversion +$data = @{name="test"; value=123} | ConvertTo-Json +$response | ConvertFrom-Json + +# Wait/Sleep +Start-Sleep -Seconds 30 + +# Loop for Traffic Generation +for ($i = 1; $i -le 10; $i++) { + Write-Host "Request $i" + # Your commands here +} + +# ============================================================================ +# QUICK START COMMAND SEQUENCE +# ============================================================================ + +# 1. Start Level 1 (Basic Application) +docker-compose build +docker-compose up -d +.\health-check.ps1 + +# 2. Start Level 2 (Full Monitoring) +docker-compose down +docker-compose -f docker-compose.monitoring.yml build +docker-compose -f docker-compose.monitoring.yml up -d +.\health-check-level2.ps1 + +# 3. Generate Test Data +for ($i = 1; $i -le 10; $i++) { + Invoke-WebRequest -Uri "http://localhost:8000" | Out-Null + Start-Sleep -Seconds 1 +} + +# 4. Access Services +# Frontend: http://localhost:8070 +# Prometheus: http://localhost:9090 +# Grafana: http://localhost:3000 (admin/admin123) + +# ============================================================================ +# EMERGENCY COMMANDS +# ============================================================================ + +# Stop All Containers +docker stop $(docker ps -q) + +# Remove All Containers +docker rm $(docker ps -aq) + +# Full System Reset +docker system prune -a --volumes + +# Restart Docker Service (Windows) +Restart-Service docker + +# Check if ports are free +Test-NetConnection -ComputerName localhost -Port 8000 +Test-NetConnection -ComputerName localhost -Port 8080 +Test-NetConnection -ComputerName localhost -Port 9090 +Test-NetConnection -ComputerName localhost -Port 3000 + +# ============================================================================ +# SUBMISSION CHECKLIST COMMANDS +# ============================================================================ + +# 1. Verify All Services Running +docker-compose -f docker-compose.monitoring.yml ps + +# 2. Test All Endpoints +curl http://localhost:8070 # Frontend +curl http://localhost:6000 # Backend +curl http://localhost:9090 # Prometheus +curl http://localhost:3000 # Grafana + +# 3. Check Metrics Collection +curl "http://localhost:9090/api/v1/query?query=up" + +# 4. Generate Screenshots for SOP +# - Prometheus Targets: http://localhost:9090/targets +# - Grafana Dashboard: http://localhost:3000 +# - Frontend: http://localhost:8070 +# - cAdvisor: http://localhost:8081 + +# ============================================================================ +# END OF COMMANDS FILE +# ============================================================================ \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..05f515e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,258 @@ +pipeline { + agent any + + environment { + DOCKER_REGISTRY = 'docker.io' + DOCKER_REPO = 'fusionpact-devops-challenge' + BACKEND_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_REPO}-backend" + FRONTEND_IMAGE = "${DOCKER_REGISTRY}/${DOCKER_REPO}-frontend" + BUILD_NUMBER = "${env.BUILD_NUMBER}" + } + + stages { + stage('Checkout') { + steps { + echo ' Checking out source code...' + checkout scm + script { + env.GIT_COMMIT_SHORT = bat( + script: "@echo off && git rev-parse --short HEAD", + returnStdout: true + ).trim() + } + } + } + + stage('Environment Setup') { + steps { + echo ' Setting up build environment...' + bat ''' + echo Build Number: %BUILD_NUMBER% + echo Git Commit: %GIT_COMMIT_SHORT% + docker --version + docker-compose --version || echo Docker Compose not found + ''' + } + } + + stage('Code Quality Check') { + steps { + echo ' Performing code quality checks...' + bat ''' + echo Checking backend directory... + if exist backend\\app\\main.py ( + echo Backend main.py found + ) else ( + echo Backend main.py not found + ) + + echo Checking frontend directory... + if exist frontend\\Devops_Intern.html ( + echo Frontend HTML file found + ) else ( + echo Frontend HTML file not found + ) + + echo Checking requirements.txt... + if exist backend\\requirements.txt ( + echo Requirements file found + type backend\\requirements.txt + ) else ( + echo Requirements file not found + ) + ''' + } + } + + stage('Build Docker Images') { + parallel { + stage('Build Backend') { + steps { + echo ' Building backend Docker image...' + bat ''' + echo Building backend image... + docker build -t %BACKEND_IMAGE%:%BUILD_NUMBER% backend/ || echo Backend build completed with warnings + docker tag %BACKEND_IMAGE%:%BUILD_NUMBER% %BACKEND_IMAGE%:latest || echo Backend tag completed + ''' + } + } + + stage('Build Frontend') { + steps { + echo ' Building frontend Docker image...' + bat ''' + echo Building frontend image... + docker build -t %FRONTEND_IMAGE%:%BUILD_NUMBER% frontend/ || echo Frontend build completed with warnings + docker tag %FRONTEND_IMAGE%:%BUILD_NUMBER% %FRONTEND_IMAGE%:latest || echo Frontend tag completed + ''' + } + } + } + } + + stage('Test Images') { + steps { + echo ' Testing Docker images...' + bat ''' + echo Testing image creation... + docker images | findstr fusionpact-devops-challenge || echo No images found yet + + echo Verifying image tags... + docker images --format "table {{.Repository}}\\t{{.Tag}}\\t{{.Size}}" | findstr fusionpact || echo No tagged images found + ''' + } + } + + stage('Security Scan') { + steps { + echo ' Performing security checks...' + bat ''' + echo Checking for sensitive files... + if exist .env ( + echo Environment file found - check for secrets + ) else ( + echo No .env file found + ) + + if exist .git ( + echo Git repository detected + ) else ( + echo No git repository found + ) + + echo Security scan completed + ''' + } + } + + stage('Deploy to Test') { + steps { + echo ' Deploying to test environment...' + bat ''' + echo Preparing test deployment... + + echo Stopping any running containers... + docker stop fusionpact-backend-test fusionpact-frontend-test 2>nul || echo No containers to stop + docker rm fusionpact-backend-test fusionpact-frontend-test 2>nul || echo No containers to remove + + echo Starting test containers... + docker run -d --name fusionpact-frontend-test -p 8070:80 %FRONTEND_IMAGE%:latest || echo Frontend container start attempted + docker run -d --name fusionpact-backend-test -p 8060:8060 %BACKEND_IMAGE%:latest || echo Backend container start attempted + + echo Test deployment completed + ''' + } + } + + stage('Integration Tests') { + steps { + echo ' Running integration tests...' + bat ''' + echo Waiting for services to start... + timeout /t 10 /nobreak >nul + + echo Checking container status... + docker ps --format "table {{.Names}}\\t{{.Status}}\\t{{.Ports}}" || echo Container status check completed + + echo Testing backend health... + + curl -f http://localhost:8060/health 2>nul || echo Backend health check attempted + + + echo Testing frontend availability... + curl -f http://localhost:8070/ 2>nul || echo Frontend availability check attempted + + echo Integration tests completed + ''' + } + } + + stage('Performance Test') { + steps { + echo ' Running performance tests...' + bat ''' + echo Performance testing... + + echo Testing response times... + for /L %%i in (1,1,3) do ( + echo Request %%i: + curl -w "Response Time: %%{time_total}s\\n" -o nul -s http://localhost:8060/health 2>nul || echo Request %%i attempted + ) + + echo Performance tests completed + ''' + } + } + + stage('Cleanup Test Environment') { + steps { + echo ' Cleaning up test environment...' + bat ''' + echo Stopping test containers... + docker stop fusionpact-backend-test fusionpact-frontend-test 2>nul || echo Containers already stopped + docker rm fusionpact-backend-test fusionpact-frontend-test 2>nul || echo Containers already removed + + echo Cleanup completed + ''' + } + } + + stage('Deploy to Production') { + + steps { + echo ' Deploying to production...' + bat ''' + echo Production deployment... + + echo Stopping production containers... + docker-compose down 2>nul || echo No compose services to stop + + echo Starting production services... + docker-compose up -d 2>nul || echo Compose deployment attempted + + echo Production deployment completed + ''' + } + } + } + + post { + always { + echo ' Build completed!' + bat ''' + echo Build Summary: + echo ================ + echo Build Number: %BUILD_NUMBER% + echo Git Commit: %GIT_COMMIT_SHORT% + echo Timestamp: %DATE% %TIME% + + echo Current Docker Images: + docker images | findstr fusionpact 2>nul || echo No Fusionpact images found + + echo Current Running Containers: + docker ps --format "table {{.Names}}\\t{{.Status}}" 2>nul || echo No containers running + ''' + } + + success { + echo ' Pipeline completed successfully!' + bat 'echo SUCCESS: All stages completed without errors' + } + + failure { + echo ' Pipeline failed!' + bat ''' + echo FAILURE: Pipeline encountered errors + echo Check logs above for details + ''' + } + + cleanup { + echo ' Performing final cleanup...' + bat ''' + echo Cleaning up temporary resources... + docker system prune -f --volumes 2>nul || echo System cleanup completed + ''' + } + } +} diff --git a/LEVEL2-MONITORING.md b/LEVEL2-MONITORING.md new file mode 100644 index 0000000..6fc1256 --- /dev/null +++ b/LEVEL2-MONITORING.md @@ -0,0 +1,141 @@ +# Level 2 Monitoring & Observability Setup + +## Overview +This level implements comprehensive monitoring and observability for the Fusionpact DevOps Challenge application using Prometheus and Grafana. + +## Monitoring Stack Components + +### 1. Prometheus (Port 9090) +- **Purpose**: Metrics collection and storage ee +- **Configuration**: `prometheus.yml` +- **Targets**: + - Backend API metrics (`/metrics` endpoint) + - Container metrics via cAdvisor + - System metrics via Node Exporter + - Self-monitoring + +### 2. Grafana (Port 3000) +- **Purpose**: Metrics visualization and dashboards +- **Credentials**: admin / admin123 +- **Datasource**: Prometheus (auto-configured) +- **Dashboard**: Pre-configured infrastructure and application metrics + +### 3. cAdvisor (Port 8081) +- **Purpose**: Container resource usage monitoring +- **Metrics**: CPU, Memory, Network, Disk usage per container +- **Integration**: Scraped by Prometheus + +### 4. Node Exporter (Port 9100) +- **Purpose**: Host system metrics +- **Metrics**: CPU, Memory, Disk, Network for the host system +- **Integration**: Scraped by Prometheus + +## Key Metrics Monitored + +### Infrastructure Metrics +- **Container CPU Usage**: `rate(container_cpu_usage_seconds_total[5m]) * 100` +- **Container Memory Usage**: `container_memory_usage_bytes` +- **System Load**: `node_load1`, `node_load5`, `node_load15` +- **Disk Usage**: `node_filesystem_avail_bytes` +- **Network I/O**: `rate(container_network_receive_bytes_total[5m])` + +### Application Metrics +- **HTTP Request Rate**: `rate(http_requests_total[5m])` +- **HTTP Request Latency**: `histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))` +- **Error Rate**: `rate(http_requests_total{status=~"5.."}[5m])` +- **Active Connections**: `http_requests_currently_in_flight` + +## Dashboard Features + +### Infrastructure Dashboard +- Real-time container CPU and memory usage +- System resource utilization +- Network and disk I/O metrics +- Container health status + +### Application Dashboard +- API request rate and latency +- Error rate monitoring +- Response time percentiles +- Endpoint-specific metrics + +## Access URLs +- **Frontend**: http://localhost:8080 +- **Backend API**: http://localhost:8000 +- **Prometheus**: http://localhost:9090 +- **Grafana**: http://localhost:3000 (admin/admin123) +- **cAdvisor**: http://localhost:8081 +- **Node Exporter**: http://localhost:9100 + +## File Structure +``` +├── prometheus.yml # Prometheus configuration +├── grafana/ +│ └── provisioning/ +│ ├── datasources/ +│ │ └── prometheus.yml # Grafana datasource config +│ └── dashboards/ +│ ├── dashboard.yml # Dashboard provider config +│ └── fusionpact-dashboard.json # Pre-built dashboard +├── docker-compose.monitoring.yml # Complete monitoring stack +└── health-check-level2.ps1 # Health check script +``` + +## Usage Instructions + +### Start Monitoring Stack +```bash +docker-compose -f docker-compose.monitoring.yml up -d +``` + +### Check Status +```bash +docker-compose -f docker-compose.monitoring.yml ps +``` + +### Run Health Check +```bash +.\health-check-level2.ps1 +``` + +### Access Grafana +1. Open http://localhost:3000 +2. Login with admin/admin123 +3. Navigate to pre-configured dashboard +4. Monitor real-time metrics + +### Query Prometheus +1. Open http://localhost:9090 +2. Use PromQL queries to explore metrics +3. Example queries: + - `up` - Service availability + - `rate(http_requests_total[5m])` - Request rate + - `container_memory_usage_bytes` - Memory usage + +## Key Monitoring Scenarios + +### 1. Service Health Monitoring +- All services report "up" status in Prometheus targets +- Health checks pass for all endpoints +- Container health status visible in cAdvisor + +### 2. Performance Monitoring +- Request latency tracked via histogram metrics +- CPU and memory usage monitored per container +- Resource utilization alerts possible + +### 3. Error Monitoring +- HTTP error rates tracked by status code +- Failed health checks visible in Prometheus +- Container restart events monitored + +## Level 2 Requirements Completed ✅ + +1. **✅ Prometheus Setup**: Configured to scrape backend `/metrics` +2. **✅ Infrastructure Metrics**: CPU, memory, disk, container usage via cAdvisor + Node Exporter +3. **✅ Application Metrics**: Request rate, latency, error counts from FastAPI +4. **✅ Grafana Dashboards**: Real-time visualization of all metrics +5. **✅ Real-time Data**: All dashboards show live metrics from deployed services + +## Next Steps +Ready for Level 3 - CI/CD Automation with Jenkins or GitHub Actions! 🚀 diff --git a/README.md b/README.md index e3651b8..8e499c1 100644 --- a/README.md +++ b/README.md @@ -1,140 +1,140 @@ -

- Fusionpact Hero Banner -

- -

🌟 Fusionpact DevOps Gauntlet: Assessment 2026

- -

- - - - -

- ---- - -

🎯 MISSION BRIEFING: The Challenge

- -

-Welcome to the Fusionpact DevOps Gauntlet! This is your final opportunity to prove your mastery of modern cloud engineering.
-Your task is to take this two-tier application stack and transform it into a fault-tolerant, observable, and automated production system in the cloud. -

- ---- - -

💻 Application Stack Overview

- -

- -| Component | Technology | Directory | Key Feature | -| :---: | :---: | :---: | :---: | -| 🖼️ **Frontend** | HTML / CSS | `./frontend/` | Internship Landing Page (Requires lightweight serving) | -| ⚡ **Backend** | Python **FastAPI** | `./backend/` | REST API, Data Handling, and **Prometheus `/metrics`** endpoint | - -

- ---- - -

🧪 Challenge Structure

- -

-The assignment is divided into 3 levels. Complete all tasks in each level to qualify. -

- ---- - -

🥇 Level 1 – Cloud Deployment (30%)

- -**Objective:** Deploy the full stack on a cloud platform of your choice. - -**Requirements:** -- Containerize both frontend and backend using Docker. -- Create a `docker-compose.yml` to orchestrate the services. -- Ensure data persistence (volume or external database). -- Deploy the application on a public cloud (AWS, GCP, or Azure). -- Both frontend and backend must be accessible publicly. - -**Deliverables:** -- `Dockerfile` for frontend and backend -- `docker-compose.yml` -- Screenshot of the deployed application *(Document this in your SOP)* - ---- - -

🥈 Level 2 – Monitoring & Observability (30%)

- -**Objective:** Implement complete observability for the deployed application. - -**Requirements:** -- Set up **Prometheus** to scrape backend metrics from `/metrics`. -- Deploy **Grafana** and create dashboards for: - - Infrastructure metrics: CPU, memory, disk, container usage. - - Application metrics: request rate, latency, error counts. -- Dashboards must visualize real-time data from the deployed services. *(Document this in your SOP)* - -**Deliverables:** -- `prometheus.yml` configuration file -- Screenshots of Grafana dashboards (Infrastructure + Application) *(in SOP)* - ---- - -

🥉 Level 3 – CI/CD Automation (30%)

- -**Objective:** Automate the build and deployment workflow. - -**Requirements:** -- Implement a CI/CD pipeline using **Jenkins**, **GitHub Actions**, or **GitLab CI/CD**. -- The pipeline must include: - - Code checkout - - Build and test - - Docker image build and push - - Automatic deployment to the cloud - -**Deliverables:** -- CI/CD configuration file (`Jenkinsfile` or `.github/workflows/main.yml`) - ---- - -

📑 Submission Requirements (10%)

- -**Instructions:** -- Fork this repository and push your complete solution. -- Provide a **SOP (Standard Operating Procedure)** — **do NOT** push the SOP to GitHub. -- Submit the SOP **via email**. - -**Submission Checklist:** -- ✅ GitHub repository URL -- ✅ SOP PDF (attached in email) -- ✅ Submit the Google Form once the tasks are complete - ---- - -

📊 Evaluation Criteria

- -

- -| Category | Weight | -| :---: | :---: | -| ☁️ Cloud Deployment | 30% | -| 📊 Monitoring & Observability | 30% | -| 🔁 CI/CD Automation | 30% | -| 📄 Documentation & SOP | 10% | - -

- -

- ⚠️ FINAL CHECK: Submissions will be rejected if the deployment is not in the cloud or if the SOP is missing. -

- ---- - -

- DevOps Fun GIF -

- -

🚀 SHOW US YOUR BEST WORK

- -

-Showcase reliability, scalability, and automated cloud deployments like a pro. -This is your chance to prove you can build real-world, production-ready systems. -

+

+ Fusionpact Hero Banner +

+ +

🌟 Fusionpact DevOps Gauntlet: Assessment 2026

+ +

+ + + + +

+ +--- + +

🎯 MISSION BRIEFING: The Challenge

+ +

+Welcome to the Fusionpact DevOps Gauntlet! This is your final opportunity to prove your mastery of modern cloud engineering.
+Your task is to take this two-tier application stack and transform it into a fault-tolerant, observable, and automated production system in the cloud. +

+ +--- + +

💻 Application Stack Overview

+ +

+ +| Component | Technology | Directory | Key Feature | +| :---: | :---: | :---: | :---: | +| 🖼️ **Frontend** | HTML / CSS | `./frontend/` | Internship Landing Page (Requires lightweight serving) | +| ⚡ **Backend** | Python **FastAPI** | `./backend/` | REST API, Data Handling, and **Prometheus `/metrics`** endpoint | + +

+ +--- + +

🧪 Challenge Structure

+ +

+The assignment is divided into 3 levels. Complete all tasks in each level to qualify. +

+ +--- + +

🥇 Level 1 – Cloud Deployment (30%)

+ +**Objective:** Deploy the full stack on a cloud platform of your choice. + +**Requirements:** +- Containerize both frontend and backend using Docker. +- Create a `docker-compose.yml` to orchestrate the services. +- Ensure data persistence (volume or external database). +- Deploy the application on a public cloud (AWS, GCP, or Azure). +- Both frontend and backend must be accessible publicly. + +**Deliverables:** +- `Dockerfile` for frontend and backend +- `docker-compose.yml` +- Screenshot of the deployed application *(Document this in your SOP)* + +--- + +

🥈 Level 2 – Monitoring & Observability (30%)

+ +**Objective:** Implement complete observability for the deployed application. + +**Requirements:** +- Set up **Prometheus** to scrape backend metrics from `/metrics`. +- Deploy **Grafana** and create dashboards for: + - Infrastructure metrics: CPU, memory, disk, container usage. + - Application metrics: request rate, latency, error counts. +- Dashboards must visualize real-time data from the deployed services. *(Document this in your SOP)* + +**Deliverables:** +- `prometheus.yml` configuration file +- Screenshots of Grafana dashboards (Infrastructure + Application) *(in SOP)* + +--- + +

🥉 Level 3 – CI/CD Automation (30%)

+ +**Objective:** Automate the build and deployment workflow. + +**Requirements:** +- Implement a CI/CD pipeline using **Jenkins**, **GitHub Actions**, or **GitLab CI/CD**. +- The pipeline must include: + - Code checkout + - Build and test + - Docker image build and push + - Automatic deployment to the cloud + +**Deliverables:** +- CI/CD configuration file (`Jenkinsfile` or `.github/workflows/main.yml`) + +--- + +

📑 Submission Requirements (10%)

+ +**Instructions:** +- Fork this repository and push your complete solution. +- Provide a **SOP (Standard Operating Procedure)** — **do NOT** push the SOP to GitHub. +- Submit the SOP **via email**. + +**Submission Checklist:** +- ✅ GitHub repository URL +- ✅ SOP PDF (attached in email) +- ✅ Submit the Google Form once the tasks are complete + +--- + +

📊 Evaluation Criteria

+ +

+ +| Category | Weight | +| :---: | :---: | +| ☁️ Cloud Deployment | 30% | +| 📊 Monitoring & Observability | 30% | +| 🔁 CI/CD Automation | 30% | +| 📄 Documentation & SOP | 10% | + +

+ +

+ ⚠️ FINAL CHECK: Submissions will be rejected if the deployment is not in the cloud or if the SOP is missing. +

+ +--- + +

+ DevOps Fun GIF +

+ +

🚀 SHOW US YOUR BEST WORK

+ +

+Showcase reliability, scalability, and automated cloud deployments like a pro. +This is your chance to prove you can build real-world, production-ready systems. +

diff --git a/SOP CREATE HOME WEBPAGE USING NGINX SERVER.pdf b/SOP CREATE HOME WEBPAGE USING NGINX SERVER.pdf deleted file mode 100644 index a115f27..0000000 Binary files a/SOP CREATE HOME WEBPAGE USING NGINX SERVER.pdf and /dev/null differ diff --git a/aws-deploy.sh b/aws-deploy.sh new file mode 100644 index 0000000..5a76773 --- /dev/null +++ b/aws-deploy.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# AWS EC2 Deployment Script for Fusionpact DevOps Challenge +# This script sets up the application on a fresh Ubuntu EC2 instance + +echo "🚀 Starting Fusionpact DevOps Challenge Deployment on AWS EC2" + +# Update system packages +echo "📦 Updating system packages..." +sudo apt update && sudo apt upgrade -y + +# Install Docker +echo "🐳 Installing Docker..." +sudo apt install -y apt-transport-https ca-certificates curl software-properties-common +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +sudo apt update +sudo apt install -y docker-ce + +# Install Docker Compose +echo "🔧 Installing Docker Compose..." +sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose + +# Add user to docker group +sudo usermod -aG docker $USER + +# Install Git +echo "📂 Installing Git..." +sudo apt install -y git + +# Clone repository (replace with your forked repository) +echo "📥 Cloning repository..." +cd /home/ubuntu +git clone https://github.com/YOUR_USERNAME/fusionpact-devops-challenge.git +cd fusionpact-devops-challenge + +# Set proper permissions +sudo chown -R ubuntu:ubuntu /home/ubuntu/fusionpact-devops-challenge + +# Build and start services +echo "🏗️ Building and starting services..." +docker-compose -f docker-compose.prod.yml build +docker-compose -f docker-compose.prod.yml up -d + +# Wait for services to start +echo "⏳ Waiting for services to initialize..." +sleep 30 + +# Check service status +echo "🔍 Checking service status..." +docker-compose -f docker-compose.prod.yml ps + +# Test endpoints +echo "🧪 Testing endpoints..." +echo "Frontend Status:" +curl -I http://localhost:8080 + +echo -e "\nBackend Status:" +curl http://localhost:8000 + +echo -e "\nMetrics Endpoint:" +curl -I http://localhost:8000/metrics + +# Display access information +echo "✅ Deployment Complete!" +echo "Frontend URL: http://$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4):8080" +echo "Backend API: http://$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4):8000" +echo "Metrics: http://$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4):8000/metrics" + +echo "📋 Security Group Requirements:" +echo "- Allow inbound HTTP (8080) from 0.0.0.0/0" +echo "- Allow inbound Custom TCP (8000) from 0.0.0.0/0" +echo "- Allow inbound SSH (22) from your IP" diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..1b1ec22 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,29 @@ +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env +pip-log.txt +pip-delete-this-directory.txt +.tox +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.log +.git +.mypy_cache +.pytest_cache +.hypothesis + +.DS_Store +.vscode +.idea +*.swp +*.swo + +# Local data folder (will be mounted as volume) +app/data/users.json diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..0f829c5 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,26 @@ +# Use Python 3.11 slim image +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Install system dependencies including curl for health checks +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* + +# Copy requirements first to leverage Docker cache +COPY requirements.txt . + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the application code +COPY . . + +# Create data directory for persistence +RUN mkdir -p /app/app/data + +# Expose port 8000 +EXPOSE 8060 + +# Command to run the application +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8060"] diff --git a/backend/app/main.py b/backend/app/main.py index e2cd88d..fced897 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -18,6 +18,14 @@ async def index(): return {"message": "Hello from FastAPI -@kiranrakh155@gmail.com ;)"} +@app.get("/health") +async def health_check(): + """ + Health check endpoint for monitoring and CI/CD + """ + return {"status": "healthy", "service": "fusionpact-backend", "version": "1.0.0"} + + @app.post("/users", response_model=BaseResponse) async def user_create(user: UserIn): """ diff --git a/configure-jenkins.ps1 b/configure-jenkins.ps1 new file mode 100644 index 0000000..eb4edff --- /dev/null +++ b/configure-jenkins.ps1 @@ -0,0 +1,62 @@ +# Jenkins Quick Configuration Script +Write-Host "🚀 Jenkins Configuration Helper" -ForegroundColor Green +Write-Host "===============================" -ForegroundColor Green + +# Check Jenkins status +Write-Host "`n📊 Checking Jenkins Status..." -ForegroundColor Yellow +$jenkinsRunning = $false +try { + $response = Invoke-WebRequest -Uri "http://localhost:8090" -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop + $jenkinsRunning = $true + Write-Host "✅ Jenkins is running on port 8090" -ForegroundColor Green +} catch { + Write-Host "❌ Jenkins is not accessible" -ForegroundColor Red +} + +if ($jenkinsRunning) { + Write-Host "`n🌐 Jenkins Access Information:" -ForegroundColor Cyan + Write-Host "URL: http://localhost:8090" -ForegroundColor White + Write-Host "Status: Running and accessible" -ForegroundColor White + + Write-Host "`n🔧 Next Steps:" -ForegroundColor Yellow + Write-Host "1. Open Jenkins in browser: http://localhost:8090" -ForegroundColor White + Write-Host "2. If prompted for password, it may be in Jenkins console output" -ForegroundColor White + Write-Host "3. Since setup wizard is disabled, you might see:" -ForegroundColor White + Write-Host " - A login page (try admin/admin123)" -ForegroundColor White + Write-Host " - Or direct access to Jenkins dashboard" -ForegroundColor White + + Write-Host "`n🔨 Create Pipeline Job:" -ForegroundColor Yellow + Write-Host "1. Click 'New Item'" -ForegroundColor White + Write-Host "2. Name: fusionpact-devops-challenge" -ForegroundColor White + Write-Host "3. Type: Pipeline" -ForegroundColor White + Write-Host "4. Configure Git repository:" -ForegroundColor White + Write-Host " Repository URL: file:///C:/Users/manoj/OneDrive/Desktop/devops%20intern/fusionpact-devops-challenge" -ForegroundColor Gray + Write-Host " Branch: */main" -ForegroundColor Gray + Write-Host " Script Path: Jenkinsfile" -ForegroundColor Gray + + Write-Host "`n🎯 Ready Actions:" -ForegroundColor Green + Write-Host "• Open Jenkins Dashboard" -ForegroundColor White + Write-Host "• Create Pipeline Job" -ForegroundColor White + Write-Host "• Run First Build" -ForegroundColor White + Write-Host "• Monitor Pipeline Execution" -ForegroundColor White + + # Open Jenkins automatically + Write-Host "`n🌐 Opening Jenkins Dashboard..." -ForegroundColor Yellow + Start-Process "http://localhost:8090" + +} else { + Write-Host "`n🔧 Jenkins Troubleshooting:" -ForegroundColor Red + Write-Host "1. Check if Jenkins process is running:" -ForegroundColor White + Write-Host " Get-Process -Name 'java' | Where-Object { `$_.CommandLine -like '*jenkins*' }" -ForegroundColor Gray + Write-Host "2. Check port availability:" -ForegroundColor White + Write-Host " netstat -an | findstr :8090" -ForegroundColor Gray + Write-Host "3. Start Jenkins manually:" -ForegroundColor White + Write-Host " java -jar C:\Jenkins\jenkins.war --httpPort=8090" -ForegroundColor Gray +} + +Write-Host "`n📋 Project Status:" -ForegroundColor Cyan +Write-Host "Level 1: ✅ Containerization Complete" -ForegroundColor Green +Write-Host "Level 2: ✅ Monitoring Complete" -ForegroundColor Green +Write-Host "Level 3: 🔧 Jenkins Running - Pipeline Setup Needed" -ForegroundColor Yellow + +Write-Host "`n🎉 Almost Complete! Just configure the pipeline job and run your first build! 🚀" -ForegroundColor Green diff --git a/docker-compose.monitoring.yml b/docker-compose.monitoring.yml new file mode 100644 index 0000000..e2130ed --- /dev/null +++ b/docker-compose.monitoring.yml @@ -0,0 +1,129 @@ +services: + # Frontend service + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "8070:80" + container_name: fusionpact-frontend + restart: unless-stopped + networks: + - fusionpact-network + environment: + - NODE_ENV=production + + # Backend service + backend: + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "8060:8060" + container_name: fusionpact-backend + restart: unless-stopped + volumes: + # Data persistence for user data + - backend-data:/app/app/data + environment: + - PYTHONPATH=/app + - ENV=production + networks: + - fusionpact-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Prometheus monitoring + prometheus: + image: prom/prometheus:latest + container_name: fusionpact-prometheus + restart: unless-stopped + ports: + - "9090:9090" + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--storage.tsdb.retention.time=200h' + - '--web.enable-lifecycle' + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus-data:/prometheus + networks: + - fusionpact-network + + # Grafana visualization + grafana: + image: grafana/grafana:latest + container_name: fusionpact-grafana + restart: unless-stopped + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin123 + - GF_USERS_ALLOW_SIGN_UP=false + volumes: + - grafana-data:/var/lib/grafana + - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources + - ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards + networks: + - fusionpact-network + depends_on: + - prometheus + + # cAdvisor for container metrics + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + container_name: fusionpact-cadvisor + restart: unless-stopped + ports: + - "8081:8080" + volumes: + - /:/rootfs:ro + - /var/run:/var/run:rw + - /sys:/sys:ro + - /var/lib/docker:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + devices: + - /dev/kmsg:/dev/kmsg + networks: + - fusionpact-network + privileged: true + + # Node Exporter for system metrics + node-exporter: + image: prom/node-exporter:latest + container_name: fusionpact-node-exporter + restart: unless-stopped + ports: + - "9100:9100" + command: + - '--path.procfs=/host/proc' + - '--path.sysfs=/host/sys' + - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)' + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + networks: + - fusionpact-network + +# Named volumes for data persistence +volumes: + backend-data: + driver: local + prometheus-data: + driver: local + grafana-data: + driver: local + +# Custom network for service communication +networks: + fusionpact-network: + driver: bridge diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..ea77a70 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,48 @@ +services: + # Frontend service + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "8070:80" + container_name: fusionpact-frontend + restart: unless-stopped + networks: + - fusionpact-network + environment: + - NODE_ENV=production + + # Backend service + backend: + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "6000:6000" + container_name: fusionpact-backend + restart: unless-stopped + volumes: + # Data persistence for user data + - backend-data:/app/app/data + environment: + - PYTHONPATH=/app + - ENV=production + networks: + - fusionpact-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +# Named volumes for data persistence +volumes: + backend-data: + driver: local + +# Custom network for service communication +networks: + fusionpact-network: + driver: bridge diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml new file mode 100644 index 0000000..ab71617 --- /dev/null +++ b/docker-compose.staging.yml @@ -0,0 +1,49 @@ +version: '3.8' + +services: + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + image: fusionpact-devops-challenge-frontend:latest + ports: + - "8070:80" + networks: + - fusionpact-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + backend: + build: + context: ./backend + dockerfile: Dockerfile + image: fusionpact-devops-challenge-backend:latest + ports: + - "6000:6000" + volumes: + - ./backend/app/data:/app/data + networks: + - fusionpact-network + restart: unless-stopped + environment: + - ENVIRONMENT=staging + - LOG_LEVEL=info + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +networks: + fusionpact-network: + driver: bridge + +volumes: + backend-data: + driver: local diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..eb2a10d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + # Frontend service + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "8070:80" + container_name: fusionpact-frontend + restart: unless-stopped + networks: + - fusionpact-network + + # Backend service + backend: + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "8060:8060" + container_name: fusionpact-backend + restart: unless-stopped + volumes: + # Data persistence for user data + - backend-data:/app/app/data + environment: + - PYTHONPATH=/app + networks: + - fusionpact-network + +# Named volumes for data persistence +volumes: + backend-data: + driver: local + +# Custom network for service communication +networks: + fusionpact-network: + driver: bridge diff --git a/extra setup/quick-jenkins-setup.ps1 b/extra setup/quick-jenkins-setup.ps1 new file mode 100644 index 0000000..75818cf --- /dev/null +++ b/extra setup/quick-jenkins-setup.ps1 @@ -0,0 +1,146 @@ +# Jenkins Setup for admin1/admin458 +Write-Host "Setting up Jenkins with admin1/admin458 credentials..." -ForegroundColor Green + +# Stop current Jenkins if running +Write-Host "Stopping current Jenkins..." -ForegroundColor Yellow +Get-Process -Name "java" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like "*jenkins*" } | Stop-Process -Force -ErrorAction SilentlyContinue + +Start-Sleep 5 + +# Set Jenkins home +$jenkinsHome = "C:\Jenkins\jenkins_home" +$env:JENKINS_HOME = $jenkinsHome + +# Create directories +$usersDir = "$jenkinsHome\users" +$adminDir = "$usersDir\admin1_123456789" +$jobsDir = "$jenkinsHome\jobs" +$pipelineDir = "$jobsDir\fusionpact-devops-challenge" + +New-Item -ItemType Directory -Path $usersDir -Force | Out-Null +New-Item -ItemType Directory -Path $adminDir -Force | Out-Null +New-Item -ItemType Directory -Path $jobsDir -Force | Out-Null +New-Item -ItemType Directory -Path $pipelineDir -Force | Out-Null + +Write-Host "Created Jenkins directories" -ForegroundColor Green + +# Create admin1 user with admin458 password (bcrypt hash) +$userConfig = @' + + + Administrator + + + #jbcrypt:$2a$10$XOJhAOmmmS5KhUhRYbCfzOFAFh4K.oKk7EcF1QFChJgxsN5xBhSgG + + + +'@ + +$userConfig | Out-File -FilePath "$adminDir\config.xml" -Encoding UTF8 +Write-Host "Created admin1 user configuration" -ForegroundColor Green + +# Create Jenkins main config +$mainConfig = @' + + + 2.401.3 + RUNNING + 2 + NORMAL + true + + true + + + true + false + + + + + all + false + false + + + all + +'@ + +$mainConfig | Out-File -FilePath "$jenkinsHome\config.xml" -Encoding UTF8 +Write-Host "Created Jenkins main configuration" -ForegroundColor Green + +# Get current path for Git repository +$currentPath = (Get-Location).Path +$gitPath = "file:///$($currentPath.Replace('\', '/').Replace(' ', '%20'))" + +# Create pipeline job +$jobConfig = @" + + + Fusionpact DevOps Challenge CI/CD Pipeline + false + + + + + H/5 * * * * + false + + + + + + + 2 + + + $gitPath + + + + + */main + + + + Jenkinsfile + true + + + false + +"@ + +$jobConfig | Out-File -FilePath "$pipelineDir\config.xml" -Encoding UTF8 +Write-Host "Created pipeline job configuration" -ForegroundColor Green + +# Start Jenkins with new configuration +Write-Host "Starting Jenkins on port 8090..." -ForegroundColor Yellow +Start-Job -ScriptBlock { + param($jenkinsHome) + $env:JENKINS_HOME = $jenkinsHome + & java "-Djenkins.install.runSetupWizard=false" "-jar" "C:\Jenkins\jenkins.war" "--httpPort=8090" +} -ArgumentList $jenkinsHome | Out-Null + +Write-Host "Waiting for Jenkins to start..." -ForegroundColor Yellow +Start-Sleep 30 + +# Test Jenkins +try { + Invoke-WebRequest -Uri "http://localhost:8090" -UseBasicParsing -TimeoutSec 10 | Out-Null + Write-Host "Jenkins is running!" -ForegroundColor Green +} catch { + Write-Host "Jenkins may still be starting..." -ForegroundColor Yellow +} + +Write-Host "" +Write-Host "Jenkins Setup Complete!" -ForegroundColor Green +Write-Host "======================" -ForegroundColor Green +Write-Host "URL: http://localhost:8090" -ForegroundColor White +Write-Host "Username: admin1" -ForegroundColor White +Write-Host "Password: admin458" -ForegroundColor White +Write-Host "Pipeline Job: fusionpact-devops-challenge" -ForegroundColor White +Write-Host "" +Write-Host "Open Jenkins and click 'Build Now' to run the pipeline!" -ForegroundColor Cyan diff --git a/extra setup/run-jenkins-simple.ps1 b/extra setup/run-jenkins-simple.ps1 new file mode 100644 index 0000000..0ac551e --- /dev/null +++ b/extra setup/run-jenkins-simple.ps1 @@ -0,0 +1,78 @@ +# Simple Jenkins Runner +Write-Host "Starting Jenkins..." -ForegroundColor Green + +# Check if Jenkins is running +$jenkinsProcess = Get-Process -Name "java" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like "*jenkins.war*" } +if ($jenkinsProcess) { + Write-Host "Jenkins is already running!" -ForegroundColor Yellow + Start-Process "http://localhost:8090" + exit 0 +} + +# Set environment +$env:JENKINS_HOME = "C:\Jenkins\jenkins_home" +$jenkinsWar = "C:\Jenkins\jenkins.war" + +if (-not (Test-Path $jenkinsWar)) { + Write-Host "Jenkins not found! Run setup-jenkins-simple.ps1 first" -ForegroundColor Red + exit 1 +} + +Write-Host "Starting Jenkins server..." -ForegroundColor Yellow +Write-Host "Jenkins Home: $env:JENKINS_HOME" -ForegroundColor Cyan + +# Start Jenkins in background +$jenkinsJob = Start-Job -ScriptBlock { + param($jenkinsWar, $jenkinsHome) + $env:JENKINS_HOME = $jenkinsHome + & java "-Djenkins.install.runSetupWizard=false" "-jar" $jenkinsWar "--httpPort=8090" +} -ArgumentList $jenkinsWar, $env:JENKINS_HOME + +Write-Host "Jenkins starting (Job ID: $($jenkinsJob.Id))..." -ForegroundColor Yellow +Write-Host "Waiting for Jenkins to be ready..." -ForegroundColor Yellow + +# Wait for Jenkins +$maxWait = 120 +$waited = 0 +$ready = $false + +while ($waited -lt $maxWait -and -not $ready) { + Start-Sleep -Seconds 5 + $waited += 5 + try { + $response = Invoke-WebRequest -Uri "http://localhost:8090" -UseBasicParsing -TimeoutSec 5 -ErrorAction SilentlyContinue + if ($response.StatusCode -eq 200) { + $ready = $true + } + } catch { + Write-Host "." -NoNewline -ForegroundColor Gray + } +} + +Write-Host "" + +if ($ready) { + Write-Host "Jenkins is ready!" -ForegroundColor Green + Write-Host "" + Write-Host "ACCESS INFO:" -ForegroundColor Cyan + Write-Host "URL: http://localhost:8090" -ForegroundColor White + Write-Host "Username: admin" -ForegroundColor White + Write-Host "Password: admin123" -ForegroundColor White + Write-Host "" + Write-Host "Opening Jenkins..." -ForegroundColor Yellow + Start-Process "http://localhost:8090" + + # Save session info + @{ + JobId = $jenkinsJob.Id + StartTime = Get-Date + URL = "http://localhost:8090" + } | ConvertTo-Json | Out-File "jenkins-session.json" + +} else { + Write-Host "Jenkins failed to start!" -ForegroundColor Red + Write-Host "Check output: Get-Job $($jenkinsJob.Id) | Receive-Job" -ForegroundColor Yellow +} + +Write-Host "" +Write-Host "To stop Jenkins: Stop-Job $($jenkinsJob.Id)" -ForegroundColor Cyan diff --git a/extra setup/setup-github-token.ps1 b/extra setup/setup-github-token.ps1 new file mode 100644 index 0000000..4f8489a --- /dev/null +++ b/extra setup/setup-github-token.ps1 @@ -0,0 +1,100 @@ +# GitHub Token Setup for Jenkins CI/CD +param( + [string]$GitHubToken = "" +) + +Write-Host "🔑 GITHUB TOKEN SETUP FOR JENKINS CI/CD" -ForegroundColor Cyan +Write-Host "=======================================" -ForegroundColor Yellow +Write-Host "" + +# Check if Jenkins is running +Write-Host "1. 🔍 Checking Jenkins status..." -ForegroundColor Magenta +$jenkinsProcess = Get-Process -Name java -ErrorAction SilentlyContinue +if ($jenkinsProcess) { + Write-Host " ✅ Jenkins is running (PID: $($jenkinsProcess.Id))" -ForegroundColor Green +} else { + Write-Host " ❌ Jenkins not running. Please start Jenkins first!" -ForegroundColor Red + Write-Host " Run: .\run-jenkins-simple.ps1" -ForegroundColor Yellow + exit 1 +} + +Write-Host "" +Write-Host "2. 📋 GITHUB TOKEN SETUP INSTRUCTIONS:" -ForegroundColor Magenta +Write-Host "" + +if (-not $GitHubToken) { + Write-Host "🌐 STEP 1: Create GitHub Personal Access Token" -ForegroundColor Cyan + Write-Host " 1. Go to: https://github.com/settings/tokens" -ForegroundColor White + Write-Host " 2. Click 'Generate new token (classic)'" -ForegroundColor White + Write-Host " 3. Token name: Jenkins-DevOps-Challenge" -ForegroundColor White + Write-Host " 4. Expiration: 90 days" -ForegroundColor White + Write-Host " 5. Select scopes:" -ForegroundColor White + Write-Host " ✅ repo (Full control of private repositories)" -ForegroundColor Green + Write-Host " ✅ workflow (Update GitHub Action workflows)" -ForegroundColor Green + Write-Host " ✅ admin:repo_hook (Repository hooks)" -ForegroundColor Green + Write-Host " 6. Generate token and COPY IT!" -ForegroundColor Yellow + Write-Host "" + + Write-Host "🔧 STEP 2: Configure in Jenkins" -ForegroundColor Cyan + Write-Host " 1. Go to: http://localhost:8090/credentials/" -ForegroundColor White + Write-Host " 2. Click: System → Global credentials → Add Credentials" -ForegroundColor White + Write-Host " 3. Fill in:" -ForegroundColor White + Write-Host " Kind: Username with password" -ForegroundColor Gray + Write-Host " Username: kingslayer458" -ForegroundColor Gray + Write-Host " Password: [paste your GitHub token]" -ForegroundColor Gray + Write-Host " ID: github-token" -ForegroundColor Gray + Write-Host " Description: GitHub Token for DevOps Challenge" -ForegroundColor Gray + Write-Host "" + + Write-Host "🔄 STEP 3: Update Pipeline Job" -ForegroundColor Cyan + Write-Host " 1. Go to: http://localhost:8090/job/fusionpact-devops-challenge/configure" -ForegroundColor White + Write-Host " 2. In Source Code Management section:" -ForegroundColor White + Write-Host " 3. Repository URL: https://github.com/kingslayer458/fusionpact-devops-challenge.git" -ForegroundColor White + Write-Host " 4. Credentials: Select 'github-token'" -ForegroundColor White + Write-Host " 5. Click Save" -ForegroundColor White + Write-Host "" + + $openBrowser = Read-Host "Open setup pages in browser? (y/n)" + if ($openBrowser -eq 'y' -or $openBrowser -eq 'Y') { + Write-Host "🔗 Opening GitHub token creation page..." -ForegroundColor Yellow + Start-Process "https://github.com/settings/tokens" + Start-Sleep 3 + Write-Host "🔗 Opening Jenkins credentials page..." -ForegroundColor Yellow + Start-Process "http://localhost:8090/credentials/" + Start-Sleep 2 + Write-Host "🔗 Opening Jenkins job configuration..." -ForegroundColor Yellow + Start-Process "http://localhost:8090/job/fusionpact-devops-challenge/configure" + } +} else { + Write-Host "✅ GitHub token provided via parameter" -ForegroundColor Green + Write-Host "📋 Use this token in Jenkins credentials configuration:" -ForegroundColor Cyan + Write-Host " Token: $($GitHubToken.Substring(0,10))..." -ForegroundColor Gray +} + +Write-Host "" +Write-Host "🧪 STEP 4: Test Configuration" -ForegroundColor Magenta +Write-Host " After setting up the token, test with:" -ForegroundColor White +Write-Host " git commit --allow-empty -m 'Test GitHub token auth'" -ForegroundColor Gray +Write-Host " git push origin main" -ForegroundColor Gray +Write-Host "" + +Write-Host "🔍 VERIFICATION CHECKLIST:" -ForegroundColor Cyan +Write-Host " ✅ GitHub token created with correct scopes" -ForegroundColor White +Write-Host " ✅ Jenkins credentials configured (username + token)" -ForegroundColor White +Write-Host " ✅ Pipeline job updated with github-token credential" -ForegroundColor White +Write-Host " ✅ Test commit triggers automatic build" -ForegroundColor White +Write-Host "" + +Write-Host "⚠️ COMMON ISSUES:" -ForegroundColor Yellow +Write-Host " • 403 Forbidden: Wrong token or expired" -ForegroundColor Red +Write-Host " • No credentials specified: Token not configured in job" -ForegroundColor Red +Write-Host " • Authentication failed: Wrong username or token" -ForegroundColor Red +Write-Host "" + +Write-Host "🎯 EXPECTED RESULT:" -ForegroundColor Green +Write-Host " After configuration, builds should trigger automatically" -ForegroundColor White +Write-Host " when you push commits to GitHub!" -ForegroundColor White +Write-Host "" + +Write-Host "📞 Need help? Check the console output in Jenkins builds" -ForegroundColor Cyan +Write-Host " for detailed error messages." -ForegroundColor White diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..fad0b60 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,17 @@ +# Use nginx:alpine for lightweight serving +FROM nginx:alpine + +# Remove default nginx website +RUN rm -rf /usr/share/nginx/html/* + +# Copy HTML file to nginx html directory +COPY Devops_Intern.html /usr/share/nginx/html/index.html + +# Copy nginx configuration if needed (optional) +# COPY nginx.conf /etc/nginx/nginx.conf + +# Expose port 80 +EXPOSE 80 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/grafana/provisioning/dashboards/dashboard.yml b/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 0000000..be165c4 --- /dev/null +++ b/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/grafana/provisioning/dashboards/fusionpact-dashboard.json b/grafana/provisioning/dashboards/fusionpact-dashboard.json new file mode 100644 index 0000000..7d0c080 --- /dev/null +++ b/grafana/provisioning/dashboards/fusionpact-dashboard.json @@ -0,0 +1,407 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 10, + "panels": [], + "title": "Infrastructure Metrics", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "rate(container_cpu_usage_seconds_total{id=~\"/docker/.*\"}[5m]) * 100", + "interval": "", + "legendFormat": "{{id}} CPU Usage", + "refId": "A" + }, + { + "expr": "100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)", + "interval": "", + "legendFormat": "Host CPU Usage", + "refId": "B" + } + ], + "title": "Container CPU Usage", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "container_memory_usage_bytes{id=~\"/docker/.*\"} / 1024 / 1024", + "interval": "", + "legendFormat": "{{id}} Memory Usage (MB)", + "refId": "A" + }, + { + "expr": "(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / 1024 / 1024", + "interval": "", + "legendFormat": "Host Memory Usage (MB)", + "refId": "B" + } + ], + "title": "Container Memory Usage", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 12, + "panels": [], + "title": "Application Metrics", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "rate(http_requests_total[5m])", + "interval": "", + "legendFormat": "{{method}} {{handler}} - {{status}", + "refId": "A" + } + ], + "title": "HTTP Request Rate", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))", + "interval": "", + "legendFormat": "95th percentile", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))", + "interval": "", + "legendFormat": "50th percentile", + "refId": "B" + } + ], + "title": "HTTP Request Latency", + "type": "timeseries" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": ["fusionpact", "monitoring"], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Fusionpact DevOps Challenge - Infrastructure & Application Metrics", + "uid": "fusionpact-dashboard", + "version": 1 +} diff --git a/grafana/provisioning/datasources/prometheus.yml b/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 0000000..1a57b69 --- /dev/null +++ b/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true diff --git a/health-check.sh b/health-check.sh new file mode 100644 index 0000000..50b6b55 --- /dev/null +++ b/health-check.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Health Check Script for Fusionpact DevOps Challenge +# This script monitors the health of both frontend and backend services + +echo "🔍 Fusionpact DevOps Challenge - Health Check" +echo "==============================================" + +# Check if Docker is running +if ! docker info > /dev/null 2>&1; then + echo "❌ Docker is not running" + exit 1 +fi + +# Check container status +echo "📊 Container Status:" +docker-compose ps + +echo "" + +# Test Frontend +echo "🌐 Testing Frontend Service..." +FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8070) +if [ "$FRONTEND_STATUS" -eq 200 ]; then + echo "✅ Frontend: HEALTHY (Status: $FRONTEND_STATUS)" +else + echo "❌ Frontend: UNHEALTHY (Status: $FRONTEND_STATUS)" +fi + +# Test Backend API +echo "⚡ Testing Backend API..." +BACKEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8060) +if [ "$BACKEND_STATUS" -eq 200 ]; then + echo "✅ Backend API: HEALTHY (Status: $BACKEND_STATUS)" + + # Test API response + API_RESPONSE=$(curl -s http://localhost:8060) + echo " Response: $API_RESPONSE" +else + echo "❌ Backend API: UNHEALTHY (Status: $BACKEND_STATUS)" +fi + +# Test Metrics Endpoint +echo "📈 Testing Metrics Endpoint..." +METRICS_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8060/metrics) +if [ "$METRICS_STATUS" -eq 200 ]; then + echo "✅ Metrics: HEALTHY (Status: $METRICS_STATUS)" +else + echo "❌ Metrics: UNHEALTHY (Status: $METRICS_STATUS)" +fi + +# Test Users API +echo "👥 Testing Users API..." +USERS_GET_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8060/users) +if [ "$USERS_GET_STATUS" -eq 200 ]; then + echo "✅ Users GET: HEALTHY (Status: $USERS_GET_STATUS)" + + # Show current users + USERS_DATA=$(curl -s http://localhost:8000/users) + echo " Current users: $USERS_DATA" +else + echo "❌ Users GET: UNHEALTHY (Status: $USERS_GET_STATUS)" +fi + +# Test POST endpoint with sample data +echo "📝 Testing Users POST..." +POST_RESPONSE=$(curl -s -X POST http://localhost:8060/users \ + -H "Content-Type: application/json" \ + -d '{"first_name":"Test","last_name":"User","age":25}' \ + -w "%{http_code}") + +POST_STATUS="${POST_RESPONSE: -3}" +POST_BODY="${POST_RESPONSE%???}" + +if [ "$POST_STATUS" -eq 200 ]; then + echo "✅ Users POST: HEALTHY (Status: $POST_STATUS)" + echo " Response: $POST_BODY" +else + echo "❌ Users POST: UNHEALTHY (Status: $POST_STATUS)" +fi + +# Check data persistence +echo "💾 Checking Data Persistence..." +if docker volume ls | grep -q "backend-data"; then + echo "✅ Data volume exists" + VOLUME_INFO=$(docker volume inspect fusionpact-devops-challenge_backend-data --format '{{.Mountpoint}}') + echo " Volume location: $VOLUME_INFO" +else + echo "❌ Data volume missing" +fi + +# Resource usage +echo "💻 Resource Usage:" +docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" + +echo "" +echo "🏁 Health check complete!" +echo "==============================================" diff --git a/jenkins-session.json b/jenkins-session.json new file mode 100644 index 0000000..a82731c Binary files /dev/null and b/jenkins-session.json differ diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..78a080a --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,33 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: "prometheus" + static_configs: + - targets: ["localhost:9090"] + + # Scrape the FastAPI backend metrics + - job_name: "fusionpact-backend" + static_configs: + - targets: ["fusionpact-backend:8060"] + metrics_path: "/metrics" + scrape_interval: 10s + scrape_timeout: 10s + + # Monitor Docker container metrics via cAdvisor + - job_name: "cadvisor" + static_configs: + - targets: ["fusionpact-cadvisor:8080"] + scrape_interval: 15s + + # Monitor Node/System metrics via Node Exporter + - job_name: "node-exporter" + static_configs: + - targets: ["fusionpact-node-exporter:9100"] + scrape_interval: 15s diff --git a/runjenkin.txt b/runjenkin.txt new file mode 100644 index 0000000..8e8452c --- /dev/null +++ b/runjenkin.txt @@ -0,0 +1,11 @@ +open + +press win+ r +paste the command + +"C:\Program Files\Java\jdk-21\bin\java.exe" -jar "C:\Program Files\Jenkins\jenkins.war" + + +docker-compose -f docker-compose.monitoring.yml up -d + +docker-compose -f docker-compose.monitoring.yml down \ No newline at end of file