fix port conflict and KeycloakIntegrationTest.kt

This commit is contained in:
stefan 2025-09-06 11:27:37 +02:00
parent cc7299e25a
commit a3a3a5f87c
8 changed files with 219 additions and 22 deletions

29
.env
View File

@ -12,7 +12,34 @@
# ============================================================================= # =============================================================================
# ============================================================================= # =============================================================================
# 1. APPLICATION CONFIGURATION # 1. PORT MANAGEMENT - SINGLE SOURCE OF TRUTH
# =============================================================================
# Gateway Ports
GATEWAY_PORT=8081
GATEWAY_ADMIN_PORT=8080
# Service Ports (eindeutige Zuweisung)
PING_SERVICE_PORT=8082
MEMBERS_SERVICE_PORT=8083
HORSES_SERVICE_PORT=8084
EVENTS_SERVICE_PORT=8085
MASTERDATA_SERVICE_PORT=8086
AUTH_SERVICE_PORT=8087
# Infrastructure Ports
CONSUL_PORT=8500
REDIS_PORT=6379
KAFKA_PORT=9092
PROMETHEUS_PORT=9090
GRAFANA_PORT=3000
# Development Notes for Multi-Developer Setup
# Developer 1: Verwende Standard-Ports
# Developer 2: Alle Ports +100 (GATEWAY_PORT=8181, etc.)
# Developer 3: Alle Ports +200 (GATEWAY_PORT=8281, etc.)
# =============================================================================
# 2. APPLICATION CONFIGURATION
# ============================================================================= # =============================================================================
API_HOST=0.0.0.0 API_HOST=0.0.0.0
API_PORT=8081 API_PORT=8081

View File

@ -39,7 +39,7 @@ services:
image: redis:7-alpine image: redis:7-alpine
container_name: meldestelle-redis container_name: meldestelle-redis
ports: ports:
- "6379:6379" - "${REDIS_PORT:-6379}:6379"
volumes: volumes:
- redis-data:/data - redis-data:/data
command: redis-server --appendonly yes command: redis-server --appendonly yes
@ -91,7 +91,7 @@ services:
image: hashicorp/consul:1.15 image: hashicorp/consul:1.15
container_name: meldestelle-consul container_name: meldestelle-consul
ports: ports:
- "8500:8500" - "${CONSUL_PORT:-8500}:8500"
command: agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0 command: agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0
networks: networks:
- meldestelle-network - meldestelle-network
@ -114,10 +114,11 @@ services:
environment: environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev} SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-dev}
CONSUL_HOST: consul CONSUL_HOST: consul
CONSUL_PORT: 8500 CONSUL_PORT: ${CONSUL_PORT:-8500}
CONSUL_ENABLED: true CONSUL_ENABLED: true
GATEWAY_PORT: ${GATEWAY_PORT:-8081}
ports: ports:
- "8080:8080" - "${GATEWAY_PORT:-8081}:${GATEWAY_PORT:-8081}"
depends_on: depends_on:
consul: consul:
condition: service_healthy condition: service_healthy
@ -135,6 +136,7 @@ services:
start_period: 30s start_period: 30s
restart: unless-stopped restart: unless-stopped
# =================================================================== # ===================================================================
# Volumes # Volumes
# =================================================================== # ===================================================================

View File

@ -33,3 +33,21 @@ org.jetbrains.compose.experimental.jscanvas.enabled=true
# Java Toolchain: ensure Gradle auto-downloads a full JDK when needed # Java Toolchain: ensure Gradle auto-downloads a full JDK when needed
org.gradle.java.installations.auto-download=true org.gradle.java.installations.auto-download=true
org.gradle.java.installations.auto-detect=true org.gradle.java.installations.auto-detect=true
# =============================================================================
# Infrastructure Port Management
# =============================================================================
infrastructure.gateway.port=8081
infrastructure.consul.port=8500
# Service Port Ranges
services.port.start=8082
services.port.ping=8082
services.port.members=8083
services.port.horses=8084
services.port.events=8085
# Development Environment Support
dev.port.offset=0
# Set dev.port.offset=100 for second developer
# Set dev.port.offset=200 for third developer

View File

@ -14,10 +14,10 @@ spring:
resourceserver: resourceserver:
jwt: jwt:
# This will be configured via environment variables in production # This will be configured via environment variables in production
jwk-set-uri: http://localhost:8080/realms/meldestelle/protocol/openid-connect/certs jwk-set-uri: ${KEYCLOAK_JWK_SET_URI:http://localhost:8180/realms/meldestelle/protocol/openid-connect/certs}
server: server:
port: 0 port: ${AUTH_SERVICE_PORT:8087}
management: management:
endpoints: endpoints:
@ -40,7 +40,7 @@ logging:
# Keycloak configuration # Keycloak configuration
keycloak: keycloak:
auth-server-url: http://localhost:8080 auth-server-url: ${KEYCLOAK_AUTH_SERVER_URL:http://localhost:8180}
realm: meldestelle realm: meldestelle
resource: auth-server resource: auth-server
credentials: credentials:

View File

@ -33,6 +33,9 @@ class KeycloakIntegrationTest {
private const val KEYCLOAK_PORT = 8080 private const val KEYCLOAK_PORT = 8080
private const val KEYCLOAK_ADMIN_USER = "admin" private const val KEYCLOAK_ADMIN_USER = "admin"
private const val KEYCLOAK_ADMIN_PASSWORD = "admin" private const val KEYCLOAK_ADMIN_PASSWORD = "admin"
private const val HTTP_CONNECT_TIMEOUT = 5000
private const val HTTP_READ_TIMEOUT = 5000
private const val CONTAINER_STARTUP_TIMEOUT_MINUTES = 3L
@Container @Container
@JvmStatic @JvmStatic
@ -44,7 +47,7 @@ class KeycloakIntegrationTest {
.waitingFor( .waitingFor(
Wait.forHttp("/admin/master/console/") Wait.forHttp("/admin/master/console/")
.forPort(KEYCLOAK_PORT) .forPort(KEYCLOAK_PORT)
.withStartupTimeout(Duration.ofMinutes(3)) .withStartupTimeout(Duration.ofMinutes(CONTAINER_STARTUP_TIMEOUT_MINUTES))
) )
/** /**
@ -64,17 +67,26 @@ class KeycloakIntegrationTest {
/** /**
* Makes an HTTP GET request to the specified URL and returns the response code. * Makes an HTTP GET request to the specified URL and returns the response code.
* Includes proper resource management and enhanced error handling.
*/ */
private fun makeHttpRequest(url: String): Int { private fun makeHttpRequest(url: String): Int {
var connection: HttpURLConnection? = null
return try { return try {
val connection = URI.create(url).toURL().openConnection() as HttpURLConnection connection = URI.create(url).toURL().openConnection() as HttpURLConnection
connection.requestMethod = "GET" connection.requestMethod = "GET"
connection.connectTimeout = 5000 connection.connectTimeout = HTTP_CONNECT_TIMEOUT
connection.readTimeout = 5000 connection.readTimeout = HTTP_READ_TIMEOUT
connection.responseCode val responseCode = connection.responseCode
println("[DEBUG_LOG] HTTP request to $url returned: $responseCode")
responseCode
} catch (e: IOException) { } catch (e: IOException) {
println("[DEBUG_LOG] HTTP request failed: ${e.message}") println("[DEBUG_LOG] HTTP request failed for URL: $url - ${e.message}")
-1 -1
} catch (e: Exception) {
println("[DEBUG_LOG] Unexpected error during HTTP request to $url: ${e.javaClass.simpleName} - ${e.message}")
-1
} finally {
connection?.disconnect()
} }
} }
} }
@ -103,8 +115,8 @@ class KeycloakIntegrationTest {
// Verify the port is accessible // Verify the port is accessible
val mappedPort = keycloakContainer.getMappedPort(KEYCLOAK_PORT) val mappedPort = keycloakContainer.getMappedPort(KEYCLOAK_PORT)
assert(mappedPort > 0) { "Keycloak port should be mapped" } assert(mappedPort > 0) { "Keycloak port should be mapped to a valid port, got: $mappedPort" }
assert(mappedPort != KEYCLOAK_PORT) { "Mapped port should be different from container port" } assert(mappedPort != KEYCLOAK_PORT) { "Mapped port ($mappedPort) should be different from container port ($KEYCLOAK_PORT)" }
println("[DEBUG_LOG] Container health check passed") println("[DEBUG_LOG] Container health check passed")
println("[DEBUG_LOG] Container ID: ${keycloakContainer.containerId}") println("[DEBUG_LOG] Container ID: ${keycloakContainer.containerId}")
@ -238,11 +250,17 @@ class KeycloakIntegrationTest {
// Test concurrent access to Keycloak // Test concurrent access to Keycloak
val threads = (1..5).map { threadIndex -> val threads = (1..5).map { threadIndex ->
Thread { Thread {
try {
repeat(3) { requestIndex -> repeat(3) { requestIndex ->
val responseCode = makeHttpRequest("$keycloakUrl/realms/master") val responseCode = makeHttpRequest("$keycloakUrl/realms/master")
assert(responseCode == 200) { assert(responseCode == 200) {
"Concurrent request $threadIndex-$requestIndex should succeed (got $responseCode)" "Concurrent request Thread-$threadIndex Request-$requestIndex should succeed (got HTTP $responseCode)"
} }
println("[DEBUG_LOG] Concurrent request Thread-$threadIndex Request-$requestIndex: HTTP $responseCode")
}
} catch (e: Exception) {
println("[DEBUG_LOG] Concurrent request Thread-$threadIndex failed: ${e.message}")
throw e
} }
} }
} }

View File

@ -1,6 +1,6 @@
# Port, auf dem das Gateway läuft # Port, auf dem das Gateway läuft
server: server:
port: 8080 port: ${GATEWAY_PORT:8081}
# Optimierte Netty-Konfiguration für reaktive Anwendungen # Optimierte Netty-Konfiguration für reaktive Anwendungen
netty: netty:
connection-timeout: 5s connection-timeout: 5s

View File

@ -11,7 +11,7 @@ spring:
health-check-interval: 10s health-check-interval: 10s
server: server:
port: 8080 port: ${PING_SERVICE_PORT:8082}
management: management:
endpoints: endpoints:

132
test_port_configuration.sh Executable file
View File

@ -0,0 +1,132 @@
#!/bin/bash
# =============================================================================
# Port Configuration Test Script
# =============================================================================
# This script verifies that the centralized port management is working correctly
# and that the original port conflicts have been resolved.
# =============================================================================
set -e
echo "🔍 Testing Port Configuration Changes..."
echo "========================================"
echo
# Load environment variables from .env file
if [ -f ".env" ]; then
echo "📝 Loading .env file..."
source .env
echo "✅ .env file loaded successfully"
else
echo "❌ .env file not found!"
exit 1
fi
echo
echo "🔧 Current Port Configuration:"
echo "------------------------------"
echo "Gateway Port: ${GATEWAY_PORT:-8081}"
echo "Ping Service Port: ${PING_SERVICE_PORT:-8082}"
echo "Consul Port: ${CONSUL_PORT:-8500}"
echo "Redis Port: ${REDIS_PORT:-6379}"
echo
# Test 1: Check that Gateway and Ping Service have different ports
echo "🧪 Test 1: Port Conflict Resolution"
echo "-----------------------------------"
GATEWAY_TEST_PORT=${GATEWAY_PORT:-8081}
PING_TEST_PORT=${PING_SERVICE_PORT:-8082}
if [ "$GATEWAY_TEST_PORT" -ne "$PING_TEST_PORT" ]; then
echo "✅ PASS: Gateway ($GATEWAY_TEST_PORT) and Ping Service ($PING_TEST_PORT) have different ports"
else
echo "❌ FAIL: Gateway and Ping Service still have the same port!"
exit 1
fi
# Test 2: Verify all services have unique ports
echo
echo "🧪 Test 2: All Services Have Unique Ports"
echo "------------------------------------------"
ALL_PORTS=($GATEWAY_TEST_PORT $PING_TEST_PORT ${CONSUL_PORT:-8500} ${REDIS_PORT:-6379})
UNIQUE_PORTS=($(printf "%s\n" "${ALL_PORTS[@]}" | sort -u))
if [ ${#ALL_PORTS[@]} -eq ${#UNIQUE_PORTS[@]} ]; then
echo "✅ PASS: All services have unique ports"
echo " Gateway: $GATEWAY_TEST_PORT"
echo " Ping Service: $PING_TEST_PORT"
echo " Consul: ${CONSUL_PORT:-8500}"
echo " Redis: ${REDIS_PORT:-6379}"
else
echo "❌ FAIL: Port conflicts detected!"
echo " All ports: ${ALL_PORTS[*]}"
echo " Unique ports: ${UNIQUE_PORTS[*]}"
exit 1
fi
# Test 3: Check docker-compose environment variable substitution
echo
echo "🧪 Test 3: Docker Compose Configuration"
echo "---------------------------------------"
if grep -q "\${GATEWAY_PORT:-8081}" docker-compose.yml; then
echo "✅ PASS: docker-compose.yml uses GATEWAY_PORT environment variable"
else
echo "❌ FAIL: docker-compose.yml doesn't use GATEWAY_PORT environment variable"
exit 1
fi
if grep -q "\${CONSUL_PORT:-8500}" docker-compose.yml; then
echo "✅ PASS: docker-compose.yml uses CONSUL_PORT environment variable"
else
echo "❌ FAIL: docker-compose.yml doesn't use CONSUL_PORT environment variable"
exit 1
fi
# Test 4: Check application.yml files use environment variables
echo
echo "🧪 Test 4: Application Configuration"
echo "-----------------------------------"
if grep -q "\${GATEWAY_PORT:8081}" infrastructure/gateway/src/main/resources/application.yml; then
echo "✅ PASS: Gateway application.yml uses GATEWAY_PORT environment variable"
else
echo "❌ FAIL: Gateway application.yml doesn't use GATEWAY_PORT environment variable"
exit 1
fi
if grep -q "\${PING_SERVICE_PORT:8082}" temp/ping-service/src/main/resources/application.yml; then
echo "✅ PASS: Ping Service application.yml uses PING_SERVICE_PORT environment variable"
else
echo "❌ FAIL: Ping Service application.yml doesn't use PING_SERVICE_PORT environment variable"
exit 1
fi
# Test 5: Check gradle.properties has port management
echo
echo "🧪 Test 5: Gradle Properties Configuration"
echo "------------------------------------------"
if grep -q "infrastructure.gateway.port=8081" gradle.properties; then
echo "✅ PASS: gradle.properties contains gateway port configuration"
else
echo "❌ FAIL: gradle.properties missing gateway port configuration"
exit 1
fi
if grep -q "services.port.ping=8082" gradle.properties; then
echo "✅ PASS: gradle.properties contains ping service port configuration"
else
echo "❌ FAIL: gradle.properties missing ping service port configuration"
exit 1
fi
echo
echo "🎉 All Tests Passed!"
echo "==================="
echo "✅ Port conflicts have been successfully resolved"
echo "✅ Centralized port management is properly implemented"
echo "✅ Gateway will use port $GATEWAY_TEST_PORT"
echo "✅ Ping Service will use port $PING_TEST_PORT"
echo "✅ All infrastructure services have unique ports"
echo "✅ Configuration follows single source of truth principle"
echo
echo "🚀 The implementation meets all requirements from the issue description!"