- Update Ansible playbooks and roles for application deployment - Add new Gitea/Traefik troubleshooting playbooks - Update Docker Compose configurations (base, local, staging, production) - Enhance EncryptedEnvLoader with improved error handling - Add deployment scripts (autossh setup, migration, secret testing) - Update CI/CD workflows and documentation - Add Semaphore stack configuration
291 lines
16 KiB
Bash
Executable File
291 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
|
set -uo pipefail
|
|
|
|
# Test script for deployment with new docker-compose files and secret management
|
|
# This script validates the deployment configuration and tests secret loading
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
SECRETS_DIR="$PROJECT_ROOT/secrets"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Test results
|
|
TESTS_PASSED=0
|
|
TESTS_FAILED=0
|
|
TESTS_TOTAL=0
|
|
|
|
print_header() {
|
|
echo ""
|
|
echo -e "${BLUE}????????????????????????????????????????????????????????????????????${NC}"
|
|
echo -e "${BLUE}$1${NC}"
|
|
echo -e "${BLUE}????????????????????????????????????????????????????????????????????${NC}"
|
|
}
|
|
|
|
print_test() {
|
|
echo -e "${YELLOW}[TEST]${NC} $1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[?]${NC} $1"
|
|
((TESTS_PASSED++))
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[?]${NC} $1"
|
|
((TESTS_FAILED++))
|
|
}
|
|
|
|
run_test() {
|
|
((TESTS_TOTAL++))
|
|
local test_name="$1"
|
|
shift
|
|
print_test "$test_name"
|
|
|
|
if "$@" 2>/dev/null; then
|
|
print_success "$test_name"
|
|
return 0
|
|
else
|
|
print_error "$test_name"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Cleanup function
|
|
cleanup() {
|
|
echo ""
|
|
echo -e "${YELLOW}Cleaning up test artifacts...${NC}"
|
|
|
|
# Remove test secrets directory
|
|
if [ -d "$SECRETS_DIR" ] && [ -f "$SECRETS_DIR/.test-marker" ]; then
|
|
rm -rf "$SECRETS_DIR"
|
|
print_success "Test secrets directory removed"
|
|
fi
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
print_header "?? Testing Deployment with New Docker Compose Files & Secret Management"
|
|
|
|
# ============================================================================
|
|
# Phase 1: Validate Docker Compose Files
|
|
# ============================================================================
|
|
print_header "Phase 1: Validating Docker Compose Files"
|
|
|
|
run_test "docker-compose.base.yml exists" test -f "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "docker-compose.local.yml exists" test -f "$PROJECT_ROOT/docker-compose.local.yml"
|
|
run_test "docker-compose.staging.yml exists" test -f "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "docker-compose.production.yml exists" test -f "$PROJECT_ROOT/docker-compose.production.yml"
|
|
|
|
# Validate docker-compose syntax
|
|
if command -v docker-compose &> /dev/null || command -v docker &> /dev/null; then
|
|
run_test "docker-compose.base.yml syntax valid" bash -c "cd '$PROJECT_ROOT' && docker-compose -f docker-compose.base.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml config > /dev/null 2>&1"
|
|
run_test "docker-compose.local.yml syntax valid" bash -c "cd '$PROJECT_ROOT' && docker-compose -f docker-compose.base.yml -f docker-compose.local.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml -f docker-compose.local.yml config > /dev/null 2>&1"
|
|
run_test "docker-compose.staging.yml syntax valid" bash -c "cd '$PROJECT_ROOT' && docker-compose -f docker-compose.base.yml -f docker-compose.staging.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml -f docker-compose.staging.yml config > /dev/null 2>&1"
|
|
run_test "docker-compose.production.yml syntax valid" bash -c "cd '$PROJECT_ROOT' && docker-compose -f docker-compose.base.yml -f docker-compose.production.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml -f docker-compose.production.yml config > /dev/null 2>&1"
|
|
else
|
|
print_error "docker-compose or docker not available, skipping syntax validation"
|
|
fi
|
|
|
|
# ============================================================================
|
|
# Phase 2: Validate Secret Configuration
|
|
# ============================================================================
|
|
print_header "Phase 2: Validating Secret Configuration"
|
|
|
|
# Check that secrets are defined in docker-compose.base.yml
|
|
run_test "secrets section exists in docker-compose.base.yml" grep -q "^secrets:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "db_root_password secret defined" grep -q "db_root_password:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "db_user_password secret defined" grep -q "db_user_password:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "redis_password secret defined" grep -q "redis_password:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "app_key secret defined" grep -q "app_key:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "vault_encryption_key secret defined" grep -q "vault_encryption_key:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "git_token secret defined" grep -q "git_token:" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
|
|
# Check that production uses secrets
|
|
run_test "production uses db_user_password secret" grep -q "db_user_password" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production uses redis_password secret" grep -q "redis_password" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production uses app_key secret" grep -q "app_key" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production uses vault_encryption_key secret" grep -q "vault_encryption_key" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
|
|
# Check that staging uses secrets
|
|
run_test "staging uses db_user_password secret" grep -q "db_user_password" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "staging uses redis_password secret" grep -q "redis_password" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "staging uses app_key secret" grep -q "app_key" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "staging uses vault_encryption_key secret" grep -q "vault_encryption_key" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
|
|
# ============================================================================
|
|
# Phase 3: Create Test Secrets
|
|
# ============================================================================
|
|
print_header "Phase 3: Creating Test Secrets"
|
|
|
|
# Create test secrets directory
|
|
mkdir -p "$SECRETS_DIR"
|
|
echo "test-marker" > "$SECRETS_DIR/.test-marker"
|
|
|
|
# Create test secret files
|
|
echo "test-db-root-password-12345" > "$SECRETS_DIR/db_root_password.txt"
|
|
echo "test-db-user-password-67890" > "$SECRETS_DIR/db_user_password.txt"
|
|
echo "test-redis-password-abcde" > "$SECRETS_DIR/redis_password.txt"
|
|
echo "test-app-key-base64encoded123456789012345678901234567890" > "$SECRETS_DIR/app_key.txt"
|
|
echo "test-vault-encryption-key-32charslong12345678" > "$SECRETS_DIR/vault_encryption_key.txt"
|
|
echo "test-git-token-ghp_test12345678901234567890" > "$SECRETS_DIR/git_token.txt"
|
|
|
|
# Set secure permissions
|
|
chmod 600 "$SECRETS_DIR"/*.txt 2>/dev/null || true
|
|
|
|
run_test "Test secrets directory created" test -d "$SECRETS_DIR"
|
|
run_test "db_root_password.txt created" test -f "$SECRETS_DIR/db_root_password.txt"
|
|
run_test "db_user_password.txt created" test -f "$SECRETS_DIR/db_user_password.txt"
|
|
run_test "redis_password.txt created" test -f "$SECRETS_DIR/redis_password.txt"
|
|
run_test "app_key.txt created" test -f "$SECRETS_DIR/app_key.txt"
|
|
run_test "vault_encryption_key.txt created" test -f "$SECRETS_DIR/vault_encryption_key.txt"
|
|
run_test "git_token.txt created" test -f "$SECRETS_DIR/git_token.txt"
|
|
|
|
# ============================================================================
|
|
# Phase 4: Test Secret File References
|
|
# ============================================================================
|
|
print_header "Phase 4: Validating Secret File References"
|
|
|
|
# Check that docker-compose files reference correct secret file paths
|
|
run_test "base.yml references ./secrets/db_root_password.txt" grep -q "./secrets/db_root_password.txt" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "base.yml references ./secrets/db_user_password.txt" grep -q "./secrets/db_user_password.txt" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "base.yml references ./secrets/redis_password.txt" grep -q "./secrets/redis_password.txt" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "base.yml references ./secrets/app_key.txt" grep -q "./secrets/app_key.txt" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "base.yml references ./secrets/vault_encryption_key.txt" grep -q "./secrets/vault_encryption_key.txt" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
run_test "base.yml references ./secrets/git_token.txt" grep -q "./secrets/git_token.txt" "$PROJECT_ROOT/docker-compose.base.yml"
|
|
|
|
# ============================================================================
|
|
# Phase 5: Test *_FILE Pattern Support
|
|
# ============================================================================
|
|
print_header "Phase 5: Testing *_FILE Pattern Support"
|
|
|
|
# Check that docker-compose files use *_FILE pattern
|
|
run_test "production uses DB_PASSWORD_FILE pattern" grep -q "DB_PASSWORD_FILE" "$PROJECT_ROOT/docker-compose.production.yml" || grep -q "db_user_password" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production uses REDIS_PASSWORD_FILE pattern" grep -q "REDIS_PASSWORD_FILE" "$PROJECT_ROOT/docker-compose.production.yml" || grep -q "redis_password" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production uses APP_KEY_FILE pattern" grep -q "APP_KEY_FILE" "$PROJECT_ROOT/docker-compose.production.yml" || grep -q "app_key" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production uses VAULT_ENCRYPTION_KEY_FILE pattern" grep -q "VAULT_ENCRYPTION_KEY_FILE" "$PROJECT_ROOT/docker-compose.production.yml" || grep -q "vault_encryption_key" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
|
|
run_test "staging uses DB_PASSWORD_FILE pattern" grep -q "DB_PASSWORD_FILE" "$PROJECT_ROOT/docker-compose.staging.yml" || grep -q "db_user_password" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "staging uses APP_KEY_FILE pattern" grep -q "APP_KEY_FILE" "$PROJECT_ROOT/docker-compose.staging.yml" || grep -q "app_key" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "staging uses VAULT_ENCRYPTION_KEY_FILE pattern" grep -q "VAULT_ENCRYPTION_KEY_FILE" "$PROJECT_ROOT/docker-compose.staging.yml" || grep -q "vault_encryption_key" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
run_test "staging uses GIT_TOKEN_FILE pattern" grep -q "GIT_TOKEN_FILE" "$PROJECT_ROOT/docker-compose.staging.yml" || grep -q "git_token" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
|
|
# ============================================================================
|
|
# Phase 6: Test DockerSecretsResolver Integration
|
|
# ============================================================================
|
|
print_header "Phase 6: Testing DockerSecretsResolver Integration"
|
|
|
|
# Check that DockerSecretsResolver exists
|
|
run_test "DockerSecretsResolver.php exists" test -f "$PROJECT_ROOT/src/Framework/Config/DockerSecretsResolver.php"
|
|
|
|
# Check that Environment.php uses DockerSecretsResolver
|
|
run_test "Environment.php imports DockerSecretsResolver" grep -q "DockerSecretsResolver" "$PROJECT_ROOT/src/Framework/Config/Environment.php"
|
|
run_test "Environment.php resolves secrets via *_FILE pattern" grep -q "secretsResolver->resolve" "$PROJECT_ROOT/src/Framework/Config/Environment.php"
|
|
|
|
# Test secret resolution logic
|
|
if command -v php &> /dev/null; then
|
|
PHP_TEST_SCRIPT=$(cat <<'PHPEOF'
|
|
<?php
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
|
|
use App\Framework\Config\DockerSecretsResolver;
|
|
|
|
$resolver = new DockerSecretsResolver();
|
|
|
|
// Test 1: Resolve secret from file
|
|
$variables = [
|
|
'DB_PASSWORD_FILE' => __DIR__ . '/secrets/db_user_password.txt',
|
|
];
|
|
|
|
$result = $resolver->resolve('DB_PASSWORD', $variables);
|
|
if ($result === 'test-db-user-password-67890') {
|
|
echo "? Secret resolution works\n";
|
|
exit(0);
|
|
} else {
|
|
echo "? Secret resolution failed: got '$result'\n";
|
|
exit(1);
|
|
}
|
|
PHPEOF
|
|
)
|
|
|
|
echo "$PHP_TEST_SCRIPT" > "$PROJECT_ROOT/test_secret_resolver.php"
|
|
|
|
run_test "DockerSecretsResolver resolves secrets correctly" bash -c "cd '$PROJECT_ROOT' && php test_secret_resolver.php > /dev/null 2>&1"
|
|
|
|
# Cleanup
|
|
rm -f "$PROJECT_ROOT/test_secret_resolver.php"
|
|
else
|
|
print_error "PHP not available, skipping DockerSecretsResolver test"
|
|
fi
|
|
|
|
# ============================================================================
|
|
# Phase 7: Test EncryptedEnvLoader Integration
|
|
# ============================================================================
|
|
print_header "Phase 7: Testing EncryptedEnvLoader Integration"
|
|
|
|
run_test "EncryptedEnvLoader.php exists" test -f "$PROJECT_ROOT/src/Framework/Config/EncryptedEnvLoader.php"
|
|
run_test "EncryptedEnvLoader loads system environment" grep -q "loadSystemEnvironment" "$PROJECT_ROOT/src/Framework/Config/EncryptedEnvLoader.php"
|
|
run_test "EncryptedEnvLoader supports encryption key" grep -q "ENCRYPTION_KEY" "$PROJECT_ROOT/src/Framework/Config/EncryptedEnvLoader.php"
|
|
|
|
# ============================================================================
|
|
# Phase 8: Test Entrypoint Script
|
|
# ============================================================================
|
|
print_header "Phase 8: Testing Entrypoint Script"
|
|
|
|
run_test "entrypoint.sh exists" test -f "$PROJECT_ROOT/docker/entrypoint.sh"
|
|
run_test "entrypoint.sh loads secrets from *_FILE pattern" grep -q "_FILE" "$PROJECT_ROOT/docker/entrypoint.sh" || grep -q "DockerSecretsResolver" "$PROJECT_ROOT/docker/entrypoint.sh"
|
|
run_test "entrypoint.sh is executable" test -x "$PROJECT_ROOT/docker/entrypoint.sh" || [ -f "$PROJECT_ROOT/docker/entrypoint.sh" ]
|
|
|
|
# ============================================================================
|
|
# Phase 9: Validate Service Configuration
|
|
# ============================================================================
|
|
print_header "Phase 9: Validating Service Configuration"
|
|
|
|
# Check that services reference secrets correctly
|
|
run_test "production php service uses secrets" grep -A 5 "php:" "$PROJECT_ROOT/docker-compose.production.yml" | grep -q "secrets:" || grep -q "APP_KEY_FILE" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "production queue-worker uses secrets" grep -A 10 "queue-worker:" "$PROJECT_ROOT/docker-compose.production.yml" | grep -q "secrets:" || grep -q "DB_PASSWORD_FILE" "$PROJECT_ROOT/docker-compose.production.yml"
|
|
run_test "staging-app uses secrets" grep -A 10 "staging-app:" "$PROJECT_ROOT/docker-compose.staging.yml" | grep -q "secrets:" || grep -q "DB_PASSWORD_FILE" "$PROJECT_ROOT/docker-compose.staging.yml"
|
|
|
|
# ============================================================================
|
|
# Phase 10: Test Docker Compose Override Chain
|
|
# ============================================================================
|
|
print_header "Phase 10: Testing Docker Compose Override Chain"
|
|
|
|
# Test that override chain works correctly
|
|
if command -v docker-compose &> /dev/null || command -v docker &> /dev/null; then
|
|
run_test "local override combines with base" bash -c "cd '$PROJECT_ROOT' && (docker-compose -f docker-compose.base.yml -f docker-compose.local.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml -f docker-compose.local.yml config > /dev/null 2>&1)"
|
|
run_test "staging override combines with base" bash -c "cd '$PROJECT_ROOT' && (docker-compose -f docker-compose.base.yml -f docker-compose.staging.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml -f docker-compose.staging.yml config > /dev/null 2>&1)"
|
|
run_test "production override combines with base" bash -c "cd '$PROJECT_ROOT' && (docker-compose -f docker-compose.base.yml -f docker-compose.production.yml config > /dev/null 2>&1 || docker compose -f docker-compose.base.yml -f docker-compose.production.yml config > /dev/null 2>&1)"
|
|
else
|
|
print_error "docker-compose not available, skipping override chain test"
|
|
fi
|
|
|
|
# ============================================================================
|
|
# Summary
|
|
# ============================================================================
|
|
print_header "Test Summary"
|
|
|
|
echo ""
|
|
echo -e "Total Tests: ${TESTS_TOTAL}"
|
|
echo -e "${GREEN}Passed: ${TESTS_PASSED}${NC}"
|
|
echo -e "${RED}Failed: ${TESTS_FAILED}${NC}"
|
|
echo ""
|
|
|
|
if [ $TESTS_FAILED -eq 0 ]; then
|
|
echo -e "${GREEN}? All tests passed!${NC}"
|
|
echo ""
|
|
echo -e "${BLUE}Next steps:${NC}"
|
|
echo " 1. Deploy secrets to your server using Ansible Vault"
|
|
echo " 2. Run: docker-compose -f docker-compose.base.yml -f docker-compose.production.yml up -d"
|
|
echo " 3. Verify secrets are loaded correctly in containers"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}? Some tests failed. Please review the errors above.${NC}"
|
|
exit 1
|
|
fi
|