use std::{ process::Command, time::SystemTime, cmp::PartialEq }; use serde::{ Serialize, Deserialize }; use uuid::Uuid; //use tokio::process::Command; use super::*; pub struct Job<'meta> { pub result: JobResult, pub meta: &'meta mut JobMeta, } impl<'meta> Job<'meta> { pub fn new(job_meta: &'meta mut JobMeta) -> Self { Self { result: JobResult { id: job_meta.id.clone(), state: job_meta.state.clone(), data: None, }, meta: job_meta, } } pub fn run(&mut self) { match self.meta.exec_type { JobType::Shell => { match self.meta.state { JobState::Queued | JobState::Pending => { self.meta.state = JobState::Running; }, JobState::Finished => { if self.meta.schedule == JobSchedule::Permanent { self.meta.state = JobState::Running; } else { return } }, JobState::Running => return } match &self.meta.payload { Some(box_payload) => { let payload = String::from_utf8_lossy(box_payload).into_owned(); let mut cmd_parts = payload .split(" ") .map(String::from) .collect::>() .into_iter(); let cmd = cmd_parts.nth(0).unwrap(); let args = cmd_parts.collect::>(); let result = Command::new(cmd) .args(args) .output(); self.result.data = Some(match result { Ok(output) => { if output.status.success() { Ok(output.stdout.to_vec()) } else { Err(output.stderr.to_vec()) } } Err(e) => Err(e.to_string().into_bytes()) }) } None => return } self.meta.state = JobState::Finished; }, _ => unimplemented!() } } fn into_result(self) -> JobResult { self.result } } #[derive(Serialize, Deserialize, Clone)] pub enum ManageAction { Ping, UpdateAvailable, JobsResultsRequest, Terminate } #[derive(Serialize, Deserialize, Clone, PartialEq)] pub enum JobSchedule { Once, Permanent, Terminate // to terminate jobs obvsl //TODO: Scheduled } #[derive(Serialize, Deserialize, Clone)] pub enum JobState { Queued, // server created a job, but client didn't get it yet Pending, // client got a job, but not running yet Running, // client is currently running a job // Rerunning, // if job is cycled Finished, } #[derive(Serialize, Deserialize, Clone)] pub enum JobType { Manage(ManageAction), Shell, Python, Binary } #[derive(Serialize, Deserialize, Clone)] pub struct JobMeta { pub id: Uuid, pub name: String, pub created: SystemTime, pub updated: SystemTime, pub state: JobState, pub exec_type: JobType, pub schedule: JobSchedule, pub append_result: bool, //true: append, false: rewrite pub payload: Option>>, } impl JobMeta { pub fn from_shell(shell_cmd: Vec) -> Self { let uid = Uuid::new_v4(); //let str_payload_name: &[u8] = shell_cmd.split(|b| &[*b] == b" ").collect(); //let job_name = format!("{} {}", uid.to_string()[..6], str_payload_name[0]); Self { id: uid.clone(), name: uid.to_string(), created: SystemTime::now(), updated: SystemTime::now(), state: JobState::Pending, exec_type: JobType::Shell, schedule: JobSchedule::Once, append_result: true, payload: Some(Box::new(shell_cmd)) } } pub fn from_shell_str(shell_cmd: String) -> Self { Self::from_shell(shell_cmd.into_bytes()) } } impl ToMsg for JobMeta {} #[derive(Serialize, Deserialize, Clone)] pub struct JobResult { id: Uuid, data: Option, Vec>>, state: JobState } impl ToMsg for JobResult {} #[cfg(test)] mod tests { use super::*; use crate::execute_jobs; #[test] fn test_shell_job() { let mut job = JobMeta::from_shell_str("whoami".into()); let mut jobs: Vec = vec![Job::new(&mut job)]; execute_jobs(&mut jobs); assert_eq!(jobs.pop().unwrap().result.data.unwrap().unwrap(), b"plazmoid\n".to_vec()); } }