|
|
@ -10,7 +10,7 @@ use serde::{ |
|
|
|
use uuid::Uuid; |
|
|
|
use uuid::Uuid; |
|
|
|
use guess_host_triple::guess_host_triple; |
|
|
|
use guess_host_triple::guess_host_triple; |
|
|
|
use tokio::process::Command; |
|
|
|
use tokio::process::Command; |
|
|
|
use crate::{models::schema::*, UError, UResult, UID, run_until_complete, append_tasks, append_task, wait_for_tasks}; |
|
|
|
use crate::{models::schema::*, UError, UResult, UID, Waiter, OneOrMany}; |
|
|
|
use diesel_derive_enum::DbEnum; |
|
|
|
use diesel_derive_enum::DbEnum; |
|
|
|
use diesel::{ |
|
|
|
use diesel::{ |
|
|
|
Queryable, |
|
|
|
Queryable, |
|
|
@ -53,12 +53,12 @@ pub enum JobType { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)] |
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub struct JobOutput { |
|
|
|
pub struct JobOutput<'s> { |
|
|
|
pub stdout: Vec<u8>, |
|
|
|
pub stdout: &'s [u8], |
|
|
|
pub stderr: Vec<u8>, |
|
|
|
pub stderr: &'s [u8], |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl JobOutput { |
|
|
|
impl<'s, 'src: 's> JobOutput<'s> { |
|
|
|
const STREAM_BORDER: &'static str = "***"; |
|
|
|
const STREAM_BORDER: &'static str = "***"; |
|
|
|
const STDOUT: &'static str = "STDOUT"; |
|
|
|
const STDOUT: &'static str = "STDOUT"; |
|
|
|
const STDERR: &'static str = "STDERR"; |
|
|
|
const STDERR: &'static str = "STDERR"; |
|
|
@ -73,17 +73,17 @@ impl JobOutput { |
|
|
|
|
|
|
|
|
|
|
|
pub fn new() -> Self { |
|
|
|
pub fn new() -> Self { |
|
|
|
Self { |
|
|
|
Self { |
|
|
|
stdout: Vec::new(), |
|
|
|
stdout: &[], |
|
|
|
stderr: Vec::new(), |
|
|
|
stderr: &[], |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn stdout(mut self, data: Vec<u8>) -> Self { |
|
|
|
pub fn stdout(mut self, data: &'s [u8]) -> Self { |
|
|
|
self.stdout = data; |
|
|
|
self.stdout = data; |
|
|
|
self |
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn stderr(mut self, data: Vec<u8>) -> Self { |
|
|
|
pub fn stderr(mut self, data: &'s [u8]) -> Self { |
|
|
|
self.stderr = data; |
|
|
|
self.stderr = data; |
|
|
|
self |
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
@ -92,40 +92,40 @@ impl JobOutput { |
|
|
|
let mut result: Vec<u8> = vec![]; |
|
|
|
let mut result: Vec<u8> = vec![]; |
|
|
|
if self.stdout.len() > 0 { |
|
|
|
if self.stdout.len() > 0 { |
|
|
|
result.extend(JobOutput::create_delim(JobOutput::STDOUT).into_bytes()); |
|
|
|
result.extend(JobOutput::create_delim(JobOutput::STDOUT).into_bytes()); |
|
|
|
result.extend(&self.stdout); |
|
|
|
result.extend(self.stdout); |
|
|
|
result.push(b'\n'); |
|
|
|
result.push(b'\n'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if self.stderr.len() > 0 { |
|
|
|
if self.stderr.len() > 0 { |
|
|
|
result.extend(JobOutput::create_delim(JobOutput::STDERR).into_bytes()); |
|
|
|
result.extend(JobOutput::create_delim(JobOutput::STDERR).into_bytes()); |
|
|
|
result.extend(&self.stderr); |
|
|
|
result.extend(self.stderr); |
|
|
|
result.push(b'\n'); |
|
|
|
result.push(b'\n'); |
|
|
|
} |
|
|
|
} |
|
|
|
result |
|
|
|
result |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn from_raw(raw: &[u8]) -> Option<Self> { |
|
|
|
pub fn from_raw(raw: &'src [u8]) -> Option<Self> { |
|
|
|
let raw = String::from_utf8_lossy(raw); |
|
|
|
let raw = String::from_utf8_lossy(raw); |
|
|
|
let err_header = JobOutput::create_delim(JobOutput::STDERR); |
|
|
|
let err_header = JobOutput::create_delim(JobOutput::STDERR); |
|
|
|
raw.strip_prefix(&JobOutput::create_delim(JobOutput::STDOUT)) |
|
|
|
raw.strip_prefix(&JobOutput::create_delim(JobOutput::STDOUT)) |
|
|
|
.map(|s: &str| { |
|
|
|
.map(|s: &str| { |
|
|
|
let mut parts = s.split(&err_header) |
|
|
|
let mut parts = s.split(&err_header) |
|
|
|
.map(|d| Vec::from(d.trim().as_bytes())) |
|
|
|
.map(|d| d.trim().as_bytes()) |
|
|
|
.collect::<Vec<Vec<u8>>>() |
|
|
|
.collect::<Vec<&[u8]>>() |
|
|
|
.into_iter(); |
|
|
|
.into_iter(); |
|
|
|
JobOutput::new() |
|
|
|
JobOutput::new() |
|
|
|
.stdout(parts.next().unwrap()) |
|
|
|
.stdout(parts.next().unwrap()) |
|
|
|
.stderr(parts.next().unwrap_or(vec![])) |
|
|
|
.stderr(parts.next().unwrap_or(&[])) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn into_appropriate(self) -> Vec<u8> { |
|
|
|
pub fn into_appropriate(self) -> &'s [u8] { |
|
|
|
if self.stdout.len() > 0 { |
|
|
|
if self.stdout.len() > 0 { |
|
|
|
self.stdout |
|
|
|
self.stdout |
|
|
|
} else if self.stderr.len() > 0 { |
|
|
|
} else if self.stderr.len() > 0 { |
|
|
|
self.stderr |
|
|
|
self.stderr |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
UError::Raw("No data").to_string().into_bytes() |
|
|
|
b"No data" |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -175,6 +175,8 @@ impl JobResult { |
|
|
|
inst.job_id = job_id; |
|
|
|
inst.job_id = job_id; |
|
|
|
inst |
|
|
|
inst |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//pub fn as_job_output(&self) -> JobOutput {}
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Default for JobResult { |
|
|
|
impl Default for JobResult { |
|
|
@ -238,8 +240,8 @@ impl Job { |
|
|
|
Ok(output) => { |
|
|
|
Ok(output) => { |
|
|
|
( |
|
|
|
( |
|
|
|
Some(JobOutput::new() |
|
|
|
Some(JobOutput::new() |
|
|
|
.stdout(output.stdout.to_vec()) |
|
|
|
.stdout(&output.stdout) |
|
|
|
.stderr(output.stderr.to_vec()) |
|
|
|
.stderr(&output.stderr) |
|
|
|
.multiline() |
|
|
|
.multiline() |
|
|
|
), |
|
|
|
), |
|
|
|
output.status.code() |
|
|
|
output.status.code() |
|
|
@ -262,6 +264,15 @@ impl Job { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn build_jobs<J: OneOrMany<JobMeta>>(job_metas: J) -> Waiter<_> { |
|
|
|
|
|
|
|
let prepared_jobs = job_metas.into_vec().into_iter().map(|job| { |
|
|
|
|
|
|
|
let j = Job::build(job).unwrap(); |
|
|
|
|
|
|
|
j.run() |
|
|
|
|
|
|
|
}).collect(); |
|
|
|
|
|
|
|
Waiter::new(prepared_jobs) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
pub async fn exec_jobs(jobs: Vec<JobMeta>) -> Vec<UResult<JobResult>> { |
|
|
|
pub async fn exec_jobs(jobs: Vec<JobMeta>) -> Vec<UResult<JobResult>> { |
|
|
|
let fids = exec_jobs_nowait(jobs).await.unwrap(); |
|
|
|
let fids = exec_jobs_nowait(jobs).await.unwrap(); |
|
|
|
wait_for_tasks(fids).await |
|
|
|
wait_for_tasks(fids).await |
|
|
@ -286,12 +297,12 @@ pub async fn exec_job_nowait(job_meta: JobMeta) -> UResult<Uuid> { |
|
|
|
let fid = append_task(job.run()).await; |
|
|
|
let fid = append_task(job.run()).await; |
|
|
|
Ok(fid) |
|
|
|
Ok(fid) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
#[cfg(test)] |
|
|
|
mod tests { |
|
|
|
mod tests { |
|
|
|
use super::*; |
|
|
|
use super::*; |
|
|
|
use crate::{exec_job, utils::vec_to_string, wait_for_task, append_task}; |
|
|
|
use crate::{build_jobs, utils::vec_to_string}; |
|
|
|
|
|
|
|
|
|
|
|
#[tokio::test] |
|
|
|
#[tokio::test] |
|
|
|
async fn test_is_really_async() { |
|
|
|
async fn test_is_really_async() { |
|
|
@ -299,20 +310,19 @@ mod tests { |
|
|
|
let job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)); |
|
|
|
let job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)); |
|
|
|
let sleep_jobs = vec![job.clone(), job.clone(), job.clone()]; |
|
|
|
let sleep_jobs = vec![job.clone(), job.clone(), job.clone()]; |
|
|
|
let now = SystemTime::now(); |
|
|
|
let now = SystemTime::now(); |
|
|
|
let fids = exec_jobs_nowait(sleep_jobs).await.unwrap(); |
|
|
|
let fids = build_jobs(sleep_jobs).run_until_complete().await; |
|
|
|
for f in fids.into_iter() { |
|
|
|
|
|
|
|
wait_for_task(f).await; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
assert_eq!(now.elapsed().unwrap().as_secs(), SLEEP_SECS) |
|
|
|
assert_eq!(now.elapsed().unwrap().as_secs(), SLEEP_SECS) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[tokio::test] |
|
|
|
#[tokio::test] |
|
|
|
async fn test_shell_job() -> UResult<()> { |
|
|
|
async fn test_shell_job() -> UResult<()> { |
|
|
|
let job = JobMeta::from_shell("whoami"); |
|
|
|
let job = JobMeta::from_shell("whoami"); |
|
|
|
let job_result = exec_job(job).await.unwrap(); |
|
|
|
let job_result = build_jobs(job) |
|
|
|
|
|
|
|
.run_one_until_complete() |
|
|
|
|
|
|
|
.await; |
|
|
|
let stdout = JobOutput::from_raw(&job_result.result.unwrap()).unwrap().stdout; |
|
|
|
let stdout = JobOutput::from_raw(&job_result.result.unwrap()).unwrap().stdout; |
|
|
|
assert_eq!( |
|
|
|
assert_eq!( |
|
|
|
vec_to_string(&stdout).trim(), |
|
|
|
vec_to_string(stdout).trim(), |
|
|
|
"plazmoid" |
|
|
|
"plazmoid" |
|
|
|
); |
|
|
|
); |
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
@ -323,20 +333,22 @@ mod tests { |
|
|
|
const SLEEP_SECS: u64 = 1; |
|
|
|
const SLEEP_SECS: u64 = 1; |
|
|
|
let now = SystemTime::now(); |
|
|
|
let now = SystemTime::now(); |
|
|
|
let longest_job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)); |
|
|
|
let longest_job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)); |
|
|
|
let longest_job_id = exec_job_nowait(longest_job).await.unwrap(); |
|
|
|
let longest_job = build_jobs(longest_job).spawn().await; |
|
|
|
let ls = exec_job(JobMeta::from_shell("ls")).await.unwrap(); |
|
|
|
let ls = build_jobs(JobMeta::from_shell("ls")) |
|
|
|
|
|
|
|
.run_one_until_complete() |
|
|
|
|
|
|
|
.await; |
|
|
|
assert_eq!(ls.retcode.unwrap(), 0); |
|
|
|
assert_eq!(ls.retcode.unwrap(), 0); |
|
|
|
let result = JobOutput::from_raw(&ls.result.unwrap()).unwrap(); |
|
|
|
let result = JobOutput::from_raw(&ls.result.unwrap()).unwrap(); |
|
|
|
let folders = String::from_utf8_lossy( |
|
|
|
let folders = String::from_utf8_lossy( |
|
|
|
&result.stdout |
|
|
|
&result.stdout |
|
|
|
); |
|
|
|
); |
|
|
|
let ls_subfolders = exec_jobs( |
|
|
|
let ls_subfolders = build_jobs( |
|
|
|
folders.lines().map(|f| JobMeta::from_shell(format!("ls {}", f))).collect() |
|
|
|
folders.lines().map(|f| JobMeta::from_shell(format!("ls {}", f))).collect() |
|
|
|
).await; |
|
|
|
).run_until_complete().await; |
|
|
|
for result in ls_subfolders { |
|
|
|
for result in ls_subfolders { |
|
|
|
assert_eq!(result.unwrap().retcode.unwrap(), 0); |
|
|
|
assert_eq!(result.unwrap().retcode.unwrap(), 0); |
|
|
|
} |
|
|
|
} |
|
|
|
wait_for_task(longest_job_id).await; |
|
|
|
longest_job.wait().await; |
|
|
|
assert_eq!(now.elapsed().unwrap().as_secs(), SLEEP_SECS); |
|
|
|
assert_eq!(now.elapsed().unwrap().as_secs(), SLEEP_SECS); |
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
@ -344,7 +356,7 @@ mod tests { |
|
|
|
#[tokio::test] |
|
|
|
#[tokio::test] |
|
|
|
async fn test_failing_shell_job() -> UResult<()> { |
|
|
|
async fn test_failing_shell_job() -> UResult<()> { |
|
|
|
let job = JobMeta::from_shell("lol_kek_puk"); |
|
|
|
let job = JobMeta::from_shell("lol_kek_puk"); |
|
|
|
let job_result = exec_job(job).await.unwrap(); |
|
|
|
let job_result = build_jobs(job).run_one_until_complete().await; |
|
|
|
let output = JobOutput::from_raw(&job_result.result.unwrap()); |
|
|
|
let output = JobOutput::from_raw(&job_result.result.unwrap()); |
|
|
|
assert!(output.is_none()); |
|
|
|
assert!(output.is_none()); |
|
|
|
assert!(job_result.retcode.is_none()); |
|
|
|
assert!(job_result.retcode.is_none()); |
|
|
@ -354,8 +366,8 @@ mod tests { |
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn test_to_multiline() { |
|
|
|
fn test_to_multiline() { |
|
|
|
let mut output = JobOutput::new(); |
|
|
|
let mut output = JobOutput::new(); |
|
|
|
output.stdout = b"lol".to_vec(); |
|
|
|
output.stdout = b"lol"; |
|
|
|
output.stderr = b"kek".to_vec(); |
|
|
|
output.stderr = b"kek"; |
|
|
|
assert_eq!( |
|
|
|
assert_eq!( |
|
|
|
output.multiline(), |
|
|
|
output.multiline(), |
|
|
|
String::from( |
|
|
|
String::from( |
|
|
@ -370,7 +382,7 @@ mod tests { |
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn test_to_multiline_stderr_only() { |
|
|
|
fn test_to_multiline_stderr_only() { |
|
|
|
let mut output = JobOutput::new(); |
|
|
|
let mut output = JobOutput::new(); |
|
|
|
output.stderr = b"kek".to_vec(); |
|
|
|
output.stderr = b"kek"; |
|
|
|
assert_eq!( |
|
|
|
assert_eq!( |
|
|
|
output.multiline(), |
|
|
|
output.multiline(), |
|
|
|
String::from( |
|
|
|
String::from( |
|
|
|