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,9 +24,10 @@ pub(crate) async fn daemon_request(
|
|||
req: hive_host_sock::HostRequest,
|
||||
label: &str,
|
||||
) -> Result<()> {
|
||||
let resp = crate::client::request(socket, req)
|
||||
.await
|
||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||
// No extra context on the request: `client::request` already names the
|
||||
// socket and classifies the failure, and wrapping it here would put a
|
||||
// vaguer line on top of the actionable one.
|
||||
let resp = crate::client::request(socket, req).await?;
|
||||
if !resp.ok {
|
||||
bail!(
|
||||
"{label}: {}",
|
||||
|
|
@ -100,13 +101,19 @@ pub(crate) fn render_lifecycle(resp: &hive_host_sock::HostResponse, verb: &str)
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Best-effort query for this hive's domain + browser-facing web URLs
|
||||
/// (`HostRequest::Urls`). `None` when the daemon is unreachable.
|
||||
pub(crate) async fn query_hive_urls(socket: &Path) -> Option<hive_host_sock::HiveUrls> {
|
||||
crate::client::request(socket, hive_host_sock::HostRequest::Urls)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|r| r.urls)
|
||||
/// Query this hive's domain + browser-facing web URLs (`HostRequest::Urls`).
|
||||
///
|
||||
/// `Ok(None)` = the daemon answered and has nothing to report; `Err` = it was
|
||||
/// never reached, and the error carries the actionable connect hint (see
|
||||
/// [`crate::client::request`]). Keep the two apart: collapsing the error into
|
||||
/// `None` here is what made a permission problem on the socket read as "is
|
||||
/// hive-c0re running?", sending the operator to fix the wrong thing.
|
||||
pub(crate) async fn query_hive_urls(socket: &Path) -> Result<Option<hive_host_sock::HiveUrls>> {
|
||||
Ok(
|
||||
crate::client::request(socket, hive_host_sock::HostRequest::Urls)
|
||||
.await?
|
||||
.urls,
|
||||
)
|
||||
}
|
||||
|
||||
/// True when `name` matches an existing hyperhive agent — i.e. it has a
|
||||
|
|
|
|||
Loading…
Reference in a new issue