hivectl: say which of the three socket failures actually happened
`hivectl open forge` on a host where the daemon is fine and the socket is fine printed "could not reach the hive-c0re daemon for URLs — is hive-c0re running?". It was running. The operator was not in `hive-admin` in that shell, and the connect got EACCES. The message was a guess, not a diagnosis, because `query_hive_urls` returned `Option` and threw the cause away with `.ok()`. Three different failures — not in the group, no socket at all, nobody listening — all arrived as the same sentence, and only one of the three is fixed by looking at the daemon. Classify the connect error in `client::request`, which every daemon-assisted verb goes through, and keep the io error as the anyhow cause so the output reads fix-first. EACCES names `hive-admin`, `services.hyperhive.adminUsers`, and — the part that actually bites — the re-login, since secondary group membership is only applied at login, so a shell opened before the grant still cannot connect. ENOENT and ECONNREFUSED point at the units instead. Then stop discarding it: `query_hive_urls` returns `Result<Option<_>>`, `open` and `require_hive_domain` propagate, and `daemon_request` drops its own "connect to daemon socket" context, which only buried the actionable line under a vaguer one. `wg init`'s domain lookup stays best-effort by an explicit `.ok().flatten()` rather than by accident. Same footgun `agent_exists` was already fixed for: a permission error collapsed into a value that reads as a different, wrong story.
This commit is contained in:
parent
053bbb1bb7
commit
f108c72f25
4 changed files with 139 additions and 28 deletions
|
|
@ -24,19 +24,24 @@ const HIVE_TLS_CA_PATH: &str = "/var/lib/hive-tls/ca.pem";
|
|||
/// Best-effort query for this hive's domain from the running daemon
|
||||
/// (`HostRequest::Urls`, which reads `HYPERHIVE_HIVE_DOMAIN` from c0re's
|
||||
/// service env). `None` when the daemon is unreachable or the domain is
|
||||
/// unset — callers decide whether that's fatal.
|
||||
/// unset — for the callers that treat both as "skip the optional block".
|
||||
/// Use [`require_hive_domain`] where the distinction matters: it keeps the
|
||||
/// connect error (and its hint) instead of flattening it away.
|
||||
async fn query_hive_domain(socket: &Path) -> Option<String> {
|
||||
query_hive_urls(socket).await.and_then(|u| u.domain)
|
||||
query_hive_urls(socket).await.ok().flatten()?.domain
|
||||
}
|
||||
|
||||
/// Require this hive's domain from the daemon for snippet generation.
|
||||
/// Errors with a clear hint when it can't be resolved, so `peer-config`
|
||||
/// never silently emits a wrong key.
|
||||
/// never silently emits a wrong key. An unreachable daemon propagates its
|
||||
/// own (classified) connect error; only a *reachable* daemon with no domain
|
||||
/// gets the config hint.
|
||||
pub(crate) async fn require_hive_domain(socket: &Path) -> Result<String> {
|
||||
query_hive_domain(socket).await.context(
|
||||
"could not determine this hive's domain from the daemon — is hive-c0re running \
|
||||
and `services.hyperhive.domain` set?",
|
||||
)
|
||||
query_hive_urls(socket)
|
||||
.await
|
||||
.context("could not determine this hive's domain from the daemon")?
|
||||
.and_then(|u| u.domain)
|
||||
.context("the daemon reported no domain — set `services.hyperhive.domain`")
|
||||
}
|
||||
|
||||
/// `wg init` — generate (if absent) the hive's WireGuard key, print its
|
||||
|
|
|
|||
Loading…
Reference in a new issue