174 lines
5.6 KiB
Rust
174 lines
5.6 KiB
Rust
use crate::options::structs::{CustomError, Services};
|
|
use crate::utils::prcs::{is_active, is_frozen};
|
|
use log::{error, warn};
|
|
use std::net::{TcpStream, ToSocketAddrs};
|
|
use std::sync::Arc;
|
|
use tokio::sync::mpsc;
|
|
use tokio::time::{Duration, Instant};
|
|
|
|
/// # Fn `service_handler`
|
|
/// ## function to realize mechanism of current process' dep services monitoring
|
|
///
|
|
/// *input* : `&str`, `&Vec<Services>`, `Arc<mpsc::Sender<u8>>`
|
|
///
|
|
/// *output* : ()
|
|
///
|
|
/// *initiator* : fn `utils::running_handler`
|
|
///
|
|
/// *managing* : process name, ref of vec of dep services, ref counter to managing channel writer
|
|
///
|
|
/// *depends on* : fn `check_service`, fn `utils::prcs::is_active`, fn `utils::prcs::is_frozen`, fn `looped_service_connecting`
|
|
///
|
|
pub async fn service_handler(
|
|
name: &str,
|
|
services: &Vec<Services>,
|
|
tx: Arc<mpsc::Sender<u8>>,
|
|
) -> Result<(), CustomError> {
|
|
// println!("service daemon on {}", name);
|
|
for serv in services {
|
|
if check_service(&serv.hostname, &serv.port).await.is_err() {
|
|
if !is_active(name).await || is_frozen(name).await {
|
|
return Err(CustomError::Fatal);
|
|
}
|
|
error!(
|
|
"Service {}:{} is unreachable for process {}",
|
|
&serv.hostname, &serv.port, &name
|
|
);
|
|
match serv.triggers.on_lost.as_str() {
|
|
"stay" => {
|
|
tx.send(4).await.unwrap();
|
|
continue;
|
|
}
|
|
"stop" => {
|
|
if looped_service_connecting(name, serv).await.is_err() {
|
|
tx.send(5).await.unwrap();
|
|
tokio::task::yield_now().await;
|
|
return Err(CustomError::Fatal);
|
|
}
|
|
}
|
|
"hold" => {
|
|
// if is_frozen(name).await {
|
|
// return Err(CustomError::Fatal);
|
|
// }
|
|
if looped_service_connecting(name, serv).await.is_err() {
|
|
tx.send(6).await.unwrap();
|
|
tokio::task::yield_now().await;
|
|
return Err(CustomError::Fatal);
|
|
}
|
|
}
|
|
_ => {
|
|
tx.send(101).await.unwrap();
|
|
return Err(CustomError::Fatal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
|
Ok(())
|
|
}
|
|
|
|
/// # Fn `looped_service_connecting`
|
|
/// ## for service's state check in loop (with delay and restriction of attempts)
|
|
///
|
|
/// *input* : `&str`, `&Services`
|
|
///
|
|
/// *output* : Ok(()) if service now available | Err(er) if still not
|
|
///
|
|
/// *initiator* : fn `service_handler`
|
|
///
|
|
/// *managing* : process name, current service struct
|
|
///
|
|
/// *depends on* : fn `check_service`
|
|
///
|
|
async fn looped_service_connecting(name: &str, serv: &Services) -> Result<(), CustomError> {
|
|
if serv.triggers.wait == 0 {
|
|
loop {
|
|
tokio::time::sleep(Duration::from_secs(serv.triggers.delay.into())).await;
|
|
warn!(
|
|
"Attempting to connect from {} process to {}:{}",
|
|
&name, &serv.hostname, &serv.port
|
|
);
|
|
match check_service(&serv.hostname, &serv.port).await {
|
|
Ok(_) => {
|
|
log::info!(
|
|
"Successfully connected to {} from {} process!",
|
|
&serv.hostname,
|
|
&name
|
|
);
|
|
break;
|
|
}
|
|
Err(_) => {
|
|
tokio::task::yield_now().await;
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
} else {
|
|
let start = Instant::now();
|
|
while start.elapsed().as_secs() < serv.triggers.wait.into() {
|
|
tokio::time::sleep(Duration::from_secs(serv.triggers.delay.into())).await;
|
|
warn!(
|
|
"Attempting to connect from {} process to {}:{}",
|
|
&name, &serv.hostname, &serv.port
|
|
);
|
|
match check_service(&serv.hostname, &serv.port).await {
|
|
Ok(_) => {
|
|
log::info!(
|
|
"Successfully connected to {} from {} process!",
|
|
&serv.hostname,
|
|
&name
|
|
);
|
|
return Ok(());
|
|
}
|
|
Err(_) => {
|
|
tokio::task::yield_now().await;
|
|
}
|
|
}
|
|
}
|
|
Err(CustomError::Fatal)
|
|
}
|
|
}
|
|
|
|
/// # Fn `check_service`
|
|
/// ## for check current service's availiability
|
|
///
|
|
/// *input* : `&str`, `&u32`
|
|
///
|
|
/// *output* : Ok(()) if service now available | Err(er) if still not
|
|
///
|
|
/// *initiator* : fn `service_handler`, fn `looped_service_connecting`
|
|
///
|
|
/// *managing* : hostname, port
|
|
///
|
|
/// *depends on* : -
|
|
///
|
|
// ! have to be rewritten
|
|
// todo: rewrite use
|
|
async fn check_service(hostname: &str, port: &u32) -> Result<(), CustomError> {
|
|
let addr = format!("{}:{}", hostname, port);
|
|
|
|
match addr.to_socket_addrs() {
|
|
Ok(mut addrs) => {
|
|
if addrs.any(|a| TcpStream::connect_timeout(&a, Duration::new(1, 0)).is_ok()) {
|
|
Ok(())
|
|
} else {
|
|
Err(CustomError::Fatal)
|
|
}
|
|
}
|
|
Err(_) => Err(CustomError::Fatal),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod service_unittests {
|
|
use super::check_service;
|
|
#[tokio::test]
|
|
async fn check_available_service() {
|
|
assert!(check_service("ya.ru", &443).await.is_ok());
|
|
}
|
|
#[tokio::test]
|
|
async fn check_unavailable_service() {
|
|
assert!(check_service("unavailable.service", &1111).await.is_err());
|
|
}
|
|
}
|