einige Ergänzungen
This commit is contained in:
Executable
+505
@@ -0,0 +1,505 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Enhanced Monitoring Setup Test Script
|
||||
# =============================================================================
|
||||
# This script provides comprehensive testing of the monitoring setup including
|
||||
# Prometheus, Grafana, and Alertmanager with improved error handling, retry
|
||||
# logic, cleanup options, and configuration validation.
|
||||
# =============================================================================
|
||||
|
||||
# Load common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=../utils/common.sh
|
||||
source "$SCRIPT_DIR/../utils/common.sh" || {
|
||||
echo "Error: Could not load common utilities from $SCRIPT_DIR/../utils/common.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Configuration
|
||||
# =============================================================================
|
||||
|
||||
readonly COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||
readonly MONITORING_SERVICES=("prometheus" "grafana" "alertmanager")
|
||||
readonly STARTUP_TIMEOUT=120
|
||||
readonly HEALTH_CHECK_TIMEOUT=30
|
||||
readonly RETRY_COUNT=3
|
||||
readonly RETRY_DELAY=10
|
||||
|
||||
# Service endpoints
|
||||
readonly PROMETHEUS_URL="http://localhost:9090"
|
||||
readonly GRAFANA_URL="http://localhost:3000"
|
||||
readonly ALERTMANAGER_URL="http://localhost:9093"
|
||||
|
||||
# Configuration files
|
||||
readonly CONFIG_FILES=(
|
||||
"config/monitoring/prometheus.yml"
|
||||
"config/monitoring/grafana/provisioning/dashboards/dashboard.yml"
|
||||
"config/monitoring/grafana/provisioning/datasources/prometheus.yml"
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# Cleanup Function
|
||||
# =============================================================================
|
||||
|
||||
cleanup() {
|
||||
if [[ "${CLEANUP_SERVICES:-true}" == "true" ]]; then
|
||||
log_info "Cleaning up monitoring services..."
|
||||
|
||||
# Stop monitoring services
|
||||
if docker-compose -f "$COMPOSE_FILE" ps | grep -q "prometheus\|grafana\|alertmanager"; then
|
||||
log_info "Stopping monitoring services..."
|
||||
docker-compose -f "$COMPOSE_FILE" stop "${MONITORING_SERVICES[@]}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Remove containers if requested
|
||||
if [[ "${REMOVE_CONTAINERS:-false}" == "true" ]]; then
|
||||
log_info "Removing monitoring containers..."
|
||||
docker-compose -f "$COMPOSE_FILE" rm -f "${MONITORING_SERVICES[@]}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
log_info "Cleanup completed"
|
||||
else
|
||||
log_info "Cleanup skipped (CLEANUP_SERVICES=false)"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Configuration Validation Functions
|
||||
# =============================================================================
|
||||
|
||||
validate_configuration() {
|
||||
log_section "Configuration Validation"
|
||||
|
||||
# Check docker-compose file
|
||||
check_file "$COMPOSE_FILE" "Docker Compose file" || return 1
|
||||
|
||||
# Validate docker-compose syntax
|
||||
log_info "Validating docker-compose syntax..."
|
||||
if docker-compose -f "$COMPOSE_FILE" config >/dev/null 2>&1; then
|
||||
print_status "OK" "Docker Compose file syntax is valid"
|
||||
else
|
||||
print_status "ERROR" "Docker Compose file has syntax errors"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check required services are defined
|
||||
for service in "${MONITORING_SERVICES[@]}"; do
|
||||
if docker-compose -f "$COMPOSE_FILE" config | grep -q "^ ${service}:"; then
|
||||
print_status "OK" "Service '$service' is defined in docker-compose"
|
||||
else
|
||||
print_status "ERROR" "Service '$service' is not defined in docker-compose"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check configuration files
|
||||
for config_file in "${CONFIG_FILES[@]}"; do
|
||||
if [[ -f "$config_file" ]]; then
|
||||
print_status "OK" "Configuration file exists: $config_file"
|
||||
|
||||
# Validate specific configuration files
|
||||
case "$config_file" in
|
||||
*prometheus.yml)
|
||||
validate_prometheus_config "$config_file"
|
||||
;;
|
||||
*grafana*)
|
||||
validate_grafana_config "$config_file"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
print_status "WARNING" "Configuration file missing: $config_file"
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validate_prometheus_config() {
|
||||
local config_file=$1
|
||||
|
||||
# Check for required sections
|
||||
if grep -q "global:" "$config_file" && grep -q "scrape_configs:" "$config_file"; then
|
||||
print_status "OK" "Prometheus configuration has required sections"
|
||||
else
|
||||
print_status "WARNING" "Prometheus configuration may be incomplete"
|
||||
fi
|
||||
|
||||
# Check for application scrape targets
|
||||
if grep -q "meldestelle" "$config_file"; then
|
||||
print_status "OK" "Prometheus configured to scrape Meldestelle application"
|
||||
else
|
||||
print_status "WARNING" "Prometheus may not be configured to scrape application metrics"
|
||||
fi
|
||||
}
|
||||
|
||||
validate_grafana_config() {
|
||||
local config_file=$1
|
||||
|
||||
# Basic validation for Grafana config files
|
||||
if [[ "$config_file" == *"datasources"* ]]; then
|
||||
if grep -q "prometheus" "$config_file"; then
|
||||
print_status "OK" "Grafana datasource configuration includes Prometheus"
|
||||
else
|
||||
print_status "WARNING" "Grafana datasource configuration may not include Prometheus"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Service Management Functions
|
||||
# =============================================================================
|
||||
|
||||
start_monitoring_services() {
|
||||
log_section "Starting Monitoring Services"
|
||||
|
||||
# Check Docker availability
|
||||
check_docker || return 1
|
||||
check_docker_compose || return 1
|
||||
|
||||
# Start services with timeout
|
||||
log_info "Starting monitoring stack: ${MONITORING_SERVICES[*]}"
|
||||
|
||||
if run_with_timeout "$STARTUP_TIMEOUT" "Start monitoring services" \
|
||||
docker-compose -f "$COMPOSE_FILE" up -d "${MONITORING_SERVICES[@]}"; then
|
||||
print_status "OK" "Monitoring services started successfully"
|
||||
else
|
||||
print_status "ERROR" "Failed to start monitoring services"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for services to be ready
|
||||
log_info "Waiting for services to be ready..."
|
||||
sleep 15 # Initial wait for containers to initialize
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Health Check Functions
|
||||
# =============================================================================
|
||||
|
||||
check_prometheus() {
|
||||
log_section "Prometheus Health Check"
|
||||
|
||||
local health_url="${PROMETHEUS_URL}/-/healthy"
|
||||
local ready_url="${PROMETHEUS_URL}/-/ready"
|
||||
|
||||
# Check if Prometheus is healthy
|
||||
if check_http_endpoint "$health_url" "Prometheus health" "$HEALTH_CHECK_TIMEOUT" "$RETRY_COUNT"; then
|
||||
print_status "OK" "Prometheus is healthy"
|
||||
else
|
||||
print_status "ERROR" "Prometheus health check failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if Prometheus is ready
|
||||
if check_http_endpoint "$ready_url" "Prometheus readiness" "$HEALTH_CHECK_TIMEOUT" 1; then
|
||||
print_status "OK" "Prometheus is ready"
|
||||
else
|
||||
print_status "WARNING" "Prometheus readiness check failed"
|
||||
fi
|
||||
|
||||
# Check Prometheus configuration
|
||||
local config_url="${PROMETHEUS_URL}/api/v1/status/config"
|
||||
if check_http_endpoint "$config_url" "Prometheus configuration" 10 1; then
|
||||
print_status "OK" "Prometheus configuration is accessible"
|
||||
else
|
||||
print_status "WARNING" "Prometheus configuration endpoint not accessible"
|
||||
fi
|
||||
|
||||
# Check targets
|
||||
check_prometheus_targets
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_prometheus_targets() {
|
||||
log_info "Checking Prometheus targets..."
|
||||
|
||||
local targets_url="${PROMETHEUS_URL}/api/v1/targets"
|
||||
local targets_response
|
||||
|
||||
targets_response=$(curl -sf "$targets_url" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$targets_response" ]]; then
|
||||
# Check for application targets
|
||||
if echo "$targets_response" | grep -q "meldestelle"; then
|
||||
print_status "OK" "Prometheus can discover application targets"
|
||||
else
|
||||
print_status "WARNING" "No application targets found in Prometheus"
|
||||
log_info "Make sure the application is running and exposing metrics"
|
||||
fi
|
||||
|
||||
# Check for healthy targets
|
||||
local healthy_targets
|
||||
healthy_targets=$(echo "$targets_response" | grep -o '"health":"up"' | wc -l)
|
||||
if [[ "$healthy_targets" -gt 0 ]]; then
|
||||
print_status "OK" "Found $healthy_targets healthy targets"
|
||||
else
|
||||
print_status "WARNING" "No healthy targets found"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Could not retrieve Prometheus targets"
|
||||
fi
|
||||
}
|
||||
|
||||
check_grafana() {
|
||||
log_section "Grafana Health Check"
|
||||
|
||||
local health_url="${GRAFANA_URL}/api/health"
|
||||
local datasources_url="${GRAFANA_URL}/api/datasources"
|
||||
|
||||
# Check if Grafana is healthy
|
||||
if check_http_endpoint "$health_url" "Grafana health" "$HEALTH_CHECK_TIMEOUT" "$RETRY_COUNT"; then
|
||||
print_status "OK" "Grafana is healthy"
|
||||
|
||||
# Parse health response
|
||||
local health_response
|
||||
health_response=$(curl -sf "$health_url" 2>/dev/null || echo "")
|
||||
if [[ "$health_response" == *"ok"* ]]; then
|
||||
print_status "OK" "Grafana health status is OK"
|
||||
else
|
||||
print_status "WARNING" "Grafana health status unclear: $health_response"
|
||||
fi
|
||||
else
|
||||
print_status "ERROR" "Grafana health check failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check datasources (requires authentication, so this might fail)
|
||||
log_info "Checking Grafana datasources..."
|
||||
local datasources_response
|
||||
datasources_response=$(curl -sf -u "admin:admin" "$datasources_url" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$datasources_response" ]] && [[ "$datasources_response" != "Unauthorized" ]]; then
|
||||
if echo "$datasources_response" | grep -q "prometheus"; then
|
||||
print_status "OK" "Grafana has Prometheus datasource configured"
|
||||
else
|
||||
print_status "WARNING" "Prometheus datasource not found in Grafana"
|
||||
fi
|
||||
else
|
||||
print_status "INFO" "Could not check Grafana datasources (authentication required)"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_alertmanager() {
|
||||
log_section "Alertmanager Health Check"
|
||||
|
||||
local health_url="${ALERTMANAGER_URL}/-/healthy"
|
||||
local ready_url="${ALERTMANAGER_URL}/-/ready"
|
||||
local status_url="${ALERTMANAGER_URL}/api/v1/status"
|
||||
|
||||
# Check if Alertmanager is healthy
|
||||
if check_http_endpoint "$health_url" "Alertmanager health" "$HEALTH_CHECK_TIMEOUT" "$RETRY_COUNT"; then
|
||||
print_status "OK" "Alertmanager is healthy"
|
||||
else
|
||||
print_status "ERROR" "Alertmanager health check failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if Alertmanager is ready
|
||||
if check_http_endpoint "$ready_url" "Alertmanager readiness" "$HEALTH_CHECK_TIMEOUT" 1; then
|
||||
print_status "OK" "Alertmanager is ready"
|
||||
else
|
||||
print_status "WARNING" "Alertmanager readiness check failed"
|
||||
fi
|
||||
|
||||
# Check Alertmanager status
|
||||
if check_http_endpoint "$status_url" "Alertmanager status" 10 1; then
|
||||
print_status "OK" "Alertmanager status endpoint is accessible"
|
||||
else
|
||||
print_status "WARNING" "Alertmanager status endpoint not accessible"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Integration Tests
|
||||
# =============================================================================
|
||||
|
||||
test_monitoring_integration() {
|
||||
log_section "Monitoring Integration Tests"
|
||||
|
||||
# Test Prometheus-Grafana integration
|
||||
log_info "Testing Prometheus-Grafana integration..."
|
||||
|
||||
# Check if Prometheus metrics are accessible from Grafana's perspective
|
||||
local prometheus_query_url="${PROMETHEUS_URL}/api/v1/query?query=up"
|
||||
if check_http_endpoint "$prometheus_query_url" "Prometheus query API" 10 1; then
|
||||
print_status "OK" "Prometheus query API is accessible for Grafana"
|
||||
else
|
||||
print_status "WARNING" "Prometheus query API may not be accessible for Grafana"
|
||||
fi
|
||||
|
||||
# Test alerting rules
|
||||
log_info "Checking alerting rules..."
|
||||
local rules_url="${PROMETHEUS_URL}/api/v1/rules"
|
||||
local rules_response
|
||||
rules_response=$(curl -sf "$rules_url" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$rules_response" ]]; then
|
||||
if echo "$rules_response" | grep -q "meldestelle"; then
|
||||
print_status "OK" "Meldestelle alerting rules are loaded"
|
||||
else
|
||||
print_status "WARNING" "No Meldestelle-specific alerting rules found"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Could not retrieve alerting rules"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Performance and Load Tests
|
||||
# =============================================================================
|
||||
|
||||
test_monitoring_performance() {
|
||||
log_section "Monitoring Performance Tests"
|
||||
|
||||
# Test Prometheus query performance
|
||||
log_info "Testing Prometheus query performance..."
|
||||
|
||||
local start_time
|
||||
local end_time
|
||||
local duration
|
||||
|
||||
start_time=$(date +%s%N)
|
||||
curl -sf "${PROMETHEUS_URL}/api/v1/query?query=up" >/dev/null 2>&1
|
||||
local query_result=$?
|
||||
end_time=$(date +%s%N)
|
||||
|
||||
duration=$(( (end_time - start_time) / 1000000 )) # Convert to milliseconds
|
||||
|
||||
if [[ $query_result -eq 0 ]]; then
|
||||
if [[ $duration -lt 1000 ]]; then
|
||||
print_status "OK" "Prometheus query performance is good (${duration}ms)"
|
||||
else
|
||||
print_status "WARNING" "Prometheus query performance is slow (${duration}ms)"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Prometheus query performance test failed"
|
||||
fi
|
||||
|
||||
# Test Grafana response time
|
||||
log_info "Testing Grafana response time..."
|
||||
|
||||
start_time=$(date +%s%N)
|
||||
curl -sf "${GRAFANA_URL}/api/health" >/dev/null 2>&1
|
||||
local grafana_result=$?
|
||||
end_time=$(date +%s%N)
|
||||
|
||||
duration=$(( (end_time - start_time) / 1000000 ))
|
||||
|
||||
if [[ $grafana_result -eq 0 ]]; then
|
||||
if [[ $duration -lt 2000 ]]; then
|
||||
print_status "OK" "Grafana response time is good (${duration}ms)"
|
||||
else
|
||||
print_status "WARNING" "Grafana response time is slow (${duration}ms)"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Grafana response time test failed"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main Execution
|
||||
# =============================================================================
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --no-cleanup Don't stop services after testing"
|
||||
echo " --remove-containers Remove containers after testing"
|
||||
echo " --config-only Only validate configuration, don't start services"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
echo "Environment Variables:"
|
||||
echo " COMPOSE_FILE Docker compose file to use (default: docker-compose.yml)"
|
||||
echo " CLEANUP_SERVICES Whether to cleanup services (default: true)"
|
||||
echo " REMOVE_CONTAINERS Whether to remove containers (default: false)"
|
||||
}
|
||||
|
||||
main() {
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--no-cleanup)
|
||||
export CLEANUP_SERVICES=false
|
||||
shift
|
||||
;;
|
||||
--remove-containers)
|
||||
export REMOVE_CONTAINERS=true
|
||||
shift
|
||||
;;
|
||||
--config-only)
|
||||
local CONFIG_ONLY=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log_section "Enhanced Monitoring Setup Test"
|
||||
|
||||
log_info "Starting comprehensive monitoring tests..."
|
||||
log_info "Compose file: $COMPOSE_FILE"
|
||||
log_info "Test timestamp: $(date)"
|
||||
|
||||
# Always validate configuration
|
||||
validate_configuration || exit 1
|
||||
|
||||
# If config-only mode, exit after validation
|
||||
if [[ "${CONFIG_ONLY:-false}" == "true" ]]; then
|
||||
log_info "Configuration validation completed (config-only mode)"
|
||||
print_summary "Monitoring Configuration Validation"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Run all tests
|
||||
local test_results=()
|
||||
|
||||
start_monitoring_services && test_results+=("Startup: PASS") || test_results+=("Startup: FAIL")
|
||||
check_prometheus && test_results+=("Prometheus: PASS") || test_results+=("Prometheus: FAIL")
|
||||
check_grafana && test_results+=("Grafana: PASS") || test_results+=("Grafana: FAIL")
|
||||
check_alertmanager && test_results+=("Alertmanager: PASS") || test_results+=("Alertmanager: FAIL")
|
||||
test_monitoring_integration && test_results+=("Integration: PASS") || test_results+=("Integration: FAIL")
|
||||
test_monitoring_performance && test_results+=("Performance: PASS") || test_results+=("Performance: FAIL")
|
||||
|
||||
# Print test results summary
|
||||
log_section "Test Results Summary"
|
||||
for result in "${test_results[@]}"; do
|
||||
if [[ "$result" == *"PASS" ]]; then
|
||||
log_success "$result"
|
||||
else
|
||||
log_error "$result"
|
||||
fi
|
||||
done
|
||||
|
||||
# Print access information
|
||||
log_section "Monitoring Access Information"
|
||||
log_info "Prometheus: ${PROMETHEUS_URL}"
|
||||
log_info "Grafana: ${GRAFANA_URL} (default credentials: admin/admin)"
|
||||
log_info "Alertmanager: ${ALERTMANAGER_URL}"
|
||||
|
||||
# Print final summary
|
||||
print_summary "Enhanced Monitoring Test"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Executable
+650
@@ -0,0 +1,650 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Enhanced Database Initialization Test Script
|
||||
# =============================================================================
|
||||
# This script provides comprehensive testing of database initialization and
|
||||
# configuration with actual connection testing, schema validation, performance
|
||||
# testing, and cleanup capabilities.
|
||||
# =============================================================================
|
||||
|
||||
# Load common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=../utils/common.sh
|
||||
source "$SCRIPT_DIR/../utils/common.sh" || {
|
||||
echo "Error: Could not load common utilities from $SCRIPT_DIR/../utils/common.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Configuration
|
||||
# =============================================================================
|
||||
|
||||
readonly COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||
readonly DB_SERVICES=("postgres" "redis")
|
||||
readonly BUILD_TIMEOUT=300
|
||||
readonly DB_STARTUP_TIMEOUT=120
|
||||
readonly CONNECTION_TIMEOUT=30
|
||||
|
||||
# Database configuration
|
||||
readonly DB_HOST="${DB_HOST:-localhost}"
|
||||
readonly DB_PORT="${DB_PORT:-5432}"
|
||||
readonly DB_NAME="${DB_NAME:-meldestelle_test}"
|
||||
readonly DB_USER="${DB_USER:-meldestelle_user}"
|
||||
readonly DB_PASSWORD="${DB_PASSWORD:-meldestelle_password}"
|
||||
|
||||
# Redis configuration
|
||||
readonly REDIS_HOST="${REDIS_HOST:-localhost}"
|
||||
readonly REDIS_PORT="${REDIS_PORT:-6379}"
|
||||
|
||||
# Service modules
|
||||
readonly SERVICE_MODULES=(
|
||||
"infrastructure:gateway"
|
||||
"horses:horses-service"
|
||||
"events:events-service"
|
||||
"masterdata:masterdata-service"
|
||||
"members:members-service"
|
||||
)
|
||||
|
||||
# Test database name
|
||||
readonly TEST_DB_NAME="meldestelle_test_$(date +%s)"
|
||||
|
||||
# =============================================================================
|
||||
# Cleanup Function
|
||||
# =============================================================================
|
||||
|
||||
cleanup() {
|
||||
log_info "Cleaning up test environment..."
|
||||
|
||||
# Drop test database if created
|
||||
if [[ "${TEST_DB_CREATED:-false}" == "true" ]]; then
|
||||
log_info "Dropping test database: $TEST_DB_NAME"
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres \
|
||||
-c "DROP DATABASE IF EXISTS $TEST_DB_NAME;" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Stop database services if started by this script
|
||||
if [[ "${STARTED_DB_SERVICES:-false}" == "true" ]]; then
|
||||
log_info "Stopping database services..."
|
||||
docker-compose -f "$COMPOSE_FILE" stop "${DB_SERVICES[@]}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Clean up temporary files
|
||||
rm -f /tmp/db_test_*.sql /tmp/db_performance_*.log 2>/dev/null || true
|
||||
|
||||
log_info "Cleanup completed"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Database Setup Functions
|
||||
# =============================================================================
|
||||
|
||||
setup_database_services() {
|
||||
log_section "Database Services Setup"
|
||||
|
||||
# Check Docker availability
|
||||
check_docker || return 1
|
||||
check_docker_compose || return 1
|
||||
|
||||
# Start database services
|
||||
log_info "Starting database services: ${DB_SERVICES[*]}"
|
||||
|
||||
if run_with_timeout "$DB_STARTUP_TIMEOUT" "Start database services" \
|
||||
docker-compose -f "$COMPOSE_FILE" up -d "${DB_SERVICES[@]}"; then
|
||||
STARTED_DB_SERVICES=true
|
||||
print_status "OK" "Database services started successfully"
|
||||
else
|
||||
print_status "ERROR" "Failed to start database services"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
if wait_for_service "PGPASSWORD=$DB_PASSWORD pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER" \
|
||||
"PostgreSQL" "$CONNECTION_TIMEOUT" 5; then
|
||||
print_status "OK" "PostgreSQL is ready"
|
||||
else
|
||||
print_status "ERROR" "PostgreSQL failed to become ready"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for Redis to be ready
|
||||
if wait_for_service "redis-cli -h $REDIS_HOST -p $REDIS_PORT ping | grep -q PONG" \
|
||||
"Redis" "$CONNECTION_TIMEOUT" 5; then
|
||||
print_status "OK" "Redis is ready"
|
||||
else
|
||||
print_status "ERROR" "Redis failed to become ready"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Environment Validation Functions
|
||||
# =============================================================================
|
||||
|
||||
validate_environment() {
|
||||
log_section "Environment Validation"
|
||||
|
||||
# Load environment file
|
||||
load_env_file
|
||||
|
||||
# Validate required environment variables
|
||||
local required_vars=(
|
||||
"DB_HOST" "DB_PORT" "DB_NAME" "DB_USER" "DB_PASSWORD"
|
||||
"REDIS_HOST" "REDIS_PORT"
|
||||
)
|
||||
|
||||
validate_env_vars "${required_vars[@]}" || return 1
|
||||
|
||||
# Check for required tools
|
||||
local required_tools=("psql" "redis-cli")
|
||||
for tool in "${required_tools[@]}"; do
|
||||
if command_exists "$tool"; then
|
||||
print_status "OK" "$tool is available"
|
||||
else
|
||||
print_status "WARNING" "$tool is not available - some tests may be skipped"
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Build Testing Functions
|
||||
# =============================================================================
|
||||
|
||||
test_service_builds() {
|
||||
log_section "Service Build Testing"
|
||||
|
||||
local build_results=()
|
||||
|
||||
# Test each service module build
|
||||
for module in "${SERVICE_MODULES[@]}"; do
|
||||
log_info "Building module: $module"
|
||||
|
||||
if run_with_timeout "$BUILD_TIMEOUT" "Build $module" \
|
||||
./gradlew ":${module}:build" -x test; then
|
||||
print_status "OK" "$module builds successfully"
|
||||
build_results+=("$module: PASS")
|
||||
else
|
||||
print_status "ERROR" "$module build failed"
|
||||
build_results+=("$module: FAIL")
|
||||
fi
|
||||
done
|
||||
|
||||
# Summary of build results
|
||||
log_info "Build Results Summary:"
|
||||
for result in "${build_results[@]}"; do
|
||||
if [[ "$result" == *"PASS" ]]; then
|
||||
log_success "$result"
|
||||
else
|
||||
log_error "$result"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check if any builds failed
|
||||
if echo "${build_results[*]}" | grep -q "FAIL"; then
|
||||
print_status "ERROR" "One or more service builds failed"
|
||||
return 1
|
||||
else
|
||||
print_status "OK" "All service builds successful"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Database Connection Testing Functions
|
||||
# =============================================================================
|
||||
|
||||
test_database_connections() {
|
||||
log_section "Database Connection Testing"
|
||||
|
||||
# Test PostgreSQL connection
|
||||
test_postgresql_connection || return 1
|
||||
|
||||
# Test Redis connection
|
||||
test_redis_connection || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_postgresql_connection() {
|
||||
log_info "Testing PostgreSQL connection..."
|
||||
|
||||
# Basic connection test
|
||||
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres \
|
||||
-c "SELECT version();" >/dev/null 2>&1; then
|
||||
print_status "OK" "PostgreSQL connection successful"
|
||||
else
|
||||
print_status "ERROR" "PostgreSQL connection failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test database creation
|
||||
log_info "Testing database creation..."
|
||||
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres \
|
||||
-c "CREATE DATABASE $TEST_DB_NAME;" >/dev/null 2>&1; then
|
||||
TEST_DB_CREATED=true
|
||||
print_status "OK" "Test database created successfully"
|
||||
else
|
||||
print_status "ERROR" "Failed to create test database"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test connection to new database
|
||||
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-c "SELECT current_database();" >/dev/null 2>&1; then
|
||||
print_status "OK" "Connection to test database successful"
|
||||
else
|
||||
print_status "ERROR" "Failed to connect to test database"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_redis_connection() {
|
||||
log_info "Testing Redis connection..."
|
||||
|
||||
# Basic connection test
|
||||
if redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping | grep -q "PONG"; then
|
||||
print_status "OK" "Redis connection successful"
|
||||
else
|
||||
print_status "ERROR" "Redis connection failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test basic operations
|
||||
local test_key="test_key_$(date +%s)"
|
||||
local test_value="test_value_$(date +%s)"
|
||||
|
||||
if redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" set "$test_key" "$test_value" >/dev/null 2>&1; then
|
||||
print_status "OK" "Redis SET operation successful"
|
||||
else
|
||||
print_status "ERROR" "Redis SET operation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local retrieved_value
|
||||
retrieved_value=$(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" get "$test_key" 2>/dev/null)
|
||||
if [[ "$retrieved_value" == "$test_value" ]]; then
|
||||
print_status "OK" "Redis GET operation successful"
|
||||
else
|
||||
print_status "ERROR" "Redis GET operation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Cleanup test key
|
||||
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" del "$test_key" >/dev/null 2>&1 || true
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Schema Validation Functions
|
||||
# =============================================================================
|
||||
|
||||
test_schema_initialization() {
|
||||
log_section "Schema Initialization Testing"
|
||||
|
||||
# Create test schema
|
||||
create_test_schema || return 1
|
||||
|
||||
# Validate schema structure
|
||||
validate_schema_structure || return 1
|
||||
|
||||
# Test schema constraints
|
||||
test_schema_constraints || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
create_test_schema() {
|
||||
log_info "Creating test schema..."
|
||||
|
||||
# Create a simple test table
|
||||
local create_table_sql="
|
||||
CREATE TABLE IF NOT EXISTS test_table (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"
|
||||
|
||||
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-c "$create_table_sql" >/dev/null 2>&1; then
|
||||
print_status "OK" "Test schema created successfully"
|
||||
return 0
|
||||
else
|
||||
print_status "ERROR" "Failed to create test schema"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_schema_structure() {
|
||||
log_info "Validating schema structure..."
|
||||
|
||||
# Check if table exists
|
||||
local table_exists
|
||||
table_exists=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'test_table';" 2>/dev/null | tr -d ' ')
|
||||
|
||||
if [[ "$table_exists" == "1" ]]; then
|
||||
print_status "OK" "Test table exists in schema"
|
||||
else
|
||||
print_status "ERROR" "Test table not found in schema"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check table columns
|
||||
local column_count
|
||||
column_count=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-t -c "SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'test_table';" 2>/dev/null | tr -d ' ')
|
||||
|
||||
if [[ "$column_count" == "4" ]]; then
|
||||
print_status "OK" "Test table has correct number of columns"
|
||||
else
|
||||
print_status "WARNING" "Test table column count unexpected: $column_count"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_schema_constraints() {
|
||||
log_info "Testing schema constraints..."
|
||||
|
||||
# Test NOT NULL constraint
|
||||
if ! PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-c "INSERT INTO test_table (name) VALUES (NULL);" >/dev/null 2>&1; then
|
||||
print_status "OK" "NOT NULL constraint working correctly"
|
||||
else
|
||||
print_status "WARNING" "NOT NULL constraint may not be working"
|
||||
fi
|
||||
|
||||
# Test UNIQUE constraint
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-c "INSERT INTO test_table (name, email) VALUES ('Test User', 'test@example.com');" >/dev/null 2>&1
|
||||
|
||||
if ! PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-c "INSERT INTO test_table (name, email) VALUES ('Another User', 'test@example.com');" >/dev/null 2>&1; then
|
||||
print_status "OK" "UNIQUE constraint working correctly"
|
||||
else
|
||||
print_status "WARNING" "UNIQUE constraint may not be working"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Performance Testing Functions
|
||||
# =============================================================================
|
||||
|
||||
test_database_performance() {
|
||||
log_section "Database Performance Testing"
|
||||
|
||||
# Test PostgreSQL performance
|
||||
test_postgresql_performance || return 1
|
||||
|
||||
# Test Redis performance
|
||||
test_redis_performance || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_postgresql_performance() {
|
||||
log_info "Testing PostgreSQL performance..."
|
||||
|
||||
# Insert performance test
|
||||
local start_time
|
||||
local end_time
|
||||
local duration
|
||||
|
||||
start_time=$(date +%s%N)
|
||||
|
||||
# Insert 1000 test records
|
||||
for i in {1..1000}; do
|
||||
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-c "INSERT INTO test_table (name, email) VALUES ('User $i', 'user$i@example.com');" >/dev/null 2>&1
|
||||
done
|
||||
|
||||
end_time=$(date +%s%N)
|
||||
duration=$(( (end_time - start_time) / 1000000 )) # Convert to milliseconds
|
||||
|
||||
if [[ $duration -lt 10000 ]]; then # Less than 10 seconds
|
||||
print_status "OK" "PostgreSQL insert performance is good (${duration}ms for 1000 records)"
|
||||
else
|
||||
print_status "WARNING" "PostgreSQL insert performance is slow (${duration}ms for 1000 records)"
|
||||
fi
|
||||
|
||||
# Query performance test
|
||||
start_time=$(date +%s%N)
|
||||
|
||||
local record_count
|
||||
record_count=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$TEST_DB_NAME" \
|
||||
-t -c "SELECT COUNT(*) FROM test_table;" 2>/dev/null | tr -d ' ')
|
||||
|
||||
end_time=$(date +%s%N)
|
||||
duration=$(( (end_time - start_time) / 1000000 ))
|
||||
|
||||
if [[ $duration -lt 1000 ]] && [[ "$record_count" -gt 0 ]]; then
|
||||
print_status "OK" "PostgreSQL query performance is good (${duration}ms, $record_count records)"
|
||||
else
|
||||
print_status "WARNING" "PostgreSQL query performance may be suboptimal (${duration}ms)"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_redis_performance() {
|
||||
log_info "Testing Redis performance..."
|
||||
|
||||
# Set performance test
|
||||
local start_time
|
||||
local end_time
|
||||
local duration
|
||||
|
||||
start_time=$(date +%s%N)
|
||||
|
||||
# Set 1000 test keys
|
||||
for i in {1..1000}; do
|
||||
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" set "perf_test_$i" "value_$i" >/dev/null 2>&1
|
||||
done
|
||||
|
||||
end_time=$(date +%s%N)
|
||||
duration=$(( (end_time - start_time) / 1000000 ))
|
||||
|
||||
if [[ $duration -lt 5000 ]]; then # Less than 5 seconds
|
||||
print_status "OK" "Redis SET performance is good (${duration}ms for 1000 keys)"
|
||||
else
|
||||
print_status "WARNING" "Redis SET performance is slow (${duration}ms for 1000 keys)"
|
||||
fi
|
||||
|
||||
# Get performance test
|
||||
start_time=$(date +%s%N)
|
||||
|
||||
for i in {1..100}; do
|
||||
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" get "perf_test_$i" >/dev/null 2>&1
|
||||
done
|
||||
|
||||
end_time=$(date +%s%N)
|
||||
duration=$(( (end_time - start_time) / 1000000 ))
|
||||
|
||||
if [[ $duration -lt 1000 ]]; then # Less than 1 second
|
||||
print_status "OK" "Redis GET performance is good (${duration}ms for 100 keys)"
|
||||
else
|
||||
print_status "WARNING" "Redis GET performance is slow (${duration}ms for 100 keys)"
|
||||
fi
|
||||
|
||||
# Cleanup performance test keys
|
||||
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" eval "
|
||||
for i=1,1000 do
|
||||
redis.call('del', 'perf_test_' .. i)
|
||||
end
|
||||
" 0 >/dev/null 2>&1 || true
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Integration Testing Functions
|
||||
# =============================================================================
|
||||
|
||||
test_database_integration() {
|
||||
log_section "Database Integration Testing"
|
||||
|
||||
# Test DatabaseFactory usage
|
||||
test_database_factory_integration || return 1
|
||||
|
||||
# Test service-specific database initialization
|
||||
test_service_database_integration || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_database_factory_integration() {
|
||||
log_info "Testing DatabaseFactory integration..."
|
||||
|
||||
# Check for direct Database.connect() calls in gateway
|
||||
local direct_connects
|
||||
direct_connects=$(find infrastructure/gateway/src -name "*.kt" -exec grep -l "Database\.connect(" {} \; 2>/dev/null || true)
|
||||
|
||||
if [[ -z "$direct_connects" ]]; then
|
||||
print_status "OK" "No direct Database.connect() calls found in gateway"
|
||||
else
|
||||
print_status "ERROR" "Found direct Database.connect() calls in gateway: $direct_connects"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check for DatabaseFactory usage in gateway
|
||||
local factory_usage
|
||||
factory_usage=$(find infrastructure/gateway/src -name "*.kt" -exec grep -l "DatabaseFactory" {} \; 2>/dev/null || true)
|
||||
|
||||
if [[ -n "$factory_usage" ]]; then
|
||||
print_status "OK" "DatabaseFactory usage found in gateway"
|
||||
else
|
||||
print_status "WARNING" "No DatabaseFactory usage found in gateway"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_service_database_integration() {
|
||||
log_info "Testing service database integration..."
|
||||
|
||||
# Check that services don't call DatabaseFactory.init()
|
||||
local factory_inits
|
||||
factory_inits=$(find . -path "*/service/config/*" -name "*.kt" -exec grep -l "DatabaseFactory\.init(" {} \; 2>/dev/null || true)
|
||||
|
||||
if [[ -z "$factory_inits" ]]; then
|
||||
print_status "OK" "No DatabaseFactory.init() calls found in service configurations"
|
||||
else
|
||||
print_status "ERROR" "Found DatabaseFactory.init() calls in service configurations: $factory_inits"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main Execution
|
||||
# =============================================================================
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --skip-builds Skip service build testing"
|
||||
echo " --skip-performance Skip performance testing"
|
||||
echo " --keep-test-data Don't cleanup test data after testing"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
echo "Environment Variables:"
|
||||
echo " DB_HOST Database host (default: localhost)"
|
||||
echo " DB_PORT Database port (default: 5432)"
|
||||
echo " DB_NAME Database name (default: meldestelle_test)"
|
||||
echo " DB_USER Database user (default: meldestelle_user)"
|
||||
echo " DB_PASSWORD Database password (default: meldestelle_password)"
|
||||
echo " REDIS_HOST Redis host (default: localhost)"
|
||||
echo " REDIS_PORT Redis port (default: 6379)"
|
||||
}
|
||||
|
||||
main() {
|
||||
local SKIP_BUILDS=false
|
||||
local SKIP_PERFORMANCE=false
|
||||
local KEEP_TEST_DATA=false
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--skip-builds)
|
||||
SKIP_BUILDS=true
|
||||
shift
|
||||
;;
|
||||
--skip-performance)
|
||||
SKIP_PERFORMANCE=true
|
||||
shift
|
||||
;;
|
||||
--keep-test-data)
|
||||
KEEP_TEST_DATA=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log_section "Enhanced Database Initialization Test"
|
||||
|
||||
log_info "Starting comprehensive database tests..."
|
||||
log_info "Test database: $TEST_DB_NAME"
|
||||
log_info "Test timestamp: $(date)"
|
||||
|
||||
# Run all tests
|
||||
local test_results=()
|
||||
|
||||
validate_environment && test_results+=("Environment: PASS") || test_results+=("Environment: FAIL")
|
||||
setup_database_services && test_results+=("Setup: PASS") || test_results+=("Setup: FAIL")
|
||||
|
||||
if [[ "$SKIP_BUILDS" != "true" ]]; then
|
||||
test_service_builds && test_results+=("Builds: PASS") || test_results+=("Builds: FAIL")
|
||||
else
|
||||
test_results+=("Builds: SKIPPED")
|
||||
fi
|
||||
|
||||
test_database_connections && test_results+=("Connections: PASS") || test_results+=("Connections: FAIL")
|
||||
test_schema_initialization && test_results+=("Schema: PASS") || test_results+=("Schema: FAIL")
|
||||
test_database_integration && test_results+=("Integration: PASS") || test_results+=("Integration: FAIL")
|
||||
|
||||
if [[ "$SKIP_PERFORMANCE" != "true" ]]; then
|
||||
test_database_performance && test_results+=("Performance: PASS") || test_results+=("Performance: FAIL")
|
||||
else
|
||||
test_results+=("Performance: SKIPPED")
|
||||
fi
|
||||
|
||||
# Print test results summary
|
||||
log_section "Test Results Summary"
|
||||
for result in "${test_results[@]}"; do
|
||||
if [[ "$result" == *"PASS" ]]; then
|
||||
log_success "$result"
|
||||
elif [[ "$result" == *"SKIPPED" ]]; then
|
||||
log_info "$result"
|
||||
else
|
||||
log_error "$result"
|
||||
fi
|
||||
done
|
||||
|
||||
# Print final summary
|
||||
print_summary "Enhanced Database Initialization Test"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Executable
+373
@@ -0,0 +1,373 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Enhanced API Gateway Test Script
|
||||
# =============================================================================
|
||||
# This script provides comprehensive testing of the API Gateway implementation
|
||||
# including build validation, runtime testing, endpoint health checks, and
|
||||
# performance measurements.
|
||||
# =============================================================================
|
||||
|
||||
# Load common utilities
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=../utils/common.sh
|
||||
source "$SCRIPT_DIR/../utils/common.sh" || {
|
||||
echo "Error: Could not load common utilities from $SCRIPT_DIR/../utils/common.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Configuration
|
||||
# =============================================================================
|
||||
|
||||
readonly GATEWAY_MODULE="infrastructure:gateway"
|
||||
readonly GATEWAY_JAR="infrastructure/gateway/build/libs/gateway-1.0.0.jar"
|
||||
readonly GATEWAY_PORT="${GATEWAY_PORT:-8080}"
|
||||
readonly GATEWAY_HOST="${GATEWAY_HOST:-localhost}"
|
||||
readonly GATEWAY_BASE_URL="http://${GATEWAY_HOST}:${GATEWAY_PORT}"
|
||||
|
||||
# Test endpoints
|
||||
readonly TEST_ENDPOINTS=(
|
||||
"/health"
|
||||
"/metrics"
|
||||
"/api/masterdata/health"
|
||||
"/api/horses/health"
|
||||
"/api/events/health"
|
||||
"/api/members/health"
|
||||
)
|
||||
|
||||
# Service discovery endpoints
|
||||
readonly SERVICE_ENDPOINTS=(
|
||||
"masterdata:8081"
|
||||
"horses:8082"
|
||||
"events:8083"
|
||||
"members:8084"
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# Cleanup Function
|
||||
# =============================================================================
|
||||
|
||||
cleanup() {
|
||||
log_info "Cleaning up test environment..."
|
||||
|
||||
# Stop gateway if running
|
||||
if [[ -n "${GATEWAY_PID:-}" ]]; then
|
||||
log_info "Stopping gateway process (PID: $GATEWAY_PID)..."
|
||||
kill "$GATEWAY_PID" 2>/dev/null || true
|
||||
wait "$GATEWAY_PID" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Stop Docker services if started by this script
|
||||
if [[ "${STARTED_SERVICES:-false}" == "true" ]]; then
|
||||
log_info "Stopping test services..."
|
||||
docker-compose down --remove-orphans >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
log_info "Cleanup completed"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Test Functions
|
||||
# =============================================================================
|
||||
|
||||
# Test 1: Build Validation
|
||||
test_build_validation() {
|
||||
log_section "Test 1: Build Validation"
|
||||
|
||||
log_info "Building gateway module..."
|
||||
if run_with_timeout 300 "Gateway build" ./gradlew ":${GATEWAY_MODULE}:build" -x test; then
|
||||
print_status "OK" "Gateway builds successfully"
|
||||
else
|
||||
print_status "ERROR" "Gateway build failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if JAR file exists
|
||||
check_file "$GATEWAY_JAR" "Gateway JAR file"
|
||||
|
||||
# Validate JAR contents
|
||||
if jar -tf "$GATEWAY_JAR" | grep -q "Application.class"; then
|
||||
print_status "OK" "Gateway JAR contains main application class"
|
||||
else
|
||||
print_status "WARNING" "Gateway JAR may not contain expected main class"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 2: Configuration Validation
|
||||
test_configuration_validation() {
|
||||
log_section "Test 2: Configuration Validation"
|
||||
|
||||
# Check required configuration files
|
||||
local config_files=(
|
||||
"infrastructure/gateway/src/main/resources/application.conf"
|
||||
"infrastructure/gateway/src/main/resources/logback.xml"
|
||||
)
|
||||
|
||||
for config_file in "${config_files[@]}"; do
|
||||
check_file "$config_file" "Configuration file"
|
||||
done
|
||||
|
||||
# Validate environment variables
|
||||
local required_vars=(
|
||||
"API_HOST"
|
||||
"API_PORT"
|
||||
"CONSUL_HOST"
|
||||
"CONSUL_PORT"
|
||||
)
|
||||
|
||||
load_env_file
|
||||
|
||||
local missing_vars=()
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
missing_vars+=("$var")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_vars[@]} -gt 0 ]]; then
|
||||
print_status "WARNING" "Missing environment variables: ${missing_vars[*]} (using defaults)"
|
||||
else
|
||||
print_status "OK" "All required environment variables are set"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 3: Service Dependencies
|
||||
test_service_dependencies() {
|
||||
log_section "Test 3: Service Dependencies"
|
||||
|
||||
# Check Docker availability
|
||||
check_docker || return 1
|
||||
check_docker_compose || return 1
|
||||
|
||||
# Start required services for testing
|
||||
log_info "Starting required services for gateway testing..."
|
||||
|
||||
local required_services=(
|
||||
"postgres"
|
||||
"redis"
|
||||
"consul"
|
||||
)
|
||||
|
||||
if start_docker_services "${required_services[@]}"; then
|
||||
STARTED_SERVICES=true
|
||||
print_status "OK" "Required services started successfully"
|
||||
else
|
||||
print_status "ERROR" "Failed to start required services"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for Consul to be ready (service discovery)
|
||||
if check_service_port 8500 "Consul" 60; then
|
||||
print_status "OK" "Consul service discovery is ready"
|
||||
else
|
||||
print_status "WARNING" "Consul not available - service discovery may not work"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 4: Gateway Runtime Testing
|
||||
test_gateway_runtime() {
|
||||
log_section "Test 4: Gateway Runtime Testing"
|
||||
|
||||
# Start the gateway
|
||||
log_info "Starting API Gateway..."
|
||||
|
||||
# Set environment for gateway
|
||||
export API_HOST="${GATEWAY_HOST}"
|
||||
export API_PORT="${GATEWAY_PORT}"
|
||||
export CONSUL_HOST="${CONSUL_HOST:-localhost}"
|
||||
export CONSUL_PORT="${CONSUL_PORT:-8500}"
|
||||
|
||||
# Start gateway in background
|
||||
java -jar "$GATEWAY_JAR" > gateway.log 2>&1 &
|
||||
GATEWAY_PID=$!
|
||||
|
||||
log_info "Gateway started with PID: $GATEWAY_PID"
|
||||
|
||||
# Wait for gateway to be ready
|
||||
if wait_for_service "curl -sf ${GATEWAY_BASE_URL}/health" "API Gateway" 120 5; then
|
||||
print_status "OK" "API Gateway is running and healthy"
|
||||
else
|
||||
print_status "ERROR" "API Gateway failed to start or become healthy"
|
||||
if [[ -f "gateway.log" ]]; then
|
||||
log_error "Gateway logs:"
|
||||
tail -20 gateway.log >&2
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 5: Endpoint Health Checks
|
||||
test_endpoint_health() {
|
||||
log_section "Test 5: Endpoint Health Checks"
|
||||
|
||||
for endpoint in "${TEST_ENDPOINTS[@]}"; do
|
||||
local url="${GATEWAY_BASE_URL}${endpoint}"
|
||||
|
||||
if check_http_endpoint "$url" "Gateway endpoint $endpoint" 10 2; then
|
||||
# Additional validation for specific endpoints
|
||||
case $endpoint in
|
||||
"/health")
|
||||
local health_response
|
||||
health_response=$(curl -sf "$url" 2>/dev/null || echo "")
|
||||
if [[ "$health_response" == *"UP"* ]] || [[ "$health_response" == *"healthy"* ]]; then
|
||||
print_status "OK" "Health endpoint returns positive status"
|
||||
else
|
||||
print_status "WARNING" "Health endpoint response unclear: $health_response"
|
||||
fi
|
||||
;;
|
||||
"/metrics")
|
||||
if curl -sf "$url" | grep -q "jvm_"; then
|
||||
print_status "OK" "Metrics endpoint returns JVM metrics"
|
||||
else
|
||||
print_status "WARNING" "Metrics endpoint may not be properly configured"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 6: Service Discovery Integration
|
||||
test_service_discovery() {
|
||||
log_section "Test 6: Service Discovery Integration"
|
||||
|
||||
# Check if gateway can discover services
|
||||
local discovery_url="${GATEWAY_BASE_URL}/admin/services"
|
||||
|
||||
if check_http_endpoint "$discovery_url" "Service discovery endpoint" 10 1; then
|
||||
local services_response
|
||||
services_response=$(curl -sf "$discovery_url" 2>/dev/null || echo "[]")
|
||||
|
||||
if [[ "$services_response" != "[]" ]] && [[ "$services_response" != "" ]]; then
|
||||
print_status "OK" "Service discovery is working - found services"
|
||||
log_debug "Discovered services: $services_response"
|
||||
else
|
||||
print_status "WARNING" "Service discovery endpoint accessible but no services found"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Service discovery endpoint not accessible"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 7: Load and Performance Testing
|
||||
test_performance() {
|
||||
log_section "Test 7: Load and Performance Testing"
|
||||
|
||||
if ! command_exists ab; then
|
||||
print_status "WARNING" "Apache Bench (ab) not available - skipping performance tests"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local health_url="${GATEWAY_BASE_URL}/health"
|
||||
|
||||
log_info "Running basic load test (100 requests, concurrency 10)..."
|
||||
|
||||
local ab_output
|
||||
ab_output=$(ab -n 100 -c 10 "$health_url" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$ab_output" ]]; then
|
||||
local requests_per_sec
|
||||
requests_per_sec=$(echo "$ab_output" | grep "Requests per second" | awk '{print $4}')
|
||||
|
||||
local mean_time
|
||||
mean_time=$(echo "$ab_output" | grep "Time per request" | head -1 | awk '{print $4}')
|
||||
|
||||
if [[ -n "$requests_per_sec" ]] && [[ -n "$mean_time" ]]; then
|
||||
print_status "OK" "Performance test completed - ${requests_per_sec} req/sec, ${mean_time}ms avg"
|
||||
|
||||
# Basic performance validation
|
||||
if (( $(echo "$requests_per_sec > 50" | bc -l 2>/dev/null || echo 0) )); then
|
||||
print_status "OK" "Gateway performance is acceptable"
|
||||
else
|
||||
print_status "WARNING" "Gateway performance may be suboptimal"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Could not parse performance test results"
|
||||
fi
|
||||
else
|
||||
print_status "WARNING" "Performance test failed to run"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Test 8: Error Handling and Resilience
|
||||
test_error_handling() {
|
||||
log_section "Test 8: Error Handling and Resilience"
|
||||
|
||||
# Test 404 handling
|
||||
local not_found_url="${GATEWAY_BASE_URL}/nonexistent"
|
||||
local response_code
|
||||
response_code=$(curl -sf -o /dev/null -w "%{http_code}" "$not_found_url" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$response_code" == "404" ]]; then
|
||||
print_status "OK" "Gateway properly handles 404 errors"
|
||||
else
|
||||
print_status "WARNING" "Gateway 404 handling unclear (got $response_code)"
|
||||
fi
|
||||
|
||||
# Test service unavailable handling
|
||||
local unavailable_service_url="${GATEWAY_BASE_URL}/api/unavailable-service/test"
|
||||
response_code=$(curl -sf -o /dev/null -w "%{http_code}" "$unavailable_service_url" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$response_code" == "503" ]] || [[ "$response_code" == "502" ]]; then
|
||||
print_status "OK" "Gateway properly handles unavailable services"
|
||||
else
|
||||
print_status "WARNING" "Gateway service unavailable handling unclear (got $response_code)"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main Execution
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
log_section "Enhanced API Gateway Testing"
|
||||
|
||||
log_info "Starting comprehensive API Gateway tests..."
|
||||
log_info "Gateway URL: $GATEWAY_BASE_URL"
|
||||
log_info "Test timestamp: $(date)"
|
||||
|
||||
# Run all tests
|
||||
local test_results=()
|
||||
|
||||
test_build_validation && test_results+=("Build: PASS") || test_results+=("Build: FAIL")
|
||||
test_configuration_validation && test_results+=("Config: PASS") || test_results+=("Config: FAIL")
|
||||
test_service_dependencies && test_results+=("Dependencies: PASS") || test_results+=("Dependencies: FAIL")
|
||||
test_gateway_runtime && test_results+=("Runtime: PASS") || test_results+=("Runtime: FAIL")
|
||||
test_endpoint_health && test_results+=("Endpoints: PASS") || test_results+=("Endpoints: FAIL")
|
||||
test_service_discovery && test_results+=("Discovery: PASS") || test_results+=("Discovery: FAIL")
|
||||
test_performance && test_results+=("Performance: PASS") || test_results+=("Performance: FAIL")
|
||||
test_error_handling && test_results+=("ErrorHandling: PASS") || test_results+=("ErrorHandling: FAIL")
|
||||
|
||||
# Print test results summary
|
||||
log_section "Test Results Summary"
|
||||
for result in "${test_results[@]}"; do
|
||||
if [[ "$result" == *"PASS" ]]; then
|
||||
log_success "$result"
|
||||
else
|
||||
log_error "$result"
|
||||
fi
|
||||
done
|
||||
|
||||
# Print final summary
|
||||
print_summary "Enhanced API Gateway Test"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Executable
+462
@@ -0,0 +1,462 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Common Utilities Library for Meldestelle Shell Scripts
|
||||
# =============================================================================
|
||||
# This library provides common functions for logging, error handling, cleanup,
|
||||
# and other utilities used across all shell scripts in the project.
|
||||
#
|
||||
# Usage: source "$(dirname "$0")/utils/common.sh" || source "scripts/utils/common.sh"
|
||||
# =============================================================================
|
||||
|
||||
# Prevent multiple sourcing
|
||||
if [[ "${COMMON_UTILS_LOADED:-}" == "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
COMMON_UTILS_LOADED=true
|
||||
|
||||
# =============================================================================
|
||||
# Configuration and Constants
|
||||
# =============================================================================
|
||||
|
||||
# Colors for output
|
||||
readonly RED='\033[0;31m'
|
||||
readonly GREEN='\033[0;32m'
|
||||
readonly YELLOW='\033[1;33m'
|
||||
readonly BLUE='\033[0;34m'
|
||||
readonly PURPLE='\033[0;35m'
|
||||
readonly CYAN='\033[0;36m'
|
||||
readonly WHITE='\033[1;37m'
|
||||
readonly NC='\033[0m' # No Color
|
||||
|
||||
# Symbols
|
||||
readonly CHECK_MARK="✓"
|
||||
readonly CROSS_MARK="✗"
|
||||
readonly WARNING_MARK="⚠"
|
||||
readonly INFO_MARK="ℹ"
|
||||
readonly ARROW_MARK="→"
|
||||
|
||||
# Global counters
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
CHECKS=0
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# =============================================================================
|
||||
# Error Handling and Cleanup
|
||||
# =============================================================================
|
||||
|
||||
# Enhanced error handling
|
||||
set -euo pipefail
|
||||
|
||||
# Error trap function
|
||||
error_trap() {
|
||||
local exit_code=$?
|
||||
local line_number=$1
|
||||
log_error "Script failed at line $line_number with exit code $exit_code"
|
||||
cleanup_on_exit
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
# Set error trap
|
||||
trap 'error_trap $LINENO' ERR
|
||||
|
||||
# Cleanup function (can be overridden by scripts)
|
||||
cleanup_on_exit() {
|
||||
if declare -f cleanup > /dev/null; then
|
||||
log_info "Running cleanup..."
|
||||
cleanup
|
||||
fi
|
||||
}
|
||||
|
||||
# Set exit trap
|
||||
trap cleanup_on_exit EXIT
|
||||
|
||||
# =============================================================================
|
||||
# Logging Functions
|
||||
# =============================================================================
|
||||
|
||||
# Get timestamp
|
||||
get_timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# Base logging function
|
||||
log_base() {
|
||||
local level=$1
|
||||
local color=$2
|
||||
local symbol=$3
|
||||
local message=$4
|
||||
local timestamp=$(get_timestamp)
|
||||
|
||||
echo -e "${color}[${timestamp}] ${symbol} [${level}]${NC} ${message}" >&2
|
||||
}
|
||||
|
||||
# Info logging
|
||||
log_info() {
|
||||
log_base "INFO" "$BLUE" "$INFO_MARK" "$1"
|
||||
}
|
||||
|
||||
# Success logging
|
||||
log_success() {
|
||||
log_base "SUCCESS" "$GREEN" "$CHECK_MARK" "$1"
|
||||
}
|
||||
|
||||
# Warning logging
|
||||
log_warning() {
|
||||
log_base "WARNING" "$YELLOW" "$WARNING_MARK" "$1"
|
||||
((WARNINGS++))
|
||||
}
|
||||
|
||||
# Error logging
|
||||
log_error() {
|
||||
log_base "ERROR" "$RED" "$CROSS_MARK" "$1"
|
||||
((ERRORS++))
|
||||
}
|
||||
|
||||
# Debug logging (only if DEBUG=true)
|
||||
log_debug() {
|
||||
if [[ "${DEBUG:-false}" == "true" ]]; then
|
||||
log_base "DEBUG" "$PURPLE" "🐛" "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Progress logging
|
||||
log_progress() {
|
||||
log_base "PROGRESS" "$CYAN" "$ARROW_MARK" "$1"
|
||||
}
|
||||
|
||||
# Section header
|
||||
log_section() {
|
||||
local title=$1
|
||||
local line=$(printf '=%.0s' {1..80})
|
||||
echo -e "\n${BLUE}${line}${NC}"
|
||||
echo -e "${BLUE}${title}${NC}"
|
||||
echo -e "${BLUE}${line}${NC}\n"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Status and Validation Functions
|
||||
# =============================================================================
|
||||
|
||||
# Print status with counter increment
|
||||
print_status() {
|
||||
local status=$1
|
||||
local message=$2
|
||||
((CHECKS++))
|
||||
|
||||
case $status in
|
||||
"OK"|"SUCCESS")
|
||||
log_success "$message"
|
||||
;;
|
||||
"WARNING"|"WARN")
|
||||
log_warning "$message"
|
||||
;;
|
||||
"ERROR"|"FAIL")
|
||||
log_error "$message"
|
||||
;;
|
||||
"INFO")
|
||||
log_info "$message"
|
||||
;;
|
||||
*)
|
||||
log_info "$message"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check if command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check if file exists with logging
|
||||
check_file() {
|
||||
local file=$1
|
||||
local description=${2:-"File"}
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
print_status "OK" "$description exists: $file"
|
||||
return 0
|
||||
else
|
||||
print_status "ERROR" "$description not found: $file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if directory exists with logging
|
||||
check_directory() {
|
||||
local dir=$1
|
||||
local description=${2:-"Directory"}
|
||||
|
||||
if [[ -d "$dir" ]]; then
|
||||
print_status "OK" "$description exists: $dir"
|
||||
return 0
|
||||
else
|
||||
print_status "ERROR" "$description not found: $dir"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if service is running on port
|
||||
check_service_port() {
|
||||
local port=$1
|
||||
local service_name=${2:-"Service"}
|
||||
local timeout=${3:-30}
|
||||
|
||||
log_info "Checking if $service_name is running on port $port..."
|
||||
|
||||
if timeout "$timeout" bash -c "until nc -z localhost $port; do sleep 1; done" 2>/dev/null; then
|
||||
print_status "OK" "$service_name is running on port $port"
|
||||
return 0
|
||||
else
|
||||
print_status "ERROR" "$service_name is not running on port $port (timeout: ${timeout}s)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check HTTP endpoint with retry
|
||||
check_http_endpoint() {
|
||||
local url=$1
|
||||
local service_name=${2:-"Service"}
|
||||
local timeout=${3:-30}
|
||||
local retry_count=${4:-3}
|
||||
|
||||
log_info "Checking HTTP endpoint: $url"
|
||||
|
||||
for ((i=1; i<=retry_count; i++)); do
|
||||
if timeout "$timeout" curl -sf "$url" >/dev/null 2>&1; then
|
||||
print_status "OK" "$service_name endpoint is healthy: $url"
|
||||
return 0
|
||||
else
|
||||
if [[ $i -lt $retry_count ]]; then
|
||||
log_warning "Attempt $i/$retry_count failed, retrying in 5 seconds..."
|
||||
sleep 5
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
print_status "ERROR" "$service_name endpoint is not healthy: $url (after $retry_count attempts)"
|
||||
return 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Utility Functions
|
||||
# =============================================================================
|
||||
|
||||
# Wait for service with timeout
|
||||
wait_for_service() {
|
||||
local check_command=$1
|
||||
local service_name=$2
|
||||
local timeout=${3:-60}
|
||||
local interval=${4:-5}
|
||||
|
||||
log_info "Waiting for $service_name to be ready (timeout: ${timeout}s)..."
|
||||
|
||||
local elapsed=0
|
||||
while [[ $elapsed -lt $timeout ]]; do
|
||||
if eval "$check_command" >/dev/null 2>&1; then
|
||||
log_success "$service_name is ready"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep "$interval"
|
||||
elapsed=$((elapsed + interval))
|
||||
log_progress "Waiting for $service_name... (${elapsed}s/${timeout}s)"
|
||||
done
|
||||
|
||||
log_error "$service_name failed to become ready within ${timeout}s"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Create directory with logging
|
||||
create_directory() {
|
||||
local dir=$1
|
||||
local description=${2:-"Directory"}
|
||||
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
if mkdir -p "$dir"; then
|
||||
log_success "$description created: $dir"
|
||||
else
|
||||
log_error "Failed to create $description: $dir"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_info "$description already exists: $dir"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup file with timestamp
|
||||
backup_file() {
|
||||
local file=$1
|
||||
local backup_dir=${2:-"./backups"}
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
create_directory "$backup_dir" "Backup directory"
|
||||
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
local backup_file="$backup_dir/$(basename "$file").backup.$timestamp"
|
||||
|
||||
if cp "$file" "$backup_file"; then
|
||||
log_success "File backed up: $file → $backup_file"
|
||||
echo "$backup_file"
|
||||
else
|
||||
log_error "Failed to backup file: $file"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_warning "File not found for backup: $file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run command with timeout and logging
|
||||
run_with_timeout() {
|
||||
local timeout_duration=$1
|
||||
local description=$2
|
||||
shift 2
|
||||
local command=("$@")
|
||||
|
||||
log_info "Running: $description"
|
||||
log_debug "Command: ${command[*]}"
|
||||
|
||||
if timeout "$timeout_duration" "${command[@]}"; then
|
||||
log_success "$description completed successfully"
|
||||
return 0
|
||||
else
|
||||
local exit_code=$?
|
||||
if [[ $exit_code -eq 124 ]]; then
|
||||
log_error "$description timed out after ${timeout_duration}s"
|
||||
else
|
||||
log_error "$description failed with exit code $exit_code"
|
||||
fi
|
||||
return $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Summary and Reporting Functions
|
||||
# =============================================================================
|
||||
|
||||
# Print execution summary
|
||||
print_summary() {
|
||||
local script_name=${1:-"Script"}
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - START_TIME))
|
||||
|
||||
log_section "Execution Summary"
|
||||
|
||||
echo -e "Script: ${WHITE}$script_name${NC}"
|
||||
echo -e "Duration: ${WHITE}${duration}s${NC}"
|
||||
echo -e "Total checks: ${WHITE}$CHECKS${NC}"
|
||||
echo -e "${GREEN}Successful: $((CHECKS - ERRORS - WARNINGS))${NC}"
|
||||
echo -e "${YELLOW}Warnings: $WARNINGS${NC}"
|
||||
echo -e "${RED}Errors: $ERRORS${NC}"
|
||||
echo
|
||||
|
||||
if [[ $ERRORS -eq 0 ]]; then
|
||||
if [[ $WARNINGS -eq 0 ]]; then
|
||||
log_success "All checks passed! $script_name completed successfully."
|
||||
return 0
|
||||
else
|
||||
log_warning "$script_name completed with warnings. Please review the warnings above."
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
log_error "$script_name failed with $ERRORS errors. Please fix the errors above."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Environment and Configuration
|
||||
# =============================================================================
|
||||
|
||||
# Load environment file if it exists
|
||||
load_env_file() {
|
||||
local env_file=${1:-.env}
|
||||
|
||||
if [[ -f "$env_file" ]]; then
|
||||
log_info "Loading environment from: $env_file"
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$env_file"
|
||||
set +a
|
||||
log_success "Environment loaded successfully"
|
||||
else
|
||||
log_warning "Environment file not found: $env_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate required environment variables
|
||||
validate_env_vars() {
|
||||
local vars=("$@")
|
||||
local missing_vars=()
|
||||
|
||||
for var in "${vars[@]}"; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
missing_vars+=("$var")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_vars[@]} -gt 0 ]]; then
|
||||
log_error "Missing required environment variables: ${missing_vars[*]}"
|
||||
return 1
|
||||
else
|
||||
log_success "All required environment variables are set"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Docker and Service Management
|
||||
# =============================================================================
|
||||
|
||||
# Check if Docker is running
|
||||
check_docker() {
|
||||
if command_exists docker && docker info >/dev/null 2>&1; then
|
||||
print_status "OK" "Docker is running"
|
||||
return 0
|
||||
else
|
||||
print_status "ERROR" "Docker is not running or not accessible"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if docker-compose is available
|
||||
check_docker_compose() {
|
||||
if command_exists docker-compose; then
|
||||
print_status "OK" "docker-compose is available"
|
||||
return 0
|
||||
elif docker compose version >/dev/null 2>&1; then
|
||||
print_status "OK" "docker compose (plugin) is available"
|
||||
return 0
|
||||
else
|
||||
print_status "ERROR" "Neither docker-compose nor docker compose is available"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Start Docker services with health check wait
|
||||
start_docker_services() {
|
||||
local services=("$@")
|
||||
local compose_file=${COMPOSE_FILE:-docker-compose.yml}
|
||||
|
||||
log_info "Starting Docker services: ${services[*]}"
|
||||
|
||||
if docker-compose -f "$compose_file" up -d "${services[@]}"; then
|
||||
log_success "Docker services started"
|
||||
|
||||
# Wait for services to be healthy
|
||||
for service in "${services[@]}"; do
|
||||
wait_for_service "docker-compose -f $compose_file ps $service | grep -q 'healthy\\|Up'" "$service" 120 10
|
||||
done
|
||||
else
|
||||
log_error "Failed to start Docker services"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Initialization
|
||||
# =============================================================================
|
||||
|
||||
log_debug "Common utilities library loaded successfully"
|
||||
Executable
+261
@@ -0,0 +1,261 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Environment Variables Validation Script
|
||||
# =============================================================================
|
||||
# This script validates that all required environment variables are properly
|
||||
# configured for the Meldestelle application.
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Counters
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
CHECKS=0
|
||||
|
||||
echo -e "${BLUE}==============================================================================${NC}"
|
||||
echo -e "${BLUE}Meldestelle - Environment Variables Validation${NC}"
|
||||
echo -e "${BLUE}==============================================================================${NC}"
|
||||
echo
|
||||
|
||||
# Function to print status
|
||||
print_status() {
|
||||
local status=$1
|
||||
local message=$2
|
||||
|
||||
case $status in
|
||||
"OK")
|
||||
echo -e "${GREEN}✓${NC} $message"
|
||||
;;
|
||||
"WARNING")
|
||||
echo -e "${YELLOW}⚠${NC} $message"
|
||||
((WARNINGS++))
|
||||
;;
|
||||
"ERROR")
|
||||
echo -e "${RED}✗${NC} $message"
|
||||
((ERRORS++))
|
||||
;;
|
||||
"INFO")
|
||||
echo -e "${BLUE}ℹ${NC} $message"
|
||||
;;
|
||||
esac
|
||||
((CHECKS++))
|
||||
}
|
||||
|
||||
# Check if .env file exists
|
||||
echo -e "${BLUE}1. Checking .env file...${NC}"
|
||||
if [ -f ".env" ]; then
|
||||
print_status "OK" ".env file exists"
|
||||
|
||||
# Load .env file
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
print_status "OK" ".env file loaded successfully"
|
||||
else
|
||||
print_status "ERROR" ".env file not found"
|
||||
echo -e "${RED}Please create a .env file based on the documentation.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
|
||||
# Check if docker-compose.yml exists
|
||||
echo -e "${BLUE}2. Checking docker-compose.yml file...${NC}"
|
||||
if [ -f "docker-compose.yml" ]; then
|
||||
print_status "OK" "docker-compose.yml file exists"
|
||||
else
|
||||
print_status "ERROR" "docker-compose.yml file not found"
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
|
||||
# Define required environment variables
|
||||
echo -e "${BLUE}3. Checking required environment variables...${NC}"
|
||||
|
||||
# Application Configuration
|
||||
check_var() {
|
||||
local var_name=$1
|
||||
local var_value=${!var_name}
|
||||
local is_required=${2:-false}
|
||||
local description=$3
|
||||
|
||||
if [ -n "$var_value" ]; then
|
||||
print_status "OK" "$var_name is set: '$var_value'"
|
||||
elif [ "$is_required" = true ]; then
|
||||
print_status "ERROR" "$var_name is required but not set ($description)"
|
||||
else
|
||||
print_status "WARNING" "$var_name is not set ($description)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Application Configuration
|
||||
echo -e "${YELLOW}Application Configuration:${NC}"
|
||||
check_var "API_HOST" true "Server host address"
|
||||
check_var "API_PORT" true "Server port"
|
||||
check_var "APP_NAME" false "Application name"
|
||||
check_var "APP_VERSION" false "Application version"
|
||||
check_var "APP_ENVIRONMENT" true "Current environment"
|
||||
echo
|
||||
|
||||
# Database Configuration
|
||||
echo -e "${YELLOW}Database Configuration:${NC}"
|
||||
check_var "DB_HOST" true "Database host"
|
||||
check_var "DB_PORT" true "Database port"
|
||||
check_var "DB_NAME" true "Database name"
|
||||
check_var "DB_USER" true "Database user"
|
||||
check_var "DB_PASSWORD" true "Database password"
|
||||
check_var "POSTGRES_USER" true "PostgreSQL container user"
|
||||
check_var "POSTGRES_PASSWORD" true "PostgreSQL container password"
|
||||
check_var "POSTGRES_DB" true "PostgreSQL container database"
|
||||
echo
|
||||
|
||||
# Redis Configuration
|
||||
echo -e "${YELLOW}Redis Configuration:${NC}"
|
||||
check_var "REDIS_EVENT_STORE_HOST" true "Redis event store host"
|
||||
check_var "REDIS_EVENT_STORE_PORT" true "Redis event store port"
|
||||
check_var "REDIS_CACHE_HOST" true "Redis cache host"
|
||||
check_var "REDIS_CACHE_PORT" true "Redis cache port"
|
||||
echo
|
||||
|
||||
# Security Configuration
|
||||
echo -e "${YELLOW}Security Configuration:${NC}"
|
||||
check_var "JWT_SECRET" true "JWT secret key"
|
||||
check_var "JWT_ISSUER" true "JWT issuer"
|
||||
check_var "JWT_AUDIENCE" true "JWT audience"
|
||||
check_var "JWT_REALM" true "JWT realm"
|
||||
check_var "API_KEY" true "API key for internal services"
|
||||
echo
|
||||
|
||||
# Keycloak Configuration
|
||||
echo -e "${YELLOW}Keycloak Configuration:${NC}"
|
||||
check_var "KEYCLOAK_ADMIN" true "Keycloak admin user"
|
||||
check_var "KEYCLOAK_ADMIN_PASSWORD" true "Keycloak admin password"
|
||||
check_var "KC_DB" true "Keycloak database type"
|
||||
check_var "KC_DB_URL" true "Keycloak database URL"
|
||||
check_var "KC_DB_USERNAME" true "Keycloak database user"
|
||||
check_var "KC_DB_PASSWORD" true "Keycloak database password"
|
||||
echo
|
||||
|
||||
# Service Discovery
|
||||
echo -e "${YELLOW}Service Discovery Configuration:${NC}"
|
||||
check_var "CONSUL_HOST" true "Consul host"
|
||||
check_var "CONSUL_PORT" true "Consul port"
|
||||
echo
|
||||
|
||||
# Messaging Configuration
|
||||
echo -e "${YELLOW}Messaging Configuration:${NC}"
|
||||
check_var "ZOOKEEPER_CLIENT_PORT" true "Zookeeper client port"
|
||||
check_var "KAFKA_BROKER_ID" true "Kafka broker ID"
|
||||
check_var "KAFKA_ZOOKEEPER_CONNECT" true "Kafka Zookeeper connection"
|
||||
echo
|
||||
|
||||
# Monitoring Configuration
|
||||
echo -e "${YELLOW}Monitoring Configuration:${NC}"
|
||||
check_var "GF_SECURITY_ADMIN_USER" true "Grafana admin user"
|
||||
check_var "GF_SECURITY_ADMIN_PASSWORD" true "Grafana admin password"
|
||||
echo
|
||||
|
||||
# Security Checks
|
||||
echo -e "${BLUE}4. Security validation...${NC}"
|
||||
|
||||
# Check JWT secret strength
|
||||
if [ -n "$JWT_SECRET" ]; then
|
||||
if [ ${#JWT_SECRET} -lt 32 ]; then
|
||||
print_status "WARNING" "JWT_SECRET should be at least 32 characters long for security"
|
||||
else
|
||||
print_status "OK" "JWT_SECRET length is adequate (${#JWT_SECRET} characters)"
|
||||
fi
|
||||
|
||||
if [[ "$JWT_SECRET" == *"default"* ]] || [[ "$JWT_SECRET" == *"change"* ]]; then
|
||||
print_status "WARNING" "JWT_SECRET appears to be a default value - change for production"
|
||||
else
|
||||
print_status "OK" "JWT_SECRET appears to be customized"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for default passwords
|
||||
if [ "$POSTGRES_PASSWORD" = "meldestelle" ]; then
|
||||
print_status "WARNING" "Using default PostgreSQL password - change for production"
|
||||
fi
|
||||
|
||||
if [ "$KEYCLOAK_ADMIN_PASSWORD" = "admin" ]; then
|
||||
print_status "WARNING" "Using default Keycloak admin password - change for production"
|
||||
fi
|
||||
|
||||
if [ "$GF_SECURITY_ADMIN_PASSWORD" = "admin" ]; then
|
||||
print_status "WARNING" "Using default Grafana admin password - change for production"
|
||||
fi
|
||||
echo
|
||||
|
||||
# Port conflict checks
|
||||
echo -e "${BLUE}5. Port conflict checks...${NC}"
|
||||
declare -A ports
|
||||
ports["API_PORT"]=$API_PORT
|
||||
ports["DB_PORT"]=$DB_PORT
|
||||
ports["REDIS_EVENT_STORE_PORT"]=$REDIS_EVENT_STORE_PORT
|
||||
ports["CONSUL_PORT"]=$CONSUL_PORT
|
||||
ports["ZOOKEEPER_CLIENT_PORT"]=$ZOOKEEPER_CLIENT_PORT
|
||||
|
||||
# Check for duplicate ports
|
||||
declare -A port_usage
|
||||
for service in "${!ports[@]}"; do
|
||||
port=${ports[$service]}
|
||||
if [ -n "$port" ]; then
|
||||
if [ -n "${port_usage[$port]}" ]; then
|
||||
print_status "ERROR" "Port conflict: $service ($port) conflicts with ${port_usage[$port]}"
|
||||
else
|
||||
port_usage[$port]=$service
|
||||
print_status "OK" "$service using port $port"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo
|
||||
|
||||
# Environment-specific checks
|
||||
echo -e "${BLUE}6. Environment-specific checks...${NC}"
|
||||
if [ "$APP_ENVIRONMENT" = "production" ]; then
|
||||
print_status "INFO" "Production environment detected - additional security checks recommended"
|
||||
|
||||
if [ "$LOGGING_LEVEL" = "DEBUG" ]; then
|
||||
print_status "WARNING" "DEBUG logging enabled in production environment"
|
||||
fi
|
||||
|
||||
if [ "$SERVER_CORS_ALLOWED_ORIGINS" = "*" ]; then
|
||||
print_status "WARNING" "CORS allows all origins in production environment"
|
||||
fi
|
||||
else
|
||||
print_status "OK" "Development environment detected"
|
||||
fi
|
||||
echo
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}==============================================================================${NC}"
|
||||
echo -e "${BLUE}Validation Summary${NC}"
|
||||
echo -e "${BLUE}==============================================================================${NC}"
|
||||
echo -e "Total checks performed: ${CHECKS}"
|
||||
echo -e "${GREEN}Successful checks: $((CHECKS - ERRORS - WARNINGS))${NC}"
|
||||
echo -e "${YELLOW}Warnings: ${WARNINGS}${NC}"
|
||||
echo -e "${RED}Errors: ${ERRORS}${NC}"
|
||||
echo
|
||||
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
if [ $WARNINGS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All checks passed! Your environment configuration is ready.${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Configuration is valid but has warnings. Review the warnings above.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ Configuration has errors that must be fixed before running the application.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user