swarm-controller: add configured default matrix homeserver URL
Adds services.hyperhive.deploy.swarm-controller.matrixHomeserverUrl, threaded to the daemon as SWARM_CONTROLLER_MATRIX_HOMESERVER_URL, and a Rust helper (homeserver_or_configured_default) that lets a caller-supplied homeserver keep overriding it. Config plumbing only: put_matrix_account does not call the helper yet, so this is a no-op for every current caller. Refs #4345
This commit is contained in:
parent
f4cabd117b
commit
c894192e4f
3 changed files with 95 additions and 2 deletions
|
|
@ -192,6 +192,15 @@ let
|
|||
SWARM_CONTROLLER_AUTH_BRIDGE_URL = deployCfg.swarm-controller.authBridgeUrl;
|
||||
};
|
||||
|
||||
# Swarm-wide default for `PUT .../matrix-accounts/{account}`'s own
|
||||
# `homeserver` field, for a request that omits one. Same shape as
|
||||
# `authBridgeEnv` above: genuinely optional, gated on the option
|
||||
# resolving rather than assumed. Not read by anything yet — see the
|
||||
# option's own description.
|
||||
matrixHomeserverEnv = lib.optionalAttrs (deployCfg.swarm-controller.matrixHomeserverUrl != null) {
|
||||
SWARM_CONTROLLER_MATRIX_HOMESERVER_URL = deployCfg.swarm-controller.matrixHomeserverUrl;
|
||||
};
|
||||
|
||||
# The swarm's own display name, for `GET /api/swarm` (swarm-ui's chrome).
|
||||
# Gated on the option resolving, not defaulted to an empty string: an
|
||||
# operator who never named the swarm gets `name: null` from the
|
||||
|
|
@ -520,6 +529,24 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
matrixHomeserverUrl = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "https://matrix.example.org";
|
||||
description = ''
|
||||
Swarm-wide default homeserver for `PUT
|
||||
.../matrix-accounts/{account}` requests that omit their own
|
||||
`homeserver` — see that route's own doc comment
|
||||
(`swarm-controller/src/matrix_account.rs`) for why the field is
|
||||
optional in token mode and what omitting it currently resolves to.
|
||||
|
||||
`null` (the default) leaves that per-request resolution exactly as
|
||||
it is today. **Not yet consulted by the route at all**: this option
|
||||
only exists to carry the value in, ahead of the route being taught
|
||||
to fall back to it.
|
||||
'';
|
||||
};
|
||||
|
||||
baoClientCertFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
|
|
@ -859,6 +886,7 @@ in
|
|||
// webhookEnv
|
||||
// authBridgeEnv
|
||||
// swarmNameEnv
|
||||
// matrixHomeserverEnv
|
||||
// baoEnv
|
||||
// otelEnv;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1621,6 +1621,17 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
};
|
||||
|
||||
// Diagnostic only — `put_matrix_account` does not call this yet (see its
|
||||
// own doc comment), so this cannot change any request's outcome. Logged
|
||||
// so an operator can tell the config landed before the slice that
|
||||
// consults it does.
|
||||
if matrix_account::homeserver_or_configured_default(None).is_none() {
|
||||
tracing::debug!(
|
||||
"{}; matrix-account requests still need their own homeserver",
|
||||
matrix_account::DEFAULT_HOMESERVER_ENV
|
||||
);
|
||||
}
|
||||
|
||||
let config_prs = forge_client.clone().map(config_pr::spawn);
|
||||
let state_forge = keep_forge_for_state(forge_client, webhook_secret.clone());
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,23 @@ fn default_mode() -> String {
|
|||
"token".to_owned()
|
||||
}
|
||||
|
||||
/// Env var the controller's NixOS module sets from
|
||||
/// `services.hyperhive.deploy.swarm-controller.matrixHomeserverUrl` — the
|
||||
/// swarm-wide default `PutMatrixAccountRequest::homeserver` falls back to
|
||||
/// when a caller omits one.
|
||||
///
|
||||
/// Not yet consulted by [`put_matrix_account`]: `homeserver_or_configured_default`
|
||||
/// below exists for a later slice of hyperhive#4345 to call; this one only
|
||||
/// wires the config through.
|
||||
pub(crate) const DEFAULT_HOMESERVER_ENV: &str = "SWARM_CONTROLLER_MATRIX_HOMESERVER_URL";
|
||||
|
||||
/// `caller`'s own homeserver, or [`DEFAULT_HOMESERVER_ENV`] when the caller
|
||||
/// left it unset. `caller` always wins — this only fills a gap it left,
|
||||
/// never replaces a value it gave.
|
||||
pub(crate) fn homeserver_or_configured_default(caller: Option<String>) -> Option<String> {
|
||||
caller.or_else(|| std::env::var(DEFAULT_HOMESERVER_ENV).ok())
|
||||
}
|
||||
|
||||
/// The credential to store for one agent's external matrix account.
|
||||
///
|
||||
/// No `Debug` derive — matching `hive-c0re::dashboard::matrix_accounts`'s
|
||||
|
|
@ -339,8 +356,8 @@ async fn matrix_password_login(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
PutMatrixAccountRequest, is_reserved_account, password_fields, resolve_credential,
|
||||
token_credential,
|
||||
DEFAULT_HOMESERVER_ENV, PutMatrixAccountRequest, homeserver_or_configured_default,
|
||||
is_reserved_account, password_fields, resolve_credential, token_credential,
|
||||
};
|
||||
|
||||
fn request(mode: &str) -> PutMatrixAccountRequest {
|
||||
|
|
@ -353,6 +370,43 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// SAFETY: single-threaded mutation of a process env var no other test
|
||||
/// in this crate reads; restored (removed) before returning.
|
||||
#[test]
|
||||
fn caller_supplied_homeserver_wins_even_with_a_default_configured() {
|
||||
unsafe {
|
||||
std::env::set_var(DEFAULT_HOMESERVER_ENV, "https://default.example.org");
|
||||
}
|
||||
let result =
|
||||
homeserver_or_configured_default(Some("https://caller.example.org".to_owned()));
|
||||
unsafe {
|
||||
std::env::remove_var(DEFAULT_HOMESERVER_ENV);
|
||||
}
|
||||
assert_eq!(result.as_deref(), Some("https://caller.example.org"));
|
||||
}
|
||||
|
||||
/// SAFETY: same as above.
|
||||
#[test]
|
||||
fn falls_back_to_the_configured_default_when_the_caller_omits_one() {
|
||||
unsafe {
|
||||
std::env::set_var(DEFAULT_HOMESERVER_ENV, "https://default.example.org");
|
||||
}
|
||||
let result = homeserver_or_configured_default(None);
|
||||
unsafe {
|
||||
std::env::remove_var(DEFAULT_HOMESERVER_ENV);
|
||||
}
|
||||
assert_eq!(result.as_deref(), Some("https://default.example.org"));
|
||||
}
|
||||
|
||||
/// SAFETY: same as above.
|
||||
#[test]
|
||||
fn none_when_neither_caller_nor_default_is_set() {
|
||||
unsafe {
|
||||
std::env::remove_var(DEFAULT_HOMESERVER_ENV);
|
||||
}
|
||||
assert_eq!(homeserver_or_configured_default(None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn main_is_reserved_but_nothing_else_is() {
|
||||
assert!(is_reserved_account("main"));
|
||||
|
|
|
|||
Loading…
Reference in a new issue