2021-06-02 01:42:40 +02:00
|
|
|
//! This contains scheduled tasks/interval tasks that are run inside of the server on a schedule
|
|
|
|
//! as background operations.
|
|
|
|
|
2022-10-01 08:08:51 +02:00
|
|
|
use std::fs;
|
|
|
|
use std::path::Path;
|
2022-12-28 08:52:25 +01:00
|
|
|
use std::str::FromStr;
|
2019-02-22 07:15:48 +01:00
|
|
|
|
2021-07-31 09:13:46 +02:00
|
|
|
use chrono::Utc;
|
2022-12-28 08:52:25 +01:00
|
|
|
use cron::Schedule;
|
2022-12-21 00:53:57 +01:00
|
|
|
|
2022-11-23 11:10:43 +01:00
|
|
|
use tokio::sync::broadcast;
|
2021-07-31 09:13:46 +02:00
|
|
|
use tokio::time::{interval, sleep, Duration};
|
2019-02-22 07:15:48 +01:00
|
|
|
|
2022-10-05 01:48:48 +02:00
|
|
|
use crate::config::OnlineBackup;
|
2022-11-23 11:10:43 +01:00
|
|
|
use crate::CoreAction;
|
2022-10-05 01:48:48 +02:00
|
|
|
|
2022-10-01 08:08:51 +02:00
|
|
|
use crate::actors::v1_read::QueryServerReadV1;
|
|
|
|
use crate::actors::v1_write::QueryServerWriteV1;
|
2022-10-05 01:48:48 +02:00
|
|
|
use kanidmd_lib::constants::PURGE_FREQUENCY;
|
|
|
|
use kanidmd_lib::event::{OnlineBackupEvent, PurgeRecycledEvent, PurgeTombstoneEvent};
|
2022-10-01 08:08:51 +02:00
|
|
|
|
2020-09-06 00:44:35 +02:00
|
|
|
pub struct IntervalActor;
|
2019-02-22 07:15:48 +01:00
|
|
|
|
2020-09-06 00:44:35 +02:00
|
|
|
impl IntervalActor {
|
2022-11-23 11:10:43 +01:00
|
|
|
pub fn start(
|
|
|
|
server: &'static QueryServerWriteV1,
|
|
|
|
mut rx: broadcast::Receiver<CoreAction>,
|
|
|
|
) -> tokio::task::JoinHandle<()> {
|
2020-09-06 00:44:35 +02:00
|
|
|
tokio::spawn(async move {
|
|
|
|
let mut inter = interval(Duration::from_secs(PURGE_FREQUENCY));
|
2022-11-23 11:10:43 +01:00
|
|
|
|
2020-09-06 00:44:35 +02:00
|
|
|
loop {
|
2022-11-23 11:10:43 +01:00
|
|
|
tokio::select! {
|
|
|
|
Ok(action) = rx.recv() => {
|
|
|
|
match action {
|
|
|
|
CoreAction::Shutdown => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ = inter.tick() => {
|
|
|
|
server
|
|
|
|
.handle_purgetombstoneevent(PurgeTombstoneEvent::new())
|
|
|
|
.await;
|
|
|
|
server
|
|
|
|
.handle_purgerecycledevent(PurgeRecycledEvent::new())
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 00:44:35 +02:00
|
|
|
}
|
2022-11-23 11:10:43 +01:00
|
|
|
|
|
|
|
info!("Stopped IntervalActor");
|
|
|
|
})
|
2019-02-22 07:15:48 +01:00
|
|
|
}
|
2021-07-31 09:13:46 +02:00
|
|
|
|
2022-09-05 15:00:48 +02:00
|
|
|
// Allow this because result is the only way to map and ? to bubble up, but we aren't
|
|
|
|
// returning an op-error here because this is in early start up.
|
|
|
|
#[allow(clippy::result_unit_err)]
|
2021-07-31 09:13:46 +02:00
|
|
|
pub fn start_online_backup(
|
|
|
|
server: &'static QueryServerReadV1,
|
|
|
|
cfg: &OnlineBackup,
|
2022-11-23 11:10:43 +01:00
|
|
|
mut rx: broadcast::Receiver<CoreAction>,
|
|
|
|
) -> Result<tokio::task::JoinHandle<()>, ()> {
|
2021-07-31 09:13:46 +02:00
|
|
|
let outpath = cfg.path.to_owned();
|
|
|
|
let versions = cfg.versions;
|
|
|
|
|
|
|
|
// Cron expression handling
|
2022-12-28 08:52:25 +01:00
|
|
|
let cron_expr = Schedule::from_str(cfg.schedule.as_str()).map_err(|e| {
|
2021-07-31 09:13:46 +02:00
|
|
|
error!("Online backup schedule parse error: {}", e);
|
2022-12-28 08:52:25 +01:00
|
|
|
error!("valid formats are:");
|
|
|
|
error!("sec min hour day of month month day of week year");
|
|
|
|
error!("@hourly | @daily | @weekly");
|
2021-07-31 09:13:46 +02:00
|
|
|
})?;
|
|
|
|
|
2022-12-28 08:52:25 +01:00
|
|
|
info!("Online backup schedule parsed as: {}", cron_expr);
|
2021-07-31 09:13:46 +02:00
|
|
|
|
2022-12-28 08:52:25 +01:00
|
|
|
if cron_expr.upcoming(Utc).next().is_none() {
|
2021-07-31 09:13:46 +02:00
|
|
|
error!(
|
|
|
|
"Online backup schedule error: '{}' will not match any date.",
|
2022-12-28 08:52:25 +01:00
|
|
|
cron_expr
|
2021-07-31 09:13:46 +02:00
|
|
|
);
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output path handling
|
|
|
|
let op = Path::new(&outpath);
|
|
|
|
|
|
|
|
// does the path exist and is a directory?
|
|
|
|
if !op.exists() {
|
|
|
|
info!(
|
|
|
|
"Online backup output folder '{}' does not exist, trying to create it.",
|
|
|
|
outpath
|
|
|
|
);
|
|
|
|
fs::create_dir_all(&outpath).map_err(|e| {
|
|
|
|
error!(
|
|
|
|
"Online backup failed to create output directory '{}': {}",
|
|
|
|
outpath.clone(),
|
|
|
|
e
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !op.is_dir() {
|
|
|
|
error!("Online backup output '{}' is not a directory or we are missing permissions to access it.", outpath);
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
2022-11-23 11:10:43 +01:00
|
|
|
let handle = tokio::spawn(async move {
|
2022-12-28 08:52:25 +01:00
|
|
|
for next_time in cron_expr.upcoming(Utc) {
|
2021-07-31 09:13:46 +02:00
|
|
|
// We add 1 second to the `wait_time` in order to get "even" timestampes
|
|
|
|
// for example: 1 + 17:05:59Z --> 17:06:00Z
|
|
|
|
let wait_seconds = 1 + (next_time - Utc::now()).num_seconds() as u64;
|
|
|
|
info!(
|
|
|
|
"Online backup next run on {}, wait_time = {}s",
|
|
|
|
next_time, wait_seconds
|
|
|
|
);
|
|
|
|
|
2022-11-23 11:10:43 +01:00
|
|
|
tokio::select! {
|
|
|
|
Ok(action) = rx.recv() => {
|
|
|
|
match action {
|
|
|
|
CoreAction::Shutdown => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ = sleep(Duration::from_secs(wait_seconds)) => {
|
|
|
|
if let Err(e) = server
|
|
|
|
.handle_online_backup(
|
|
|
|
OnlineBackupEvent::new(),
|
|
|
|
outpath.clone().as_str(),
|
|
|
|
versions,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
2023-01-10 04:50:53 +01:00
|
|
|
error!(?e, "An online backup error occurred.");
|
2022-11-23 11:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
2022-09-02 06:21:20 +02:00
|
|
|
}
|
2021-07-31 09:13:46 +02:00
|
|
|
}
|
2022-11-23 11:10:43 +01:00
|
|
|
info!("Stopped OnlineBackupActor");
|
2021-07-31 09:13:46 +02:00
|
|
|
});
|
|
|
|
|
2022-11-23 11:10:43 +01:00
|
|
|
Ok(handle)
|
2021-07-31 09:13:46 +02:00
|
|
|
}
|
2019-02-22 07:15:48 +01:00
|
|
|
}
|