809 lines
26 KiB
Markdown
809 lines
26 KiB
Markdown
---
|
|
phase: 02-lab-01-iam-sicurezza
|
|
plan: 01
|
|
type: execute
|
|
wave: 0
|
|
depends_on: []
|
|
files_modified:
|
|
- labs/lab-01-iam/tests/test-01-user-creation.sh
|
|
- labs/lab-01-iam/tests/test-02-docker-access.sh
|
|
- labs/lab-01-iam/tests/03-non-root-test.sh
|
|
- labs/lab-01-iam/tests/99-final-verification.sh
|
|
- labs/lab-01-iam/tests/run-all-tests.sh
|
|
autonomous: true
|
|
requirements: [TEST-01, TEST-05, INF-01]
|
|
user_setup: []
|
|
must_haves:
|
|
truths:
|
|
- "Test scripts exist and can validate user creation and Docker access"
|
|
- "Test scripts verify non-root container execution (INF-01)"
|
|
- "Final verification script runs all checks for student self-validation"
|
|
- "Test harness can be executed with single command"
|
|
artifacts:
|
|
- path: "labs/lab-01-iam/tests/test-01-user-creation.sh"
|
|
provides: "User and group creation validation"
|
|
min_lines: 40
|
|
- path: "labs/lab-01-iam/tests/test-02-docker-access.sh"
|
|
provides: "Docker socket access control validation"
|
|
min_lines: 30
|
|
- path: "labs/lab-01-iam/tests/03-non-root-test.sh"
|
|
provides: "Non-root container verification (INF-01)"
|
|
min_lines: 35
|
|
- path: "labs/lab-01-iam/tests/99-final-verification.sh"
|
|
provides: "Final double-check command for students"
|
|
min_lines: 25
|
|
- path: "labs/lab-01-iam/tests/run-all-tests.sh"
|
|
provides: "Test suite orchestration"
|
|
min_lines: 15
|
|
key_links:
|
|
- from: "run-all-tests.sh"
|
|
to: "test-01-user-creation.sh, test-02-docker-access.sh, 03-non-root-test.sh, 99-final-verification.sh"
|
|
via: "Sequential execution with exit code handling"
|
|
pattern: "bash.*tests/.*\\.sh"
|
|
---
|
|
|
|
<objective>
|
|
Create test infrastructure following TDD methodology (RED phase first). Test scripts validate user creation, Docker socket access control, and non-root container execution before any implementation exists.
|
|
|
|
Purpose: Enable Test-Driven Infrastructure (TDI) by writing failing tests first, then implementing infrastructure to make them pass.
|
|
Output: Four test scripts (user creation, Docker access, non-root verification, final check) plus test orchestration script.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/home/luca/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/02-lab-01-iam-sicurezza/02-RESEARCH.md
|
|
@.planning/phases/02-lab-01-iam-sicurezza/02-VALIDATION.md
|
|
@CLAUDE.md
|
|
|
|
# Key patterns from RESEARCH.md
|
|
|
|
## TDD Methodology for Infrastructure
|
|
```bash
|
|
# RED Phase: Test should fail initially (no infrastructure exists)
|
|
# GREEN Phase: Implement minimum infrastructure to pass tests
|
|
# REFACTOR Phase: Optimize without breaking tests
|
|
|
|
# Example test structure from RESEARCH.md:
|
|
test_unauthorized_access() {
|
|
sudo useradd -m -s /bin/bash test_user 2>/dev/null || true
|
|
if sudo -u test_user docker ps &>/dev/null; then
|
|
echo "FAIL: test_user can access docker without being in docker group"
|
|
return 1
|
|
else
|
|
echo "PASS: test_user correctly denied access"
|
|
return 0
|
|
fi
|
|
}
|
|
```
|
|
|
|
## INF-01 Verification Pattern
|
|
```bash
|
|
# From RESEARCH.md - Non-root container verification
|
|
for service in $(docker-compose ps --services); do
|
|
container_name=$(docker-compose ps -q $service)
|
|
actual_user=$(docker exec $container_name whoami 2>/dev/null)
|
|
if [ "$actual_user" = "root" ]; then
|
|
echo "FAIL: $service running as root"
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "PASS: All containers running as non-root"
|
|
```
|
|
|
|
## Common Pitfalls to Handle
|
|
- Group membership requires re-login (use `groups` command for testing)
|
|
- Test as non-privileged user (root bypasses Docker socket permissions)
|
|
- Verify with multiple methods: `docker exec whoami`, `docker inspect`, `docker top`
|
|
|
|
## Test Framework from RESEARCH.md
|
|
- Framework: BASH (Bourne Again Shell) >= 4.0
|
|
- No config file needed - inline test functions
|
|
- Quick run: `bash labs/lab-01-iam/tests/quick-test.sh`
|
|
- Full suite: `bash labs/lab-01-iam/tests/run-all-tests.sh`
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Create user creation test script</name>
|
|
<files>labs/lab-01-iam/tests/test-01-user-creation.sh</files>
|
|
<behavior>
|
|
- Test 1: Non-existent user returns appropriate failure
|
|
- Test 2: User not in docker group cannot access Docker socket
|
|
- Test 3: User can be added to docker group
|
|
- Test 4: Group membership verified with `groups` command
|
|
</behavior>
|
|
<action>
|
|
Create test script for Linux user and group management:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Test: Linux user creation and Docker group membership
|
|
# Phase: RED - This test will fail initially (no users configured)
|
|
|
|
set -euo pipefail
|
|
|
|
# Color output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
pass_count=0
|
|
fail_count=0
|
|
|
|
test_user_not_exists() {
|
|
local user="lab01_student"
|
|
if id "$user" &>/dev/null; then
|
|
echo -e "${YELLOW}SKIP${NC}: User $user already exists"
|
|
return 0
|
|
fi
|
|
echo -e "${GREEN}PASS${NC}: User $user does not exist (test environment clean)"
|
|
((pass_count++))
|
|
return 0
|
|
}
|
|
|
|
test_user_without_docker_group() {
|
|
local user="lab01_student"
|
|
# Create test user if doesn't exist
|
|
if ! id "$user" &>/dev/null; then
|
|
sudo useradd -m -s /bin/bash "$user" 2>/dev/null || true
|
|
fi
|
|
|
|
# Check if user is in docker group
|
|
if groups "$user" 2>/dev/null | grep -q docker; then
|
|
echo -e "${RED}FAIL${NC}: User $user is in docker group (should not be yet)"
|
|
((fail_count++))
|
|
return 1
|
|
fi
|
|
|
|
echo -e "${GREEN}PASS${NC}: User $user is not in docker group"
|
|
((pass_count++))
|
|
return 0
|
|
}
|
|
|
|
test_docker_access_denied() {
|
|
local user="lab01_student"
|
|
|
|
# Test that user cannot access docker socket
|
|
if sudo -u "$user" docker ps &>/dev/null; then
|
|
echo -e "${RED}FAIL${NC}: User $user can access docker without docker group membership"
|
|
((fail_count++))
|
|
return 1
|
|
fi
|
|
|
|
echo -e "${GREEN}PASS${NC}: Docker access correctly denied for $user"
|
|
((pass_count++))
|
|
return 0
|
|
}
|
|
|
|
# Run all tests
|
|
echo "Running user creation tests..."
|
|
echo "================================"
|
|
test_user_not_exists
|
|
test_user_without_docker_group
|
|
test_docker_access_denied
|
|
echo "================================"
|
|
echo "Tests passed: $pass_count"
|
|
echo "Tests failed: $fail_count"
|
|
|
|
if [ $fail_count -gt 0 ]; then
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
```
|
|
|
|
Key implementation points:
|
|
- Use `groups` command to verify group membership (handles re-login issue)
|
|
- Run Docker commands as test user with `sudo -u`
|
|
- Test the negative case first (user without access)
|
|
- Return proper exit codes (0=pass, 1=fail)
|
|
</action>
|
|
<verify>
|
|
<automated>chmod +x labs/lab-01-iam/tests/test-01-user-creation.sh && bash labs/lab-01-iam/tests/test-01-user-creation.sh</automated>
|
|
</verify>
|
|
<done>Script exists, is executable, and tests user/group creation behavior</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Create Docker access control test script</name>
|
|
<files>labs/lab-01-iam/tests/test-02-docker-access.sh</files>
|
|
<behavior>
|
|
- Test 1: User in docker group can execute docker ps
|
|
- Test 2: User in docker group can run basic containers
|
|
- Test 3: Socket permissions are correctly set (660 or stricter)
|
|
- Test 4: Group membership propagation is verified
|
|
</behavior>
|
|
<action>
|
|
Create test script for Docker socket access control:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Test: Docker socket access control via group membership
|
|
# Phase: RED - This test will fail initially (no users configured)
|
|
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
pass_count=0
|
|
fail_count=0
|
|
|
|
test_socket_permissions() {
|
|
local socket="/var/run/docker.sock"
|
|
local perms=$(stat -c "%a" "$socket" 2>/dev/null || echo "000")
|
|
|
|
# Socket should be 660 or stricter (no world-readable/writable)
|
|
if [ "$perms" = "660" ] || [ "$perms" = "600" ]; then
|
|
echo -e "${GREEN}PASS${NC}: Docker socket permissions are $perms"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${YELLOW}WARN${NC}: Docker socket permissions are $perms (expected 660)"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
test_docker_group_exists() {
|
|
if getent group docker >/dev/null 2>&1; then
|
|
echo -e "${GREEN}PASS${NC}: Docker group exists"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${RED}FAIL${NC}: Docker group does not exist"
|
|
((fail_count++))
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_user_can_add_to_docker_group() {
|
|
local user="lab01_student"
|
|
|
|
# This test verifies the MECHANISM, not that it's done yet
|
|
if command -v usermod >/dev/null 2>&1; then
|
|
echo -e "${GREEN}PASS${NC}: usermod command available for group management"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${RED}FAIL${NC}: usermod command not available"
|
|
((fail_count++))
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_docker_accessible_by_group() {
|
|
# Check that docker group members can access the socket
|
|
local socket_group=$(stat -c "%G" /var/run/docker.sock 2>/dev/null || echo "unknown")
|
|
|
|
if [ "$socket_group" = "docker" ]; then
|
|
echo -e "${GREEN}PASS${NC}: Docker socket owned by docker group"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${YELLOW}WARN${NC}: Docker socket owned by $socket_group (expected docker)"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Run all tests
|
|
echo "Running Docker access control tests..."
|
|
echo "======================================"
|
|
test_socket_permissions
|
|
test_docker_group_exists
|
|
test_user_can_add_to_docker_group
|
|
test_docker_accessible_by_group
|
|
echo "======================================"
|
|
echo "Tests passed: $pass_count"
|
|
echo "Tests failed: $fail_count"
|
|
|
|
if [ $fail_count -gt 0 ]; then
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
```
|
|
|
|
Key implementation points:
|
|
- Verify socket ownership and permissions
|
|
- Check docker group exists
|
|
- Validate group management commands available
|
|
- Test mechanism for adding users to docker group
|
|
</action>
|
|
<verify>
|
|
<automated>chmod +x labs/lab-01-iam/tests/test-02-docker-access.sh && bash labs/lab-01-iam/tests/test-02-docker-access.sh</automated>
|
|
</verify>
|
|
<done>Script validates Docker socket access control mechanisms</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 3: Create non-root container verification script (INF-01)</name>
|
|
<files>labs/lab-01-iam/tests/03-non-root-test.sh</files>
|
|
<behavior>
|
|
- Test 1: Container configured with USER directive runs as non-root
|
|
- Test 2: docker exec whoami returns non-root user
|
|
- Test 3: docker inspect shows User field set
|
|
- Test 4: docker top shows non-root UID (not 0)
|
|
</behavior>
|
|
<action>
|
|
Create test script for non-root container verification (INF-01 requirement):
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Test: Non-root container execution (INF-01 requirement)
|
|
# Phase: RED - This test will fail initially (no containers exist)
|
|
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
pass_count=0
|
|
fail_count=0
|
|
|
|
# Container name to test
|
|
CONTAINER_NAME="lab01-test-container"
|
|
|
|
test_non_root_dockerfile_exists() {
|
|
local dockerfile="labs/lab-01-iam/Dockerfile.test"
|
|
|
|
if [ -f "$dockerfile" ]; then
|
|
echo -e "${GREEN}PASS${NC}: Test Dockerfile exists"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${YELLOW}SKIP${NC}: Test Dockerfile not created yet (expected in RED phase)"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
test_container_runs_as_non_root() {
|
|
# Check if container exists
|
|
if ! docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
echo -e "${YELLOW}SKIP${NC}: Container ${CONTAINER_NAME} does not exist yet (expected in RED phase)"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
|
|
# Check if container is running
|
|
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
echo -e "${YELLOW}SKIP${NC}: Container ${CONTAINER_NAME} not running"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
|
|
# Method 1: docker exec whoami
|
|
local actual_user=$(docker exec "${CONTAINER_NAME}" whoami 2>/dev/null || echo "unknown")
|
|
|
|
if [ "$actual_user" = "root" ]; then
|
|
echo -e "${RED}FAIL${NC}: Container running as root (whoami)"
|
|
((fail_count++))
|
|
return 1
|
|
elif [ "$actual_user" = "unknown" ]; then
|
|
echo -e "${YELLOW}SKIP${NC}: Cannot determine user (container not running or no exec)"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${GREEN}PASS${NC}: Container running as non-root user: $actual_user"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
test_container_user_configured() {
|
|
if ! docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
echo -e "${YELLOW}SKIP${NC}: Container not created yet"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
|
|
# Method 2: docker inspect for User field
|
|
local user_config=$(docker inspect --format='{{.Config.User}}' "${CONTAINER_NAME}" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$user_config" ]; then
|
|
# Check compose file for user directive (may override Dockerfile)
|
|
if [ -f "labs/lab-01-iam/docker-compose.yml" ]; then
|
|
if grep -q "user:" labs/lab-01-iam/docker-compose.yml; then
|
|
echo -e "${GREEN}PASS${NC}: User directive found in docker-compose.yml"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
echo -e "${YELLOW}WARN${NC}: No User directive found in Dockerfile or compose"
|
|
((pass_count++))
|
|
return 0
|
|
else
|
|
echo -e "${GREEN}PASS${NC}: User configured in container: $user_config"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
test_no_container_runs_as_root() {
|
|
# INF-01 requirement: NO container should run as root
|
|
local compose_file="labs/lab-01-iam/docker-compose.yml"
|
|
|
|
if [ ! -f "$compose_file" ]; then
|
|
echo -e "${YELLOW}SKIP${NC}: docker-compose.yml not created yet"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
|
|
# Get all services from compose file
|
|
local services=$(docker-compose -f "$compose_file" ps --services 2>/dev/null || echo "")
|
|
|
|
if [ -z "$services" ]; then
|
|
echo -e "${YELLOW}SKIP${NC}: No services defined yet"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
|
|
local root_containers=0
|
|
while IFS= read -r service; do
|
|
if [ -n "$service" ]; then
|
|
local container_name=$(docker-compose -f "$compose_file" ps -q "$service" 2>/dev/null || echo "")
|
|
if [ -n "$container_name" ]; then
|
|
local user=$(docker exec "$container_name" whoami 2>/dev/null || echo "unknown")
|
|
if [ "$user" = "root" ]; then
|
|
echo -e "${RED}FAIL${NC}: Service $service running as root"
|
|
((root_containers++))
|
|
fi
|
|
fi
|
|
fi
|
|
done <<< "$services"
|
|
|
|
if [ $root_containers -gt 0 ]; then
|
|
echo -e "${RED}FAIL${NC}: $root_containers container(s) running as root (INF-01 violation)"
|
|
((fail_count++))
|
|
return 1
|
|
else
|
|
echo -e "${GREEN}PASS${NC}: No containers running as root (INF-01 satisfied)"
|
|
((pass_count++))
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Run all tests
|
|
echo "Running non-root container tests (INF-01)..."
|
|
echo "============================================"
|
|
test_non_root_dockerfile_exists
|
|
test_container_runs_as_non_root
|
|
test_container_user_configured
|
|
test_no_container_runs_as_root
|
|
echo "============================================"
|
|
echo "Tests passed: $pass_count"
|
|
echo "Tests failed: $fail_count"
|
|
|
|
if [ $fail_count -gt 0 ]; then
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
```
|
|
|
|
Key implementation points:
|
|
- INF-01 requirement: NO container runs as root
|
|
- Multiple verification methods (whoami, inspect, compose check)
|
|
- Handle not-yet-created infrastructure gracefully (skip with warning)
|
|
- Check all services in docker-compose.yml for compliance
|
|
</action>
|
|
<verify>
|
|
<automated>chmod +x labs/lab-01-iam/tests/03-non-root-test.sh && bash labs/lab-01-iam/tests/03-non-root-test.sh</automated>
|
|
</verify>
|
|
<done>Script verifies INF-01: no container runs as root</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 4: Create final verification script (double-check for students)</name>
|
|
<files>labs/lab-01-iam/tests/99-final-verification.sh</files>
|
|
<behavior>
|
|
- Test 1: All previous tests can be run
|
|
- Test 2: Student can verify their work end-to-end
|
|
- Test 3: Clear PASS/FAIL report for all requirements
|
|
- Test 4: Exit code indicates overall success/failure
|
|
</behavior>
|
|
<action>
|
|
Create final verification script for student self-check:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Final Verification: Lab 01 - IAM & Sicurezza
|
|
# This is the "double check" command students run to verify their work
|
|
# Usage: bash tests/99-final-verification.sh
|
|
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}Lab 01 - IAM & Sicurezza${NC}"
|
|
echo -e "${BLUE}Final Verification (Double Check)${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo ""
|
|
|
|
# Track overall results
|
|
all_passed=true
|
|
|
|
# Test 1: User and group configuration
|
|
echo -e "${BLUE}[1/5] Checking user and group configuration...${NC}"
|
|
if id lab01_student &>/dev/null; then
|
|
echo -e " ${GREEN}✓${NC} User lab01_student exists"
|
|
if groups lab01_student 2>/dev/null | grep -q docker; then
|
|
echo -e " ${GREEN}✓${NC} User lab01_student is in docker group"
|
|
else
|
|
echo -e " ${RED}✗${NC} User lab01_student is NOT in docker group"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
echo -e " ${YELLOW}○${NC} User lab01_student does not exist (not created yet)"
|
|
all_passed=false
|
|
fi
|
|
echo ""
|
|
|
|
# Test 2: Docker access control
|
|
echo -e "${BLUE}[2/5] Checking Docker access control...${NC}"
|
|
if sudo -u lab01_student docker ps &>/dev/null; then
|
|
echo -e " ${GREEN}✓${NC} lab01_student can access Docker socket"
|
|
else
|
|
echo -e " ${RED}✗${NC} lab01_student cannot access Docker socket"
|
|
echo -e " ${YELLOW} Hint: User may need to re-login for group membership to take effect${NC}"
|
|
all_passed=false
|
|
fi
|
|
echo ""
|
|
|
|
# Test 3: Non-root container execution (INF-01)
|
|
echo -e "${BLUE}[3/5] Checking non-root container execution (INF-01)...${NC}"
|
|
compose_file="labs/lab-01-iam/docker-compose.yml"
|
|
if [ ! -f "$compose_file" ]; then
|
|
echo -e " ${YELLOW}○${NC} docker-compose.yml not found"
|
|
all_passed=false
|
|
else
|
|
echo -e " ${GREEN}✓${NC} docker-compose.yml exists"
|
|
|
|
# Check for user directive in services
|
|
if grep -A 10 "services:" "$compose_file" | grep -q "user:"; then
|
|
echo -e " ${GREEN}✓${NC} Services configured with non-root user directive"
|
|
else
|
|
echo -e " ${RED}✗${NC} No user directive found in docker-compose.yml"
|
|
all_passed=false
|
|
fi
|
|
|
|
# If containers are running, verify they're not root
|
|
if docker-compose -f "$compose_file" ps --services 2>/dev/null | grep -q .; then
|
|
local root_count=0
|
|
while IFS= read -r service; do
|
|
[ -z "$service" ] && continue
|
|
local container=$(docker-compose -f "$compose_file" ps -q "$service" 2>/dev/null || echo "")
|
|
if [ -n "$container" ]; then
|
|
local user=$(docker exec "$container" whoami 2>/dev/null || echo "unknown")
|
|
if [ "$user" = "root" ]; then
|
|
echo -e " ${RED}✗${NC} Service $service running as ROOT (INF-01 violation)"
|
|
((root_count++))
|
|
fi
|
|
fi
|
|
done <<< "$(docker-compose -f "$compose_file" ps --services 2>/dev/null)"
|
|
|
|
if [ $root_count -eq 0 ]; then
|
|
echo -e " ${GREEN}✓${NC} All running containers are non-root"
|
|
else
|
|
all_passed=false
|
|
fi
|
|
else
|
|
echo -e " ${YELLOW}○${NC} No containers running (start with docker-compose up)"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# Test 4: Documentation completeness (Diátaxis)
|
|
echo -e "${BLUE}[4/5] Checking documentation (Diátaxis framework)...${NC}"
|
|
doc_count=0
|
|
for doc_type in "tutorial" "how-to-guides" "reference" "explanation"; do
|
|
if [ -d "labs/lab-01-iam/$doc_type" ]; then
|
|
local md_files=$(find "labs/lab-01-iam/$doc_type" -name "*.md" 2>/dev/null | wc -l)
|
|
if [ "$md_files" -gt 0 ]; then
|
|
echo -e " ${GREEN}✓${NC} $doc_type: $md_files document(s)"
|
|
((doc_count++))
|
|
else
|
|
echo -e " ${YELLOW}○${NC} $doc_type: directory exists but empty"
|
|
fi
|
|
else
|
|
echo -e " ${RED}✗${NC} $doc_type: missing"
|
|
fi
|
|
done
|
|
|
|
if [ $doc_count -eq 4 ]; then
|
|
echo -e " ${GREEN}✓${NC} All 4 Diátaxis document types present"
|
|
else
|
|
echo -e " ${YELLOW}○${NC} $doc_count/4 Diátaxis document types present"
|
|
all_passed=false
|
|
fi
|
|
echo ""
|
|
|
|
# Test 5: IAM parallels documentation
|
|
echo -e "${BLUE}[5/5] Checking IAM parallels explanation...${NC}"
|
|
explanation_file="labs/lab-01-iam/explanation/docker-iam-parallels.md"
|
|
if [ -f "$explanation_file" ]; then
|
|
if grep -qi "IAM.*Linux\|Linux.*IAM" "$explanation_file"; then
|
|
echo -e " ${GREEN}✓${NC} IAM parallels documented"
|
|
else
|
|
echo -e " ${YELLOW}○${NC} Explanation exists but IAM parallels unclear"
|
|
fi
|
|
|
|
if grep -qi "differenza\|difference" "$explanation_file"; then
|
|
echo -e " ${GREEN}✓${NC} Local vs cloud differences documented"
|
|
else
|
|
echo -e " ${YELLOW}○${NC} Local vs cloud differences not clearly documented"
|
|
fi
|
|
else
|
|
echo -e " ${RED}✗${NC} IAM parallels explanation not found"
|
|
all_passed=false
|
|
fi
|
|
echo ""
|
|
|
|
# Final summary
|
|
echo -e "${BLUE}========================================${NC}"
|
|
if [ "$all_passed" = true ]; then
|
|
echo -e "${GREEN}ALL CHECKS PASSED${NC}"
|
|
echo -e "${GREEN}Lab 01 is complete!${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}SOME CHECKS FAILED${NC}"
|
|
echo -e "${YELLOW}Review the output above and complete the missing items${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
Key implementation points:
|
|
- Student-friendly double-check command
|
|
- Clear visual indicators (✓ pass, ✗ fail, ○ skip)
|
|
- Tests all phase requirements: LAB-01, INF-01, DOCT-01/02/03/04, PARA-01/03
|
|
- Exit code 0 for all-pass, 1 for any failure
|
|
- Helpful hints for common issues (re-login for group membership)
|
|
</action>
|
|
<verify>
|
|
<automated>chmod +x labs/lab-01-iam/tests/99-final-verification.sh && bash labs/lab-01-iam/tests/99-final-verification.sh</automated>
|
|
</verify>
|
|
<done>Student can run single command to verify all lab requirements are met</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 5: Create test orchestration script</name>
|
|
<files>labs/lab-01-iam/tests/run-all-tests.sh</files>
|
|
<behavior>
|
|
- Test 1: Script executes all test files in sequence
|
|
- Test 2: Script stops on first failure (fail-fast)
|
|
- Test 3: Script aggregates results and provides summary
|
|
- Test 4: Script can be run from project root
|
|
</behavior>
|
|
<action>
|
|
Create test orchestration script:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Test Suite Runner: Lab 01 - IAM & Sicurezza
|
|
# Runs all tests in sequence and provides summary
|
|
# Usage: bash labs/lab-01-iam/tests/run-all-tests.sh
|
|
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}Lab 01 Test Suite${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo ""
|
|
|
|
# Array of test files in order
|
|
declare -a tests=(
|
|
"$TEST_DIR/test-01-user-creation.sh"
|
|
"$TEST_DIR/test-02-docker-access.sh"
|
|
"$TEST_DIR/03-non-root-test.sh"
|
|
)
|
|
|
|
total_tests=${#tests[@]}
|
|
passed_tests=0
|
|
failed_tests=0
|
|
|
|
for i in "${!tests[@]}"; do
|
|
test_num=$((i + 1))
|
|
test_file="${tests[$i]}"
|
|
test_name=$(basename "$test_file")
|
|
|
|
echo -e "${BLUE}[$test_num/$total_tests] Running $test_name...${NC}"
|
|
|
|
if bash "$test_file"; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
echo ""
|
|
((passed_tests++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo ""
|
|
((failed_tests++))
|
|
# Fail-fast: stop on first failure
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Summary
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}Test Suite Summary${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo "Passed: $passed_tests/$total_tests"
|
|
echo "Failed: $failed_tests/$total_tests"
|
|
echo ""
|
|
|
|
if [ $failed_tests -eq 0 ]; then
|
|
echo -e "${GREEN}All tests passed!${NC}"
|
|
echo ""
|
|
echo "Run final verification:"
|
|
echo " bash labs/lab-01-iam/tests/99-final-verification.sh"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}Some tests failed${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
Key implementation points:
|
|
- Fail-fast approach (stops on first failure for TDD RED phase)
|
|
- Executes tests in dependency order
|
|
- Provides summary and next steps
|
|
- Can be run from any directory (uses absolute paths)
|
|
</action>
|
|
<verify>
|
|
<automated>chmod +x labs/lab-01-iam/tests/run-all-tests.sh && bash labs/lab-01-iam/tests/run-all-tests.sh</automated>
|
|
</verify>
|
|
<done>Orchestration script runs all tests and provides summary</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
1. All test scripts are executable (chmod +x)
|
|
2. Test scripts can run individually and return proper exit codes
|
|
3. Test orchestration script executes all tests in sequence
|
|
4. Test scripts follow TDD RED phase (will fail before implementation exists)
|
|
5. All tests handle missing infrastructure gracefully (skip with warning)
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
1. Test infrastructure is in place before any implementation (Wave 0 complete)
|
|
2. All requirement IDs (TEST-01, TEST-05, INF-01) have test coverage
|
|
3. Tests follow bash scripting best practices (set -euo pipefail, proper exit codes)
|
|
4. Student can run individual tests or full suite
|
|
5. Final verification script provides clear pass/fail report for all lab requirements
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/02-lab-01-iam-sicurezza/02-01-SUMMARY.md`
|
|
</output>
|