docs(02): create phase 2 plans (3 plans)
This commit is contained in:
808
.planning/phases/02-lab-01-iam-sicurezza/02-01-PLAN.md
Normal file
808
.planning/phases/02-lab-01-iam-sicurezza/02-01-PLAN.md
Normal file
@@ -0,0 +1,808 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user