diff --git a/src/main.rs b/src/main.rs index 175a090..8eee7dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::convert::TryFrom; use std::time::{Duration, SystemTime}; +use std::fmt; const RESTIC_ALARM_BUCKET: &str = "restic-alarm-state"; const RESTIC_ALARM_STATE_FILE: &str = "state.toml"; @@ -51,6 +52,30 @@ struct RepoConfig { alert_duration: u64, } +struct AlertStatus { + alert: bool, + inactivity: Duration, + last_alert: Option, +} + +impl fmt::Display for AlertStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let days = |d: Duration| { + d.as_secs() / (3600 * 24) + }; + let hours = |d: Duration| { + (d.as_secs() / 3600) % 24 + }; + write!(f, "{}: inactivity: {} days, {} hours, last_alert: {}", + (if self.alert { "ALERT" } else { "no alert" }), + days(self.inactivity), hours(self.inactivity), + match self.last_alert { + None => "none".to_owned(), + Some(d) => format!("{} days, {} hours", days(d), hours(d)) + }) + } +} + fn is_alert_needed( cfg: &RepoConfig, inactive_for: Duration, @@ -200,17 +225,21 @@ async fn repo_last_snapshot(client: &s3::Client, repo: &str) -> eyre::Result eyre::Result<()> { +async fn check_repo(client: &s3::Client, state: &mut State, repo: &str) -> eyre::Result> { let config = read_repo_config(client, repo).await?; - if let Some(last_snapshot) = repo_last_snapshot(client, repo).await? { - if is_alert_needed(&config, last_snapshot, state.last_alert(repo)) { + 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, last_snapshot).await?; + send_email(&config.email, repo, inactivity).await?; } state.update_last_alert(repo); write_state(client, state).await?; + Ok(Some(AlertStatus { alert, inactivity, last_alert })) + } else { + Ok(None) } - Ok(()) } #[::tokio::main] @@ -224,17 +253,20 @@ async fn main() -> eyre::Result<()> { let client = aws_sdk_s3::Client::from_conf(config); let repos = to_watch(&client).await?; - println!("Watching repos:"); + println!("Watching {} repos", repos.len()); for r in repos.iter() { - println!("{}", r); + println!("- {}", r); } let mut state = read_state(&client).await?; println!("Current state: {:?}", &state); for repo in repos { match check_repo(&client, &mut state, &repo).await { - Ok(()) => { - println!("{}: OK", &repo); + Ok(None) => { + println!("{}: no snapshot, skipping", &repo) + }, + Ok(Some(status)) => { + println!("{}: {}", &repo, status); }, Err(err) => { // is this the best way to log the error?