feat(lab-03): complete Phase 4 - Compute & EC2 lab
Phase Plans (5 files): - 04-RESEARCH.md: Domain research on Docker limits, healthchecks, EC2 parallels - 04-VALIDATION.md: Success criteria and validation strategy - 04-01-PLAN.md: Test infrastructure (RED phase) - 04-02-PLAN.md: Diátxis documentation - 04-03-PLAN.md: Infrastructure implementation (GREEN phase) Test Scripts (6 files, 1300+ lines): - 01-resource-limits-test.sh: Validate INF-03 compliance - 02-healthcheck-test.sh: Validate healthcheck configuration - 03-enforcement-test.sh: Verify resource limits with docker stats - 04-verify-infrastructure.sh: Infrastructure verification - 99-final-verification.sh: End-to-end student verification - run-all-tests.sh: Test orchestration with fail-fast - quick-test.sh: Fast validation (<30s) Documentation (11 files, 2500+ lines): Tutorials (3): - 01-set-resource-limits.md: EC2 instance types, Docker limits syntax - 02-implement-healthchecks.md: ELB health check parallels - 03-dependencies-with-health.md: depends_on with service_healthy How-to Guides (4): - check-resource-usage.md: docker stats monitoring - test-limits-enforcement.md: Stress testing CPU/memory - custom-healthcheck.md: HTTP, TCP, database healthchecks - instance-type-mapping.md: Docker limits → EC2 mapping Reference (3): - compose-resources-syntax.md: Complete deploy.resources reference - healthcheck-syntax.md: All healthcheck parameters - ec2-instance-mapping.md: Instance type mapping table Explanation (1): - compute-ec2-parallels.md: Container=EC2, Limits=Instance Type, Healthcheck=ELB Infrastructure: - docker-compose.yml: 5 services (web, app, worker, db, stress-test) All services: INF-03 compliant (cpus + memory limits) All services: healthcheck configured EC2 parallels: t2.nano, t2.micro, t2.small, t2.medium, m5.large - Dockerfile: Alpine 3.19 + stress tools + non-root user Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
331
labs/lab-03-compute/tests/99-final-verification.sh
Executable file
331
labs/lab-03-compute/tests/99-final-verification.sh
Executable file
@@ -0,0 +1,331 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user