files restoring mechanism

migrate
prplV 2025-06-05 13:55:31 +03:00
parent 262b739c0c
commit d7f39d8a99
9 changed files with 162 additions and 18 deletions

View File

@ -11,4 +11,6 @@ NOXIS_REMOTE_SERVER_URL = "ip.ip.ip.ip:port"
NOXIS_CONFIG_PATH = "./settings.json"
NOXIS_METRICS_MODE = "full"
NOXIS_SOCKET_PATH = "/path/to/noxis.sock"
NOXIS_BACKUP_FOLDER = "/path/to/backups/folder"
NOXIS_MAX_LOG_LEVEL = "TRACE"

View File

@ -22,3 +22,4 @@ futures = "0.3.31"
async-trait = "0.1.88"
crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] }
lazy_static = "1.5.0"
ulid = "1.2.1"

View File

@ -11,7 +11,35 @@
"src": "./tests/examples/",
"triggers": {
"onDelete": "stop",
"onChange": "restart"
"onChange": "restart",
"doRestore" : true
}
},
{
"filename": "none.json",
"src": "./tests/examples/",
"triggers": {
"onDelete": "stop",
"onChange": "restart",
"doRestore" : false
}
},
{
"filename": "invalid_config.json",
"src": "./tests/examples/",
"triggers": {
"onDelete": "stop",
"onChange": "restart",
"doRestore" : false
}
},
{
"filename": "save-conf.json",
"src": "./tests/examples/",
"triggers": {
"onDelete": "stop",
"onChange": "restart",
"doRestore" : true
}
}
],
@ -23,6 +51,14 @@
"wait": 2,
"onLost": "stop"
}
},
{
"hostname": "8.8.8.8",
"port": 443,
"triggers": {
"wait": 2,
"onLost": "stop"
}
}
]
}

View File

@ -108,6 +108,7 @@ async fn main() -> anyhow::Result<()> {
handler.push(ctrlc);
let tx_bus = tx_to_bus.clone();
let preboot_cli = preboot.clone();
let monitoring = tokio::spawn(async move {
let config = {
let mut tick = tokio::time::interval(Duration::from_millis(500));
@ -119,7 +120,7 @@ async fn main() -> anyhow::Result<()> {
};
}
};
if let Err(er) = init_monitoring(config, rx_to_supervisor, tx_bus).await {
if let Err(er) = init_monitoring(config, preboot_cli, rx_to_supervisor, tx_bus).await {
error!("Monitoring mod failed due to {}", er);
}
});

View File

@ -189,6 +189,7 @@ pub struct PrebootParams {
pub config: PathBuf,
pub metrics: MetricsPrebootParams,
pub self_socket: PathBuf,
pub backup_folder: PathBuf,
}
/// # implementation for `MetricsPrebootParams`
@ -267,6 +268,29 @@ impl PrebootParams {
}
}
},
backup_folder: {
match var("NOXIS_BACKUP_FOLDER") {
Ok(val) => {
let path = PathBuf::from(val);
if path.exists() && path.is_dir() {
path
} else {
PathBuf::from(std::env::current_dir()
.expect("Crushed on getting current_dir path. Check fs state!")
)
}
},
Err(_) => {
let default = std::env::current_dir()
.expect("Crushed on getting current_dir path. Check fs state!");
warn!(
"$NOXIS_BACKUP_FOLDER wans't set. Default value - {}",
default.display()
);
PathBuf::from(default)
}
}
},
}
}
}

View File

@ -470,6 +470,8 @@ pub struct FileTriggers {
pub on_delete: String,
#[serde(rename = "onChange")]
pub on_change: String,
#[serde(rename = "doRestore")]
pub do_restore: bool,
}
/// # Metrics struct

View File

@ -26,6 +26,7 @@ lazy_static! {
pub mod v2 {
use super::*;
use crate::options::preboot::PrebootParams;
use crate::utils::metrics::processes::{ProcessesAll, ProcessesQuery};
use crate::{
options::structs::{
@ -65,10 +66,14 @@ pub mod v2 {
bus: (bus_reciever, bus_sender),
}
}
pub async fn with_config(mut self, config: Processes) -> Supervisor {
pub async fn with_config(
mut self,
config: Processes,
preboot : Arc<PrebootParams>
) -> Supervisor {
self.config = Arc::from(config);
let _ = self.config.processes.iter().for_each(|prc| {
let (rx, tx) = mpsc::channel::<Events>(10);
let (rx, tx) = mpsc::channel::<Events>(100);
let temp = ProcessesController::new(&prc.name, tx).with_exe(&prc.path);
if !self.prcs.contains(&temp) {
self.prcs.push_back(temp);
@ -84,8 +89,21 @@ pub mod v2 {
};
hm.insert(proc_name.clone(), (triggers, rx.clone()));
let backup_file = {
if file.triggers.do_restore {
use ulid::Ulid;
format!("{}{}.bak", {
let path = preboot.backup_folder.to_string_lossy();
if path.ends_with("/") { path.to_string() }
else { format!("{}/", path) }
}, Ulid::new())
} else {
String::new()
}
};
let tempfile =
FilesController::new(&file.filename.as_str(), hm).with_path(&file.src);
FilesController::new(&file.filename.as_str(), hm).with_path(&file.src, backup_file);
if let Ok(file) = tempfile {
if let Some(current_file) = self.files.iter_mut().find(|a| &&file == a) {
@ -164,6 +182,7 @@ pub mod v2 {
name: file.filename.to_string(),
path: file.src.to_string(),
status: file_cont.get_state(),
backup_file : file_cont.get_backup_file(),
triggers: file.triggers.to_owned(),
});
}
@ -351,11 +370,12 @@ pub mod v2 {
pub async fn init_monitoring(
config: Processes,
preboot : Arc<PrebootParams>,
bus_reciever: BusReciever,
bus_sender: BusSender,
) -> anyhow::Result<()> {
let mut supervisor = Supervisor::new(bus_reciever, bus_sender)
.with_config(config)
.with_config(config, preboot)
.await;
info!("Monitoring: {} ", &supervisor.get_stats());
supervisor.process().await;

View File

@ -29,6 +29,7 @@ pub mod v2 {
name: Arc<str>,
path: String,
code_name: Arc<str>,
backup_file : String,
state: FileState,
watcher: Option<Inotify>,
triggers: EventHandlers,
@ -51,10 +52,11 @@ pub mod v2 {
watcher: None,
triggers,
code_name: name.clone(),
backup_file: String::new(),
}
}
#[inline(always)]
pub fn with_path(mut self, path: impl AsRef<Path>) -> anyhow::Result<FilesController> {
pub fn with_path(mut self, path: impl AsRef<Path>, backup : String) -> anyhow::Result<FilesController> {
self.path = path.as_ref().to_string_lossy().into_owned();
self.watcher = {
match create_watcher(&self.name, &self.path) {
@ -69,6 +71,11 @@ pub mod v2 {
}
};
self.code_name = Arc::from(format!("{}{}", &self.path, &self.code_name));
self.backup_file = backup;
match create_backup(&self.code_name, &self.backup_file) {
Ok(_) => info!("Backup file for {} was created ({})", &self.code_name, &self.backup_file),
Err(er) => warn!("{}. Ignoring ...", er),
}
Ok(self)
}
pub fn add_event(&mut self, file_controller: FilesController) {
@ -97,6 +104,9 @@ pub mod v2 {
pub fn get_code_name(&self) -> Arc<str> {
self.code_name.clone()
}
pub fn get_backup_file(&self) -> String {
self.backup_file.to_string()
}
}
#[async_trait]
impl ProcessUnit for FilesController {
@ -120,9 +130,24 @@ pub mod v2 {
a || mask.mask == EventMask::DELETE_SELF,
b || mask.mask == EventMask::MODIFY,
)
});
if let (recreate_watcher, true) = (need_to_recreate, was_modifired) {
}
);
if self.backup_file.is_empty() {
} else {
}
if let (mut recreate_watcher, true) = (need_to_recreate, was_modifired) {
if self.backup_file.is_empty() {
warn!("File {} ({}) was changed", self.name, &self.path);
self.trigger_on(Some(FileTriggerType::OnChange)).await;
} else {
recreate_watcher = true;
match restore_file(&self.code_name, &self.backup_file).await {
Ok(_) => info!("File {} was successfully restored", &self.code_name),
Err(er) => error!("Cannot restore file {} : {}", &self.code_name, er),
}
}
if recreate_watcher {
self.watcher = match create_watcher(&self.name, &self.path) {
Ok(notifier) => Some(notifier),
@ -135,21 +160,42 @@ pub mod v2 {
}
}
}
self.trigger_on(Some(FileTriggerType::OnChange)).await;
return;
}
}
}
None => { /* DEAD END */ }
None => return,
}
} else {
if let FileState::Ok = self.state {
if self.backup_file.is_empty() {
warn!(
"File {} ({}) was not found in determined scope",
self.name, &self.path
);
self.state = FileState::NotFound;
self.trigger_on(Some(FileTriggerType::OnDelete)).await;
} else {
warn!(
"File {} ({}) was not found in determined scope. Restoring from backup-file ...",
self.name, &self.path
);
match restore_file(&self.code_name, &self.backup_file).await {
Err(er) => error!("Cannot restore file {} : {}", &self.code_name, er),
Ok(_) => {
info!("File {} was successfully restored", &self.code_name);
self.watcher = match create_watcher(&self.name, &self.path) {
Ok(notifier) => Some(notifier),
Err(er) => {
error!(
"Failed to recreate watcher for {} ({}) : {}",
self.name, &self.path, er
);
None
}
}
},
}
}
}
return;
}
@ -158,6 +204,18 @@ pub mod v2 {
}
}
pub fn create_backup(target: &str, backup: &str) -> anyhow::Result<u64> {
return if !backup.is_empty() {
Ok(std::fs::copy(target, backup)?)
} else {
Err(anyhow::Error::msg(format!("No need to create backup-file for {}", target)))
}
}
pub async fn restore_file(target: &str, backup: &str) -> anyhow::Result<u64> {
Ok(tokio::fs::copy(backup, target).await?)
}
/// # Fn `create_watcher`
/// ## for creating watcher on file's delete | update events
///

View File

@ -5,7 +5,6 @@ use crate::{
options::structs::ProcessState,
utils::metrics::processes::{ProcessesGeneral, ProcessesQuery},
};
use chrono::Datelike;
use log::warn;
use noxis_cli::metrics_models::MetricsMode;
use std::{any::Any, collections::BTreeMap, sync::Arc};
@ -327,6 +326,7 @@ pub mod processes {
pub name: String,
pub path: String,
pub status: FileState,
pub backup_file : String,
pub triggers: FileTriggers,
}
#[derive(Debug, serde::Serialize)]