-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #547 from Nukesor/internal-refactor
Internal refactor
- Loading branch information
Showing
52 changed files
with
1,381 additions
and
1,496 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
use std::collections::HashMap; | ||
|
||
use chrono::{DateTime, Local}; | ||
use handlebars::{Handlebars, RenderError}; | ||
use log::{debug, error, info}; | ||
use pueue_lib::{ | ||
log::{get_log_path, read_last_log_file_lines}, | ||
process_helper::compile_shell_command, | ||
settings::Settings, | ||
task::{Task, TaskResult, TaskStatus}, | ||
}; | ||
|
||
use super::state_helper::LockedState; | ||
|
||
/// Users can specify a callback that's fired whenever a task finishes. | ||
/// Execute the callback by spawning a new subprocess. | ||
pub fn spawn_callback(settings: &Settings, state: &mut LockedState, task: &Task) { | ||
// Return early, if there's no callback specified | ||
let Some(template_string) = &settings.daemon.callback else { | ||
return; | ||
}; | ||
|
||
// Build the command to be called from the template string in the configuration file. | ||
let callback_command = match build_callback_command(settings, task, template_string) { | ||
Ok(callback_command) => callback_command, | ||
Err(err) => { | ||
error!("Failed to create callback command from template with error: {err}"); | ||
return; | ||
} | ||
}; | ||
|
||
let mut command = compile_shell_command(settings, &callback_command); | ||
|
||
// Spawn the callback subprocess and log if it fails. | ||
let spawn_result = command.spawn(); | ||
let child = match spawn_result { | ||
Err(error) => { | ||
error!("Failed to spawn callback with error: {error}"); | ||
return; | ||
} | ||
Ok(child) => child, | ||
}; | ||
|
||
debug!("Spawned callback for task {}", task.id); | ||
state.callbacks.push(child); | ||
} | ||
|
||
/// Take the callback template string from the configuration and insert all parameters from the | ||
/// finished task. | ||
pub fn build_callback_command( | ||
settings: &Settings, | ||
task: &Task, | ||
template_string: &str, | ||
) -> Result<String, RenderError> { | ||
// Init Handlebars. We set to strict, as we want to show an error on missing variables. | ||
let mut handlebars = Handlebars::new(); | ||
handlebars.set_strict_mode(true); | ||
|
||
// Add templating variables. | ||
let mut parameters = HashMap::new(); | ||
parameters.insert("id", task.id.to_string()); | ||
parameters.insert("command", task.command.clone()); | ||
parameters.insert("path", (*task.path.to_string_lossy()).to_owned()); | ||
parameters.insert("group", task.group.clone()); | ||
|
||
// Result takes the TaskResult Enum strings, unless it didn't finish yet. | ||
if let TaskStatus::Done(result) = &task.status { | ||
parameters.insert("result", result.to_string()); | ||
} else { | ||
parameters.insert("result", "None".into()); | ||
} | ||
|
||
// Format and insert start and end times. | ||
let print_time = |time: Option<DateTime<Local>>| { | ||
time.map(|time| time.timestamp().to_string()) | ||
.unwrap_or_default() | ||
}; | ||
parameters.insert("start", print_time(task.start)); | ||
parameters.insert("end", print_time(task.end)); | ||
|
||
// Read the last lines of the process' output and make it available. | ||
if let Ok(output) = read_last_log_file_lines( | ||
task.id, | ||
&settings.shared.pueue_directory(), | ||
settings.daemon.callback_log_lines, | ||
) { | ||
parameters.insert("output", output); | ||
} else { | ||
parameters.insert("output", "".to_string()); | ||
} | ||
|
||
let out_path = get_log_path(task.id, &settings.shared.pueue_directory()); | ||
// Using Display impl of PathBuf which isn't necessarily a perfect | ||
// representation of the path but should work for most cases here | ||
parameters.insert("output_path", out_path.display().to_string()); | ||
|
||
// Get the exit code | ||
if let TaskStatus::Done(result) = &task.status { | ||
match result { | ||
TaskResult::Success => parameters.insert("exit_code", "0".into()), | ||
TaskResult::Failed(code) => parameters.insert("exit_code", code.to_string()), | ||
_ => parameters.insert("exit_code", "None".into()), | ||
}; | ||
} else { | ||
parameters.insert("exit_code", "None".into()); | ||
} | ||
|
||
handlebars.render_template(template_string, ¶meters) | ||
} | ||
|
||
/// Look at all running callbacks and log any errors. | ||
/// If everything went smoothly, simply remove them from the list. | ||
pub fn check_callbacks(state: &mut LockedState) { | ||
let mut finished = Vec::new(); | ||
for (id, child) in state.callbacks.iter_mut().enumerate() { | ||
match child.try_wait() { | ||
// Handle a child error. | ||
Err(error) => { | ||
error!("Callback failed with error {error:?}"); | ||
finished.push(id); | ||
} | ||
// Child process did not exit yet. | ||
Ok(None) => continue, | ||
Ok(exit_status) => { | ||
info!("Callback finished with exit code {exit_status:?}"); | ||
finished.push(id); | ||
} | ||
} | ||
} | ||
|
||
finished.reverse(); | ||
for id in finished.iter() { | ||
state.callbacks.remove(*id); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.