Skip to content

Commit 5885c9b

Browse files
Pigbibicodex
andcommitted
fix: gate shared scheduler cleanup
Co-Authored-By: Codex <noreply@openai.com>
1 parent 44980d5 commit 5885c9b

2 files changed

Lines changed: 115 additions & 15 deletions

File tree

.github/workflows/sync-cloud-run-env.yml

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,19 +1177,6 @@ jobs:
11771177
esac
11781178
done
11791179
1180-
if [ "${DEPLOYMENT_LABEL:-}" = "SG" ]; then
1181-
monitor_job_name="longbridge-monitor-dispatcher-scheduler"
1182-
if gcloud scheduler jobs describe "${monitor_job_name}" \
1183-
--project="${GCP_PROJECT_ID}" \
1184-
--location="${scheduler_location}" >/dev/null 2>&1; then
1185-
echo "Deleting obsolete shared monitor dispatcher ${monitor_job_name}."
1186-
gcloud scheduler jobs delete "${monitor_job_name}" \
1187-
--project="${GCP_PROJECT_ID}" \
1188-
--location="${scheduler_location}" \
1189-
--quiet
1190-
fi
1191-
fi
1192-
11931180
- name: Reconcile legacy Cloud Scheduler jobs
11941181
if: steps.config.outputs.env_sync_enabled == 'true'
11951182
env:
@@ -1270,3 +1257,109 @@ jobs:
12701257
--async \
12711258
--quiet
12721259
done <<< "${old_digests}"
1260+
1261+
cleanup-shared-monitor:
1262+
name: Retire shared monitor dispatcher
1263+
needs: sync
1264+
if: >
1265+
needs.sync.result == 'success' &&
1266+
(
1267+
github.event_name == 'workflow_run' ||
1268+
(github.event_name == 'workflow_dispatch' && inputs.target == 'configured')
1269+
)
1270+
runs-on: ubuntu-latest
1271+
timeout-minutes: 10
1272+
environment: longbridge-sg
1273+
permissions:
1274+
contents: read
1275+
id-token: write
1276+
env:
1277+
CLOUD_RUN_SERVICE_TARGETS_JSON: ${{ vars.CLOUD_RUN_SERVICE_TARGETS_JSON }}
1278+
CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }}
1279+
CLOUD_SCHEDULER_LOCATION: ${{ vars.CLOUD_SCHEDULER_LOCATION }}
1280+
steps:
1281+
- name: Authenticate to Google Cloud
1282+
uses: google-github-actions/auth@v3
1283+
with:
1284+
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
1285+
service_account: ${{ env.GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT }}
1286+
1287+
- name: Set up gcloud
1288+
uses: google-github-actions/setup-gcloud@v3
1289+
with:
1290+
project_id: ${{ env.GCP_PROJECT_ID }}
1291+
version: ">= 416.0.0"
1292+
1293+
- name: Verify per-service scheduler replacements
1294+
id: replacements
1295+
run: |
1296+
set -euo pipefail
1297+
1298+
replacements_ready="true"
1299+
mapfile -t configured_targets < <(python3 - <<'PY'
1300+
import json
1301+
import os
1302+
1303+
payload = json.loads(os.environ.get("CLOUD_RUN_SERVICE_TARGETS_JSON") or "{}")
1304+
for target in payload.get("targets") or []:
1305+
if not isinstance(target, dict):
1306+
continue
1307+
service_name = str(target.get("service_name") or target.get("service") or "").strip()
1308+
scheduler = target.get("scheduler") if isinstance(target.get("scheduler"), dict) else {}
1309+
location = str(
1310+
target.get("scheduler_location")
1311+
or scheduler.get("location")
1312+
or target.get("region")
1313+
or target.get("cloud_run_region")
1314+
or ""
1315+
).strip()
1316+
if service_name:
1317+
print(f"{service_name}\t{location}")
1318+
PY
1319+
)
1320+
1321+
if [ "${#configured_targets[@]}" -eq 0 ]; then
1322+
echo "No configured LongBridge targets; keeping the shared monitor dispatcher." >&2
1323+
replacements_ready="false"
1324+
fi
1325+
1326+
for configured_target in "${configured_targets[@]}"; do
1327+
IFS=$'\t' read -r service_name scheduler_location <<< "${configured_target}"
1328+
if [ -z "${scheduler_location}" ]; then
1329+
echo "No scheduler location for ${service_name}; keeping the shared monitor dispatcher." >&2
1330+
replacements_ready="false"
1331+
continue
1332+
fi
1333+
replacement_jobs=("${service_name}-probe-scheduler" "${service_name}-precheck-scheduler")
1334+
for replacement_job in "${replacement_jobs[@]}"; do
1335+
if ! gcloud scheduler jobs describe "${replacement_job}" \
1336+
--project="${GCP_PROJECT_ID}" \
1337+
--location="${scheduler_location}" >/dev/null 2>&1; then
1338+
echo "Replacement ${replacement_job} is not ready in ${scheduler_location}." >&2
1339+
replacements_ready="false"
1340+
fi
1341+
done
1342+
done
1343+
1344+
echo "ready=${replacements_ready}" >> "$GITHUB_OUTPUT"
1345+
1346+
- name: Delete obsolete shared monitor dispatcher
1347+
if: steps.replacements.outputs.ready == 'true'
1348+
run: |
1349+
set -euo pipefail
1350+
1351+
scheduler_location="${CLOUD_SCHEDULER_LOCATION:-${CLOUD_RUN_REGION}}"
1352+
if [ -z "${scheduler_location}" ]; then
1353+
echo "Shared monitor dispatcher cleanup requires a scheduler location." >&2
1354+
exit 1
1355+
fi
1356+
monitor_job_name="longbridge-monitor-dispatcher-scheduler"
1357+
if gcloud scheduler jobs describe "${monitor_job_name}" \
1358+
--project="${GCP_PROJECT_ID}" \
1359+
--location="${scheduler_location}" >/dev/null 2>&1; then
1360+
echo "Deleting obsolete shared monitor dispatcher ${monitor_job_name}."
1361+
gcloud scheduler jobs delete "${monitor_job_name}" \
1362+
--project="${GCP_PROJECT_ID}" \
1363+
--location="${scheduler_location}" \
1364+
--quiet
1365+
fi

tests/test_scheduler_workflow.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ def test_replacement_probe_and_precheck_exist_before_shared_dispatcher_cleanup()
66

77
probe = workflow.index('probe_job_name="${CLOUD_RUN_SERVICE}-probe-scheduler"')
88
precheck = workflow.index('precheck_job_name="${CLOUD_RUN_SERVICE}-precheck-scheduler"')
9+
cleanup_job = workflow.index("cleanup-shared-monitor:")
910
cleanup = workflow.index('gcloud scheduler jobs delete "${monitor_job_name}"')
1011

11-
assert probe < cleanup
12-
assert precheck < cleanup
12+
assert probe < cleanup_job < cleanup
13+
assert precheck < cleanup_job < cleanup
14+
15+
cleanup_section = workflow[cleanup_job:]
16+
assert "needs: sync" in cleanup_section
17+
assert "environment: longbridge-sg" in cleanup_section
18+
assert 'replacement_jobs=("${service_name}-probe-scheduler" "${service_name}-precheck-scheduler")' in cleanup_section
19+
assert "if: steps.replacements.outputs.ready == 'true'" in cleanup_section

0 commit comments

Comments
 (0)