files restoring mechanism
parent
262b739c0c
commit
d7f39d8a99
|
|
@ -11,4 +11,6 @@ NOXIS_REMOTE_SERVER_URL = "ip.ip.ip.ip:port"
|
||||||
NOXIS_CONFIG_PATH = "./settings.json"
|
NOXIS_CONFIG_PATH = "./settings.json"
|
||||||
NOXIS_METRICS_MODE = "full"
|
NOXIS_METRICS_MODE = "full"
|
||||||
NOXIS_SOCKET_PATH = "/path/to/noxis.sock"
|
NOXIS_SOCKET_PATH = "/path/to/noxis.sock"
|
||||||
|
NOXIS_BACKUP_FOLDER = "/path/to/backups/folder"
|
||||||
|
|
||||||
NOXIS_MAX_LOG_LEVEL = "TRACE"
|
NOXIS_MAX_LOG_LEVEL = "TRACE"
|
||||||
|
|
@ -22,3 +22,4 @@ futures = "0.3.31"
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] }
|
crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
ulid = "1.2.1"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,35 @@
|
||||||
"src": "./tests/examples/",
|
"src": "./tests/examples/",
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"onDelete": "stop",
|
"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,
|
"wait": 2,
|
||||||
"onLost": "stop"
|
"onLost": "stop"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hostname": "8.8.8.8",
|
||||||
|
"port": 443,
|
||||||
|
"triggers": {
|
||||||
|
"wait": 2,
|
||||||
|
"onLost": "stop"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
handler.push(ctrlc);
|
handler.push(ctrlc);
|
||||||
|
|
||||||
let tx_bus = tx_to_bus.clone();
|
let tx_bus = tx_to_bus.clone();
|
||||||
|
let preboot_cli = preboot.clone();
|
||||||
let monitoring = tokio::spawn(async move {
|
let monitoring = tokio::spawn(async move {
|
||||||
let config = {
|
let config = {
|
||||||
let mut tick = tokio::time::interval(Duration::from_millis(500));
|
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);
|
error!("Monitoring mod failed due to {}", er);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ pub struct PrebootParams {
|
||||||
pub config: PathBuf,
|
pub config: PathBuf,
|
||||||
pub metrics: MetricsPrebootParams,
|
pub metrics: MetricsPrebootParams,
|
||||||
pub self_socket: PathBuf,
|
pub self_socket: PathBuf,
|
||||||
|
pub backup_folder: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # implementation for `MetricsPrebootParams`
|
/// # 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -470,6 +470,8 @@ pub struct FileTriggers {
|
||||||
pub on_delete: String,
|
pub on_delete: String,
|
||||||
#[serde(rename = "onChange")]
|
#[serde(rename = "onChange")]
|
||||||
pub on_change: String,
|
pub on_change: String,
|
||||||
|
#[serde(rename = "doRestore")]
|
||||||
|
pub do_restore: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Metrics struct
|
/// # Metrics struct
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ lazy_static! {
|
||||||
|
|
||||||
pub mod v2 {
|
pub mod v2 {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::options::preboot::PrebootParams;
|
||||||
use crate::utils::metrics::processes::{ProcessesAll, ProcessesQuery};
|
use crate::utils::metrics::processes::{ProcessesAll, ProcessesQuery};
|
||||||
use crate::{
|
use crate::{
|
||||||
options::structs::{
|
options::structs::{
|
||||||
|
|
@ -65,10 +66,14 @@ pub mod v2 {
|
||||||
bus: (bus_reciever, bus_sender),
|
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);
|
self.config = Arc::from(config);
|
||||||
let _ = self.config.processes.iter().for_each(|prc| {
|
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);
|
let temp = ProcessesController::new(&prc.name, tx).with_exe(&prc.path);
|
||||||
if !self.prcs.contains(&temp) {
|
if !self.prcs.contains(&temp) {
|
||||||
self.prcs.push_back(temp);
|
self.prcs.push_back(temp);
|
||||||
|
|
@ -84,8 +89,21 @@ pub mod v2 {
|
||||||
};
|
};
|
||||||
hm.insert(proc_name.clone(), (triggers, rx.clone()));
|
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 =
|
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 Ok(file) = tempfile {
|
||||||
if let Some(current_file) = self.files.iter_mut().find(|a| &&file == a) {
|
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(),
|
name: file.filename.to_string(),
|
||||||
path: file.src.to_string(),
|
path: file.src.to_string(),
|
||||||
status: file_cont.get_state(),
|
status: file_cont.get_state(),
|
||||||
|
backup_file : file_cont.get_backup_file(),
|
||||||
triggers: file.triggers.to_owned(),
|
triggers: file.triggers.to_owned(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -351,11 +370,12 @@ pub mod v2 {
|
||||||
|
|
||||||
pub async fn init_monitoring(
|
pub async fn init_monitoring(
|
||||||
config: Processes,
|
config: Processes,
|
||||||
|
preboot : Arc<PrebootParams>,
|
||||||
bus_reciever: BusReciever,
|
bus_reciever: BusReciever,
|
||||||
bus_sender: BusSender,
|
bus_sender: BusSender,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut supervisor = Supervisor::new(bus_reciever, bus_sender)
|
let mut supervisor = Supervisor::new(bus_reciever, bus_sender)
|
||||||
.with_config(config)
|
.with_config(config, preboot)
|
||||||
.await;
|
.await;
|
||||||
info!("Monitoring: {} ", &supervisor.get_stats());
|
info!("Monitoring: {} ", &supervisor.get_stats());
|
||||||
supervisor.process().await;
|
supervisor.process().await;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ pub mod v2 {
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
path: String,
|
path: String,
|
||||||
code_name: Arc<str>,
|
code_name: Arc<str>,
|
||||||
|
backup_file : String,
|
||||||
state: FileState,
|
state: FileState,
|
||||||
watcher: Option<Inotify>,
|
watcher: Option<Inotify>,
|
||||||
triggers: EventHandlers,
|
triggers: EventHandlers,
|
||||||
|
|
@ -51,10 +52,11 @@ pub mod v2 {
|
||||||
watcher: None,
|
watcher: None,
|
||||||
triggers,
|
triggers,
|
||||||
code_name: name.clone(),
|
code_name: name.clone(),
|
||||||
|
backup_file: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[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.path = path.as_ref().to_string_lossy().into_owned();
|
||||||
self.watcher = {
|
self.watcher = {
|
||||||
match create_watcher(&self.name, &self.path) {
|
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.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)
|
Ok(self)
|
||||||
}
|
}
|
||||||
pub fn add_event(&mut self, file_controller: FilesController) {
|
pub fn add_event(&mut self, file_controller: FilesController) {
|
||||||
|
|
@ -97,6 +104,9 @@ pub mod v2 {
|
||||||
pub fn get_code_name(&self) -> Arc<str> {
|
pub fn get_code_name(&self) -> Arc<str> {
|
||||||
self.code_name.clone()
|
self.code_name.clone()
|
||||||
}
|
}
|
||||||
|
pub fn get_backup_file(&self) -> String {
|
||||||
|
self.backup_file.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ProcessUnit for FilesController {
|
impl ProcessUnit for FilesController {
|
||||||
|
|
@ -120,9 +130,24 @@ pub mod v2 {
|
||||||
a || mask.mask == EventMask::DELETE_SELF,
|
a || mask.mask == EventMask::DELETE_SELF,
|
||||||
b || mask.mask == EventMask::MODIFY,
|
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);
|
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 {
|
if recreate_watcher {
|
||||||
self.watcher = match create_watcher(&self.name, &self.path) {
|
self.watcher = match create_watcher(&self.name, &self.path) {
|
||||||
Ok(notifier) => Some(notifier),
|
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 {
|
} else {
|
||||||
if let FileState::Ok = self.state {
|
if let FileState::Ok = self.state {
|
||||||
|
if self.backup_file.is_empty() {
|
||||||
warn!(
|
warn!(
|
||||||
"File {} ({}) was not found in determined scope",
|
"File {} ({}) was not found in determined scope",
|
||||||
self.name, &self.path
|
self.name, &self.path
|
||||||
);
|
);
|
||||||
self.state = FileState::NotFound;
|
self.state = FileState::NotFound;
|
||||||
self.trigger_on(Some(FileTriggerType::OnDelete)).await;
|
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;
|
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`
|
/// # Fn `create_watcher`
|
||||||
/// ## for creating watcher on file's delete | update events
|
/// ## for creating watcher on file's delete | update events
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
options::structs::ProcessState,
|
options::structs::ProcessState,
|
||||||
utils::metrics::processes::{ProcessesGeneral, ProcessesQuery},
|
utils::metrics::processes::{ProcessesGeneral, ProcessesQuery},
|
||||||
};
|
};
|
||||||
use chrono::Datelike;
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use noxis_cli::metrics_models::MetricsMode;
|
use noxis_cli::metrics_models::MetricsMode;
|
||||||
use std::{any::Any, collections::BTreeMap, sync::Arc};
|
use std::{any::Any, collections::BTreeMap, sync::Arc};
|
||||||
|
|
@ -327,6 +326,7 @@ pub mod processes {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub status: FileState,
|
pub status: FileState,
|
||||||
|
pub backup_file : String,
|
||||||
pub triggers: FileTriggers,
|
pub triggers: FileTriggers,
|
||||||
}
|
}
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue