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:
Luca Sacchi Ricciardi
2026-04-03 15:16:58 +02:00
parent 39b9a56850
commit 23a9ffe443
26 changed files with 5457 additions and 1 deletions

View File

@@ -0,0 +1,215 @@
#!/bin/bash
# Test 01: Resource Limits Configuration
# Verifies that docker-compose.yml services have mandatory resource limits
# Usage: bash labs/lab-03-compute/tests/01-resource-limits-test.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"
}
# Main verification
print_header "Lab 03 Resource Limits Verification"
cd "$LAB_DIR"
# 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"
exit 1
fi
# Test 2: docker-compose.yml is valid
print_test "Validating docker-compose.yml syntax"
if docker compose config &> /dev/null; then
print_pass "docker-compose.yml has valid syntax"
else
print_fail "docker-compose.yml has syntax errors"
exit 1
fi
# Test 3: Services are defined
print_test "Verifying services are defined"
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -gt 0 ]]; then
print_pass "Found $SERVICE_COUNT services"
echo "$SERVICES" | while read -r service; do
print_info " - $service"
done
else
print_fail "No services defined"
exit 1
fi
# Test 4: INF-03 compliance check - all services must have resource limits
print_test "Checking INF-03 compliance (resource limits)"
NON_COMPLIANT=0
for service in $SERVICES; do
# Check for deploy.resources section
has_deploy=$(docker compose config 2>/dev/null | grep -A 20 "^$service:" | grep -c "deploy:" || echo "0")
has_resources=$(docker compose config 2>/dev/null | grep -A 25 "^$service:" | grep -c "resources:" || echo "0")
if [[ $has_deploy -eq 0 || $has_resources -eq 0 ]]; then
print_fail " $service: Missing deploy.resources section"
((NON_COMPLIANT++)) || true
else
# Check for CPU limit
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
# Check for memory limit
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
fi
if [[ $has_memory -eq 0 ]]; then
print_fail " $service: Missing memory limit"
((NON_COMPLIANT++)) || true
fi
if [[ $has_cpu -gt 0 && $has_memory -gt 0 ]]; then
# Extract limits for reporting
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '")
print_pass " $service: cpus: $cpu_limit, memory: $mem_limit"
fi
fi
done
if [[ $NON_COMPLIANT -eq 0 ]]; then
print_pass "INF-03 compliance: All services have resource limits"
else
print_fail "INF-03 compliance: $NON_COMPLIANT services missing limits"
fi
# Test 5: Verify specific EC2 instance type parallels
print_test "Verifying EC2 instance type parallels"
INSTANCE_TYPES=0
for service in $SERVICES; do
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"" || echo "")
# Map to EC2 instance types
if [[ -n "$cpu_limit" && -n "$mem_limit" ]]; then
case "$cpu_limit:$mem_limit" in
"0.5:512M"|"0.5:512")
instance_type="t2.nano"
;;
"1:1G"|"1:1024M"|"1:1024")
instance_type="t2.micro"
;;
"1:2G"|"1:2048M"|"1:2048")
instance_type="t2.small"
;;
"2:4G"|"2:4096M"|"2:4096")
instance_type="t2.medium"
;;
"2:8G"|"2:8192M"|"2:8192")
instance_type="m5.large"
;;
*)
instance_type="custom"
;;
esac
print_info " $service$instance_type (cpus: $cpu_limit, memory: $mem_limit)"
((INSTANCE_TYPES++)) || true
fi
done
if [[ $INSTANCE_TYPES -gt 0 ]]; then
print_pass "EC2 instance type mapping complete for $INSTANCE_TYPES services"
else
print_fail "No services with valid resource limits found"
fi
# Test 6: Verify resource limit formats
print_test "Verifying resource limit formats"
FORMAT_OK=0
for service in $SERVICES; do
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
# CPU limit should be a number or decimal
if [[ -n "$cpu_limit" ]]; then
if [[ "$cpu_limit" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
((FORMAT_OK++)) || true
else
print_fail " $service: Invalid CPU format: $cpu_limit"
fi
fi
done
if [[ $FORMAT_OK -eq $SERVICE_COUNT ]]; then
print_pass "All resource limits use valid format"
else
print_fail "Some resource limits have invalid format"
fi
# Summary
print_header "Resource Limits Verification Summary"
echo -e "Tests run: $((pass_count + fail_count))"
echo -e "${GREEN}Passed: $pass_count${NC}"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓ ALL RESOURCE LIMITS CHECKS PASSED${NC}"
echo -e "\nINF-03 compliance verified!"
exit 0
else
echo -e "\n${RED}Some resource limits checks failed${NC}"
echo -e "Please ensure all services have cpus and memory limits."
exit 1
fi

View File

@@ -0,0 +1,255 @@
#!/bin/bash
# Test 02: Healthcheck Configuration
# Verifies that docker-compose.yml services have healthchecks configured
# Usage: bash labs/lab-03-compute/tests/02-healthcheck-test.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"
}
# Main verification
print_header "Lab 03 Healthcheck Verification"
cd "$LAB_DIR"
# 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"
exit 1
fi
# Test 2: docker-compose.yml is valid
print_test "Validating docker-compose.yml syntax"
if docker compose config &> /dev/null; then
print_pass "docker-compose.yml has valid syntax"
else
print_fail "docker-compose.yml has syntax errors"
exit 1
fi
# Test 3: Services are defined
print_test "Verifying services are defined"
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -gt 0 ]]; then
print_pass "Found $SERVICE_COUNT services"
else
print_fail "No services defined"
exit 1
fi
# Test 4: Healthcheck configuration check
print_test "Checking healthcheck configuration"
MISSING_HEALTHCHECK=0
for service in $SERVICES; do
# Check for healthcheck section
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -eq 0 ]]; then
print_fail " $service: Missing healthcheck section"
((MISSING_HEALTHCHECK++)) || true
else
# Verify healthcheck has test command
has_test=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep -c "test:" || echo "0")
if [[ $has_test -eq 0 ]]; then
print_fail " $service: Missing healthcheck test command"
((MISSING_HEALTHCHECK++)) || true
else
# Extract test command for reporting
test_cmd=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "test:" | head -1 | sed 's/.*test: //' | tr -d '"')
print_pass " $service: healthcheck configured"
print_info " test: $test_cmd"
fi
fi
done
if [[ $MISSING_HEALTHCHECK -eq 0 ]]; then
print_pass "All services have healthchecks configured"
else
print_fail "$MISSING_HEALTHCHECK services missing healthchecks"
fi
# Test 5: Healthcheck parameters verification
print_test "Verifying healthcheck parameters"
PARAMS_OK=0
for service in $SERVICES; do
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -gt 0 ]]; then
# Check for interval
has_interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "interval:" || echo "0")
# Check for timeout
has_timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "timeout:" || echo "0")
# Check for retries
has_retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "retries:" || echo "0")
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
if [[ $has_interval -gt 0 && $has_timeout -gt 0 && $has_retries -gt 0 ]]; then
print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
((PARAMS_OK++)) || true
else
print_info " $service: interval=$interval, timeout=$timeout, retries=$retries"
# Check for start_period (optional)
has_start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "start_period:" || echo "0")
if [[ $has_start_period -gt 0 ]]; then
start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "start_period:" | sed 's/.*start_period: //' | tr -d " '"" || echo "")
print_info " start_period=$start_period"
fi
fi
fi
done
if [[ $PARAMS_OK -gt 0 ]]; then
print_pass "$PARAMS_OK services have complete healthcheck parameters"
else
print_fail "No services with complete healthcheck parameters"
fi
# Test 6: Healthcheck command types
print_test "Analyzing healthcheck command types"
HTTP_COUNT=0
CMD_SHELL_COUNT=0
CMD_COUNT=0
CUSTOM_COUNT=0
for service in $SERVICES; do
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -gt 0 ]]; then
# Get test command
test_section=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" || echo "")
if echo "$test_section" | grep -q "CMD-SHELL"; then
print_info " $service: CMD-SHELL healthcheck"
((CMD_SHELL_COUNT++)) || true
elif echo "$test_section" | grep -q "CMD"; then
print_info " $service: CMD healthcheck"
((CMD_COUNT++)) || true
else
print_info " $service: Custom healthcheck format"
((CUSTOM_COUNT++)) || true
fi
# Check if HTTP-based
if echo "$test_section" | grep -qiE "(wget|curl|http)"; then
((HTTP_COUNT++)) || true
fi
fi
done
print_info "Healthcheck types: CMD-SHELL ($CMD_SHELL_COUNT), CMD ($CMD_COUNT), Custom ($CUSTOM_COUNT)"
if [[ $HTTP_COUNT -gt 0 ]]; then
print_pass "$HTTP_COUNT services use HTTP-based healthchecks"
fi
# Test 7: ELB Health Check Parallel verification
print_test "Verifying ELB Health Check parallelism"
ELB_PARALLEL=0
for service in $SERVICES; do
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -gt 0 ]]; then
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "")
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "")
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "")
# Map to ELB defaults
if [[ "$interval" == "30s" || "$interval" =~ ^30 ]]; then
elb_interval="default (30s)"
((ELB_PARALLEL++)) || true
else
elb_interval="custom ($interval)"
fi
if [[ "$timeout" == "5s" || "$timeout" =~ ^5 ]]; then
elb_timeout="default (5s)"
((ELB_PARALLEL++)) || true
else
elb_timeout="custom ($timeout)"
fi
if [[ "$retries" == "3" || "$retries" =~ ^3 ]]; then
elb_retries="default (3)"
((ELB_PARALLEL++)) || true
else
elb_retries="custom ($retries)"
fi
print_info " $service: ELB parallel (interval: $elb_interval, timeout: $elb_timeout, retries: $elb_retries)"
fi
done
if [[ $ELB_PARALLEL -gt 0 ]]; then
print_pass "ELB Health Check parallelism verified"
fi
# Summary
print_header "Healthcheck Verification Summary"
echo -e "Tests run: $((pass_count + fail_count))"
echo -e "${GREEN}Passed: $pass_count${NC}"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓ ALL HEALTHCHECK CHECKS PASSED${NC}"
echo -e "\nHealthcheck configuration verified!"
exit 0
else
echo -e "\n${RED}Some healthcheck checks failed${NC}"
echo -e "Please ensure all services have healthchecks configured."
exit 1
fi

View File

@@ -0,0 +1,287 @@
#!/bin/bash
# Test 03: Resource Enforcement Verification
# Verifies that resource limits are actually enforced using docker stats
# Usage: bash labs/lab-03-compute/tests/03-enforcement-test.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 "Cleaning up test containers..."
cd "$LAB_DIR"
docker compose down -v 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
# Main verification
print_header "Lab 03 Resource Enforcement Verification"
cd "$LAB_DIR"
# Test 1: Verify docker-compose.yml exists and is valid
print_test "Verifying docker-compose.yml exists and is valid"
if [[ ! -f "docker-compose.yml" ]]; then
print_fail "docker-compose.yml not found"
exit 1
fi
if ! docker compose config &> /dev/null; then
print_fail "docker-compose.yml has syntax errors"
exit 1
fi
print_pass "docker-compose.yml is valid"
# Test 2: Start services
print_test "Starting Docker Compose services"
if docker compose up -d &> /dev/null; then
print_pass "Services started successfully"
sleep 5 # Give services time to start
else
print_fail "Failed to start services"
exit 1
fi
# Test 3: Verify containers are running
print_test "Verifying containers are running"
RUNNING_CONTAINERS=$(docker compose ps --services --filter "status=running" | wc -l)
if [[ $RUNNING_CONTAINERS -ge 1 ]]; then
print_pass "Services running: $RUNNING_CONTAINERS containers"
docker compose ps
else
print_fail "Not enough containers running"
exit 1
fi
# Test 4: Check resource limits are applied
print_test "Verifying resource limits are applied to containers"
LIMITS_APPLIED=0
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Get CPU limit (NanoCPUs: 1e9 = 1 CPU core)
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
# Convert NanoCPUs to CPU cores
if [[ $nano_cpus -gt 0 ]]; then
cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
else
cpu_cores="N/A"
fi
# Convert memory to GB/MB
if [[ $mem_limit -gt 0 ]]; then
if [[ $mem_limit -ge 1073741824 ]]; then
mem_gb=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")
mem_display="${mem_gb}G"
else
mem_mb=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")
mem_display="${mem_mb}M"
fi
else
mem_display="N/A"
fi
if [[ "$cpu_cores" != "N/A" && "$mem_display" != "N/A" ]]; then
print_pass " $container_name: cpus: $cpu_cores, memory: $mem_display"
((LIMITS_APPLIED++)) || true
else
print_fail " $container_name: Could not read limits (cpus: $cpu_cores, memory: $mem_display)"
fi
fi
done
if [[ $LIMITS_APPLIED -gt 0 ]]; then
print_pass "Resource limits applied to $LIMITS_APPLIED containers"
else
print_fail "No containers with resource limits found"
fi
# Test 5: Monitor resource usage with docker stats
print_test "Monitoring resource usage with docker stats"
STATS_OK=0
# Get initial stats
print_info "Current resource usage:"
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || true
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Get CPU percentage and memory usage
cpu_percent=$(docker stats "$container_name" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
mem_usage=$(docker stats "$container_name" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
if [[ "$cpu_percent" != "N/A" && "$mem_usage" != "N/A" ]]; then
print_info " $container_name: CPU: ${cpu_percent}%, Memory: $mem_usage"
((STATS_OK++)) || true
fi
fi
done
if [[ $STATS_OK -gt 0 ]]; then
print_pass "docker stats successfully monitored $STATS_OK containers"
else
print_fail "Failed to monitor container stats"
fi
# Test 6: Verify health status
print_test "Verifying container health status"
HEALTHY_COUNT=0
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Get health status
health_status=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
if [[ "$health_status" == "healthy" ]]; then
print_pass " $container_name: $health_status"
((HEALTHY_COUNT++)) || true
elif [[ "$health_status" == "no-healthcheck" ]]; then
print_info " $container_name: no healthcheck configured"
else
print_info " $container_name: $health_status"
fi
fi
done
if [[ $HEALTHY_COUNT -gt 0 ]]; then
print_pass "$HEALTHY_COUNT containers are healthy"
else
print_warning "No healthy containers (services may still be starting)"
fi
# Test 7: Stress test for resource enforcement (if stress container exists)
print_test "Stress testing resource enforcement (if stress container available)"
STRESS_SERVICE=$(docker compose config --services | grep -E "(stress|test)" || echo "")
if [[ -n "$STRESS_SERVICE" ]]; then
print_info "Found stress service: $STRESS_SERVICE"
# Get the container name
stress_container="lab03-$STRESS_SERVICE"
if docker ps --format '{{.Names}}' | grep -q "$stress_container"; then
# Get limits
nano_cpus=$(docker inspect "$stress_container" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_limit=$(docker inspect "$stress_container" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
cpu_limit_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
if [[ $mem_limit -ge 1073741824 ]]; then
mem_limit_display=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")"G"
else
mem_limit_display=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")"M"
fi
print_info "Limits: cpus: $cpu_limit_cores, memory: $mem_limit_display"
# Monitor during stress test
print_info "Monitoring during stress (10 seconds)..."
for i in {1..5}; do
cpu_percent=$(docker stats "$stress_container" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
mem_usage=$(docker stats "$stress_container" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
print_info " Sample $i: CPU: ${cpu_percent}%, Memory: $mem_usage"
sleep 2
done
print_pass "Stress test completed"
fi
else
print_info "No stress service found - skipping stress test"
fi
# Test 8: Verify resource limit enforcement
print_test "Verifying resource limits are enforced"
ENFORCED=0
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Check if limits are set (not 0 or -1)
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
if [[ $nano_cpus -gt 0 && $mem_limit -gt 0 ]]; then
print_pass " $container_name: Limits enforced (CPU and memory)"
((ENFORCED++)) || true
else
print_fail " $container_name: Limits not enforced (CPU: $nano_cpus, Memory: $mem_limit)"
fi
fi
done
if [[ $ENFORCED -gt 0 ]]; then
print_pass "Resource limits enforced for $ENFORCED containers"
else
print_fail "Resource limits not enforced for any container"
fi
# Summary
print_header "Resource Enforcement Verification Summary"
echo -e "Tests run: $((pass_count + fail_count))"
echo -e "${GREEN}Passed: $pass_count${NC}"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓ ALL ENFORCEMENT CHECKS PASSED${NC}"
echo -e "\nResource limits are properly enforced!"
exit 0
else
echo -e "\n${RED}Some enforcement checks failed${NC}"
echo -e "Please verify resource limit configuration."
exit 1
fi

View File

@@ -0,0 +1,84 @@
#!/bin/bash
# Infrastructure Verification for Lab 03
# Verifies docker-compose.yml is correctly deployed with resource limits and healthchecks
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m'
pass_count=0; fail_count=0
inc_pass() { ((pass_count++)) || true; }
inc_fail() { ((fail_count++)) || true; }
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; }
cd "$(dirname "$0")/.."
print_header "Lab 03 Infrastructure Verification"
# File and syntax
print_test "Verifying docker-compose.yml exists and valid"
[[ -f "docker-compose.yml" ]] && print_pass "docker-compose.yml found" || { print_fail "docker-compose.yml not found"; exit 1; }
docker compose config &> /dev/null && print_pass "Syntax valid" || { print_fail "Syntax error"; exit 1; }
# Services and limits
print_test "Checking INF-03 compliance (resource limits)"
SERVICES=$(docker compose config --services 2>/dev/null)
NON_COMPLIANT=0
for s in $SERVICES; do
has_cpu=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "cpus:" || echo "0")
has_mem=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "memory:" || echo "0")
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
print_fail " $s: missing limits"
((NON_COMPLIANT++)) || true
else
cpu_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
mem_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '""")
print_pass " $s: cpus=$cpu_val, memory=$mem_val"
fi
done
[[ $NON_COMPLIANT -eq 0 ]] && print_pass "INF-03 COMPLIANT" || print_fail "INF-03 VIOLATION: $NON_COMPLIANT services"
# Healthchecks
print_test "Verifying healthcheck configuration"
MISSING_HC=0
for s in $SERVICES; do
has_hc=$(docker compose config 2>/dev/null | grep -A50 "^$s:" | grep -c "healthcheck:" || echo "0")
[[ $has_hc -gt 0 ]] && print_pass " $s: healthcheck configured" || { print_fail " $s: missing healthcheck"; ((MISSING_HC++)); }
done
[[ $MISSING_HC -eq 0 ]] && print_pass "All services have healthchecks" || print_fail "$MISSING_HC missing healthchecks"
# Deploy and verify
print_test "Starting services"
docker compose up -d &> /dev/null && print_pass "Services started" && sleep 8 || { print_fail "Failed to start"; exit 1; }
print_test "Checking running containers"
RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
[[ $RUNNING -ge 4 ]] && print_pass "$RUNNING containers running" || print_fail "Only $RUNNING containers"
# Enforcement
print_test "Verifying resource limits enforced"
ENFORCED=0
for s in $SERVICES; do
c="lab03-$s"
if docker ps --format '{{.Names}}' | grep -q "$c"; then
nano_cpus=$(docker inspect "$c" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_bytes=$(docker inspect "$c" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
[[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && print_pass " $c: limits enforced" || print_fail " $c: limits not applied"
[[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && ((ENFORCED++)) || true
fi
done
[[ $ENFORCED -gt 0 ]] && print_pass "Limits enforced for $ENFORCED containers"
# Summary
print_header "Summary"
echo "Tests: $((pass_count + fail_count)) | ${GREEN}Passed: $pass_count${NC} ${RED}Failed: $fail_count${NC}"
[[ $fail_count -eq 0 ]] && { echo -e "\n${GREEN}${BOLD}✓ ALL CHECKS PASSED${NC}\n"; exit 0; } || { echo -e "\n${RED}Some checks failed${NC}\n"; exit 1; }

View 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

View File

@@ -0,0 +1,79 @@
#!/bin/bash
# Quick Test - Lab 03: Compute & EC2
# Fast validation for development (< 30 seconds)
# Usage: bash labs/lab-03-compute/tests/quick-test.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)"
# Quick checks (no verbose output)
echo -e "${BLUE}Quick Test - Lab 03: Compute & EC2${NC}\n"
# Check 1: File exists
if [[ ! -f "$LAB_DIR/docker-compose.yml" ]]; then
echo -e "${RED}✗ docker-compose.yml not found${NC}"
exit 1
fi
echo -e "${GREEN}${NC} docker-compose.yml exists"
# Check 2: Syntax valid
if ! docker compose config &> /dev/null; then
echo -e "${RED}✗ docker-compose.yml syntax error${NC}"
exit 1
fi
echo -e "${GREEN}${NC} Syntax valid"
# Check 3: Services defined
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -lt 3 ]]; then
echo -e "${RED}✗ Only $SERVICE_COUNT services (need 3+)${NC}"
exit 1
fi
echo -e "${GREEN}${NC} $SERVICE_COUNT services defined"
# Check 4: Resource limits (INF-03)
NON_COMPLIANT=0
for service in $SERVICES; do
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
has_mem=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
((NON_COMPLIANT++)) || true
fi
done
if [[ $NON_COMPLIANT -gt 0 ]]; then
echo -e "${RED}$NON_COMPLIANT services missing resource limits (INF-03)${NC}"
exit 1
fi
echo -e "${GREEN}${NC} INF-03 compliant (all services have limits)"
# Check 5: Healthchecks
MISSING_HC=0
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
((MISSING_HC++)) || true
fi
done
if [[ $MISSING_HC -gt 0 ]]; then
echo -e "${YELLOW}$MISSING_HC services missing healthchecks${NC}"
else
echo -e "${GREEN}${NC} All services have healthchecks"
fi
# Summary
echo -e "\n${GREEN}${BOLD}✓ Quick test PASSED${NC}\n"
echo -e "For full verification, run:"
echo -e " bash tests/run-all-tests.sh"
echo -e " bash tests/99-final-verification.sh"

View File

@@ -0,0 +1,138 @@
#!/bin/bash
# Run All Tests - Lab 03: Compute & EC2
# Executes all test scripts in sequence with fail-fast behavior
# Usage: bash labs/lab-03-compute/tests/run-all-tests.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
total_passed=0
total_failed=0
tests_run=0
# Helper functions
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_section() {
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
}
print_pass() {
echo -e "${GREEN}${NC} $1"
}
print_fail() {
echo -e "${RED}${NC} $1"
}
# Main execution
print_header "Lab 03 Test Suite - Compute & EC2"
cd "$LAB_DIR"
echo -e "${BLUE}Running all tests in sequence...${NC}\n"
# Test 1: Resource Limits Configuration
print_section "Test 1/4: Resource Limits Configuration"
if bash "$TEST_DIR/01-resource-limits-test.sh"; then
print_pass "Resource limits tests PASSED"
((total_passed++)) || true
else
print_fail "Resource limits tests FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/01-resource-limits-test.sh"
exit 1
fi
((tests_run++)) || true
# Test 2: Healthcheck Configuration
print_section "Test 2/4: Healthcheck Configuration"
if bash "$TEST_DIR/02-healthcheck-test.sh"; then
print_pass "Healthcheck tests PASSED"
((total_passed++)) || true
else
print_fail "Healthcheck tests FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/02-healthcheck-test.sh"
exit 1
fi
((tests_run++)) || true
# Test 3: Resource Enforcement
print_section "Test 3/4: Resource Enforcement Verification"
if bash "$TEST_DIR/03-enforcement-test.sh"; then
print_pass "Resource enforcement tests PASSED"
((total_passed++)) || true
else
print_fail "Resource enforcement tests FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/03-enforcement-test.sh"
exit 1
fi
((tests_run++)) || true
# Test 4: Final Verification
print_section "Test 4/4: Final Verification"
if bash "$TEST_DIR/99-final-verification.sh"; then
print_pass "Final verification PASSED"
((total_passed++)) || true
else
print_fail "Final verification FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/99-final-verification.sh"
exit 1
fi
((tests_run++)) || true
# Summary
print_header "Test Suite Summary"
echo -e "Tests run: $tests_run"
echo -e "${GREEN}Passed: $total_passed${NC}"
if [[ $total_failed -gt 0 ]]; then
echo -e "${RED}Failed: $total_failed${NC}"
fi
if [[ $total_failed -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓✓✓ ALL TESTS PASSED ✓✓✓${NC}\n"
echo -e "Your Lab 03 infrastructure is ready!"
echo -e "Proceed with the tutorials to learn more about:"
echo -e " • EC2 instance type parallels"
echo -e " • Resource limits enforcement"
echo -e " • Healthcheck implementation"
exit 0
else
echo -e "\n${RED}Some tests failed${NC}\n"
echo -e "Run individual tests for details:"
echo -e " bash tests/01-resource-limits-test.sh"
echo -e " bash tests/02-healthcheck-test.sh"
echo -e " bash tests/03-enforcement-test.sh"
echo -e " bash tests/99-final-verification.sh"
exit 1
fi