diff --git a/benchpark-bridge/scripts/ci_generator.sh b/benchpark-bridge/scripts/ci_generator.sh index fdcaaca..4dc34d4 100644 --- a/benchpark-bridge/scripts/ci_generator.sh +++ b/benchpark-bridge/scripts/ci_generator.sh @@ -61,7 +61,8 @@ ${job_prefix}_send: script: - echo \"Checking CI variables\" - test -n \"\$RESULT_SERVER\" && echo \"RESULT_SERVER is set\" || echo \"RESULT_SERVER is NOT set\" - - test -n \"\$RESULT_SERVER_KEY\" && echo \"RESULT_SERVER_KEY is set\" || echo \"RESULT_SERVER_KEY is NOT set\" + - test -n \"\$RESULT_SERVER_CLIENT_CERT\" && echo \"RESULT_SERVER_CLIENT_CERT is set\" || echo \"RESULT_SERVER_CLIENT_CERT is NOT set\" + - test -n \"\$RESULT_SERVER_CLIENT_KEY\" && echo \"RESULT_SERVER_CLIENT_KEY is set\" || echo \"RESULT_SERVER_CLIENT_KEY is NOT set\" - echo \"Sending results to server\" - bash scripts/result_server/send_results.sh diff --git a/docs/guides/add-site.md b/docs/guides/add-site.md index 9684ef2..99adc9c 100644 --- a/docs/guides/add-site.md +++ b/docs/guides/add-site.md @@ -694,7 +694,7 @@ NewSystem,NewSystem,Example CPU,2,64,Example GPU,4,512GB,10 5. `result0.json` が作られること - `scripts/result.sh` が Result JSON を組み立てられることを確認します。 6. `send_results` まで通ること - - API key、Result Server 接続、`results/result*.json` の配置を確認します。 + - mTLS client certificate/key、Result Server 接続、`results/result*.json` の配置を確認します。 最初の動作確認では、既存アプリで最小の 1 条件だけ `list.csv` に足して `scripts/test_submit.sh` で試すのが安全です。 @@ -709,7 +709,7 @@ NewSystem,NewSystem,Example CPU,2,64,Example GPU,4,512GB,10 | build | `module load` の typo、コンパイラ不一致、依存ライブラリ不足 | `programs//build.sh` | | run | `mpirun` / `srun` の引数、affinity、ノード側環境差異 | `programs//run.sh`, scheduler log | | result 生成 | `results/result` がない、FOM 出力形式が違う、`bk_emit_*` 未使用 | `scripts/result.sh`, `scripts/bk_functions.sh` | -| send_results | API key、Result Server 接続、JSON 不足 | `scripts/result_server/send_results.sh`, Result Server log | +| send_results | mTLS client certificate/key、Result Server 接続、JSON 不足 | `scripts/result_server/send_results.sh`, Result Server log | CI ログでは、まず「どの stage まで進んだか」を見ると切り分けが早くなります。`get_sources` 前、build 前、run 前、send_results 前で原因の層がかなり絞れます。 diff --git a/docs/guides/developer-reference.md b/docs/guides/developer-reference.md index 7363267..7ec33e4 100644 --- a/docs/guides/developer-reference.md +++ b/docs/guides/developer-reference.md @@ -77,7 +77,9 @@ The supported baseline is that contributors can add apps, sites, and estimation - detailed result and estimate pages - usage reporting - TOTP-based authentication -- admin pages for user management +- admin pages for user management, execution profile governance, Portal-managed + GitLab trigger submission, trigger-runner observations, and related operation + history ### Main Route Groups @@ -100,7 +102,8 @@ The supported baseline is that contributors can add apps, sites, and estimation - `result_server/routes/auth.py` Login, setup, logout, and TOTP flow. - `result_server/routes/admin.py` - Admin-only user management. + Admin-only user management, execution profile registry, manual GitLab trigger + submission, trigger definitions, trigger run history, and runner observations. ### Main API Endpoints @@ -143,6 +146,8 @@ Data or `*.ncu-rep`. TOTP setup page. - `result_server/templates/admin_users.html` Admin user management page. +- `result_server/templates/admin_execution_profiles.html` + Admin execution profile, trigger definition, trigger run, and observation page. ## CI Pipeline Structure diff --git a/result_server/tests/test_profile_usage_overview.py b/result_server/tests/test_profile_usage_overview.py index 040a680..351624c 100644 --- a/result_server/tests/test_profile_usage_overview.py +++ b/result_server/tests/test_profile_usage_overview.py @@ -114,6 +114,82 @@ def test_profile_usage_overview_links_profile_triggers_results_and_node_hours(tm assert row["latest_result"]["environment_snapshot"]["allocation_project_id"] == "rkp00010" +def test_profile_usage_overview_does_not_scope_match_triggered_results_to_other_profiles(tmp_path): + db_path = tmp_path / "cx_portal.sqlite3" + received_dir = tmp_path / "received" + received_dir.mkdir() + + store = ExecutionProfileStore(str(db_path)) + store.upsert_profile(_profile(id="qws-fugaku"), actor="admin@test.com") + store.upsert_profile( + _profile( + id="qws-test", + system=["Fugaku", "MiyabiG"], + allocation_project_id="", + ), + actor="admin@test.com", + ) + store.upsert_trigger_definition(_trigger(profile_id="qws-fugaku"), actor="admin@test.com") + (received_dir / "result_20260810_170000_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee.json").write_text( + json.dumps( + { + "code": "qws", + "system": "Fugaku", + "Exp": "CASE0", + "node_count": "1", + "execution_mode": "cross", + "pipeline_timing": {"run_time": 3600}, + "execution_trigger": { + "id": "qws-fugaku-time", + "type": "scheduled", + "reason": "cron:0 14 * * *", + }, + } + ), + encoding="utf-8", + ) + (received_dir / "result_20260810_180000_cccccccc-dddd-eeee-ffff-000000000000.json").write_text( + json.dumps( + { + "code": "qws", + "system": "Fugaku", + "Exp": "CASE0", + "node_count": "1", + "execution_mode": "cross", + "pipeline_timing": {"run_time": 900}, + "execution_trigger": { + "id": "qws-fugaku", + "type": "manual_button", + "reason": "manual_button:qws-fugaku", + }, + } + ), + encoding="utf-8", + ) + (received_dir / "result_20260809_170000_bbbbbbbb-cccc-dddd-eeee-ffffffffffff.json").write_text( + json.dumps( + { + "code": "qws", + "system": "Fugaku", + "Exp": "CASE0", + "node_count": "1", + "execution_mode": "cross", + "pipeline_timing": {"run_time": 1800}, + } + ), + encoding="utf-8", + ) + + overview = build_profile_usage_overview(str(received_dir), str(db_path)) + + rows = {row["profile_id"]: row for row in overview["rows"]} + assert rows["qws-fugaku"]["result_count"] == 3 + assert rows["qws-fugaku"]["node_hours"] == 1.75 + assert rows["qws-test"]["result_count"] == 1 + assert rows["qws-test"]["node_hours"] == 0.5 + assert rows["qws-test"]["latest_result"]["timestamp"] == "2026-08-09 17:00:00" + + def test_profile_usage_overview_handles_missing_db(tmp_path): overview = build_profile_usage_overview(str(tmp_path), None) diff --git a/result_server/utils/profile_usage_overview.py b/result_server/utils/profile_usage_overview.py index c1bceab..fd80751 100644 --- a/result_server/utils/profile_usage_overview.py +++ b/result_server/utils/profile_usage_overview.py @@ -155,8 +155,8 @@ def _result_matches_profile( trigger_ids: set[str], ) -> bool: trigger_id = extract_execution_trigger(result)["id"] - if trigger_id and trigger_id in trigger_ids: - return True + if trigger_id: + return trigger_id == profile.get("id") or trigger_id in trigger_ids return ( _scope_matches(profile.get("code", []), result.get("code")) and _scope_matches(profile.get("system", []), result.get("system"))