feat(api): readiness probe at /api/v1/ready (PMS-130) #68

Merged
nrupard merged 2 commits from feat/pms-130-readiness into main 2026-06-04 21:37:18 +02:00
Owner

Summary

  • Adds GET /api/v1/ready for container orchestrators. Returns {"status":"ready","checks":{"db":"ok","infisical":"ok"|"skipped"}} on 200, or {"status":"not_ready",...} with per-dependency error strings on 503.
  • DB check: SELECT 1 via the existing Database::health_check.
  • Infisical check: best-effort. Probes <base>/api/status with a 2s timeout when INFISICAL_BASE_URL is set. Skipped cleanly when unset (single-machine deployments without an Infisical sibling).
  • Legacy /health stays unchanged as the cheap liveness probe (no downstream calls).
  • New tests/readiness.rs pins both contracts; CI integration-tests step picks up the new binary.

Test plan

  • CI: fmt + clippy + compile (verified locally via the shared rust-builder image)
  • CI: cargo test --test readiness (two cases)
  • Manual: curl http://...:port/api/v1/ready returns 200 against a healthy stack; returns 503 when Postgres is down.

Closes PMS-130.

## Summary - Adds `GET /api/v1/ready` for container orchestrators. Returns `{"status":"ready","checks":{"db":"ok","infisical":"ok"|"skipped"}}` on 200, or `{"status":"not_ready",...}` with per-dependency error strings on 503. - DB check: `SELECT 1` via the existing `Database::health_check`. - Infisical check: best-effort. Probes `<base>/api/status` with a 2s timeout when `INFISICAL_BASE_URL` is set. Skipped cleanly when unset (single-machine deployments without an Infisical sibling). - Legacy `/health` stays unchanged as the cheap liveness probe (no downstream calls). - New `tests/readiness.rs` pins both contracts; CI integration-tests step picks up the new binary. ## Test plan - [ ] CI: fmt + clippy + compile (verified locally via the shared rust-builder image) - [ ] CI: `cargo test --test readiness` (two cases) - [ ] Manual: `curl http://...:port/api/v1/ready` returns 200 against a healthy stack; returns 503 when Postgres is down. Closes PMS-130.
feat(api): readiness probe at /api/v1/ready (PMS-130)
All checks were successful
Build OCI container / Build and push mokosh-api image (push) Successful in 3m5s
Check / fmt + clippy + compile + tests (pull_request) Successful in 4m17s
6174aa49a2
Adds `GET /api/v1/ready` for container orchestrators. Returns `{"status":"ready","checks":{"db":"ok","infisical":"ok"|"skipped"}}` on 200 or `{"status":"not_ready",...}` with a per-dependency error string on 503.

The DB check runs `SELECT 1` via the existing `Database::health_check`. The Infisical check is best-effort: it activates only when the operator sets `INFISICAL_BASE_URL` (so single-machine deployments without an Infisical sibling skip cleanly), pings `<base>/api/status` with a 2-second timeout, and reports the underlying error on failure. mokosh-server loads Infisical secrets at boot only, so a transient Infisical outage does not break in-flight requests; the probe still surfaces the outage because the next process restart would otherwise fail to boot.

Pairs the new endpoint with `tests/readiness.rs` (two `#[sqlx::test]` cases: `/ready` returns 200+`db:ok`+`infisical:skipped` against a fresh DB without `INFISICAL_BASE_URL`; the legacy `/health` stays plain-text `OK`) and wires the binary into the CI integration-tests step. The legacy `/health` is left untouched as the cheap liveness probe (no downstream calls).

#PMS-130
fix(api): address code-review findings on /ready (PMS-130)
All checks were successful
Check / fmt + clippy + compile + tests (pull_request) Successful in 1m22s
Create release / Create release from merged PR (pull_request) Has been skipped
Build OCI container / Build and push mokosh-api image (push) Successful in 6m10s
bbe985583f
Five concerns from the cavecrew review of #68:

- **reqwest::Client per request -> cached.** A fresh `reqwest::Client` was being built on every probe, leaking file descriptors and re-resolving DNS under repeated orchestrator polling. Captures the parsed config + the HTTP client in a `OnceLock<Option<InfisicalProbe>>` initialised on the first probe; subsequent probes reuse the connection pool.
- **Env var per request -> read once.** The `OnceLock` also pins `INFISICAL_BASE_URL` at first probe so the env read happens once per process lifetime.
- **Credential leak in error string.** `format!("{url}: {e}")` interpolated the raw env var into the response body and access log. Operators occasionally embed userinfo / query strings in the URL; now strip user/password/query/fragment via `url::Url` before storing the display form, and reference only that display form in error messages.
- **No `Cache-Control` on 503.** The probe response now sets `Cache-Control: no-store` so an intermediate proxy / CDN does not pin the failing state past the next probe.
- **Timeout > k8s default.** Dropped the Infisical probe timeout from 2s to 1s to match the Kubernetes `timeoutSeconds` default; otherwise the orchestrator times out the whole probe before the handler completes and the error body is wasted.

Adds two more `#[sqlx::test]` cases to `tests/readiness.rs` pinning the new contracts: `Cache-Control: no-store` is present on the response, and a `503` with `status:not_ready` + `checks.db: "error: ..."` is returned when the pool is closed before the request (covers the failure path the original suite missed).

#PMS-130
nrupard deleted branch feat/pms-130-readiness 2026-06-04 21:37:18 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!68
No description provided.