feature: allow specifying short names for repos
This commit is contained in:
parent
e6917b13fb
commit
7a4818c60a
1 changed files with 55 additions and 24 deletions
79
src/main.rs
79
src/main.rs
|
@ -39,6 +39,7 @@ fn default_alert_duration() -> u64 {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct RepoConfig {
|
struct RepoConfig {
|
||||||
|
name: Option<String>,
|
||||||
email: String,
|
email: String,
|
||||||
// all durations below are measured in days
|
// all durations below are measured in days
|
||||||
inactivity: u64,
|
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(
|
fn is_alert_needed(
|
||||||
cfg: &RepoConfig,
|
cfg: &RepoConfig,
|
||||||
inactive_for: Duration,
|
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};
|
use lettre::{AsyncSendmailTransport, AsyncTransport, Message};
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
.from("infracoll <root@infracoll>".parse().unwrap())
|
.from("infracoll <root@infracoll>".parse().unwrap())
|
||||||
.to(email.parse()?)
|
.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!(
|
.body(format!(
|
||||||
"Alert: Repository {} has been inactive for {} days.\n",
|
"Alert: Repository {} has been inactive for {} days.\n",
|
||||||
repo,
|
repo_name(repo, name),
|
||||||
inactive.as_secs() / (3600 * 24)
|
inactive.as_secs() / (3600 * 24)
|
||||||
))
|
))
|
||||||
.unwrap();
|
.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
|
// this function can fail for reasons that depend on the user-provided 'repo' config
|
||||||
// (e.g. if it fails to parse).
|
// (e.g. if it fails to parse).
|
||||||
// So the error must not be propagated to the toplevel, which would abort the
|
// 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.
|
// alert for remaining repositories; it should instead just be reported/logged.
|
||||||
async fn check_repo(
|
async fn check_repo(client: &s3::Client, state: &mut State, repo: &str) -> eyre::Result<RepoInfo> {
|
||||||
client: &s3::Client,
|
|
||||||
state: &mut State,
|
|
||||||
repo: &str,
|
|
||||||
) -> eyre::Result<Option<AlertStatus>> {
|
|
||||||
let config = read_repo_config(client, repo).await?;
|
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 last_alert = state.last_alert(repo);
|
||||||
let alert = is_alert_needed(&config, inactivity, last_alert);
|
let alert = is_alert_needed(&config, inactivity, last_alert);
|
||||||
if alert {
|
if alert {
|
||||||
println!("Sending alert to {} about bucket {}", config.email, repo);
|
println!(
|
||||||
send_email(&config.email, repo, inactivity).await?;
|
"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);
|
state.update_last_alert(repo);
|
||||||
write_state(client, state).await?;
|
write_state(client, state).await?;
|
||||||
}
|
}
|
||||||
Ok(Some(AlertStatus {
|
Some(AlertStatus {
|
||||||
alert,
|
alert,
|
||||||
inactivity,
|
inactivity,
|
||||||
last_alert,
|
last_alert,
|
||||||
}))
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
None
|
||||||
}
|
};
|
||||||
|
Ok(RepoInfo {
|
||||||
|
name: config.name.clone(),
|
||||||
|
alert_status,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[::tokio::main]
|
#[::tokio::main]
|
||||||
|
@ -260,15 +288,18 @@ async fn main() -> eyre::Result<()> {
|
||||||
|
|
||||||
for repo in repos {
|
for repo in repos {
|
||||||
match check_repo(&client, &mut state, &repo).await {
|
match check_repo(&client, &mut state, &repo).await {
|
||||||
Ok(None) => {
|
Ok(RepoInfo {
|
||||||
println!("{}: no snapshot, skipping", &repo)
|
name,
|
||||||
}
|
alert_status: None,
|
||||||
Ok(Some(status)) => {
|
}) => println!("{}: no snapshot, skipping", repo_name(&repo, &name)),
|
||||||
println!("{}: {}", &repo, status);
|
Ok(RepoInfo {
|
||||||
}
|
name,
|
||||||
Err(err) => {
|
alert_status: Some(status),
|
||||||
// is this the best way to log the error?
|
}) => println!("{}: {}", repo_name(&repo, &name), status),
|
||||||
println!("Error ({}): {:?}", &repo, err)
|
Err(err) =>
|
||||||
|
// is this the best way to log the error?
|
||||||
|
{
|
||||||
|
println!("{}: ERROR: {:?}", &repo, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue