use tracing::info;
pub use control::{reload, restart};
pub use logs::{LogsArgs, show_logs};
pub use status::get_status;
mod control;
mod logs;
mod status;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReportedUpdateFailure {
pub version: String,
pub reason: Option<String>,
}
impl ReportedUpdateFailure {
fn from_status(body: &serde_json::Value) -> Option<Self> {
let field = |key| body.get(key).and_then(serde_json::Value::as_str);
Some(Self {
version: field("failed_version")?.to_owned(),
reason: field("failed_reason").map(str::to_owned),
})
}
}
pub async fn fetch_update_failure(
client: &reqwest::Client,
base_url: &str,
) -> Option<ReportedUpdateFailure> {
let response = client
.get(format!("{base_url}/tasks/self-update/status"))
.send()
.await
.ok()?;
if !response.status().is_success() {
return None;
}
let body: serde_json::Value = response.json().await.ok()?;
ReportedUpdateFailure::from_status(&body)
}
pub fn default_server_addrs() -> Vec<std::net::SocketAddr> {
vec![
"[::1]:8271".parse().unwrap(),
"127.0.0.1:8271".parse().unwrap(),
]
}
pub async fn try_connect_daemon(
addrs: &[std::net::SocketAddr],
) -> miette::Result<(reqwest::Client, String)> {
let client = crate::alertd::http_client();
let mut last_error = None;
for addr in addrs {
let url = format!("http://{}", addr);
info!("trying to connect to daemon at {}", url);
let test_response = match client.get(format!("{}/status", url)).send().await {
Ok(resp) => resp,
Err(e) => {
info!("failed to connect to {}: {}", url, e);
last_error = Some(e);
continue;
}
};
if test_response.status().is_success() {
info!("connected to daemon at {}", url);
return Ok((client, url));
}
}
if let Some(err) = last_error {
Err(miette::miette!(
"failed to connect to daemon at any of {} address(es): {}",
addrs.len(),
err
))
} else {
Err(miette::miette!(
"no daemon found at any of {} address(es)",
addrs.len()
))
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn update_failure_with_reason() {
assert_eq!(
ReportedUpdateFailure::from_status(&json!({
"current": "2.2.4",
"failed_version": "2.2.5",
"failed_reason": "signature did not verify",
})),
Some(ReportedUpdateFailure {
version: "2.2.5".into(),
reason: Some("signature did not verify".into()),
})
);
}
#[test]
fn update_failure_from_a_daemon_without_reasons() {
assert_eq!(
ReportedUpdateFailure::from_status(&json!({
"current": "2.2.4",
"failed_version": "2.2.5",
})),
Some(ReportedUpdateFailure {
version: "2.2.5".into(),
reason: None,
})
);
}
#[test]
fn no_update_failure() {
assert_eq!(
ReportedUpdateFailure::from_status(&json!({
"current": "2.2.4",
"failed_version": null,
"failed_reason": null,
})),
None
);
}
}