Compare commits

...

3 commits

Author SHA1 Message Date
Armaël Guéneau 7068046c14 add Cargo.nix & update flake.nix accordingly 2024-04-10 15:10:44 +02:00
Armaël Guéneau 35cb6c2267 update Cargo.lock 2024-04-10 15:07:21 +02:00
Armaël Guéneau 63af6a613d more logging 2024-04-10 15:06:13 +02:00
4 changed files with 3768 additions and 48 deletions

56
Cargo.lock generated
View file

@ -218,9 +218,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]]
name = "async-trait"
version = "0.1.73"
version = "0.1.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681"
dependencies = [
"proc-macro2",
"quote",
@ -241,9 +241,9 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "aws-config"
version = "1.1.9"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297b64446175a73987cedc3c438d79b2a654d0fff96f65ff530fbe039347644c"
checksum = "48730d0b4c3d91c43d0d37168831d9fd0e065ad4a889a2ee9faf8d34c3d2804d"
dependencies = [
"aws-credential-types",
"aws-runtime",
@ -284,9 +284,9 @@ dependencies = [
[[package]]
name = "aws-runtime"
version = "1.1.8"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b13dc54b4b49f8288532334bba8f87386a40571c47c37b1304979b556dc613c8"
checksum = "c4ee6903f9d0197510eb6b44c4d86b493011d08b4992938f7b9be0333b6685aa"
dependencies = [
"aws-credential-types",
"aws-sigv4",
@ -308,9 +308,9 @@ dependencies = [
[[package]]
name = "aws-sdk-s3"
version = "1.21.0"
version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc075ffee2a40cb1590bed35d7ec953589a564e768fa91947c565425cd569269"
checksum = "644c5939c1b78097d37f3341708978d68490070d4b0f8fa91f0878678c06a7ef"
dependencies = [
"ahash",
"aws-credential-types",
@ -343,9 +343,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "019a07902c43b03167ea5df0182f0cb63fae89f9a9682c44d18cf2e4a042cb34"
checksum = "b2be5ba83b077b67a6f7a1927eb6b212bf556e33bd74b5eaa5aa6e421910803a"
dependencies = [
"aws-credential-types",
"aws-runtime",
@ -365,9 +365,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c46ee08a48a7f4eaa4ad201dcc1dd537b49c50859d14d4510e00ad9d3f9af2"
checksum = "022ca669825f841aef17b12d4354ef2b8651e4664be49f2d9ea13e4062a80c9f"
dependencies = [
"aws-credential-types",
"aws-runtime",
@ -387,9 +387,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.18.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f752ac730125ca6017f72f9db5ec1772c9ecc664f87aa7507a7d81b023c23713"
checksum = "8e4a5f5cb007347c1ab34a6d56456301dfada921fc9e57d687ecb08baddd11ff"
dependencies = [
"aws-credential-types",
"aws-runtime",
@ -600,9 +600,9 @@ dependencies = [
[[package]]
name = "aws-types"
version = "1.1.8"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dbf2f3da841a8930f159163175cf6a3d16ddde517c1b0fba7aa776822800f40"
checksum = "afb278e322f16f59630a83b6b2dc992a0b48aa74ed47b4130f193fae0053d713"
dependencies = [
"aws-credential-types",
"aws-smithy-async",
@ -701,9 +701,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.15.4"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytes"
@ -723,9 +723,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.90"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
[[package]]
name = "cfg-if"
@ -1136,9 +1136,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if",
"libc",
@ -1675,9 +1675,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
@ -2033,9 +2033,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.34"
version = "0.3.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
checksum = "ef89ece63debf11bc32d1ed8d078ac870cbeb44da02afb02a9ff135ae7ca0582"
dependencies = [
"deranged",
"num-conv",
@ -2053,9 +2053,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.17"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",

3673
Cargo.nix Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,29 @@
{
description = "restic-alarm";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
outputs = { self, nixpkgs } :
let pkgs = import nixpkgs { system = "x86_64-linux"; };
in {
defaultPackage.x86_64-linux = pkgs.rustPlatform.buildRustPackage rec {
pname = "restic-alarm";
version = "0.0.1";
src = ./.;
cargoLock.lockFile = ./Cargo.lock;
};
};
inputs = {
cargo2nix.url = "github:cargo2nix/cargo2nix/release-0.11.0";
flake-utils.follows = "cargo2nix/flake-utils";
nixpkgs.follows = "cargo2nix/nixpkgs";
};
outputs = inputs: with inputs;
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [cargo2nix.overlays.default];
};
rustPkgs = pkgs.rustBuilder.makePackageSet {
rustVersion = "1.77.2";
packageFun = import ./Cargo.nix;
};
in rec {
packages = {
restic-alarm = (rustPkgs.workspace.restic-alarm {});
default = packages.restic-alarm;
};
}
);
}

View file

@ -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<Duration>,
}
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<Opt
// (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<()> {
async fn check_repo(client: &s3::Client, state: &mut State, repo: &str) -> eyre::Result<Option<AlertStatus>> {
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?