Repository navigation
Conversation
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 12 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Security review (Opus) at 57cd4cd: approve.
LOWs are filed as #18424: retention, the missing |
✅ SSOT Configuration Compliance: Passing🎉 No new hardcoded values of either class — Known backlog in |
Thinking Path
The owner decided #17455 on 2026-10-10 (all four yes): node-major post-sync order across roles, with slm-backend last and chromadb before ai-stack, and the rule living once in
_post_sync_plan; ollama restarted once per node; the orchestrator restart usingsudo -nwith failures recorded; and a newpost_sync_action_runstable. The survey showed that nothing synced several roles on one node in one run.SyncOrchestrator.sync_node_rolehandled one (node, role) pair, its only caller wasPOST /roles/{role}/sync, and update-all's fleet stage uses Ansible, not the orchestrator. So the coordinator chose a node-level entry point (option A), which keeps the owner's ordering from being inert in the automated path. It builds on #18186's single SSH restart executor (merged as #18352, together with the #18359 command-injection fix) and is rebased onto that main.What Changed
One plan (
api/_post_sync_plan.py).ordered_post_sync_plankeeps install → schema → build → restart and adds, by unit name (_RESTART_PRECEDENCE,_RESTART_LAST_UNITS, both citing feat(sync): post-sync actions need a durable record and a node-level plan in the orchestrator #17455):autobot-chromadbrestarts beforeautobot-ai-stack;autobot-slm-backendgoes last, even when mixed into another restart group or named by a non-restart action;ollama, shared by the cpu and gpu LLM roles, restarts once through the existing unit dedupe.code_sync._COMPONENT_SERVICESand_SELF_SERVICE_NAME.POST /api/nodes/{node_id}/sync(new,api/node_post_sync.py).services/post_sync_plan_runner.execute_plan.sync_node_roledid on a post-sync failure.success=false.POST /api/code-sync/roles/{role_name}/syncnow goes through the same plan path. It uses onerun_idper call, shared across nodes, and its response keeps every field and addsrun_idandactions.sync_node_role,_run_post_sync_command,_restart_systemd_serviceand_sync_nodes_for_role.sync_role_filesandrecord_role_syncedremain.GET /api/code-sync/post-sync-runs/{run_id}(new) returns the run's rows in order, or 404.Self-restart safety. On the SLM's own node (
code_sync._get_local_slm_node_id):success = null) is committed first;run_idand every row;fire_and_forgetafterservice_restart.wait_for_response_flush(), the same flush-then-restart mechanism the Ansible self-update uses (Bug: 3 SLM frontend/backend code bugs from console log analysis #816);Decision 3 (
services/post_sync_runner.py). The restart runs through tech-debt(slm): restarting units over SSH is implemented three times with different sudo, timeout and failure handling #18186'srun_systemctl_over_sshwithuse_noninteractive_sudo=Trueand aBatchMode=yesssh base (build_service_ssh_base_cmd). The returncode is checked. A non-zero exit, a timeout or a spawn error becomes a recorded failure instead of logging "Restarted". Timeouts are env-backed (AUTOBOT_SYNC_RESTART_TIMEOUT_S, default 60, registered inenv_registry_slm.pyand inENV_VARS.md).Decision 4.
models/post_sync_action_run.pyaddsPostSyncActionRun(post_sync_action_runs): run_id, node_id, role_name, category, label, command, units, started_at, finished_at, exit_code, success (nullable, for "issued"), output_tail and error.command,output_tailanderrorgo throughautobot_shared.secret_redaction.redact_text(REDACTION_BOUNDARY.md:125), and the tail is capped at 800 characters.migrations/add_post_sync_action_runs_table.pyis idempotent (table_exists) and additive only: nothing is dropped or altered, so no data can be lost. It is appended last inMIGRATIONS.services/database.pyregisters the model forcreate_all.Rebase onto fix(slm): one SSH systemctl executor with quoted, validated units and gated service routes (#18359) #18352 (tech-debt(slm): restarting units over SSH is implemented three times with different sudo, timeout and failure handling #18186 + security(slm): remote command injection — service_name from the URL path is interpolated unquoted into an ssh 'sudo -n systemctl' shell command (reachable by admin/operator users) #18359): where the two overlap, main's hardening wins.
systemctl_remote_command(validate_unit_name+shlex.quote+--) viarun_systemctl_over_ssh. feat(sync): post-sync actions need a durable record and a node-level plan in the orchestrator #17455's own earlier unquoted builder andshell_suffixare gone (grep finds neither in the feat(sync): post-sync actions need a durable record and a node-level plan in the orchestrator #17455 modules).systemctl restart (rejected: invalid unit name), and it is never deferred as an "issued" self-restart.Test-isolation fix (found during the rebase):
test_post_sync_recording_17455.pyrun in one process withtest_colocated_managed_update_11605.pymade 11605 fail at collection (aMagicMockresponse model). The fix that matters is in 11605: its dev-host stub now also applies when a realmodelspackage carries the conftest'sMagicMockschemas. A contrast with only that change, and the helper reverted, passes. Additionally,import_modules_with_real_ormnow drops modules first imported during its swap. That is defence in depth, not the cause.Verification
migrations/*_test.py66;test_ssh_restart_*_18186.py60; injection 18359 64; code-sync-preserves-install 15; root guards (remote-command quoting, one-executor, reach ×2, size ratchet) 633. A single batched run of all 9import_modules_with_real_ormusers + 11605 + the 17455 route and plan tests: 218 passed.autobot-slm-backendtargeted set before the rebase: 144 passed. That covers the plan order, the node route, recording, the 18186 equivalence file, the 14275 structural test andmigrations/. The implementer's wider run gave 170 passed, and repo guards (router auth, mount, duplicate-route, size ratchet, one-executor) gave 130 passed.test_orchestrator_argv_has_no_dash_n_one_call_all_units_and_literal_60→…has_sudo_n_batchmode_one_call_all_units_env_backed_60_17455. tech-debt(slm): restarting units over SSH is implemented three times with different sudo, timeout and failure handling #18186 pinned the oldsudowithout-n, pending this decision. Decision 3 changes it on purpose.test_orchestrator_swallows_a_nonzero_exit_and_logs_restarted→…records_a_nonzero_exit_as_a_failure_not_restarted_17455. The swallowed exit was the defect decision 3 removes.test_code_sync_preserves_install_14275.pystructural pins follow the code intopost_sync_runner.py. The AST guard requirescode != 0(Compare against 0), and the plan-runner abort is pinned.migrate, stale docstrings.sudo -n, redaction, removed code and ratchets were confirmed correct.Depends(get_current_user)and mount onnodes_router/code_sync_router, whichmain.pyincludes withdependencies=_SM_OR_AGENT. A test asserts the dependency and the mounts.services/sync_orchestrator.pygoes 649 → 561 (under 600, entry deleted), andapi/code_sync.pygoes 5860 → 5829, in both baselines. No ceiling is raised, and no newnoqais added.Risks
sudo -nforsystemctl. A host where only interactive sudo works will record a failed restart instead of restarting silently. The other two executor callers already use-n.autobot-slm-frontend/src/types/generated/api.tsstale. The generator's exact diff is taken from theverify-generated-types-slmjob log and committed as a follow-up push, per the reviewer, never hand-written.tests/apihas 4 environmental failures, identical on the base tree, filed as test(slm): four tests/api tests fail outside CI — pricing reads the deployed .env, admin-socket fails even alone #18234.Model Used
Anthropic Claude Opus 5.5 (claude-opus-5-5), coordinating and gate review. Anthropic Claude Sonnet 5.5 (claude-sonnet-5-5) subagents for the survey and implementation.
Issue Link
Refs #17455
Not closed by this PR: the AC "the record is reachable from wherever the sync run is shown" is met at the API (the sync responses and
GET /post-sync-runs/{run_id}). The SLM fleet-view UI that shows it is the child #18235.Single-issue rationale: this is one owner decision on #17455 that spans the plan, the orchestrator, routes and the schema. Its only remaining part, the SLM UI, is its own child issue, #18235.
Changelog fragment