commit e232cf29ca1418cc54143de326ccbbc49ab51784 Author: prplV Date: Thu Jun 27 17:17:36 2024 -0400 0.1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0e813b6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,89 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "runner-rs" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ae13396 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "runner-rs" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.203", features = ["derive"] } +serde_json = "1.0.118" diff --git a/service-checker.sh b/service-checker.sh new file mode 100644 index 0000000..223ee28 --- /dev/null +++ b/service-checker.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +hostname=$1 +port=$2 + + if nc -z -w1 $hostname $port > /dev/null 2>&1; then + echo "Service $hostname:$port is running" + exit 0 + else + echo "Service $hostname:$port is unreachable" + exit 1 + fi + diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..4d37d8a --- /dev/null +++ b/settings.json @@ -0,0 +1,22 @@ +{ + "processes" : [{ + "name" : "web-server", + "path" : "/home/vladislav/web/web-server", + "dependencies" : { + "files" : [ + { + "filename" : "control-file", + "src" : "/home/vladislav/web/" + }, + { + "filename" : "config-file", + "src" : "/home/vladislav/web/" + } + ], + "services" : [{ + "hostname" : "ya.ru", + "port" : 443 + }] + } + }] +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1973ec3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,130 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{self, error}; +use std::fs; +use std::path::Path; +use std::process::Command; + +enum CustomError { + Fatal, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Processes { + #[serde(default)] + processes : Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +struct TrackingProcess { + name : String, + path : String, + dependencies: Dependencies, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Dependencies { + #[serde(default)] + files : Vec, + #[serde(default)] + services: Vec, +} +#[derive(Debug, Serialize, Deserialize)] +struct Files { + filename : String, + src : String, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Services { + hostname : String, + port : u32, +} + +fn main() { + let processes = load_processes("settings.json"); + let mut error_counter = 0; + + if processes.processes.len() == 0 { + eprintln!("Error: Processes list is null, runner-rs initialization is stopped"); + return; + } + + processes.processes.iter().for_each(|proc| { + println!("\nProcess '{}' on stage:\n{}\nDepends on {} file(s), {} service(s)\n", + proc.name, + proc.path, + proc.dependencies.files.len(), + proc.dependencies.services.len()); + + proc.dependencies.files.iter().for_each(|file| { + if let Err(_) = check_file(&file.filename, &file.src) { + println!("Error: Process {} cannot run without file {}{}", proc.name, file.src, file.filename); + error_counter += 1; + } + }); + proc.dependencies.services.iter().for_each(|ser| { + if let Err(_) = check_service(&ser.hostname, &ser.port) { + println!("Error: Process {} cannot run while service {}:{} is down", proc.name, ser.hostname, ser.port); + error_counter += 1; + } + }); + + if (error_counter > 0) { + return; + } + + match start_process(&proc.name, &proc.path) { + Ok(_) => { + println!("Success!"); + }, + Err(_) => { + println!("Error: Cannot run process"); + }, + } + return; + }); +} +fn load_processes(json_filename: &str) -> Processes{ + let read = fs::read_to_string(json_filename).expect(format!("Missing '{}' file. Cannot start runner", json_filename).as_str()); + serde_json::from_str::(&read).expect(format!("Parsing error in '{}' file. Cannot start runner", json_filename).as_str()) +} +fn start_process(name: &str, path: &str) -> Result<(), CustomError> { + let runsh = format!("{}{}", path, "/run.sh"); + let mut command = Command::new("bash"); + command.arg(runsh); + + match command.spawn() { + Ok(mut child) => { + println!("Process {} is running now!", name); + child.wait(); + return Ok(()); + }, + Err(_) => return Err(CustomError::Fatal), + } +} +fn check_file(filename: &str, path: &str) -> Result<(), CustomError> { + let fileconcat = format!("{}{}", path, filename); + let path = Path::new(&fileconcat); + if path.exists() { + Ok(()) + } else { + Err(CustomError::Fatal) + } +} +fn check_service(host: &str, port: &u32) -> Result<(), CustomError> { + let mut command = Command::new("bash"); + command.args(["service-checker.sh", host, &port.to_string()]); + + match command.output() { + Ok(output) => { + if (output.status.success()) { + return Ok(()); + } else { + return Err(CustomError::Fatal); + } + }, + Err(_) => { + return Err(CustomError::Fatal); + }, + }; +} \ No newline at end of file