#!/bin/bash # Final Verification: Lab 03 - Compute & EC2 # Comprehensive end-to-end verification for students # Usage: bash labs/lab-03-compute/tests/99-final-verification.sh set -euo pipefail # Color definitions RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' BOLD='\033[1m' NC='\033[0m' # Get script directory TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LAB_DIR="$(cd "$TEST_DIR/.." && pwd)" # Counter helpers pass_count=0 fail_count=0 inc_pass() { ((pass_count++)) || true; } inc_fail() { ((fail_count++)) || true; } # Helper functions print_header() { echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║${NC} ${BOLD}$1${NC}" echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}" } print_test() { echo -e "\n${BLUE}[TEST]${NC} $1" } print_pass() { echo -e " ${GREEN}[✓]${NC} $1" inc_pass } print_fail() { echo -e " ${RED}[✗]${NC} $1" inc_fail } print_info() { echo -e " ${BLUE}[i]${NC} $1" } print_warning() { echo -e " ${YELLOW}[!]${NC} $1" } # Cleanup function cleanup() { print_info "\nCleaning up..." cd "$LAB_DIR" docker compose down -v 2>/dev/null || true } # Set trap for cleanup on exit trap cleanup EXIT # Main verification print_header "Lab 03 Final Verification - Compute & EC2" cd "$LAB_DIR" echo -e "\n${BLUE}This script verifies:${NC}" echo -e " • Resource limits (INF-03 compliance)" echo -e " • Healthcheck configuration" echo -e " • Resource enforcement with docker stats" echo -e " • EC2 instance type parallels" echo -e " • ELB health check parallels" # === SECTION 1: File and Syntax === print_header "Section 1: Files and Syntax" # Test 1: docker-compose.yml exists print_test "Verifying docker-compose.yml exists" if [[ -f "docker-compose.yml" ]]; then print_pass "docker-compose.yml found" else print_fail "docker-compose.yml not found - create it first!" exit 1 fi # Test 2: Syntax validation print_test "Validating docker-compose.yml syntax" if docker compose config &> /dev/null; then print_pass "docker-compose.yml syntax is valid" else print_fail "docker-compose.yml has syntax errors - run 'docker compose config' to debug" exit 1 fi # Test 3: Services defined print_test "Checking defined services" SERVICES=$(docker compose config --services 2>/dev/null) SERVICE_COUNT=$(echo "$SERVICES" | wc -l) if [[ $SERVICE_COUNT -ge 3 ]]; then print_pass "Found $SERVICE_COUNT services (minimum 3 required)" else print_fail "Only $SERVICE_COUNT services found (need at least 3)" exit 1 fi # === SECTION 2: INF-03 Compliance === print_header "Section 2: INF-03 Compliance (Resource Limits)" print_test "Verifying all services have resource limits" NON_COMPLIANT=0 LIMITS_SUMMARY="" for service in $SERVICES; do # Check for resource limits has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0") has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0") if [[ $has_cpu -eq 0 ]]; then print_fail " $service: Missing cpus limit" ((NON_COMPLIANT++)) || true elif [[ $has_memory -eq 0 ]]; then print_fail " $service: Missing memory limit" ((NON_COMPLIANT++)) || true else cpu_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "') mem_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"") # Map to EC2 instance type case "$cpu_val:$mem_val" in "0.5:512M"|"0.5:512") instance="t2.nano" ;; "1:1G"|"1:1024M") instance="t2.micro" ;; "1:2G"|"1:2048M") instance="t2.small" ;; "2:4G"|"2:4096M") instance="t2.medium" ;; "2:8G"|"2:8192M") instance="m5.large" ;; "4:16G"|"4:16384M") instance="m5.xlarge" ;; *) instance="custom" ;; esac print_pass " $service: cpus=$cpu_val, memory=$mem_val → $instance" LIMITS_SUMMARY="$LIMITS_SUMMARY\n • $service → $instance" fi done if [[ $NON_COMPLIANT -eq 0 ]]; then print_pass "✓ INF-03 COMPLIANT: All services have resource limits" else print_fail "✗ INF-03 VIOLATION: $NON_COMPLIANT services missing limits" fi # === SECTION 3: Healthcheck Configuration === print_header "Section 3: Healthcheck Configuration" print_test "Verifying healthcheck configuration" MISSING_HC=0 HC_SUMMARY="" for service in $SERVICES; do has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0") if [[ $has_hc -eq 0 ]]; then print_fail " $service: Missing healthcheck" ((MISSING_HC++)) || true else interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A") timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A") retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A") print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries" HC_SUMMARY="$HC_SUMMARY\n • $service: interval=$interval, timeout=$timeout, retries=$retries" fi done if [[ $MISSING_HC -eq 0 ]]; then print_pass "✓ All services have healthchecks configured" else print_fail "✗ $MISSING_HC services missing healthchecks" fi # === SECTION 4: Infrastructure Deployment === print_header "Section 4: Infrastructure Deployment" print_test "Starting services" if docker compose up -d &> /dev/null; then print_pass "Services started successfully" sleep 8 # Wait for services to be ready else print_fail "Failed to start services" exit 1 fi print_test "Verifying containers are running" RUNNING=$(docker compose ps --services --filter "status=running" | wc -l) if [[ $RUNNING -ge 3 ]]; then print_pass "$RUNNING containers running" docker compose ps else print_fail "Only $RUNNING containers running (expected 3+)" fi # === SECTION 5: Resource Enforcement === print_header "Section 5: Resource Enforcement Verification" print_test "Checking resource limits are applied" LIMITS_ENFORCED=0 for service in $SERVICES; do container_name="lab03-$service" if docker ps --format '{{.Names}}' | grep -q "$container_name"; then nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0") mem_bytes=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0") if [[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]]; then cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A") if [[ $mem_bytes -ge 1073741824 ]]; then mem_display=$(echo "scale=2; $mem_bytes / 1073741824" | bc)G else mem_display=$(echo "scale=0; $mem_bytes / 1048576" | bc)M fi print_pass " $container_name: ${cpu_cores} CPU, $mem_display RAM" ((LIMITS_ENFORCED++)) || true else print_fail " $container_name: Limits not applied" fi fi done if [[ $LIMITS_ENFORCED -gt 0 ]]; then print_pass "✓ Resource limits enforced for $LIMITS_ENFORCED containers" else print_fail "✗ Resource limits not enforced" fi # Test docker stats print_test "Monitoring resource usage" echo -e "\n${BLUE}Live Resource Usage:${NC}" docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || print_fail "docker stats failed" # === SECTION 6: Health Status === print_header "Section 6: Health Status Verification" print_test "Checking container health status" HEALTHY=0 for service in $SERVICES; do container_name="lab03-$service" if docker ps --format '{{.Names}}' | grep -q "$container_name"; then health=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck") if [[ "$health" == "healthy" ]]; then print_pass " $container_name: healthy ✓" ((HEALTHY++)) || true elif [[ "$health" == "no-healthcheck" ]]; then print_warning " $container_name: no healthcheck configured" else print_info " $container_name: $health" fi fi done if [[ $HEALTHY -gt 0 ]]; then print_pass "✓ $HEALTHY containers are healthy" else print_warning "No healthy containers yet (may still be starting)" fi # === SECTION 7: Cloud Parallels === print_header "Section 7: Cloud Parallels Verification" print_test "EC2 Instance Type Parallels" echo -e "${BLUE}Your Docker configuration maps to these EC2 instances:${NC}" echo -e "$LIMITS_SUMMARY" print_test "ELB Health Check Parallels" echo -e "${BLUE}Your healthchecks parallel ELB health checks:${NC}" echo -e "$HC_SUMMARY" # === FINAL SUMMARY === print_header "Final Verification Summary" TOTAL_TESTS=$((pass_count + fail_count)) PASS_PERCENT=$((pass_count * 100 / TOTAL_TESTS)) echo -e "Total Tests: $TOTAL_TESTS" echo -e "${GREEN}Passed: $pass_count${NC} ($PASS_PERCENT%)" if [[ $fail_count -gt 0 ]]; then echo -e "${RED}Failed: $fail_count${NC}" fi # INF-03 Compliance Summary echo -e "\n${BLUE}INF-03 Compliance:${NC}" if [[ $NON_COMPLIANT -eq 0 ]]; then echo -e " ${GREEN}✓ COMPLIANT${NC} - All services have resource limits" else echo -e " ${RED}✗ NON-COMPLIANT${NC} - $NON_COMPLIANT services missing limits" fi # Healthcheck Summary echo -e "\n${BLUE}Healthcheck Coverage:${NC}" if [[ $MISSING_HC -eq 0 ]]; then echo -e " ${GREEN}✓ COMPLETE${NC} - All services have healthchecks" else echo -e " ${RED}✗ INCOMPLETE${NC} - $MISSING_HC services missing healthchecks" fi # Final verdict if [[ $fail_count -eq 0 ]]; then echo -e "\n${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}${BOLD}║${NC} ${BOLD}✓✓✓ ALL VERIFICATIONS PASSED ✓✓✓${NC}" echo -e "${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}" echo -e "\n${GREEN}Congratulations! Your Lab 03 infrastructure is correctly deployed.${NC}" echo -e "\nYou have successfully:" echo -e " • Configured resource limits (EC2 instance types)" echo -e " • Implemented healthchecks (ELB health checks)" echo -e " • Verified resource enforcement with docker stats" echo -e "\nYou can now proceed with the tutorials!" exit 0 else echo -e "\n${RED}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}" echo -e "${RED}${BOLD}║${NC} ${BOLD}✗✗✗ SOME VERIFICATIONS FAILED ✗✗✗${NC}" echo -e "${RED}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}" echo -e "\n${RED}Please review the failures above.${NC}" echo -e "\nCommon issues:" echo -e " • Missing resource limits → Add deploy.resources.limits to services" echo -e " • Missing healthchecks → Add healthcheck section to services" echo -e " • Services not starting → Check logs with 'docker compose logs'" exit 1 fi