more code organization

master
rasul 5 years ago
parent 0ab51a8dfe
commit c8b84223e7

@ -1,4 +1,4 @@
use std::process::Command;
use std::process::{Command, ExitStatus};
use crate::result::Result;
@ -40,4 +40,17 @@ impl App {
Err(e) => Err(Box::from(e)),
}
}
pub fn handle_exit(&self, status: ExitStatus) -> bool {
if status.success() {
info!("application exited: {}", &self.name);
self.restart_on_success.unwrap_or(false)
} else if let Some(code) = status.code() {
info!("application failed: {} ({})", &self.name, code);
self.restart_on_failure.unwrap_or(false)
} else {
info!("application terminated: {}", &self.name);
self.restart_on_terminate.unwrap_or(false)
}
}
}

@ -0,0 +1,111 @@
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use crate::app::App;
use crate::proc::Proc;
use crate::result::Result;
#[derive(Debug, Deserialize)]
pub struct Apps {
pub app: Vec<App>,
}
impl Apps {
pub fn load<T: Into<PathBuf>>(p: T) -> Result<Self> {
let mut file = File::open(p.into())?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
Ok(toml::from_str(&buf)?)
}
pub fn apps(&self) -> Vec<App> {
self.app.clone()
}
pub fn start(&self) -> (Vec<Proc>, Option<i8>) {
let mut procs: Vec<Proc> = Vec::new();
let mut holds: Option<i8> = None;
for app in self.apps() {
if app.wait.unwrap_or(false) {
if let Err(e) = app.wait_start() {
error!("app failed to start: {}: {:?}", &app.name, e);
}
} else {
let name = app.name.clone();
match Proc::start(app) {
Ok(proc) => {
if proc.app.hold.unwrap_or(false) {
holds = Some(holds.unwrap_or(0) + 1);
}
procs.push(proc);
}
Err(e) => error!("app failed to start: {}: {:?}", name, e),
};
}
}
(procs, holds)
}
pub fn run(&self, procs: Vec<Proc>, holds: Option<i8>) -> Result<()> {
let mut procs = procs;
let mut holds = holds;
if holds.unwrap_or(0) < 1 {
error!("no holds configured");
return Ok(());
}
while holds > Some(0) {
let mut newprocs: Vec<Proc> = Vec::new();
while let Some(mut proc) = procs.pop() {
match proc.child.try_wait() {
Ok(Some(status)) => {
let hold = proc.app.hold.unwrap_or(false);
if proc.app.handle_exit(status) {
let name = proc.app.name.clone();
match Proc::start(proc.app) {
Ok(p) => newprocs.push(p),
Err(e) => {
error!("error restarting {}: {:?}", name, e);
if hold {
holds = Some(holds.unwrap_or(0) - 1);
}
}
};
} else if hold {
holds = Some(holds.unwrap_or(0) - 1);
}
}
Ok(None) => {
if let Some(s) = &proc.check_stdout() {
for line in s.lines() {
info!("[{}] stdout: {}", &proc.app.name, line);
}
}
if let Some(s) = &proc.check_stderr() {
for line in s.lines() {
info!("[{}] stderr: {}", &proc.app.name, line);
}
}
newprocs.push(proc);
}
Err(e) => error!("error: {:?}", e),
}
}
procs = newprocs;
}
info!("shutting down");
for mut proc in procs {
info!("stopping {}", &proc.app.name);
let _ = proc.child.kill();
}
Ok(())
}
}

@ -7,144 +7,28 @@ extern crate serde_derive;
extern crate toml;
mod app;
mod apps;
mod proc;
mod result;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::process::ExitStatus;
use app::App;
use proc::Proc;
use apps::Apps;
use result::Result;
#[derive(Debug, Deserialize)]
struct Config {
app: Vec<App>,
}
impl Config {
fn load<T: Into<PathBuf>>(p: T) -> Result<Config> {
let mut file = File::open(p.into())?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
Ok(toml::from_str(&buf)?)
}
fn apps(&self) -> Vec<App> {
self.app.clone()
}
}
fn main() {
if let Err(e) = rag::init() {
println!("FATAL: Error initializing logger: {:?}", e);
std::process::exit(1);
}
if let Err(e) = run() {
if let Err(e) = startup() {
error!("FATAL: {:?}", e);
std::process::exit(1);
}
}
fn start_apps(apps: Vec<App>) -> (Vec<Proc>, Option<i8>) {
let mut procs: Vec<Proc> = Vec::new();
let mut holds: Option<i8> = None;
for app in apps {
if app.wait.unwrap_or(false) {
if let Err(e) = app.wait_start() {
error!("app failed to start: {}: {:?}", &app.name, e);
}
} else {
let name = app.name.clone();
match Proc::start(app) {
Ok(proc) => {
if proc.app.hold.unwrap_or(false) {
holds = Some(holds.unwrap_or(0) + 1);
}
procs.push(proc);
}
Err(e) => error!("app failed to start: {}: {:?}", name, e),
};
}
}
(procs, holds)
}
fn run() -> Result<()> {
let config = Config::load("sup.toml")?;
info!("loaded config: sup.toml");
let (mut procs, mut holds) = start_apps(config.apps());
if holds.unwrap_or(0) < 1 {
error!("no holds configured");
return Ok(());
}
while holds > Some(0) {
let mut newprocs: Vec<Proc> = Vec::new();
while let Some(mut proc) = procs.pop() {
match proc.child.try_wait() {
Ok(Some(status)) => {
let hold = proc.app.hold.unwrap_or(false);
if handle_exit(&proc.app, status) {
let name = proc.app.name.clone();
match Proc::start(proc.app) {
Ok(p) => newprocs.push(p),
Err(e) => {
error!("error restarting {}: {:?}", name, e);
if hold {
holds = Some(holds.unwrap_or(0) - 1);
}
}
};
} else if hold {
holds = Some(holds.unwrap_or(0) - 1);
}
}
Ok(None) => {
if let Some(s) = &proc.check_stdout() {
for line in s.lines() {
info!("[{}] stdout: {}", &proc.app.name, line);
}
}
if let Some(s) = &proc.check_stderr() {
for line in s.lines() {
info!("[{}] stderr: {}", &proc.app.name, line);
}
}
newprocs.push(proc);
}
Err(e) => error!("error: {:?}", e),
}
}
procs = newprocs;
}
info!("shutting down");
for mut proc in procs {
info!("stopping {}", &proc.app.name);
let _ = proc.child.kill();
}
fn startup() -> Result<()> {
let apps = Apps::load("sup.toml")?;
let (procs, holds) = apps.start();
apps.run(procs, holds)?;
Ok(())
}
fn handle_exit(app: &App, status: ExitStatus) -> bool {
if status.success() {
info!("application exited: {}", &app.name);
app.restart_on_success.unwrap_or(false)
} else if let Some(code) = status.code() {
info!("application failed: {} ({})", &app.name, code);
app.restart_on_failure.unwrap_or(false)
} else {
info!("application terminated: {}", &app.name);
app.restart_on_terminate.unwrap_or(false)
}
}

Loading…
Cancel
Save