integration-module/crates/api-grub/src/config.rs

180 lines
5.2 KiB
Rust

// mod to communicate with api-grub config file
// 1) check changes in unix-socket
// 2) save changes in local config file
use anyhow::{Error, Ok, Result};
use log::{info, warn, error};
use std::{fs, path::Path};
use serde_json::from_str;
use tokio::{io::AsyncReadExt, net::UnixListener};
use tokio::time::{sleep, Duration};
use std::result::Result::Ok as stdOk;
use tokio::sync::mpsc::Sender;
use integr_structs::api::v3::Config;
const CONFIG_PATH: &str = "config_api.json";
const SOCKET_PATH: &str = "api-grub.sock";
// TODO: rewrite to use current_exe
/// # Fn `pull_local_config`
///
/// ## function to one-time pulling local config by straight reading
///
/// ### Dev-Info :
///
/// *input* : -
///
/// *output* : `anyhow::Result<integr_structs::api::v3::Config>`
///
/// *initiator* : fn `main`
///
/// *managing* : -
///
pub async fn pull_local_config() -> Result<Config> {
let path = Path::new(CONFIG_PATH);
if path.exists() && path.is_file() {
let config: Config = from_str(
&fs::read_to_string(CONFIG_PATH)?
)?;
Ok(config)
} else {
Err(Error::msg("No local conf was found"))
}
}
// for config pulling
/// # Fn `pull_local_config`
///
/// ## function to init Unix-Socket listening for grabbing new configs
///
/// ### Dev-Info :
///
/// *input* : `&tokio::sync::mpsc::Sender<integr_structs::api::v3::Config>`
///
/// *output* : `anyhow::Result<()>`
///
/// *initiator* : fn `main`
///
/// *managing* : -
///
/// *depends on* : `const SOCKET_PATH`
///
pub async fn init_config_grub_mechanism(tx: &Sender<Config>) -> Result<()> {
info!("Initializing Unix-Socket listening for pulling new configs...");
let server = init_unix_listener().await?;
info!("Listening Unix-Socket...");
let mut buffer = String::new();
loop {
if let stdOk((mut stream, _)) = server.accept().await {
if let Err(er) = stream.read_to_string(&mut buffer).await {
warn!("Cannot read config from stream due to {}", er);
} else {
let config: Result<Config, serde_json::Error> = from_str(&buffer);
if let stdOk(conf) = config {
info!("New config was pulled from Unix-Stream. Saving it locally and sharing with API-grub module...");
if let Err(er) = save_new_config(&buffer).await {
error!("Cannot save new config locally due to: {}", er);
}
let _ = tx.send(conf).await;
} else if let Err(er) = config {
warn!("Invalid config was pulled. Error: {}", er);
}
}
}
sleep(Duration::from_millis(500)).await;
}
}
// saving new config locally
/// # Fn `save_new_config`
///
/// ## function for saving new config locally
///
/// ### Dev-Info :
///
/// *input* : `&String | &str`
///
/// *output* : `anyhow::Result<()>`
///
/// *initiator* : fn `main`
///
/// *managing* : -
///
/// *depends on* : `const CONFIG_PATH`
///
async fn save_new_config(config: &String) -> Result<()> {
fs::write(CONFIG_PATH, config)?;
Ok(())
}
/// # Fn `pull_local_config`
///
/// ## function for saving new config locally
///
/// ### Dev-Info :
///
/// *input* : `&tokio::sync::mpsc::Sender<Config>`
///
/// *output* : `anyhow::Result<tokio::net::UnixListener>`
///
/// *initiator* : fn `init_config_grub_mechanism`
///
/// *managing* : -
///
/// *depends on* : `const SOCKET_PATH`
///
async fn init_unix_listener() -> Result<UnixListener> {
let _ = fs::remove_file(SOCKET_PATH);
Ok(UnixListener::bind(SOCKET_PATH)?)
}
#[cfg(test)]
mod config_unittests {
use super::*;
use tokio::test;
#[test]
async fn check_init_unix_listener() {
let res = init_unix_listener().await;
if res.is_ok() {
assert!(fs::remove_file(SOCKET_PATH).is_ok())
} else {
assert!(false)
}
}
#[test]
async fn check_save_new_config() {
use std::fs;
use integr_structs::api::v3::Config;
use serde_json::to_string;
let test_config_path = "test_config_api.json";
// config gen
let config = to_string::<Config>(&Config::default());
assert!(config.is_ok());
let config = config.unwrap();
// config file gen and write
assert!(fs::File::create(test_config_path).is_ok());
assert!(fs::write(test_config_path, config).is_ok());
// config file reading and checking content
let file = fs::read_to_string(test_config_path);
assert!(file.is_ok());
let file = file.unwrap();
assert_ne!(file.len(), 0);
// deleting test config file
assert!(fs::remove_file(test_config_path).is_ok())
}
#[test]
async fn check_pull_local_config() {
use std::path::Path;
let local_config = Path::new(CONFIG_PATH);
assert_eq!(local_config.is_file() && local_config.exists(), pull_local_config().await.is_ok())
}
}