-
Notifications
You must be signed in to change notification settings - Fork 46.7k
fix(update): restart hermes-serve systemd units alongside gateways #83595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3452,7 +3452,8 @@ def _for_each_systemd_gateway_unit( | |
| process_unit, | ||
| on_unit_timeout, | ||
| ) -> None: | ||
| """Process each ``hermes-gateway*.service`` from ``systemctl list-units``. | ||
| """Process each ``hermes-gateway*.service``/``hermes-serve*.service`` unit | ||
| from ``systemctl list-units``. | ||
|
|
||
| ``subprocess.TimeoutExpired`` raised by ``process_unit`` is isolated to | ||
| that unit via ``on_unit_timeout`` so one wedged systemctl call cannot | ||
|
|
@@ -3466,15 +3467,27 @@ def _for_each_systemd_gateway_unit( | |
| if not unit.endswith(".service"): | ||
| continue | ||
| # list-units is already pattern-filtered, but keep the name gate so a | ||
| # stray non-gateway line cannot enter the restart path. | ||
| if not unit.startswith("hermes-gateway"): | ||
| # stray non-gateway/serve line cannot enter the restart path. | ||
| if not (unit.startswith("hermes-gateway") or unit.startswith("hermes-serve")): | ||
| continue | ||
| svc_name = unit.removesuffix(".service") | ||
| try: | ||
| process_unit(svc_name) | ||
| except subprocess.TimeoutExpired as exc: | ||
| on_unit_timeout(svc_name, exc) | ||
|
|
||
| def _service_unit_supports_graceful_sigusr1_restart(svc_name: str) -> bool: | ||
| """Whether *svc_name* wires SIGUSR1 to a graceful drain-then-restart. | ||
|
|
||
| Only ``hermes-gateway*`` units run ``gateway/run.py``, which installs the | ||
| SIGUSR1 handler. ``hermes-serve*`` units (#83438) don't, so sending them | ||
| SIGUSR1 would just invoke the default terminate action and burn the full | ||
| drain budget waiting for an exit that was never graceful — go straight to | ||
| the blunt ``systemctl restart`` path for those instead. | ||
| """ | ||
| return svc_name.startswith("hermes-gateway") | ||
|
|
||
|
|
||
| def _warn_incomplete_gateway_fleet_restart(failed_units: list) -> None: | ||
| """Print an explicit incomplete-update warning for unrestarted units.""" | ||
| if not failed_units: | ||
|
|
@@ -3488,7 +3501,7 @@ def _warn_incomplete_gateway_fleet_restart(failed_units: list) -> None: | |
| seen.add(name) | ||
| ordered.append(name) | ||
| print() | ||
| print("⚠ Update incomplete — some gateway units were not restarted:") | ||
| print("⚠ Update incomplete — some units were not restarted:") | ||
| for name in ordered: | ||
| print(f" - {name}") | ||
| print(" Skipped units may still be running pre-update code (mixed") | ||
|
|
@@ -5210,7 +5223,8 @@ def _resolve_manage_cmd(scope_: str, scope_cmd_: list, svc_name_: str): | |
| externally_supervised_profiles = [] | ||
|
|
||
| # --- Systemd services (Linux) --- | ||
| # Discover all hermes-gateway* units (default + profiles) | ||
| # Discover all hermes-gateway* units (default + profiles) plus | ||
| # hermes-serve* units (the Desktop app's backend, #83438). | ||
| if supports_systemd_services(): | ||
| try: | ||
| _ensure_user_systemd_env() | ||
|
|
@@ -5227,6 +5241,7 @@ def _resolve_manage_cmd(scope_: str, scope_cmd_: list, svc_name_: str): | |
| + [ | ||
| "list-units", | ||
| "hermes-gateway*", | ||
| "hermes-serve*", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: this adds a first restart for |
||
| "--plain", | ||
| "--no-legend", | ||
| "--no-pager", | ||
|
|
@@ -5273,27 +5288,33 @@ def _restart_one_systemd_gateway_unit(svc_name: str) -> None: | |
| # The gateway's SIGUSR1 handler calls | ||
| # request_restart(via_service=True) → drain → | ||
| # exit; systemd's Restart=always respawns the unit. | ||
| # hermes-serve has no such handler (it isn't | ||
| # gateway/run.py), so skip straight to the blunt | ||
| # restart below rather than sending it an unhandled | ||
| # signal and waiting out the drain budget for | ||
| # nothing. | ||
| _main_pid = 0 | ||
| try: | ||
| _show = subprocess.run( | ||
| scope_cmd | ||
| + [ | ||
| "show", | ||
| svc_name, | ||
| "--property=MainPID", | ||
| "--value", | ||
| ], | ||
| capture_output=True, | ||
| text=True, encoding="utf-8", errors="replace", | ||
| timeout=5, | ||
| ) | ||
| _main_pid = int((_show.stdout or "").strip() or 0) | ||
| except ( | ||
| ValueError, | ||
| subprocess.TimeoutExpired, | ||
| FileNotFoundError, | ||
| ): | ||
| _main_pid = 0 | ||
| if _service_unit_supports_graceful_sigusr1_restart(svc_name): | ||
| try: | ||
| _show = subprocess.run( | ||
| scope_cmd | ||
| + [ | ||
| "show", | ||
| svc_name, | ||
| "--property=MainPID", | ||
| "--value", | ||
| ], | ||
| capture_output=True, | ||
| text=True, encoding="utf-8", errors="replace", | ||
| timeout=5, | ||
| ) | ||
| _main_pid = int((_show.stdout or "").strip() or 0) | ||
| except ( | ||
| ValueError, | ||
| subprocess.TimeoutExpired, | ||
| FileNotFoundError, | ||
| ): | ||
| _main_pid = 0 | ||
|
|
||
| _graceful_ok = False | ||
| if _main_pid > 0: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: this prefix also accepts
hermes-server.service, and thehermes-serve*systemctl pattern selects it. Consider constraining the match to the exact base unit or the hyphenated profile family (hermes-serve/hermes-serve-*) and adding a near-prefix rejection test.