feature: allow specifying short names for repos

This commit is contained in:
Armaël Guéneau 2024-04-10 19:39:50 +02:00
parent e6917b13fb
commit 7a4818c60a

View file

@ -39,6 +39,7 @@ fn default_alert_duration() -> u64 {
#[derive(Serialize, Deserialize)]
struct RepoConfig {
name: Option<String>,
email: String,
// all durations below are measured in days
inactivity: u64,
@ -72,6 +73,13 @@ impl fmt::Display for AlertStatus {
}
}
fn repo_name(repo: &str, name: &Option<String>) -> String {
match name {
None => repo.to_owned(),
Some(short_name) => format!("{} ({})", short_name, repo),
}
}
fn is_alert_needed(
cfg: &RepoConfig,
inactive_for: Duration,
@ -86,15 +94,26 @@ fn is_alert_needed(
}
}
async fn send_email(email: &str, repo: &str, inactive: Duration) -> eyre::Result<()> {
async fn send_email(
name: &Option<String>,
email: &str,
repo: &str,
inactive: Duration,
) -> eyre::Result<()> {
use lettre::{AsyncSendmailTransport, AsyncTransport, Message};
let email = Message::builder()
.from("infracoll <root@infracoll>".parse().unwrap())
.to(email.parse()?)
.subject(format!("restic-alarm: inactive repository {}", repo))
.subject(format!(
"restic-alarm: inactive repository {}",
match name {
None => repo,
Some(name) => name,
}
))
.body(format!(
"Alert: Repository {} has been inactive for {} days.\n",
repo,
repo_name(repo, name),
inactive.as_secs() / (3600 * 24)
))
.unwrap();
@ -217,33 +236,42 @@ async fn repo_last_snapshot(client: &s3::Client, repo: &str) -> eyre::Result<Opt
}
}
struct RepoInfo {
name: Option<String>,
alert_status: Option<AlertStatus>,
}
// this function can fail for reasons that depend on the user-provided 'repo' config
// (e.g. if it fails to parse).
// So the error must not be propagated to the toplevel, which would abort the
// alert for remaining repositories; it should instead just be reported/logged.
async fn check_repo(
client: &s3::Client,
state: &mut State,
repo: &str,
) -> eyre::Result<Option<AlertStatus>> {
async fn check_repo(client: &s3::Client, state: &mut State, repo: &str) -> eyre::Result<RepoInfo> {
let config = read_repo_config(client, repo).await?;
if let Some(inactivity) = repo_last_snapshot(client, repo).await? {
let alert_status = if let Some(inactivity) = repo_last_snapshot(client, repo).await? {
let last_alert = state.last_alert(repo);
let alert = is_alert_needed(&config, inactivity, last_alert);
if alert {
println!("Sending alert to {} about bucket {}", config.email, repo);
send_email(&config.email, repo, inactivity).await?;
println!(
"Sending alert to {} about repo {}",
config.email,
repo_name(repo, &config.name)
);
send_email(&config.name, &config.email, repo, inactivity).await?;
state.update_last_alert(repo);
write_state(client, state).await?;
}
Ok(Some(AlertStatus {
Some(AlertStatus {
alert,
inactivity,
last_alert,
}))
})
} else {
Ok(None)
}
None
};
Ok(RepoInfo {
name: config.name.clone(),
alert_status,
})
}
#[::tokio::main]
@ -260,15 +288,18 @@ async fn main() -> eyre::Result<()> {
for repo in repos {
match check_repo(&client, &mut state, &repo).await {
Ok(None) => {
println!("{}: no snapshot, skipping", &repo)
}
Ok(Some(status)) => {
println!("{}: {}", &repo, status);
}
Err(err) => {
// is this the best way to log the error?
println!("Error ({}): {:?}", &repo, err)
Ok(RepoInfo {
name,
alert_status: None,
}) => println!("{}: no snapshot, skipping", repo_name(&repo, &name)),
Ok(RepoInfo {
name,
alert_status: Some(status),
}) => println!("{}: {}", repo_name(&repo, &name), status),
Err(err) =>
// is this the best way to log the error?
{
println!("{}: ERROR: {:?}", &repo, err)
}
}
}