Refactor ACME module and add cert issuance conflict handling

- Move acme.sh utilities (_run_acme, _find_acme, etc.) from lib/state to lib/acme
- Rewrite _parse_list_output to support pipe, tab, and column-separated formats
- Add ConflictError (409) to block issuing when cert already exists
- Move _find_issuance helper to detect in-progress issuance per domain
- Update issue_cert to check existing certs and return issuance status
- Fix start_polling to accept event loop explicitly
- Add sudoers entry for chown on vacuum-wall.conf
- Extend systemd ReadWritePaths for /run/nginx.pid and /var/log/nginx
- Update frontend to handle 'existing' issuance status
This commit is contained in:
2026-06-27 00:38:49 +00:00
parent feaf253403
commit 398831b6e2
11 changed files with 268 additions and 176 deletions
+11 -3
View File
@@ -159,6 +159,12 @@ class NotFoundError(Exception):
pass
class ConflictError(Exception):
"""Raised when a request conflicts with an existing resource."""
pass
def ok(data: Any = None) -> web.Response:
"""Create a success JSON response.
@@ -247,6 +253,8 @@ async def _handle_request(request: web.Request) -> web.Response:
result = await result
except NotFoundError as exc:
return error(str(exc), 404)
except ConflictError as exc:
return error(str(exc), 409)
except ValueError as exc:
return error(str(exc), 400)
except RuntimeError as exc:
@@ -420,10 +428,10 @@ async def _poll_loop(subsystem: str, interval: int) -> None:
await asyncio.sleep(interval)
def start_polling() -> None:
def start_polling(loop: asyncio.AbstractEventLoop) -> None:
"""Start one poll loop task per subsystem."""
for subsystem, interval in _POLL_INTERVALS.items():
task = asyncio.create_task(_poll_loop(subsystem, interval))
task = loop.create_task(_poll_loop(subsystem, interval))
task.add_done_callback(_poll_tasks.discard)
_poll_tasks.add(task)
@@ -547,7 +555,7 @@ def main() -> None:
for subsystem in state_store.SUBSYSTEMS:
if state_store.get(subsystem) is not None:
state_store.bump(subsystem)
loop.run_until_complete(start_polling())
start_polling(loop)
logger.info("vacuum-walld listening on %s", socket_path)
logger.info("WebSocket on 127.0.0.1:%d", _WS_PORT)