review-classifier: a failed store login names its cause chain
This commit is contained in:
parent
7aa8441aa1
commit
10a6f92244
1 changed files with 63 additions and 2 deletions
|
|
@ -186,6 +186,28 @@ impl KeySource for Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `e` and each of its sources, joined by `: `. The store client's errors
|
||||||
|
/// carry only a generic message at the top; the URL and the failure kind sit
|
||||||
|
/// below it. Neither holds a token or key value.
|
||||||
|
fn chain(e: &dyn std::error::Error) -> String {
|
||||||
|
let mut out = e.to_string();
|
||||||
|
let mut next = e.source();
|
||||||
|
while let Some(cause) = next {
|
||||||
|
out.push_str(": ");
|
||||||
|
out.push_str(&cause.to_string());
|
||||||
|
next = cause.source();
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The run failure for a store login that failed with `e`.
|
||||||
|
fn login_failure(what: &str, e: &dyn std::error::Error) -> String {
|
||||||
|
format!(
|
||||||
|
"logging in to the secret store to read the {what}: {}",
|
||||||
|
chain(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// The key `field` takes from the object at `path`, trimmed; `None` when
|
/// The key `field` takes from the object at `path`, trimmed; `None` when
|
||||||
/// nothing or a blank is stored.
|
/// nothing or a blank is stored.
|
||||||
async fn read_key<T: serde::de::DeserializeOwned>(
|
async fn read_key<T: serde::de::DeserializeOwned>(
|
||||||
|
|
@ -195,11 +217,11 @@ async fn read_key<T: serde::de::DeserializeOwned>(
|
||||||
) -> Result<Option<String>, String> {
|
) -> Result<Option<String>, String> {
|
||||||
let store = swarm_secret_client::SecretStore::from_env(crate::CERT_ROLE)
|
let store = swarm_secret_client::SecretStore::from_env(crate::CERT_ROLE)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("logging in to the secret store to read the {what}: {e}"))?;
|
.map_err(|e| login_failure(what, &e))?;
|
||||||
let stored: Option<T> = store
|
let stored: Option<T> = store
|
||||||
.read_optional(path)
|
.read_optional(path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("reading secret/{path}: {e}"))?;
|
.map_err(|e| format!("reading secret/{path}: {}", chain(&e)))?;
|
||||||
Ok(stored
|
Ok(stored
|
||||||
.map(field)
|
.map(field)
|
||||||
.map(|key| key.trim().to_owned())
|
.map(|key| key.trim().to_owned())
|
||||||
|
|
@ -380,6 +402,45 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// A failure with a cause, as the store client's errors nest them.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Level(&'static str, Option<Box<Level>>);
|
||||||
|
|
||||||
|
impl std::fmt::Display for Level {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Level {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
self.1.as_deref().map(|l| l as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chain_renders_every_level() {
|
||||||
|
let e = Level(
|
||||||
|
"An error occurred with the request",
|
||||||
|
Some(Box::new(Level(
|
||||||
|
"error sending request for url (https://bao/v1/auth/cert/login)",
|
||||||
|
Some(Box::new(Level("tls handshake eof", None))),
|
||||||
|
))),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
chain(&e),
|
||||||
|
"An error occurred with the request: error sending request for url \
|
||||||
|
(https://bao/v1/auth/cert/login): tls handshake eof"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
login_failure("API key", &e),
|
||||||
|
"logging in to the secret store to read the API key: An error occurred with the \
|
||||||
|
request: error sending request for url (https://bao/v1/auth/cert/login): tls \
|
||||||
|
handshake eof"
|
||||||
|
);
|
||||||
|
assert_eq!(chain(&Level("alone", None)), "alone");
|
||||||
|
}
|
||||||
|
|
||||||
/// The API key runtime's key is `k` and the ACP runtime's `p`, or both
|
/// The API key runtime's key is `k` and the ACP runtime's `p`, or both
|
||||||
/// fail with the same error.
|
/// fail with the same error.
|
||||||
struct Key(Result<(), &'static str>);
|
struct Key(Result<(), &'static str>);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue