hive-c0re: wire routes individually — routes! macro panics on multi-path batches
This commit is contained in:
parent
423427abeb
commit
2c51fb70d0
1 changed files with 150 additions and 64 deletions
|
|
@ -141,69 +141,84 @@ pub async fn serve(
|
|||
// No static fallback — the gateway owns the dist; unmatched paths 404.
|
||||
;
|
||||
|
||||
// `routes!()` folds every handler it's given into ONE shared
|
||||
// `MethodRouter` for the whole macro invocation — it does NOT group
|
||||
// by path internally, so passing it handlers for more than one
|
||||
// distinct path panics at runtime ("Overlapping method route")
|
||||
// the moment two of them share an HTTP method, which most
|
||||
// GET-vs-GET or POST-vs-POST pairs across different paths do. Each
|
||||
// call below is therefore scoped to exactly one path — two handlers
|
||||
// in the same call only when they genuinely share a path with
|
||||
// different methods (`schedules::api_schedules`/`post_schedule_new`
|
||||
// on `/api/schedules`, `matrix_accounts::get_github_account`/
|
||||
// `post_github_account` on `/api/github-account`) — chained via
|
||||
// repeated `.routes(...)` calls instead of one giant `routes!(...)`
|
||||
// with everything in it.
|
||||
let (router, api) = OpenApiRouter::<AppState>::with_openapi(ApiDoc::openapi())
|
||||
.routes(routes!(health::get_health_live))
|
||||
.routes(routes!(health::get_health_ready))
|
||||
.routes(routes!(journal::get_journal))
|
||||
.routes(routes!(journal::get_journal_host))
|
||||
.routes(routes!(state_snapshot::api_state))
|
||||
.routes(routes!(state_files::get_state_file))
|
||||
.routes(routes!(matrix_accounts::get_matrix_accounts))
|
||||
.routes(routes!(matrix_accounts::post_matrix_account_login))
|
||||
.routes(routes!(
|
||||
health::get_health_live,
|
||||
health::get_health_ready,
|
||||
journal::get_journal,
|
||||
journal::get_journal_host,
|
||||
state_snapshot::api_state,
|
||||
state_files::get_state_file,
|
||||
matrix_accounts::get_matrix_accounts,
|
||||
matrix_accounts::post_matrix_account_login,
|
||||
matrix_accounts::post_github_account,
|
||||
matrix_accounts::get_github_account,
|
||||
extra_forges::get_extra_forges,
|
||||
extra_forges::post_extra_forge_account,
|
||||
misc_api::api_operator_inbox,
|
||||
misc_api::api_stats_hive,
|
||||
misc_api::api_container_resources,
|
||||
misc_api::api_audit_log,
|
||||
misc_api::post_mark_all_read,
|
||||
misc_api::post_request_spawn,
|
||||
misc_api::post_op_send,
|
||||
build_logs::get_build_logs_all,
|
||||
build_logs::get_build_log_for_node,
|
||||
build_logs::get_build_log_raw_for_node,
|
||||
build_logs::get_build_logs_agent,
|
||||
build_logs::get_build_log_full,
|
||||
build_logs::get_build_log_raw,
|
||||
topology::post_set_parent,
|
||||
topology::post_set_parent_bulk,
|
||||
permissions::get_tool_groups,
|
||||
permissions::post_tool_groups,
|
||||
permissions::get_capabilities,
|
||||
permissions::post_capabilities,
|
||||
permissions::post_permissions,
|
||||
permissions::get_stale_permissions,
|
||||
permissions::delete_agent_permissions,
|
||||
schedules::api_schedules,
|
||||
schedules::post_schedule_new,
|
||||
schedules::patch_schedule,
|
||||
schedules::post_schedule_cancel,
|
||||
schedules::post_schedule_pause,
|
||||
schedules::post_schedule_resume,
|
||||
schedules::post_schedule_fire_now,
|
||||
schedules::post_rebuild_queue_cancel,
|
||||
webhook::post_webhook_knowledge,
|
||||
webhook::post_webhook_config_pr,
|
||||
approvals::post_approve,
|
||||
approvals::post_deny,
|
||||
lifecycle_ops::post_destroy,
|
||||
lifecycle_ops::post_kill,
|
||||
lifecycle_ops::post_restart,
|
||||
lifecycle_ops::post_start,
|
||||
lifecycle_ops::post_rebuild,
|
||||
lifecycle_ops::post_pause,
|
||||
lifecycle_ops::post_resume,
|
||||
lifecycle_ops::post_resource_limits,
|
||||
lifecycle_ops::post_update_all,
|
||||
infra_containers::post_infra_container,
|
||||
questions::post_answer_question,
|
||||
questions::post_cancel_question,
|
||||
tombstones::post_purge_tombstone,
|
||||
meta_inputs::post_meta_update,
|
||||
matrix_accounts::get_github_account
|
||||
))
|
||||
.routes(routes!(extra_forges::get_extra_forges))
|
||||
.routes(routes!(extra_forges::post_extra_forge_account))
|
||||
.routes(routes!(misc_api::api_operator_inbox))
|
||||
.routes(routes!(misc_api::api_stats_hive))
|
||||
.routes(routes!(misc_api::api_container_resources))
|
||||
.routes(routes!(misc_api::api_audit_log))
|
||||
.routes(routes!(misc_api::post_mark_all_read))
|
||||
.routes(routes!(misc_api::post_request_spawn))
|
||||
.routes(routes!(misc_api::post_op_send))
|
||||
.routes(routes!(build_logs::get_build_logs_all))
|
||||
.routes(routes!(build_logs::get_build_log_for_node))
|
||||
.routes(routes!(build_logs::get_build_log_raw_for_node))
|
||||
.routes(routes!(build_logs::get_build_logs_agent))
|
||||
.routes(routes!(build_logs::get_build_log_full))
|
||||
.routes(routes!(build_logs::get_build_log_raw))
|
||||
.routes(routes!(topology::post_set_parent))
|
||||
.routes(routes!(topology::post_set_parent_bulk))
|
||||
.routes(routes!(permissions::get_tool_groups))
|
||||
.routes(routes!(permissions::post_tool_groups))
|
||||
.routes(routes!(permissions::get_capabilities))
|
||||
.routes(routes!(permissions::post_capabilities))
|
||||
.routes(routes!(permissions::post_permissions))
|
||||
.routes(routes!(permissions::get_stale_permissions))
|
||||
.routes(routes!(permissions::delete_agent_permissions))
|
||||
.routes(routes!(
|
||||
schedules::api_schedules,
|
||||
schedules::post_schedule_new
|
||||
))
|
||||
.routes(routes!(schedules::patch_schedule))
|
||||
.routes(routes!(schedules::post_schedule_cancel))
|
||||
.routes(routes!(schedules::post_schedule_pause))
|
||||
.routes(routes!(schedules::post_schedule_resume))
|
||||
.routes(routes!(schedules::post_schedule_fire_now))
|
||||
.routes(routes!(schedules::post_rebuild_queue_cancel))
|
||||
.routes(routes!(webhook::post_webhook_knowledge))
|
||||
.routes(routes!(webhook::post_webhook_config_pr))
|
||||
.routes(routes!(approvals::post_approve))
|
||||
.routes(routes!(approvals::post_deny))
|
||||
.routes(routes!(lifecycle_ops::post_destroy))
|
||||
.routes(routes!(lifecycle_ops::post_kill))
|
||||
.routes(routes!(lifecycle_ops::post_restart))
|
||||
.routes(routes!(lifecycle_ops::post_start))
|
||||
.routes(routes!(lifecycle_ops::post_rebuild))
|
||||
.routes(routes!(lifecycle_ops::post_pause))
|
||||
.routes(routes!(lifecycle_ops::post_resume))
|
||||
.routes(routes!(lifecycle_ops::post_resource_limits))
|
||||
.routes(routes!(lifecycle_ops::post_update_all))
|
||||
.routes(routes!(infra_containers::post_infra_container))
|
||||
.routes(routes!(questions::post_answer_question))
|
||||
.routes(routes!(questions::post_cancel_question))
|
||||
.routes(routes!(tombstones::post_purge_tombstone))
|
||||
.routes(routes!(meta_inputs::post_meta_update))
|
||||
.merge(app.into())
|
||||
.split_for_parts();
|
||||
let app = router
|
||||
|
|
@ -360,13 +375,84 @@ mod tests {
|
|||
mod router_build_probe {
|
||||
use super::*;
|
||||
|
||||
/// Regression test for the exact panic this file's `serve()` used to
|
||||
/// hit at startup: `routes!()` folds every handler passed to ONE
|
||||
/// macro invocation into a single shared `MethodRouter`, so two
|
||||
/// handlers on different paths but the same HTTP method panic with
|
||||
/// "Overlapping method route" the moment they're in the same
|
||||
/// `routes!(...)` call — see `serve()`'s comment above its own
|
||||
/// `.routes(...)` chain. This mirrors that exact chain (one
|
||||
/// `.routes()` call per distinct path, multi-method pairs grouped
|
||||
/// only where they share a path) so a future regression that
|
||||
/// accidentally merges two single-path calls back into one
|
||||
/// multi-path `routes!(...)` call fails here instead of at daemon
|
||||
/// startup.
|
||||
#[test]
|
||||
fn probe_multi_path_routes_macro_does_not_panic() {
|
||||
let _ = OpenApiRouter::<AppState>::with_openapi(ApiDoc::openapi()).routes(routes!(
|
||||
health::get_health_live,
|
||||
health::get_health_ready,
|
||||
journal::get_journal,
|
||||
journal::get_journal_host,
|
||||
));
|
||||
let _ = OpenApiRouter::<AppState>::with_openapi(ApiDoc::openapi())
|
||||
.routes(routes!(health::get_health_live))
|
||||
.routes(routes!(health::get_health_ready))
|
||||
.routes(routes!(journal::get_journal))
|
||||
.routes(routes!(journal::get_journal_host))
|
||||
.routes(routes!(state_snapshot::api_state))
|
||||
.routes(routes!(state_files::get_state_file))
|
||||
.routes(routes!(matrix_accounts::get_matrix_accounts))
|
||||
.routes(routes!(matrix_accounts::post_matrix_account_login))
|
||||
.routes(routes!(
|
||||
matrix_accounts::post_github_account,
|
||||
matrix_accounts::get_github_account
|
||||
))
|
||||
.routes(routes!(extra_forges::get_extra_forges))
|
||||
.routes(routes!(extra_forges::post_extra_forge_account))
|
||||
.routes(routes!(misc_api::api_operator_inbox))
|
||||
.routes(routes!(misc_api::api_stats_hive))
|
||||
.routes(routes!(misc_api::api_container_resources))
|
||||
.routes(routes!(misc_api::api_audit_log))
|
||||
.routes(routes!(misc_api::post_mark_all_read))
|
||||
.routes(routes!(misc_api::post_request_spawn))
|
||||
.routes(routes!(misc_api::post_op_send))
|
||||
.routes(routes!(build_logs::get_build_logs_all))
|
||||
.routes(routes!(build_logs::get_build_log_for_node))
|
||||
.routes(routes!(build_logs::get_build_log_raw_for_node))
|
||||
.routes(routes!(build_logs::get_build_logs_agent))
|
||||
.routes(routes!(build_logs::get_build_log_full))
|
||||
.routes(routes!(build_logs::get_build_log_raw))
|
||||
.routes(routes!(topology::post_set_parent))
|
||||
.routes(routes!(topology::post_set_parent_bulk))
|
||||
.routes(routes!(permissions::get_tool_groups))
|
||||
.routes(routes!(permissions::post_tool_groups))
|
||||
.routes(routes!(permissions::get_capabilities))
|
||||
.routes(routes!(permissions::post_capabilities))
|
||||
.routes(routes!(permissions::post_permissions))
|
||||
.routes(routes!(permissions::get_stale_permissions))
|
||||
.routes(routes!(permissions::delete_agent_permissions))
|
||||
.routes(routes!(
|
||||
schedules::api_schedules,
|
||||
schedules::post_schedule_new
|
||||
))
|
||||
.routes(routes!(schedules::patch_schedule))
|
||||
.routes(routes!(schedules::post_schedule_cancel))
|
||||
.routes(routes!(schedules::post_schedule_pause))
|
||||
.routes(routes!(schedules::post_schedule_resume))
|
||||
.routes(routes!(schedules::post_schedule_fire_now))
|
||||
.routes(routes!(schedules::post_rebuild_queue_cancel))
|
||||
.routes(routes!(webhook::post_webhook_knowledge))
|
||||
.routes(routes!(webhook::post_webhook_config_pr))
|
||||
.routes(routes!(approvals::post_approve))
|
||||
.routes(routes!(approvals::post_deny))
|
||||
.routes(routes!(lifecycle_ops::post_destroy))
|
||||
.routes(routes!(lifecycle_ops::post_kill))
|
||||
.routes(routes!(lifecycle_ops::post_restart))
|
||||
.routes(routes!(lifecycle_ops::post_start))
|
||||
.routes(routes!(lifecycle_ops::post_rebuild))
|
||||
.routes(routes!(lifecycle_ops::post_pause))
|
||||
.routes(routes!(lifecycle_ops::post_resume))
|
||||
.routes(routes!(lifecycle_ops::post_resource_limits))
|
||||
.routes(routes!(lifecycle_ops::post_update_all))
|
||||
.routes(routes!(infra_containers::post_infra_container))
|
||||
.routes(routes!(questions::post_answer_question))
|
||||
.routes(routes!(questions::post_cancel_question))
|
||||
.routes(routes!(tombstones::post_purge_tombstone))
|
||||
.routes(routes!(meta_inputs::post_meta_update));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue