feat(infra): GET /v1/infra/mirror/health — the route the canvas mirror node already polls - #124
Conversation
ansible-galaxy install -p ./collections drops ~4200 vendored files into the worktree; they are a local install artifact, not source.
Salvaged from feat/local-apt-mirror (87de7eb, unmerged since May 20). deployer-ui already ships InfraNodeMirror.vue, which polls this endpoint every 30s — with no route on dev the node has always rendered offline. Changes from the original commit: - mounted under /v1 rather than /v0: this is new surface and v0 is legacy and shrinking. The UI fetch path is updated in lockstep. - uses the project structlog logger instead of stdlib logging - adds the tests the original lacked: unconfigured, healthy, air-gapped backend label, degraded, and both transport failures — a down mirror must report offline rather than 500, since the node polls on a timer
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 49e5de512e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| except httpx.ConnectError: | ||
| return JSONResponse({"status": "offline", "detail": "connection refused"}, status_code=503) | ||
| except httpx.TimeoutException: | ||
| return JSONResponse({"status": "offline", "detail": "timeout"}, status_code=503) |
There was a problem hiding this comment.
Handle all HTTPX transport failures as offline
When the mirror accepts a connection but then resets it or sends an invalid/truncated response, HTTPX raises another RequestError subtype such as ReadError or RemoteProtocolError. Neither handler catches those failures, so this polling endpoint surfaces a 500 instead of the documented offline response; catch the broader transport/request exception family while preserving the timeout detail if needed.
Useful? React with 👍 / 👎.
| return JSONResponse( | ||
| { | ||
| "status": "healthy", | ||
| "backend": "aptly" if os.getenv("APT_MIRROR_AIRGAPPED") else "acng", |
There was a problem hiding this comment.
Parse the air-gapped setting as a boolean
When deployment configuration explicitly supplies APT_MIRROR_AIRGAPPED=false or 0 rather than omitting it, os.getenv(...) returns a nonempty string and this reports the backend as aptly. Parse recognized true values, as the existing boolean settings do, so an explicitly false flag continues to identify the ACNG backend.
Useful? React with 👍 / 👎.
Salvaged from
feat/local-apt-mirror(87de7eb), which has sat unmerged since 20 May. Checked while cleaning up stale branches: there is no v1 replacement — there is no replacement at all.deployer-uidevshipssrc/components/nodes/InfraNodeMirror.vue, which polls/v0/infra/mirror/healthevery 30 seconds. No such route exists on backenddev(app/routes/infra.pydoes not exist there, and nothing referencesAPT_MIRROR_*), so the mirror node has always rendered offline — the same failure mode as the/v1/.../vms/{vmid}/configroute the UI was calling in #98.Changes from the original commit
/v1, not/v0. This is new surface, and v0 is legacy and shrinking (Bundle rename alignment: dotted names, generic/ tier, proxmox decom #113 removed a large chunk of it today). deployer-ui9ac8258updates the fetch path in lockstep, so the two land together.app.core.logging.get_logger) instead of stdliblogging.offline, not surface a 500.Authorship of the original commit is preserved.
Also in here
.gitignorenow excludescollections/—ansible-galaxy install -p ./collectionsdrops ~4200 vendored files into the worktree, and they nearly ended up committed.443 passed, ruff clean,
openapi.jsonregenerated.Once this merges,
feat/local-apt-mirrorcan be deleted; its only commit is superseded by this one.