clippy refactorings

pull/1/head
plazmoid 3 years ago
parent ac20f1b343
commit 700154e311
  1. 9
      Makefile.toml
  2. 6
      bin/u_agent/src/lib.rs
  3. 1
      bin/u_agent/src/main.rs
  4. 3
      bin/u_panel/Cargo.toml
  5. 137
      bin/u_panel/src/argparse.rs
  6. 140
      bin/u_panel/src/main.rs
  7. 23
      bin/u_panel/src/server/app.rs
  8. 3
      bin/u_panel/src/server/mod.rs
  9. 8
      bin/u_server/src/db.rs
  10. 4
      bin/u_server/src/filters.rs
  11. 14
      bin/u_server/src/handlers.rs
  12. 1
      bin/u_server/src/u_server.rs
  13. 4
      integration/get_docker_ip.sh
  14. 14
      integration/tests/helpers/panel.rs
  15. 16
      integration/tests/tests.rs
  16. 2
      lib/u_api_proc_macro/src/lib.rs
  17. 2
      lib/u_lib/src/executor.rs
  18. 5
      lib/u_lib/src/messaging/base.rs
  19. 2
      lib/u_lib/src/models/agent.rs
  20. 27
      lib/u_lib/src/models/jobs/assigned.rs
  21. 16
      lib/u_lib/src/models/jobs/meta.rs
  22. 149
      lib/u_lib/src/models/jobs/misc.rs
  23. 2
      lib/u_lib/src/utils/combined_result.rs
  24. 1
      lib/u_lib/src/utils/fmt/stripped.rs
  25. 2
      lib/u_lib/src/utils/mod.rs
  26. 163
      lib/u_lib/src/utils/proc_output.rs
  27. 2
      scripts/build_musl_libs.sh

@ -17,15 +17,10 @@ script = "./scripts/build_musl_libs.sh"
command = "${CARGO}" command = "${CARGO}"
args = ["clean"] args = ["clean"]
[tasks.debug] [tasks.build]
dependencies = ["build_static_libs"] dependencies = ["build_static_libs"]
command = "${CARGO}" command = "${CARGO}"
args = ["build", "--target", "${TARGET}","${@}"] args = ["build", "--target", "${TARGET}", "${@}"]
[tasks.release]
dependencies = ["build_static_libs"]
command = "${CARGO}"
args = ["build", "--target", "${TARGET}", "--release", "${@}"]
[tasks.run] [tasks.run]
script = ''' script = '''

@ -27,7 +27,7 @@ use u_lib::{
const ITERATION_LATENCY: u64 = 5; const ITERATION_LATENCY: u64 = 5;
pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHandler) { pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHandler) {
if job_requests.len() > 0 { if !job_requests.is_empty() {
for jr in &job_requests { for jr in &job_requests {
if !JobCache::contains(&jr.job_id) { if !JobCache::contains(&jr.job_id) {
debug!("Fetching job: {}", &jr.job_id); debug!("Fetching job: {}", &jr.job_id);
@ -53,7 +53,7 @@ pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHand
); );
let mut builder = JobBuilder::from_request(job_requests); let mut builder = JobBuilder::from_request(job_requests);
let errors = builder.pop_errors(); let errors = builder.pop_errors();
if errors.len() > 0 { if !errors.is_empty() {
errors.into_iter().for_each(ErrChan::send) errors.into_iter().for_each(ErrChan::send)
} }
builder.unwrap_one().spawn().await; builder.unwrap_one().spawn().await;
@ -86,7 +86,7 @@ async fn do_stuff(client: Arc<ClientHandler>) -> ! {
Err(err) => ErrChan::send(err), Err(err) => ErrChan::send(err),
} }
let result: Vec<Reportable> = pop_completed().await.into_iter().collect(); let result: Vec<Reportable> = pop_completed().await.into_iter().collect();
if result.len() > 0 { if !result.is_empty() {
if let Err(err) = client.report(&result).await { if let Err(err) = client.report(&result).await {
ErrChan::send(err) ErrChan::send(err)
} }

@ -1,4 +1,3 @@
use tokio;
use u_agent::run_forever; use u_agent::run_forever;
#[tokio::main] #[tokio::main]

@ -7,7 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread", "process"] }
structopt = "0.3.21" structopt = "0.3.21"
log = "^0.4" log = "^0.4"
env_logger = "0.7.1" env_logger = "0.7.1"
@ -17,3 +16,5 @@ openssl = "*"
u_lib = { version = "*", path = "../../lib/u_lib" } u_lib = { version = "*", path = "../../lib/u_lib" }
serde_json = "1.0.4" serde_json = "1.0.4"
serde = { version = "1.0.114", features = ["derive"] } serde = { version = "1.0.114", features = ["derive"] }
actix-web = "3.3.2"
tokio = "1.11.0"

@ -0,0 +1,137 @@
use std::env;
use std::fmt;
use structopt::StructOpt;
use u_lib::{
api::ClientHandler, datatypes::DataResult, messaging::AsMsg, models::JobMeta, UError, UResult,
};
use uuid::Uuid;
#[derive(StructOpt, Debug)]
pub struct Args {
#[structopt(subcommand)]
cmd: Cmd,
#[structopt(long)]
json: bool,
}
#[derive(StructOpt, Debug)]
enum Cmd {
Agents(LD),
Jobs(JobALD),
Jobmap(JobMapALD),
Server,
}
#[derive(StructOpt, Debug)]
enum JobALD {
Add {
#[structopt(long, parse(try_from_str = parse_uuid))]
agent: Option<Uuid>,
#[structopt(long)]
alias: String,
#[structopt(subcommand)]
cmd: JobCmd,
},
#[structopt(flatten)]
LD(LD),
}
#[derive(StructOpt, Debug)]
enum JobCmd {
#[structopt(external_subcommand)]
Cmd(Vec<String>),
}
#[derive(StructOpt, Debug)]
enum JobMapALD {
Add {
#[structopt(parse(try_from_str = parse_uuid))]
agent_uid: Uuid,
job_idents: Vec<String>,
},
List {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Option<Uuid>,
},
Delete {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Uuid,
},
}
#[derive(StructOpt, Debug)]
enum LD {
List {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Option<Uuid>,
},
Delete {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Uuid,
},
}
fn parse_uuid(src: &str) -> Result<Uuid, String> {
Uuid::parse_str(src).map_err(|e| e.to_string())
}
pub async fn process_cmd(args: Args) -> UResult<()> {
struct Printer {
json: bool,
}
impl Printer {
pub fn print<Msg: AsMsg + fmt::Display>(&self, data: UResult<Msg>) {
if self.json {
let data = match data {
Ok(r) => DataResult::Ok(r),
Err(e) => DataResult::Err(e),
};
println!("{}", serde_json::to_string_pretty(&data).unwrap());
} else {
match data {
Ok(r) => println!("{}", r),
Err(e) => eprintln!("Error: {}", e),
}
}
}
}
let token = env::var("ADMIN_AUTH_TOKEN").map_err(|_| UError::WrongToken)?;
let cli_handler = ClientHandler::new(None).password(token);
let printer = Printer { json: args.json };
match args.cmd {
Cmd::Agents(action) => match action {
LD::List { uid } => printer.print(cli_handler.get_agents(uid).await),
LD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await),
},
Cmd::Jobs(action) => match action {
JobALD::Add {
cmd: JobCmd::Cmd(cmd),
alias,
..
} => {
let job = JobMeta::builder()
.with_shell(cmd.join(" "))
.with_alias(alias)
.build()?;
printer.print(cli_handler.upload_jobs(&[job]).await);
}
JobALD::LD(LD::List { uid }) => printer.print(cli_handler.get_jobs(uid).await),
JobALD::LD(LD::Delete { uid }) => printer.print(cli_handler.del(Some(uid)).await),
},
Cmd::Jobmap(action) => match action {
JobMapALD::Add {
agent_uid,
job_idents,
} => printer.print(cli_handler.set_jobs(Some(agent_uid), &job_idents).await),
JobMapALD::List { uid } => printer.print(cli_handler.get_agent_jobs(uid).await),
JobMapALD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await),
},
Cmd::Server => crate::server::serve().unwrap(),
}
Ok(())
}

@ -1,140 +1,10 @@
use std::env; mod argparse;
use std::fmt; mod server;
use argparse::{process_cmd, Args};
use std::process; use std::process;
use structopt::StructOpt; use structopt::StructOpt;
use u_lib::{ use u_lib::utils::init_env;
api::ClientHandler, datatypes::DataResult, messaging::AsMsg, models::JobMeta, utils::init_env,
UError, UResult,
};
use uuid::Uuid;
#[derive(StructOpt, Debug)]
struct Args {
#[structopt(subcommand)]
cmd: Cmd,
#[structopt(long)]
json: bool,
}
#[derive(StructOpt, Debug)]
enum Cmd {
Agents(LD),
Jobs(JobALD),
Jobmap(JobMapALD),
}
#[derive(StructOpt, Debug)]
enum JobALD {
Add {
#[structopt(long, parse(try_from_str = parse_uuid))]
agent: Option<Uuid>,
#[structopt(long)]
alias: String,
#[structopt(subcommand)]
cmd: JobCmd,
},
#[structopt(flatten)]
LD(LD),
}
#[derive(StructOpt, Debug)]
enum JobCmd {
#[structopt(external_subcommand)]
Cmd(Vec<String>),
}
#[derive(StructOpt, Debug)]
enum JobMapALD {
Add {
#[structopt(parse(try_from_str = parse_uuid))]
agent_uid: Uuid,
job_idents: Vec<String>,
},
List {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Option<Uuid>,
},
Delete {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Uuid,
},
}
#[derive(StructOpt, Debug)]
enum LD {
List {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Option<Uuid>,
},
Delete {
#[structopt(parse(try_from_str = parse_uuid))]
uid: Uuid,
},
}
fn parse_uuid(src: &str) -> Result<Uuid, String> {
Uuid::parse_str(src).map_err(|e| e.to_string())
}
async fn process_cmd(args: Args) -> UResult<()> {
struct Printer {
json: bool,
}
impl Printer {
pub fn print<Msg: AsMsg + fmt::Display>(&self, data: UResult<Msg>) {
if self.json {
let data = match data {
Ok(r) => DataResult::Ok(r),
Err(e) => DataResult::Err(e),
};
println!("{}", serde_json::to_string_pretty(&data).unwrap());
} else {
match data {
Ok(r) => println!("{}", r),
Err(e) => eprintln!("Error: {}", e),
}
}
}
}
let token = env::var("ADMIN_AUTH_TOKEN").map_err(|_| UError::WrongToken)?;
let cli_handler = ClientHandler::new(None).password(token);
let printer = Printer { json: args.json };
match args.cmd {
Cmd::Agents(action) => match action {
LD::List { uid } => printer.print(cli_handler.get_agents(uid).await),
LD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await),
},
Cmd::Jobs(action) => match action {
JobALD::Add {
cmd: JobCmd::Cmd(cmd),
alias,
agent: _agent,
} => {
let job = JobMeta::builder()
.with_shell(cmd.join(" "))
.with_alias(alias)
.build()?;
printer.print(cli_handler.upload_jobs(&[job]).await);
}
JobALD::LD(LD::List { uid }) => printer.print(cli_handler.get_jobs(uid).await),
JobALD::LD(LD::Delete { uid }) => printer.print(cli_handler.del(Some(uid)).await),
},
Cmd::Jobmap(action) => match action {
JobMapALD::Add {
agent_uid,
job_idents,
} => printer.print(cli_handler.set_jobs(Some(agent_uid), &job_idents).await),
JobMapALD::List { uid } => printer.print(cli_handler.get_agent_jobs(uid).await),
JobMapALD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await),
},
}
Ok(())
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {

@ -0,0 +1,23 @@
/*
Tabs: Agents, Tasks, Summary
every tab has list page and item page with more info/actions
Agents:
| id | alias | ..see struct | tasks done
| stripped | alias | ... | clickable number of assigned jobs
almost all fields are editable, rows are deletable
*/
use actix_web::{web, App, HttpResponse, HttpServer};
#[actix_web::main]
pub async fn serve() -> std::io::Result<()> {
let addr = "127.0.0.1:8080";
let app = || App::new().route("/", web::get().to(|| HttpResponse::Ok().body("ok")));
println!("Serving at http://{}", addr);
HttpServer::new(app).bind(addr)?.run().await
}

@ -0,0 +1,3 @@
mod app;
pub use app::serve;

@ -151,7 +151,7 @@ impl UDB {
} }
}) })
.collect::<Vec<String>>(); .collect::<Vec<String>>();
if not_found_jobs.len() > 0 { if !not_found_jobs.is_empty() {
return Err(ULocalError::NotFound(not_found_jobs.join(", "))); return Err(ULocalError::NotFound(not_found_jobs.join(", ")));
} }
let job_requests = job_uids let job_requests = job_uids
@ -172,7 +172,7 @@ impl UDB {
Ok(assigned_uids) Ok(assigned_uids)
} }
pub fn del_jobs(&self, uids: &Vec<Uuid>) -> ULocalResult<usize> { pub fn del_jobs(&self, uids: &[Uuid]) -> ULocalResult<usize> {
use schema::jobs; use schema::jobs;
let mut affected = 0; let mut affected = 0;
for &uid in uids { for &uid in uids {
@ -184,7 +184,7 @@ impl UDB {
Ok(affected) Ok(affected)
} }
pub fn del_results(&self, uids: &Vec<Uuid>) -> ULocalResult<usize> { pub fn del_results(&self, uids: &[Uuid]) -> ULocalResult<usize> {
use schema::results; use schema::results;
let mut affected = 0; let mut affected = 0;
for &uid in uids { for &uid in uids {
@ -196,7 +196,7 @@ impl UDB {
Ok(affected) Ok(affected)
} }
pub fn del_agents(&self, uids: &Vec<Uuid>) -> ULocalResult<usize> { pub fn del_agents(&self, uids: &[Uuid]) -> ULocalResult<usize> {
use schema::agents; use schema::agents;
let mut affected = 0; let mut affected = 0;
for &uid in uids { for &uid in uids {

@ -48,12 +48,12 @@ pub fn make_filters() -> impl Filter<Extract = (impl Reply,), Error = Rejection>
.map(Some) .map(Some)
.or_else(infallible_none), .or_else(infallible_none),
) )
.and_then(|uid| Endpoints::get_agent_jobs(uid)); .and_then(Endpoints::get_agent_jobs);
let get_personal_jobs = warp::get() let get_personal_jobs = warp::get()
.and(warp::path("get_personal_jobs")) .and(warp::path("get_personal_jobs"))
.and(warp::path::param::<Uuid>().map(Some)) .and(warp::path::param::<Uuid>().map(Some))
.and_then(|uid| Endpoints::get_personal_jobs(uid)); .and_then(Endpoints::get_personal_jobs);
let del = warp::get() let del = warp::get()
.and(warp::path("del")) .and(warp::path("del"))

@ -46,7 +46,7 @@ impl Endpoints {
info!("hnd: get_agents"); info!("hnd: get_agents");
UDB::lock_db() UDB::lock_db()
.get_agents(uid) .get_agents(uid)
.map(|m| build_message(m)) .map(build_message)
.or_else(|e| Ok(build_err(e))) .or_else(|e| Ok(build_err(e)))
} }
@ -54,7 +54,7 @@ impl Endpoints {
info!("hnd: get_jobs"); info!("hnd: get_jobs");
UDB::lock_db() UDB::lock_db()
.get_jobs(uid) .get_jobs(uid)
.map(|m| build_message(m)) .map(build_message)
.or_else(|e| Ok(build_err(e))) .or_else(|e| Ok(build_err(e)))
} }
@ -62,14 +62,14 @@ impl Endpoints {
info!("hnd: get_agent_jobs"); info!("hnd: get_agent_jobs");
UDB::lock_db() UDB::lock_db()
.get_exact_jobs(uid, false) .get_exact_jobs(uid, false)
.map(|m| build_message(m)) .map(build_message)
.or_else(|e| Ok(build_err(e))) .or_else(|e| Ok(build_err(e)))
} }
pub async fn get_personal_jobs(uid: Option<Uuid>) -> Result<Response<Body>, Rejection> { pub async fn get_personal_jobs(uid: Option<Uuid>) -> Result<Response<Body>, Rejection> {
info!("hnd: get_personal_jobs"); info!("hnd: get_personal_jobs");
let agents = UDB::lock_db().get_agents(uid).unwrap(); let agents = UDB::lock_db().get_agents(uid).unwrap();
if agents.len() == 0 { if agents.is_empty() {
let db = UDB::lock_db(); let db = UDB::lock_db();
db.insert_agent(&Agent::with_id(uid.unwrap())).unwrap(); db.insert_agent(&Agent::with_id(uid.unwrap())).unwrap();
let job = db.find_job_by_alias("agent_hello").unwrap(); let job = db.find_job_by_alias("agent_hello").unwrap();
@ -105,7 +105,7 @@ impl Endpoints {
let db = UDB::lock_db(); let db = UDB::lock_db();
let del_fns = &[UDB::del_agents, UDB::del_jobs, UDB::del_results]; let del_fns = &[UDB::del_agents, UDB::del_jobs, UDB::del_results];
for del_fn in del_fns { for del_fn in del_fns {
let affected = del_fn(&db, &vec![uid]).unwrap(); let affected = del_fn(&db, &[uid]).unwrap();
if affected > 0 { if affected > 0 {
return Ok(build_message(affected as i32)); return Ok(build_message(affected as i32));
} }
@ -130,7 +130,7 @@ impl Endpoints {
match jobs { match jobs {
Ok(j) => UDB::lock_db() Ok(j) => UDB::lock_db()
.set_jobs_for_agent(&agent_uid, &j) .set_jobs_for_agent(&agent_uid, &j)
.map(|assigned_uids| build_message(assigned_uids)) .map(build_message)
.or_else(|e| Ok(build_err(e))), .or_else(|e| Ok(build_err(e))),
Err(e) => Ok(build_err(e)), Err(e) => Ok(build_err(e)),
} }
@ -172,7 +172,7 @@ impl Endpoints {
Reportable::Dummy => (), Reportable::Dummy => (),
} }
} }
if failed.len() > 0 { if !failed.is_empty() {
let err_msg = ULocalError::ProcessingError(failed.join(", ")); let err_msg = ULocalError::ProcessingError(failed.join(", "));
return Ok(build_err(err_msg)); return Ok(build_err(err_msg));
} }

@ -59,6 +59,7 @@ fn init_all() {
prefill_jobs(); prefill_jobs();
} }
//TODO: tracing-subscriber
pub async fn serve() { pub async fn serve() {
init_all(); init_all();
let routes = make_filters(); let routes = make_filters();

@ -0,0 +1,4 @@
#!/bin/bash
PAT=".+IPAddress\W+\"([0-9\.]+).+"
docker ps | grep unki/$1 | cut -d' ' -f1 | xargs docker inspect | grep -P $PAT | sed -r "s/$PAT/\1/"

@ -1,8 +1,12 @@
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde_json::{from_slice, Value}; use serde_json::{from_slice, Value};
use shlex::split; use shlex::split;
use std::fmt::Display;
use std::process::{Command, Output}; use std::process::{Command, Output};
use u_lib::{datatypes::DataResult, utils::VecDisplay}; use u_lib::{
datatypes::DataResult,
utils::{bytes_to_string, ProcOutput},
};
const PANEL_BINARY: &str = "/u_panel"; const PANEL_BINARY: &str = "/u_panel";
@ -21,10 +25,11 @@ impl Panel {
pub fn output_argv<T: DeserializeOwned>(argv: &[&str]) -> PanelResult<T> { pub fn output_argv<T: DeserializeOwned>(argv: &[&str]) -> PanelResult<T> {
let result = Self::run(argv); let result = Self::run(argv);
from_slice(&result.stdout).map_err(|e| { let output = ProcOutput::from_output(&result).to_appropriate();
from_slice(&output).map_err(|e| {
eprintln!( eprintln!(
"Failed to decode panel response: '{}'", "Failed to decode panel response: '{}'",
VecDisplay(result.stdout) bytes_to_string(&output)
); );
e.to_string() e.to_string()
}) })
@ -48,7 +53,8 @@ impl Panel {
} }
} }
pub fn check_status(args: impl Into<String>) { pub fn check_status(args: impl Into<String> + Display) {
println!("Panel: executing '{}'", &args);
let result: PanelResult<Value> = Self::output(args); let result: PanelResult<Value> = Self::output(args);
Self::status_is_ok(result); Self::status_is_ok(result);
} }

@ -2,5 +2,21 @@ mod behaviour;
mod fixtures; mod fixtures;
mod helpers; mod helpers;
use std::env;
#[macro_use] #[macro_use]
extern crate rstest; extern crate rstest;
// TODO: huita
#[ignore]
#[tokio::test]
async fn test_non_auth_connection_dropped() {
let env_server = env::var("U_SERVER").unwrap();
match reqwest::get(format!("https://{}", env_server)).await {
Err(e) => {
dbg!(e.to_string());
assert!(e.is_request())
}
_ => panic!("no error occured on foreign client connection"),
}
}

@ -108,7 +108,7 @@ fn parse_fn_args(raw: Punctuated<FnArg, Token![,]>) -> FnArgs {
if &arg_name != "url_param" && &arg_name != "payload" { if &arg_name != "url_param" && &arg_name != "payload" {
panic!("Wrong arg name: {}", &arg_name) panic!("Wrong arg name: {}", &arg_name)
} }
let arg_type = *argt.ty.clone(); let arg_type = *argt.ty;
Some((arg_name, arg_type)) Some((arg_name, arg_type))
} else { } else {
None None

@ -118,7 +118,7 @@ pub async fn pop_completed() -> Vec<Reportable> {
.lock() .lock()
.await .await
.keys() .keys()
.map(|k| *k) .copied()
.collect::<Vec<Uuid>>(); .collect::<Vec<Uuid>>();
for fid in fids { for fid in fids {
if let Some(r) = pop_task_if_completed(fid).await { if let Some(r) = pop_task_if_completed(fid).await {

@ -46,10 +46,7 @@ impl<'cow, I: AsMsg> BaseMessage<'cow, I> {
C: Into<Moo<'cow, I>>, C: Into<Moo<'cow, I>>,
{ {
let Moo(inner) = inner.into(); let Moo(inner) = inner.into();
Self { Self { id: *UID, inner }
id: UID.clone(),
inner,
}
} }
pub fn into_inner(self) -> I { pub fn into_inner(self) -> I {

@ -106,7 +106,7 @@ impl Default for Agent {
fn default() -> Self { fn default() -> Self {
Self { Self {
alias: None, alias: None,
id: UID.clone(), id: *UID,
hostname: String::new(), hostname: String::new(),
is_root: false, is_root: false,
is_root_allowed: false, is_root_allowed: false,

@ -3,13 +3,13 @@ use crate::{
cache::JobCache, cache::JobCache,
errors::UError, errors::UError,
messaging::Reportable, messaging::Reportable,
models::{schema::*, JobOutput}, models::schema::*,
utils::{systime_to_string, TempFile}, utils::{systime_to_string, ProcOutput, TempFile},
UID, UID,
}; };
use diesel::{Identifiable, Insertable, Queryable}; use diesel::{Identifiable, Insertable, Queryable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt, process::Output, time::SystemTime}; use std::{fmt, time::SystemTime};
use tokio::process::Command; use tokio::process::Command;
use uuid::Uuid; use uuid::Uuid;
@ -101,19 +101,12 @@ impl AssignedJob {
let args = split_cmd.collect::<Vec<String>>(); let args = split_cmd.collect::<Vec<String>>();
let cmd_result = Command::new(cmd).args(args).output().await; let cmd_result = Command::new(cmd).args(args).output().await;
let (data, retcode) = match cmd_result { let (data, retcode) = match cmd_result {
Ok(Output { Ok(output) => (
status, ProcOutput::from_output(&output).into_combined(),
stdout, output.status.code(),
stderr,
}) => (
JobOutput::new()
.stdout(stdout)
.stderr(stderr)
.into_combined(),
status.code(),
), ),
Err(e) => ( Err(e) => (
JobOutput::new() ProcOutput::new()
.stderr(e.to_string().into_bytes()) .stderr(e.to_string().into_bytes())
.into_combined(), .into_combined(),
None, None,
@ -134,15 +127,15 @@ impl AssignedJob {
} }
} }
pub fn as_job_output(&self) -> Option<JobOutput> { pub fn as_job_output(&self) -> Option<ProcOutput> {
self.result self.result
.as_ref() .as_ref()
.and_then(|r| JobOutput::from_combined(r)) .and_then(|r| ProcOutput::from_combined(r))
} }
pub fn to_raw_result(&self) -> Vec<u8> { pub fn to_raw_result(&self) -> Vec<u8> {
match self.result.as_ref() { match self.result.as_ref() {
Some(r) => match JobOutput::from_combined(r) { Some(r) => match ProcOutput::from_combined(r) {
Some(o) => o.to_appropriate(), Some(o) => o.to_appropriate(),
None => r.clone(), None => r.clone(),
}, },

@ -26,7 +26,7 @@ impl JobMeta {
JobMetaBuilder::default() JobMetaBuilder::default()
} }
pub fn from_shell<S: Into<String>>(cmd: S) -> UResult<Self> { pub fn from_shell(cmd: impl Into<String>) -> UResult<Self> {
Self::builder().with_shell(cmd).build() Self::builder().with_shell(cmd).build()
} }
} }
@ -80,17 +80,17 @@ impl Default for JobMetaBuilder {
} }
impl JobMetaBuilder { impl JobMetaBuilder {
pub fn with_shell<S: Into<String>>(mut self, shell_cmd: S) -> Self { pub fn with_shell(mut self, shell_cmd: impl Into<String>) -> Self {
self.inner.argv = shell_cmd.into(); self.inner.argv = shell_cmd.into();
self self
} }
pub fn with_payload<C: Into<Vec<u8>>>(mut self, payload: C) -> Self { pub fn with_payload(mut self, payload: impl Into<Vec<u8>>) -> Self {
self.inner.payload = Some(payload.into()); self.inner.payload = Some(payload.into());
self self
} }
pub fn with_alias<S: Into<String>>(mut self, alias: S) -> Self { pub fn with_alias(mut self, alias: impl Into<String>) -> Self {
self.inner.alias = Some(alias.into()); self.inner.alias = Some(alias.into());
self self
} }
@ -104,13 +104,13 @@ impl JobMetaBuilder {
let mut inner = self.inner; let mut inner = self.inner;
match inner.exec_type { match inner.exec_type {
JobType::Shell => { JobType::Shell => {
if inner.argv == "" { if inner.argv.is_empty() {
inner.argv = String::from("/bin/bash -c {}") inner.argv = String::from("/bin/bash -c {}")
} }
let argv_parts = let argv_parts =
shlex::split(&inner.argv).ok_or(UError::JobArgsError("Shlex failed".into()))?; shlex::split(&inner.argv).ok_or(UError::JobArgsError("Shlex failed".into()))?;
let empty_err = UError::JobArgsError("Empty argv".into()); let empty_err = UError::JobArgsError("Empty argv".into());
if argv_parts.get(0).ok_or(empty_err.clone())?.len() == 0 { if argv_parts.get(0).ok_or(empty_err.clone())?.is_empty() {
return Err(empty_err.into()); return Err(empty_err.into());
} }
match inner.payload.as_ref() { match inner.payload.as_ref() {
@ -120,8 +120,6 @@ impl JobMetaBuilder {
"Argv contains no executable placeholder".into(), "Argv contains no executable placeholder".into(),
) )
.into()); .into());
} else {
()
} }
} }
None => { None => {
@ -131,8 +129,6 @@ impl JobMetaBuilder {
.into(), .into(),
) )
.into()); .into());
} else {
()
} }
} }
}; };

@ -35,152 +35,3 @@ pub enum JobType {
Shell, Shell,
Python, Python,
} }
#[derive(Clone, Debug)]
pub struct JobOutput {
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
}
impl JobOutput {
const STREAM_BORDER: &'static str = "***";
const STDOUT: &'static str = "STDOUT";
const STDERR: &'static str = "STDERR";
#[inline]
fn create_delim(header: &'static str) -> Vec<u8> {
format!(
"<{border}{head}{border}>",
border = Self::STREAM_BORDER,
head = header
)
.into_bytes()
}
pub fn new() -> Self {
Self {
stdout: vec![],
stderr: vec![],
}
}
pub fn stdout(mut self, data: Vec<u8>) -> Self {
self.stdout = data;
self
}
pub fn stderr(mut self, data: Vec<u8>) -> Self {
self.stderr = data;
self
}
pub fn into_combined(self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
if self.stdout.len() > 0 {
result.extend(Self::create_delim(Self::STDOUT));
result.extend(self.stdout);
}
if self.stderr.len() > 0 {
result.extend(Self::create_delim(Self::STDERR));
result.extend(self.stderr);
}
result
}
pub fn from_combined(raw: &[u8]) -> Option<Self> {
enum ParseFirst {
Stdout,
Stderr,
}
fn split_by_subslice<'s>(slice: &'s [u8], subslice: &[u8]) -> Option<(&'s [u8], &'s [u8])> {
slice
.windows(subslice.len())
.position(|w| w == subslice)
.map(|split_pos| {
let splitted = slice.split_at(split_pos);
(&splitted.0[..split_pos], &splitted.1[subslice.len()..])
})
}
let splitter = |p: ParseFirst| {
let (first_hdr, second_hdr) = match p {
ParseFirst::Stdout => (Self::STDOUT, Self::STDERR),
ParseFirst::Stderr => (Self::STDERR, Self::STDOUT),
};
let first_hdr = Self::create_delim(first_hdr);
let second_hdr = Self::create_delim(second_hdr);
split_by_subslice(raw, &first_hdr).map(|(_, p2)| {
match split_by_subslice(p2, &second_hdr) {
Some((p2_1, p2_2)) => Self::new().stdout(p2_1.to_vec()).stderr(p2_2.to_vec()),
None => Self::new().stdout(p2.to_vec()),
}
})
};
splitter(ParseFirst::Stdout).or(splitter(ParseFirst::Stderr))
}
pub fn to_appropriate(&self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
if self.stdout.len() > 0 {
result.extend(&self.stdout);
}
if self.stderr.len() > 0 {
if result.len() > 0 {
result.push(b'\n');
}
result.extend(&self.stderr);
}
if result.len() == 0 {
result.extend(b"No data");
}
result
}
}
#[cfg(test)]
mod tests {
use crate::{models::JobOutput, utils::bytes_to_string};
use test_case::test_case;
const STDOUT: &str = "<***STDOUT***>";
const STDERR: &str = "<***STDERR***>";
#[test_case(
"lol",
"kek",
&format!("{}lol{}kek", STDOUT, STDERR)
;"stdout stderr"
)]
#[test_case(
"",
"kek",
&format!("{}kek", STDERR)
;"stderr"
)]
fn test_to_combined(stdout: &str, stderr: &str, result: &str) {
let output = JobOutput::new()
.stdout(stdout.as_bytes().to_vec())
.stderr(stderr.as_bytes().to_vec());
assert_eq!(&bytes_to_string(&output.into_combined()), result)
}
#[test_case(
&format!("{}lal{}kik", STDOUT, STDERR),
"lal\nkik"
;"stdout stderr"
)]
#[test_case(
&format!("{}qeq", STDOUT),
"qeq"
;"stdout"
)]
#[test_case(
&format!("{}vev", STDERR),
"vev"
;"stderr"
)]
fn test_from_combined(src: &str, result: &str) {
let output = JobOutput::from_combined(src.as_bytes()).unwrap();
assert_eq!(bytes_to_string(&output.to_appropriate()).trim(), result);
}
}

@ -31,7 +31,7 @@ impl<T, E> CombinedResult<T, E> {
} }
pub fn has_err(&self) -> bool { pub fn has_err(&self) -> bool {
self.err.len() > 0 !self.err.is_empty()
} }
pub fn unwrap_one(self) -> T { pub fn unwrap_one(self) -> T {

@ -6,7 +6,6 @@ use std::str::Chars;
const MAX_DATA_LEN: usize = 200; const MAX_DATA_LEN: usize = 200;
pub trait Strippable { pub trait Strippable {
//TODO: waiting for stabilizing GATs
type Item: fmt::Display; type Item: fmt::Display;
type TypeIter: Iterator<Item = Self::Item>; type TypeIter: Iterator<Item = Self::Item>;

@ -2,6 +2,7 @@ mod combined_result;
mod conv; mod conv;
mod fmt; mod fmt;
mod misc; mod misc;
mod proc_output;
mod storage; mod storage;
mod tempfile; mod tempfile;
mod vec_display; mod vec_display;
@ -10,6 +11,7 @@ pub use combined_result::*;
pub use conv::*; pub use conv::*;
pub use fmt::*; pub use fmt::*;
pub use misc::*; pub use misc::*;
pub use proc_output::*;
pub use storage::*; pub use storage::*;
pub use tempfile::*; pub use tempfile::*;
pub use vec_display::*; pub use vec_display::*;

@ -0,0 +1,163 @@
use std::process::Output;
#[derive(Clone, Debug)]
pub struct ProcOutput {
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
}
impl ProcOutput {
const STREAM_BORDER: &'static str = "***";
const STDOUT: &'static str = "STDOUT";
const STDERR: &'static str = "STDERR";
#[inline]
fn create_delim(header: &'static str) -> Vec<u8> {
format!(
"<{border}{head}{border}>",
border = Self::STREAM_BORDER,
head = header
)
.into_bytes()
}
pub fn from_output(output: &Output) -> Self {
let mut this = Self::new().stdout(output.stdout.to_vec());
if !output.status.success() && output.stderr.len() > 0 {
this.stderr = output.stderr.to_vec();
}
this
}
pub fn new() -> Self {
Self {
stdout: vec![],
stderr: vec![],
}
}
pub fn stdout(mut self, data: Vec<u8>) -> Self {
self.stdout = data;
self
}
pub fn stderr(mut self, data: Vec<u8>) -> Self {
self.stderr = data;
self
}
/// Make bytestring like '<***STDOUT***>...<***STDERR***>...'
pub fn into_combined(self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
if !self.stdout.is_empty() {
result.extend(Self::create_delim(Self::STDOUT));
result.extend(self.stdout);
}
if !self.stderr.is_empty() {
result.extend(Self::create_delim(Self::STDERR));
result.extend(self.stderr);
}
result
}
pub fn from_combined(raw: &[u8]) -> Option<Self> {
enum ParseFirst {
Stdout,
Stderr,
}
fn split_by_subslice<'s>(slice: &'s [u8], subslice: &[u8]) -> Option<(&'s [u8], &'s [u8])> {
slice
.windows(subslice.len())
.position(|w| w == subslice)
.map(|split_pos| {
let splitted = slice.split_at(split_pos);
(&splitted.0[..split_pos], &splitted.1[subslice.len()..])
})
}
let splitter = |p: ParseFirst| {
let (first_hdr, second_hdr) = match p {
ParseFirst::Stdout => (Self::STDOUT, Self::STDERR),
ParseFirst::Stderr => (Self::STDERR, Self::STDOUT),
};
let first_hdr = Self::create_delim(first_hdr);
let second_hdr = Self::create_delim(second_hdr);
split_by_subslice(raw, &first_hdr).map(|(_, p2)| {
match split_by_subslice(p2, &second_hdr) {
Some((p2_1, p2_2)) => Self::new().stdout(p2_1.to_vec()).stderr(p2_2.to_vec()),
None => Self::new().stdout(p2.to_vec()),
}
})
};
splitter(ParseFirst::Stdout).or_else(|| splitter(ParseFirst::Stderr))
}
/// Chooses between stdout and stderr or both wisely
pub fn to_appropriate(&self) -> Vec<u8> {
let mut result: Vec<u8> = vec![];
let mut altered = false;
if !self.stdout.is_empty() {
result.extend(&self.stdout);
altered = true;
}
if !self.stderr.is_empty() {
if altered {
result.push(b'\n');
}
result.extend(&self.stderr);
altered = true;
}
if !altered {
result.extend(b"No data");
}
result
}
}
#[cfg(test)]
mod tests {
use crate::utils::{bytes_to_string, ProcOutput};
use test_case::test_case;
const STDOUT: &str = "<***STDOUT***>";
const STDERR: &str = "<***STDERR***>";
#[test_case(
"lol",
"kek",
&format!("{}lol{}kek", STDOUT, STDERR)
;"stdout stderr"
)]
#[test_case(
"",
"kek",
&format!("{}kek", STDERR)
;"stderr"
)]
fn test_to_combined(stdout: &str, stderr: &str, result: &str) {
let output = ProcOutput::new()
.stdout(stdout.as_bytes().to_vec())
.stderr(stderr.as_bytes().to_vec());
assert_eq!(&bytes_to_string(&output.into_combined()), result)
}
#[test_case(
&format!("{}lal{}kik", STDOUT, STDERR),
"lal\nkik"
;"stdout stderr"
)]
#[test_case(
&format!("{}qeq", STDOUT),
"qeq"
;"stdout"
)]
#[test_case(
&format!("{}vev", STDERR),
"vev"
;"stderr"
)]
fn test_from_combined(src: &str, result: &str) {
let output = ProcOutput::from_combined(src.as_bytes()).unwrap();
assert_eq!(bytes_to_string(&output.to_appropriate()).trim(), result);
}
}

@ -2,7 +2,7 @@
set -ex set -ex
source $(dirname $0)/rootdir.sh #set ROOTDIR source $(dirname $0)/rootdir.sh #set ROOTDIR
ARGS=$@ ARGS=$@
STATIC_LIBS=static STATIC_LIBS=./static
DOCKER_EXCHG=/musl-share DOCKER_EXCHG=/musl-share
IMAGE=unki/musllibs IMAGE=unki/musllibs
mkdir -p $STATIC_LIBS mkdir -p $STATIC_LIBS

Loading…
Cancel
Save