159 lines
4.6 KiB
159 lines
4.6 KiB
use super::JobType; |
|
#[cfg(feature = "server")] |
|
use crate::models::schema::*; |
|
use crate::utils::Platform; |
|
use crate::{UError, UResult}; |
|
#[cfg(feature = "server")] |
|
use diesel::{Identifiable, Insertable, Queryable}; |
|
use serde::{Deserialize, Serialize}; |
|
use std::fs; |
|
use uuid::Uuid; |
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)] |
|
#[cfg_attr( |
|
feature = "server", |
|
derive(Queryable, Identifiable, Insertable, AsChangeset), |
|
table_name = "jobs" |
|
)] |
|
pub struct JobMeta { |
|
#[serde(default)] |
|
pub alias: Option<String>, |
|
|
|
/// string like `bash -c {} -a 1 --arg2`, |
|
/// where {} is replaced by executable's tmp path |
|
#[serde(default)] |
|
pub argv: String, |
|
|
|
#[serde(default = "Uuid::new_v4")] |
|
pub id: Uuid, |
|
|
|
#[serde(default)] |
|
pub exec_type: JobType, |
|
|
|
///target triple |
|
#[serde(default)] |
|
pub platform: String, |
|
|
|
#[serde(default)] |
|
pub payload: Option<Vec<u8>>, |
|
|
|
/// if payload should be read from external resource |
|
#[serde(default)] |
|
pub payload_path: Option<String>, |
|
|
|
///cron-like string |
|
#[serde(default)] |
|
pub schedule: Option<String>, |
|
} |
|
|
|
impl JobMeta { |
|
pub fn builder() -> JobMetaBuilder { |
|
JobMetaBuilder::default() |
|
} |
|
|
|
pub fn into_builder(self) -> JobMetaBuilder { |
|
JobMetaBuilder { inner: self } |
|
} |
|
|
|
pub fn from_shell(cmd: impl Into<String>) -> UResult<JobMeta> { |
|
Self::builder().with_shell(cmd).build() |
|
} |
|
} |
|
|
|
impl Default for JobMeta { |
|
fn default() -> Self { |
|
Self { |
|
id: Uuid::new_v4(), |
|
alias: None, |
|
argv: String::new(), |
|
exec_type: JobType::Shell, |
|
platform: Platform::current().into_string(), |
|
payload: None, |
|
schedule: None, |
|
payload_path: None, |
|
} |
|
} |
|
} |
|
|
|
#[derive(Default)] |
|
pub struct JobMetaBuilder { |
|
inner: JobMeta, |
|
} |
|
|
|
impl JobMetaBuilder { |
|
pub fn with_shell(mut self, shell_cmd: impl Into<String>) -> Self { |
|
self.inner.argv = shell_cmd.into(); |
|
self.inner.exec_type = JobType::Shell; |
|
self |
|
} |
|
|
|
pub fn with_payload(mut self, payload: impl Into<Vec<u8>>) -> Self { |
|
self.inner.payload = Some(payload.into()); |
|
self |
|
} |
|
|
|
pub fn with_payload_src(mut self, path: impl Into<String>) -> Self { |
|
self.inner.payload_path = Some(path.into()); |
|
self |
|
} |
|
|
|
pub fn with_alias(mut self, alias: impl Into<String>) -> Self { |
|
self.inner.alias = Some(alias.into()); |
|
self |
|
} |
|
|
|
pub fn with_type(mut self, e_type: JobType) -> Self { |
|
self.inner.exec_type = e_type; |
|
self |
|
} |
|
|
|
pub fn build(self) -> UResult<JobMeta> { |
|
let mut inner = self.inner; |
|
match inner.exec_type { |
|
JobType::Shell => { |
|
if inner.argv.is_empty() { |
|
// TODO: fix detecting |
|
inner.argv = String::from("/bin/bash -c {}") |
|
} |
|
let argv_parts = |
|
shlex::split(&inner.argv).ok_or(UError::JobArgsError("Shlex failed".into()))?; |
|
let empty_err = UError::JobArgsError("Empty argv".into()); |
|
if argv_parts.get(0).ok_or(empty_err.clone())?.is_empty() { |
|
return Err(empty_err.into()); |
|
} |
|
if let Some(path) = &inner.payload_path { |
|
let data = fs::read(path) |
|
.map_err(|e| UError::FSError(path.to_string(), e.to_string()))?; |
|
inner.payload = Some(data) |
|
} |
|
match inner.payload.as_ref() { |
|
Some(_) => { |
|
if !inner.argv.contains("{}") { |
|
return Err(UError::JobArgsError( |
|
"Argv contains no executable placeholder".into(), |
|
) |
|
.into()); |
|
} |
|
} |
|
None => { |
|
if inner.argv.contains("{}") { |
|
return Err(UError::JobArgsError( |
|
"No payload provided, but argv contains executable placeholder" |
|
.into(), |
|
) |
|
.into()); |
|
} |
|
} |
|
}; |
|
if !Platform::new(&inner.platform).check() { |
|
return Err(UError::JobArgsError(format!( |
|
"Unknown platform {}", |
|
inner.platform |
|
))); |
|
} |
|
Ok(inner.into()) |
|
} |
|
_ => Ok(inner.into()), |
|
} |
|
} |
|
}
|
|
|