From aa2848c3b6ceefe2d96243d85678c2879cdcc9c9 Mon Sep 17 00:00:00 2001 From: Lectom Date: Mon, 8 Jun 2026 08:30:51 +0900 Subject: [PATCH] =?UTF-8?q?restart=20policy=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/staging_code_pull.yml | 50 +++++++++++++++++- docker/staging_pull_compose.template.yaml | 10 ++++ test/staging_frontend_deploy_policy_test.sh | 5 ++ test/staging_pull_restart_policy_test.sh | 56 +++++++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 test/staging_pull_restart_policy_test.sh diff --git a/.gitea/workflows/staging_code_pull.yml b/.gitea/workflows/staging_code_pull.yml index e051132e..3fe6e06e 100644 --- a/.gitea/workflows/staging_code_pull.yml +++ b/.gitea/workflows/staging_code_pull.yml @@ -133,6 +133,8 @@ jobs: ORGFRONT_CALLBACK_URLS=${{ vars.ORGFRONT_CALLBACK_URLS }} KRATOS_ALLOWED_RETURN_URLS_JSON=${{ vars.KRATOS_ALLOWED_RETURN_URLS_JSON }} KRATOS_ALLOWED_RETURN_URLS_EXTRA=${{ vars.KRATOS_ALLOWED_RETURN_URLS_EXTRA }} + STAGING_PUBLIC_HEALTH_URL=${{ vars.STAGING_PUBLIC_HEALTH_URL }} + STAGING_PUBLIC_HEALTH_MAX_ATTEMPTS=${{ vars.STAGING_PUBLIC_HEALTH_MAX_ATTEMPTS }} # OATHKEEPER_INTROSPECT_CLIENT_ID=${{ vars.OATHKEEPER_INTROSPECT_CLIENT_ID }} # OATHKEEPER_INTROSPECT_CLIENT_SECRET=${{ secrets.STG_OATHKEEPER_INTROSPECT_CLIENT_SECRET }} EOF @@ -190,7 +192,7 @@ jobs: max="${FRONTEND_HEALTH_MAX_ATTEMPTS:-60}" i=1 while [ "${i}" -le "${max}" ]; do - if docker exec "${name}" node -e "fetch('http://127.0.0.1:${port}/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" >/dev/null 2>&1; then + if docker exec "${name}" sh -c "if command -v wget >/dev/null 2>&1; then wget -qO- 'http://127.0.0.1:${port}/' >/dev/null; elif command -v node >/dev/null 2>&1; then node -e \"fetch('http://127.0.0.1:${port}/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"; else exit 127; fi" >/dev/null 2>&1; then echo "Frontend ready: ${name}:${port}" return 0 fi @@ -203,9 +205,55 @@ jobs: return 1 } + check_container_url() { + name="$1" + url="$2" + max="${FRONTEND_HEALTH_MAX_ATTEMPTS:-60}" + i=1 + while [ "${i}" -le "${max}" ]; do + if docker exec "${name}" sh -c "if command -v wget >/dev/null 2>&1; then wget -qO- '${url}' >/dev/null; elif command -v node >/dev/null 2>&1; then node -e \"fetch('${url}').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"; else exit 127; fi" >/dev/null 2>&1; then + echo "Container URL ready: ${name} ${url}" + return 0 + fi + echo "Waiting for container URL: ${name} ${url} (${i}/${max})" + i=$((i + 1)) + sleep 2 + done + echo "ERROR: container URL not ready: ${name} ${url}" >&2 + docker logs "${name}" --tail 200 >&2 || true + return 1 + } + + check_public_http() { + url="$1" + if [ -z "${url}" ]; then + echo "ERROR: STAGING_PUBLIC_HEALTH_URL is required." >&2 + return 1 + fi + max="${STAGING_PUBLIC_HEALTH_MAX_ATTEMPTS:-30}" + i=1 + while [ "${i}" -le "${max}" ]; do + if curl -fsS --max-time 10 "${url}" >/dev/null; then + echo "Public staging URL ready: ${url}" + return 0 + fi + echo "Waiting for public staging URL: ${url} (${i}/${max})" + i=$((i + 1)) + sleep 2 + done + echo "ERROR: public staging URL not ready: ${url}" >&2 + docker compose -f staging_pull_compose.yaml ps >&2 || true + docker logs baron_gateway --tail 200 >&2 || true + return 1 + } + + check_container_url baron_backend http://127.0.0.1:3000/health + check_container_http baron_userfront 5000 + check_container_http baron_gateway 5000 check_container_http baron_adminfront 5173 check_container_http baron_devfront 5173 check_container_http baron_orgfront 5175 + check_public_http "${STAGING_PUBLIC_HEALTH_URL}" echo "===== INIT-RP LOGS =====" docker compose -f staging_pull_compose.yaml logs init-rp || true diff --git a/docker/staging_pull_compose.template.yaml b/docker/staging_pull_compose.template.yaml index e7ac39df..260bc30a 100644 --- a/docker/staging_pull_compose.template.yaml +++ b/docker/staging_pull_compose.template.yaml @@ -79,6 +79,7 @@ services: postgres_ory: image: postgres:${ORY_POSTGRES_TAG:-17-alpine} container_name: ory_postgres + restart: unless-stopped environment: - POSTGRES_USER=${ORY_POSTGRES_USER:-ory} - POSTGRES_PASSWORD=${ORY_POSTGRES_PASSWORD:-secret} @@ -125,6 +126,7 @@ services: kratos: image: oryd/kratos:${KRATOS_VERSION:-v26.2.0} container_name: ory_kratos + restart: unless-stopped environment: - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB:-ory_kratos}?sslmode=disable&max_conns=20 - COOKIE_SECRET=${COOKIE_SECRET:-localcookie123} @@ -163,6 +165,7 @@ services: hydra: image: oryd/hydra:${HYDRA_VERSION:-v26.2.0} container_name: ory_hydra + restart: unless-stopped environment: - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${HYDRA_DB:-ory_hydra}?sslmode=disable&max_conns=20 - URLS_SELF_ISSUER=${HYDRA_PUBLIC_URL} @@ -196,6 +199,7 @@ services: keto: image: oryd/keto:${KETO_VERSION:-v26.2.0} container_name: ory_keto + restart: unless-stopped environment: - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KETO_DB:-ory_keto}?sslmode=disable&max_conns=20 volumes: @@ -255,6 +259,7 @@ services: ory_clickhouse: image: clickhouse/clickhouse-server:latest container_name: ory_clickhouse + restart: unless-stopped environment: - CLICKHOUSE_USER=${ORY_CLICKHOUSE_USER:-ory} - CLICKHOUSE_PASSWORD=${ORY_CLICKHOUSE_PASSWORD:-orypass} @@ -360,6 +365,7 @@ services: context: ./backend dockerfile: Dockerfile container_name: baron_backend + restart: unless-stopped env_file: - .env environment: @@ -424,6 +430,7 @@ services: VITE_OIDC_CLIENT_ID: adminfront ORGFRONT_URL: ${ORGFRONT_URL:-} container_name: baron_adminfront + restart: unless-stopped env_file: - .env environment: @@ -449,6 +456,7 @@ services: VITE_OIDC_AUTHORITY: ${VITE_OIDC_AUTHORITY:-} VITE_OIDC_CLIENT_ID: devfront container_name: baron_devfront + restart: unless-stopped env_file: - .env environment: @@ -474,6 +482,7 @@ services: VITE_OIDC_AUTHORITY: ${VITE_OIDC_AUTHORITY:-} VITE_OIDC_CLIENT_ID: orgfront container_name: baron_orgfront + restart: unless-stopped env_file: - .env environment: @@ -496,6 +505,7 @@ services: context: . dockerfile: userfront/Dockerfile container_name: baron_userfront + restart: unless-stopped env_file: - .env environment: diff --git a/test/staging_frontend_deploy_policy_test.sh b/test/staging_frontend_deploy_policy_test.sh index 3a11c6ca..b8aa8928 100644 --- a/test/staging_frontend_deploy_policy_test.sh +++ b/test/staging_frontend_deploy_policy_test.sh @@ -62,12 +62,17 @@ for workflow in "$staging_pull"; do assert_contains "$workflow" 'ORGFRONT_URL=${{ vars.ORGFRONT_URL }}' assert_contains "$workflow" 'KRATOS_ALLOWED_RETURN_URLS_JSON=${{ vars.KRATOS_ALLOWED_RETURN_URLS_JSON }}' assert_contains "$workflow" 'KRATOS_ALLOWED_RETURN_URLS_EXTRA=${{ vars.KRATOS_ALLOWED_RETURN_URLS_EXTRA }}' + assert_contains "$workflow" 'STAGING_PUBLIC_HEALTH_URL=${{ vars.STAGING_PUBLIC_HEALTH_URL }}' + assert_contains "$workflow" 'STAGING_PUBLIC_HEALTH_MAX_ATTEMPTS=${{ vars.STAGING_PUBLIC_HEALTH_MAX_ATTEMPTS }}' done assert_contains "$staging_pull" 'bash scripts/render_ory_config.sh' assert_contains "$staging_pull" 'chmod -R 777 config/.generated/ory' assert_contains "$staging_pull" 'docker compose -f staging_pull_compose.yaml build --pull' assert_contains "$staging_pull" 'docker compose -f staging_pull_compose.yaml up -d --remove-orphans --renew-anon-volumes' +assert_contains "$staging_pull" 'check_container_http baron_gateway 5000' +assert_contains "$staging_pull" 'check_public_http "${STAGING_PUBLIC_HEALTH_URL}"' +assert_contains "$staging_pull" 'curl -fsS --max-time 10 "${url}"' assert_contains "$userfront_dockerfile" "FROM ghcr.io/cirruslabs/flutter:3.38.0 AS build" assert_contains "$userfront_dockerfile" "RUN flutter build web --release --wasm" diff --git a/test/staging_pull_restart_policy_test.sh b/test/staging_pull_restart_policy_test.sh new file mode 100644 index 00000000..91277f47 --- /dev/null +++ b/test/staging_pull_restart_policy_test.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env sh +set -eu + +compose_file="docker/staging_pull_compose.template.yaml" + +if [ ! -f "$compose_file" ]; then + echo "missing expected file: $compose_file" >&2 + exit 1 +fi + +assert_service_has_restart_policy() { + service="$1" + awk -v service="$service" ' + $0 ~ "^ " service ":" { + in_service = 1 + found = 0 + next + } + in_service && /^ [A-Za-z0-9_-]+:/ { + exit found ? 0 : 1 + } + in_service && /^[[:space:]]+restart:[[:space:]]+(always|unless-stopped)[[:space:]]*$/ { + found = 1 + } + END { + if (in_service) { + exit found ? 0 : 1 + } + } + ' "$compose_file" || { + echo "ERROR: long-running staging service must define restart: always or restart: unless-stopped: $service" >&2 + exit 1 + } +} + +for service in \ + postgres \ + clickhouse \ + redis \ + gateway \ + postgres_ory \ + kratos \ + hydra \ + keto \ + oathkeeper \ + ory_clickhouse \ + backend \ + adminfront \ + devfront \ + orgfront \ + userfront +do + assert_service_has_restart_policy "$service" +done + +echo "staging pull restart policy checks passed"