diff --git a/.gitea/workflows/eapim-portal-ci.yml b/.gitea/workflows/eapim-portal-ci.yml index 3bf6f2f..f3f34ce 100644 --- a/.gitea/workflows/eapim-portal-ci.yml +++ b/.gitea/workflows/eapim-portal-ci.yml @@ -4,6 +4,8 @@ on: push: branches: - master + - develop + - stage - feats/ci-test jobs: @@ -80,7 +82,7 @@ jobs: cp eapim-portal/build/libs/eapim-portal.war "$DEPLOY_DIR/ROOT.war" ls -la "$DEPLOY_DIR" - - name: Start tomcat and health check (timeout 90s) + - name: Start tomcat and readiness probe (timeout 120s) working-directory: ${{ github.workspace }} run: | LOG="$CATALINA_BASE/logs/catalina.$(date +%Y-%m-%d).log" @@ -90,14 +92,34 @@ jobs: mkdir -p "$(dirname $CATALINA_PID)" JAVA_HOME=$JAVA_HOME_TOMCAT "$CATALINA_HOME/bin/startup.sh" - DEADLINE=$(($(date +%s) + 90)) + DEADLINE=$(($(date +%s) + 120)) + LIVE_OK=0 STATUS=000 + BODY="" while [ $(date +%s) -lt $DEADLINE ]; do - STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 http://localhost:$DEPLOY_HTTP_PORT/ 2>/dev/null || echo "000") - if [ "$STATUS" = "200" ]; then - echo "HTTP 200 received from /" - break + # Stage 1: liveness — servlet 응답 가능? + if [ "$LIVE_OK" = "0" ]; then + LIVE=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \ + http://localhost:$DEPLOY_HTTP_PORT/health 2>/dev/null || echo "000") + if [ "$LIVE" = "200" ]; then + echo "Liveness OK (/health)" + LIVE_OK=1 + fi fi + + # Stage 2: readiness — DB(EMS, Gateway)까지 검증 + if [ "$LIVE_OK" = "1" ]; then + BODY=$(curl -sS -o /tmp/ready-body.json -w '%{http_code}' --max-time 3 \ + http://localhost:$DEPLOY_HTTP_PORT/health/ready 2>/dev/null || echo "000") + STATUS=$BODY + if [ "$STATUS" = "200" ]; then + echo "Readiness OK (/health/ready)" + cat /tmp/ready-body.json + echo + break + fi + fi + if [ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | grep -qE "Application listener .* Exception|SEVERE.*startup.Catalina"; then echo "::error::Startup error detected in log" tail -c +$((BEFORE+1)) "$LOG" | tail -80 @@ -107,24 +129,11 @@ jobs: done if [ "$STATUS" != "200" ]; then - echo "::error::Health check failed within 90s (last HTTP status: $STATUS)" + echo "::error::Readiness probe failed within 120s (last HTTP status: $STATUS)" + [ -f /tmp/ready-body.json ] && echo "--- last readiness body ---" && cat /tmp/ready-body.json && echo [ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | tail -100 exit 1 fi echo "--- key boot log lines ---" tail -c +$((BEFORE+1)) "$LOG" | grep -E "Server startup|Deployment of web" | head -10 || true - - - name: Stop tomcat (post-clean) - if: always() - working-directory: ${{ github.workspace }} - run: | - if [ -f "$CATALINA_PID" ] && kill -0 "$(cat $CATALINA_PID)" 2>/dev/null; then - JAVA_HOME=$JAVA_HOME_TOMCAT "$CATALINA_HOME/bin/shutdown.sh" 30 -force || true - for i in $(seq 1 30); do - [ -f "$CATALINA_PID" ] && kill -0 "$(cat $CATALINA_PID)" 2>/dev/null || break - sleep 1 - done - [ -f "$CATALINA_PID" ] && kill -9 "$(cat $CATALINA_PID)" 2>/dev/null || true - fi - rm -f "$CATALINA_PID" diff --git a/src/main/java/com/eactive/apim/portal/apps/ReadinessController.java b/src/main/java/com/eactive/apim/portal/apps/ReadinessController.java new file mode 100644 index 0000000..125bb9f --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/apps/ReadinessController.java @@ -0,0 +1,67 @@ +package com.eactive.apim.portal.apps; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Readiness probe. + * + *
Distinct from {@link HealthCheckController} (liveness — servlet alive?). + * This endpoint validates both EMS and Gateway datasources via JDBC + * {@code Connection.isValid(timeout)} to confirm the app is ready to serve + * requests that depend on the database. + * + *
HTTP 200 + JSON when all checks pass. + *
HTTP 503 + JSON when any check fails — body still includes the per-component
+ * status so CI/CD logs the failing dependency.
+ */
+@RestController
+public class ReadinessController {
+
+ private static final int VALIDATION_TIMEOUT_SECONDS = 2;
+
+ private final DataSource portalDataSource;
+ private final DataSource gatewayDataSource;
+
+ public ReadinessController(
+ @Qualifier("portalDataSource") DataSource portalDataSource,
+ @Qualifier("gatewayDataSource") DataSource gatewayDataSource) {
+ this.portalDataSource = portalDataSource;
+ this.gatewayDataSource = gatewayDataSource;
+ }
+
+ @GetMapping(value = "/health/ready", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity