panel adapter for integration tests

14-integration-tests
plazmoid 3 years ago
parent 3e0c9ecd77
commit 4e88f49f96
  1. 4
      bin/u_agent/src/lib.rs
  2. 2
      bin/u_panel/Cargo.toml
  3. 85
      bin/u_panel/src/main.rs
  4. 4
      bin/u_server/src/lib.rs
  5. 1
      integration/Cargo.toml
  6. 5
      integration/docker-compose.yml
  7. 2
      integration/images/u_agent.Dockerfile
  8. 37
      integration/tests/behaviour.rs
  9. 4
      integration/tests/helpers/mod.rs
  10. 39
      integration/tests/helpers/panel.rs
  11. 54
      integration/tests/tests.rs
  12. 20
      lib/u_lib/src/api.rs
  13. 10
      lib/u_lib/src/builder.rs
  14. 6
      lib/u_lib/src/executor.rs
  15. 4
      lib/u_lib/src/messaging.rs
  16. 6
      lib/u_lib/src/models/jobs/misc.rs
  17. 13
      lib/u_lib/src/models/mod.rs
  18. 168
      lib/u_lib/src/utils.rs
  19. 40
      lib/u_lib/src/utils/combined_result.rs
  20. 19
      lib/u_lib/src/utils/conv.rs
  21. 71
      lib/u_lib/src/utils/misc.rs
  22. 11
      lib/u_lib/src/utils/mod.rs
  23. 38
      lib/u_lib/src/utils/tempfile.rs
  24. 40
      lib/u_lib/src/utils/vec_display.rs
  25. 2
      scripts/cargo_musl.sh

@ -77,11 +77,11 @@ pub async fn run_forever() {
info!("Connecting to the server");
loop {
let job_requests: Vec<AssignedJob> =
retry_until_ok!(instance.get_agent_jobs(Some(*UID)).await);
retry_until_ok!(instance.get_agent_jobs(Some(*UID)).await).into_builtin_vec();
process_request(job_requests, &instance).await;
let result: Vec<ExecResult> = pop_completed().await.into_iter().collect();
if result.len() > 0 {
retry_until_ok!(instance.report(&result).await)
retry_until_ok!(instance.report(&result).await);
}
sleep(Duration::from_secs(5)).await;
}

@ -15,3 +15,5 @@ uuid = "0.6.5"
reqwest = { version = "0.11", features = ["json"] }
openssl = "*"
u_lib = { version = "*", path = "../../lib/u_lib" }
serde_json = "1.0.4"
serde = { version = "1.0.114", features = ["derive"] }

@ -1,14 +1,18 @@
use serde::Serialize;
use std::env;
use std::fmt;
use structopt::StructOpt;
use u_lib::{api::ClientHandler, models::JobMeta, utils::init_env, UError};
use u_lib::{
api::ClientHandler, messaging::AsMsg, models::JobMeta, utils::init_env, UError, UResult,
};
use uuid::Uuid;
const DELIM: &'static str = "*************\n";
#[derive(StructOpt, Debug)]
struct Args {
#[structopt(subcommand)]
cmd: Cmd,
#[structopt(long)]
json: bool,
}
#[derive(StructOpt, Debug)]
@ -72,60 +76,63 @@ fn parse_uuid(src: &str) -> Result<Uuid, String> {
Uuid::parse_str(src).map_err(|e| e.to_string())
}
async fn process_cmd(cmd: Cmd) -> Result<(), UError> {
let token = env::var("ADMIN_AUTH_TOKEN").unwrap();
async fn process_cmd(args: Args) {
fn printer<Msg: AsMsg + fmt::Display>(data: UResult<Msg>, json: bool) {
if json {
#[derive(Serialize)]
#[serde(rename_all = "lowercase")]
#[serde(tag = "status", content = "data")]
enum DataResult<M> {
Ok(M),
Err(UError),
}
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").expect("Authentication token is not set");
let cli_handler = ClientHandler::new(None).password(token);
match cmd {
let json = args.json;
match args.cmd {
Cmd::Agents(action) => match action {
LD::List { uid } => cli_handler
.get_agents(uid)
.await?
.into_iter()
.for_each(|r| println!("{}{}", DELIM, r)),
LD::Delete { uid } => {
println!("{}", cli_handler.del(Some(uid)).await?);
}
LD::List { uid } => printer(cli_handler.get_agents(uid).await, json),
LD::Delete { uid } => printer(cli_handler.del(Some(uid)).await, json),
},
Cmd::Jobs(action) => match action {
JobALD::Add {
cmd: JobCmd::Cmd(cmd),
agent,
agent: _agent,
} => {
let job = JobMeta::from_shell(cmd.join(" "))?;
let job_uid = job.id;
cli_handler.upload_jobs(&vec![job]).await?;
if agent.is_some() {
cli_handler.set_jobs(agent, &vec![job_uid]).await?
}
}
JobALD::LD(LD::List { uid }) => cli_handler
.get_jobs(uid)
.await?
.into_iter()
.for_each(|r| println!("{}{}", DELIM, r)),
JobALD::LD(LD::Delete { uid }) => {
println!("{}", cli_handler.del(Some(uid)).await?)
let job = JobMeta::from_shell(cmd.join(" ")).unwrap();
printer(cli_handler.upload_jobs(&[job]).await, json);
}
JobALD::LD(LD::List { uid }) => printer(cli_handler.get_jobs(uid).await, json),
JobALD::LD(LD::Delete { uid }) => printer(cli_handler.del(Some(uid)).await, json),
},
Cmd::Jobmap(action) => match action {
JmALD::Add {
agent_uid,
job_uids,
} => cli_handler.set_jobs(Some(agent_uid), &job_uids).await?,
JmALD::List { uid } => cli_handler
.get_agent_jobs(uid)
.await?
.into_iter()
.for_each(|r| println!("{}{}", DELIM, r)),
JmALD::Delete { uid } => println!("{}", cli_handler.del(Some(uid)).await?),
} => printer(cli_handler.set_jobs(Some(agent_uid), &job_uids).await, json),
JmALD::List { uid } => printer(cli_handler.get_agent_jobs(uid).await, json),
JmALD::Delete { uid } => printer(cli_handler.del(Some(uid)).await, json),
},
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), UError> {
async fn main() {
init_env();
let args: Args = Args::from_args();
process_cmd(args.cmd).await
process_cmd(args).await;
}

@ -109,11 +109,11 @@ fn make_filters() -> impl Filter<Extract = (impl Reply,), Error = Rejection> + C
.and(get_content::<Vec<ExecResult>>().and_then(Endpoints::report));
let auth_token = format!("Bearer {}", env::var("ADMIN_AUTH_TOKEN").unwrap()).into_boxed_str();
let authenticated = warp::header::exact("authorization", Box::leak(auth_token));
let auth_header = warp::header::exact("authorization", Box::leak(auth_token));
let agent_zone = get_jobs.clone().or(get_personal_jobs).or(report);
let auth_zone = authenticated.and(
let auth_zone = auth_header.and(
get_agents
.or(get_jobs)
.or(upload_jobs)

@ -15,6 +15,7 @@ reqwest = { version = "0.11", features = ["json"] }
serde_json = "1.0"
serde = { version = "1.0.114", features = ["derive"] }
futures = "0.3.5"
shlex = "1.0.0"
[[test]]

@ -75,6 +75,8 @@ services:
- ~/.cargo/registry:/root/.cargo/registry
working_dir:
/tests/
env_file:
- ../.env
depends_on:
u_agent_1:
condition: service_started
@ -83,4 +85,5 @@ services:
u_server:
condition: service_healthy
environment:
RUST_BACKTRACE: 1
RUST_BACKTRACE: 1
U_SERVER: u_server

@ -1,3 +1,3 @@
FROM centos:7
CMD yum update
RUN yum update -y

@ -1,38 +1 @@
use crate::helpers::client::AgentClient;
use serde_json::json;
use uuid::Uuid;
type TestResult<R = ()> = Result<R, Box<dyn std::error::Error>>;
async fn register_agent() -> Uuid {
let cli = AgentClient::new();
let agent_uid = Uuid::new_v4();
let resp = cli.get(format!("get_agent_jobs/{}", agent_uid)).await;
let job_id = &resp["job_id"];
let resp = cli.get(format!("get_jobs/{}", job_id)).await;
assert_eq!(&resp["alias"], "agent_hello");
let agent_data = json! {
{"id": &agent_uid,"inner":[
{"Agent":
{"alias":null,
"hostname":"3b1030fa6324",
"id":&agent_uid,
"is_root":false,
"is_root_allowed":false,
"last_active":{"secs_since_epoch":1625271265,"nanos_since_epoch":92814921},
"platform":"x86_64-unknown-linux-gnu",
"regtime":{"secs_since_epoch":1625271265,"nanos_since_epoch":92814945},
"state":"New",
"token":null,
"username":"root"}
}]}
};
cli.post("report", &agent_data).await;
agent_uid
}
#[tokio::test]
async fn test_first_connection() -> TestResult {
register_agent().await;
Ok(())
}

@ -1 +1,5 @@
pub mod client;
pub mod panel;
pub use client::AgentClient;
pub use panel::Panel;

@ -1,3 +1,40 @@
const BINARY: &str = "/u_panel";
use serde_json::{from_slice, Value};
use shlex::split;
use std::process::{Command, Output};
const PANEL_BINARY: &str = "/u_panel";
pub struct Panel;
impl Panel {
fn run(args: &[&str]) -> Output {
Command::new(PANEL_BINARY)
.arg("--json")
.args(args)
.output()
.unwrap()
}
pub fn output_argv(args: &[&str]) -> Value {
let result = Self::run(args);
assert!(result.status.success());
from_slice(&result.stdout).unwrap()
}
pub fn output(args: &str) -> Value {
let splitted = split(args).unwrap();
Self::output_argv(
splitted
.iter()
.map(|s| s.as_ref())
.collect::<Vec<&str>>()
.as_ref(),
)
}
pub fn check_output(args: &str) -> Vec<Value> {
let result = Self::output(args);
assert_eq!(result["status"], "ok");
result["data"].as_array().unwrap().clone()
}
}

@ -1,2 +1,54 @@
mod behaviour;
mod helpers;
use helpers::{AgentClient, Panel};
use serde_json::json;
use uuid::Uuid;
type TestResult<R = ()> = Result<R, Box<dyn std::error::Error>>;
async fn register_agent() -> Uuid {
let cli = AgentClient::new();
let agent_uid = Uuid::new_v4();
let resp = cli.get(format!("get_agent_jobs/{}", agent_uid)).await;
let job_id = &resp["job_id"];
let resp = cli.get(format!("get_jobs/{}", job_id)).await;
assert_eq!(&resp["alias"], "agent_hello");
let agent_data = json! {
{"id": &agent_uid,"inner":[
{"Agent":
{"alias":null,
"hostname":"3b1030fa6324",
"id":&agent_uid,
"is_root":false,
"is_root_allowed":false,
"last_active":{"secs_since_epoch":1625271265,"nanos_since_epoch":92814921},
"platform":"x86_64-unknown-linux-gnu",
"regtime":{"secs_since_epoch":1625271265,"nanos_since_epoch":92814945},
"state":"New",
"token":null,
"username":"root"}
}]}
};
cli.post("report", &agent_data).await;
agent_uid
}
#[tokio::test]
async fn test_first_connection() -> TestResult {
let uid = register_agent().await;
let agents = Panel::check_output("agents list");
dbg!(&agents);
assert_eq!(agents.len(), 2);
let found = agents
.iter()
.find(|v| v["id"].as_str().unwrap() == uid.to_string());
assert!(found.is_some());
Ok(())
}
#[tokio::test]
async fn test_setup_tasks() -> TestResult {
register_agent().await;
Ok(())
}

@ -3,10 +3,11 @@ use crate::{
config::{MASTER_PORT, MASTER_SERVER},
messaging::{AsMsg, BaseMessage},
models::*,
utils::opt_to_string,
utils::{opt_to_string, VecDisplay},
UError, UResult,
};
use reqwest::{Client, RequestBuilder, Url};
use std::env;
use u_api_proc_macro::api_route;
use uuid::Uuid;
@ -18,9 +19,8 @@ pub struct ClientHandler {
impl ClientHandler {
pub fn new(server: Option<&str>) -> Self {
let master_server = server
//.map(|s| Ipv4Addr::from_str(&s).unwrap())
.unwrap_or(MASTER_SERVER);
let env_server = env::var("U_SERVER").unwrap_or(String::from(MASTER_SERVER));
let master_server = server.unwrap_or(env_server.as_str());
Self {
client: Client::new(),
base_url: Url::parse(&format!("http://{}:{}", master_server, MASTER_PORT)).unwrap(),
@ -52,24 +52,24 @@ impl ClientHandler {
//
// get jobs for client
#[api_route("GET")]
fn get_agent_jobs(&self, url_param: Option<Uuid>) -> Vec<AssignedJob> {}
fn get_agent_jobs(&self, url_param: Option<Uuid>) -> VecDisplay<AssignedJob> {}
//
// send something to server
#[api_route("POST")]
fn report<M: AsMsg>(&self, payload: &M) {}
fn report<M: AsMsg>(&self, payload: &M) -> Empty {}
//##########// Admin area //##########//
/// client listing
#[api_route("GET")]
fn get_agents(&self, url_param: Option<Uuid>) -> Vec<Agent> {}
fn get_agents(&self, url_param: Option<Uuid>) -> VecDisplay<Agent> {}
//
// get all available jobs
#[api_route("GET")]
fn get_jobs(&self, url_param: Option<Uuid>) -> Vec<JobMeta> {}
fn get_jobs(&self, url_param: Option<Uuid>) -> VecDisplay<JobMeta> {}
//
// create and upload job
#[api_route("POST")]
fn upload_jobs(&self, payload: &Vec<JobMeta>) {}
fn upload_jobs(&self, payload: &[JobMeta]) -> Empty {}
//
// delete something
#[api_route("GET")]
@ -77,5 +77,5 @@ impl ClientHandler {
//
// set jobs for client
#[api_route("POST")]
fn set_jobs(&self, url_param: Option<Uuid>, payload: &Vec<Uuid>) {}
fn set_jobs(&self, url_param: Option<Uuid>, payload: &[Uuid]) -> Empty {}
}

@ -2,7 +2,7 @@ use crate::{
cache::JobCache,
executor::{FutRes, Waiter, DynFut},
models::{Agent, AssignedJob, JobMeta, JobType},
utils::{CombinedResult, OneOrMany},
utils::{CombinedResult, OneOrVec},
UError,
};
use guess_host_triple::guess_host_triple;
@ -13,7 +13,7 @@ pub struct JobBuilder {
}
impl JobBuilder {
pub fn from_request<J: OneOrMany<AssignedJob>>(job_requests: J) -> CombinedResult<Self> {
pub fn from_request<J: OneOrVec<AssignedJob>>(job_requests: J) -> CombinedResult<Self> {
let job_requests = job_requests.into_vec();
let mut prepared: Vec<DynFut> = vec![];
let mut result = CombinedResult::new();
@ -52,7 +52,7 @@ impl JobBuilder {
result
}
pub fn from_meta<J: OneOrMany<JobMeta>>(job_metas: J) -> CombinedResult<Self> {
pub fn from_meta<J: OneOrVec<JobMeta>>(job_metas: J) -> CombinedResult<Self> {
let job_requests = job_metas
.into_vec()
.into_iter()
@ -90,7 +90,7 @@ pub struct NamedJobBuilder {
}
impl NamedJobBuilder {
pub fn from_shell<J: OneOrMany<(&'static str, &'static str)>>(
pub fn from_shell<J: OneOrVec<(&'static str, &'static str)>>(
named_jobs: J,
) -> CombinedResult<Self> {
let mut result = CombinedResult::new();
@ -111,7 +111,7 @@ impl NamedJobBuilder {
result
}
pub fn from_meta<J: OneOrMany<(&'static str, JobMeta)>>(named_jobs: J) -> Self {
pub fn from_meta<J: OneOrVec<(&'static str, JobMeta)>>(named_jobs: J) -> Self {
let mut job_names = vec![];
let job_metas: Vec<JobMeta> = named_jobs
.into_vec()

@ -1,7 +1,7 @@
use crate::{utils::OneOrMany, models::ExecResult};
use crate::{models::ExecResult, utils::OneOrVec};
use futures::{future::BoxFuture, lock::Mutex};
use lazy_static::lazy_static;
use std::{collections::HashMap};
use std::collections::HashMap;
use tokio::{
spawn,
sync::mpsc::{channel, Receiver, Sender},
@ -37,7 +37,7 @@ pub struct Waiter {
}
impl Waiter {
pub fn new<S: OneOrMany<DynFut>>(tasks: S) -> Self {
pub fn new<S: OneOrVec<DynFut>>(tasks: S) -> Self {
Self {
tasks: tasks.into_vec(),
fids: vec![],

@ -1,6 +1,8 @@
use crate::utils::VecDisplay;
use crate::UID;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::Display;
use uuid::Uuid;
pub struct Moo<'cow, T: AsMsg + Clone>(pub Cow<'cow, T>);
@ -29,6 +31,8 @@ impl<'cow, M: AsMsg> From<&'cow M> for Moo<'cow, M> {
}
impl<M: AsMsg> AsMsg for Vec<M> {}
impl<M: AsMsg + Display> AsMsg for VecDisplay<M> {}
impl<'msg, M: AsMsg> AsMsg for &'msg [M] {}
#[derive(Serialize, Deserialize, Debug)]
pub struct BaseMessage<'cow, I: AsMsg> {

@ -139,7 +139,7 @@ impl JobOutput {
#[cfg(test)]
mod tests {
use crate::{models::JobOutput, utils::vec_to_string};
use crate::{models::JobOutput, utils::bytes_to_string};
use test_case::test_case;
const STDOUT: &str = "<***STDOUT***>";
@ -161,7 +161,7 @@ mod tests {
let output = JobOutput::new()
.stdout(stdout.as_bytes().to_vec())
.stderr(stderr.as_bytes().to_vec());
assert_eq!(&vec_to_string(&output.into_combined()), result)
assert_eq!(&bytes_to_string(&output.into_combined()), result)
}
#[test_case(
@ -181,6 +181,6 @@ mod tests {
)]
fn test_from_combined(src: &str, result: &str) {
let output = JobOutput::from_combined(src.as_bytes()).unwrap();
assert_eq!(vec_to_string(&output.to_appropriate()).trim(), result);
assert_eq!(bytes_to_string(&output.to_appropriate()).trim(), result);
}
}

@ -6,6 +6,8 @@ pub mod schema;
use crate::messaging::AsMsg;
pub use crate::models::result::ExecResult;
pub use crate::models::{agent::*, jobs::*};
use serde::{Deserialize, Serialize};
use std::fmt;
use uuid::Uuid;
impl AsMsg for Agent {}
@ -14,4 +16,13 @@ impl AsMsg for ExecResult {}
impl AsMsg for JobMeta {}
impl AsMsg for String {}
impl AsMsg for Uuid {}
impl AsMsg for () {}
impl AsMsg for Empty {}
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct Empty;
impl fmt::Display for Empty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<empty>")
}
}

@ -1,168 +0,0 @@
use crate::UError;
use chrono::{offset::Local, DateTime};
use nix::{
sys::signal::{signal, SigHandler, Signal},
unistd::{chdir, close as fdclose, fork, getppid, setsid, ForkResult},
};
use std::{
env::temp_dir, fs, ops::Drop, os::unix::fs::PermissionsExt, path::PathBuf, process::exit,
time::SystemTime,
};
use uuid::Uuid;
pub trait OneOrMany<T> {
fn into_vec(self) -> Vec<T>;
}
impl<T> OneOrMany<T> for T {
fn into_vec(self) -> Vec<T> {
vec![self]
}
}
impl<T> OneOrMany<T> for Vec<T> {
fn into_vec(self) -> Vec<T> {
self
}
}
pub struct TempFile {
path: PathBuf,
}
impl TempFile {
pub fn get_path(&self) -> String {
self.path.to_string_lossy().to_string()
}
pub fn new() -> Self {
let name = Uuid::simple(&Uuid::new_v4()).to_string();
let mut path = temp_dir();
path.push(name);
Self { path }
}
pub fn write_all(&self, data: &[u8]) -> Result<(), String> {
fs::write(&self.path, data).map_err(|e| e.to_string())
}
pub fn write_exec(data: &[u8]) -> Result<Self, (String, String)> {
let this = Self::new();
let path = this.get_path();
this.write_all(data).map_err(|e| (path.clone(), e))?;
let perms = fs::Permissions::from_mode(0o555);
fs::set_permissions(&path, perms).map_err(|e| (path, e.to_string()))?;
Ok(this)
}
}
impl Drop for TempFile {
fn drop(&mut self) {
fs::remove_file(&self.path).ok();
}
}
pub struct CombinedResult<T, E = UError> {
ok: Vec<T>,
err: Vec<E>,
}
impl<T, E> CombinedResult<T, E> {
pub fn new() -> Self {
Self {
ok: vec![],
err: vec![],
}
}
pub fn ok<I: OneOrMany<T>>(&mut self, result: I) {
self.ok.extend(result.into_vec());
}
pub fn err<I: OneOrMany<E>>(&mut self, err: I) {
self.err.extend(err.into_vec());
}
pub fn unwrap(self) -> Vec<T> {
let err_len = self.err.len();
if err_len > 0 {
panic!("CombinedResult has {} errors", err_len);
}
self.ok
}
pub fn unwrap_one(self) -> T {
self.unwrap().pop().unwrap()
}
pub fn pop_errors(&mut self) -> Vec<E> {
self.err.drain(..).collect()
}
}
#[macro_export]
macro_rules! unwrap_enum {
($src:ident, $t:path) => {
if let $t(result) = $src {
result
} else {
panic!("wrong type")
}
};
}
pub fn daemonize() {
if getppid().as_raw() != 1 {
setsig(Signal::SIGTTOU, SigHandler::SigIgn);
setsig(Signal::SIGTTIN, SigHandler::SigIgn);
setsig(Signal::SIGTSTP, SigHandler::SigIgn);
}
for fd in 0..=2 {
match fdclose(fd) {
_ => (),
}
}
match chdir("/") {
_ => (),
};
match fork() {
Ok(ForkResult::Parent { .. }) => {
exit(0);
}
Ok(ForkResult::Child) => match setsid() {
_ => (),
},
Err(_) => exit(255),
}
}
pub fn setsig(sig: Signal, hnd: SigHandler) {
unsafe {
signal(sig, hnd).unwrap();
}
}
pub fn vec_to_string(v: &[u8]) -> String {
String::from_utf8_lossy(v).to_string()
}
pub fn opt_to_string<T: ToString>(item: Option<T>) -> String {
match item {
Some(s) => s.to_string(),
None => String::new(),
}
}
pub fn systime_to_string(time: &SystemTime) -> String {
DateTime::<Local>::from(*time)
.format("%d/%m/%Y %T")
.to_string()
}
pub fn init_env() {
let envs = [".env"];
for envfile in &envs {
dotenv::from_filename(envfile).ok();
}
}

@ -0,0 +1,40 @@
use crate::utils::OneOrVec;
use crate::UError;
pub struct CombinedResult<T, E = UError> {
ok: Vec<T>,
err: Vec<E>,
}
impl<T, E> CombinedResult<T, E> {
pub fn new() -> Self {
Self {
ok: vec![],
err: vec![],
}
}
pub fn ok<I: OneOrVec<T>>(&mut self, result: I) {
self.ok.extend(result.into_vec());
}
pub fn err<I: OneOrVec<E>>(&mut self, err: I) {
self.err.extend(err.into_vec());
}
pub fn unwrap(self) -> Vec<T> {
let err_len = self.err.len();
if err_len > 0 {
panic!("CombinedResult has {} errors", err_len);
}
self.ok
}
pub fn unwrap_one(self) -> T {
self.unwrap().pop().unwrap()
}
pub fn pop_errors(&mut self) -> Vec<E> {
self.err.drain(..).collect()
}
}

@ -0,0 +1,19 @@
use chrono::{offset::Local, DateTime};
use std::time::SystemTime;
pub fn bytes_to_string(v: &[u8]) -> String {
String::from_utf8_lossy(v).to_string()
}
pub fn opt_to_string<T: ToString>(item: Option<T>) -> String {
match item {
Some(s) => s.to_string(),
None => String::new(),
}
}
pub fn systime_to_string(time: &SystemTime) -> String {
DateTime::<Local>::from(*time)
.format("%d/%m/%Y %T")
.to_string()
}

@ -0,0 +1,71 @@
use nix::{
sys::signal::{signal, SigHandler, Signal},
unistd::{chdir, close as fdclose, fork, getppid, setsid, ForkResult},
};
use std::process::exit;
pub trait OneOrVec<T> {
fn into_vec(self) -> Vec<T>;
}
impl<T> OneOrVec<T> for T {
fn into_vec(self) -> Vec<T> {
vec![self]
}
}
impl<T> OneOrVec<T> for Vec<T> {
fn into_vec(self) -> Vec<T> {
self
}
}
#[macro_export]
macro_rules! unwrap_enum {
($src:ident, $t:path) => {
if let $t(result) = $src {
result
} else {
panic!("wrong type")
}
};
}
pub fn daemonize() {
if getppid().as_raw() != 1 {
setsig(Signal::SIGTTOU, SigHandler::SigIgn);
setsig(Signal::SIGTTIN, SigHandler::SigIgn);
setsig(Signal::SIGTSTP, SigHandler::SigIgn);
}
for fd in 0..=2 {
match fdclose(fd) {
_ => (),
}
}
match chdir("/") {
_ => (),
};
match fork() {
Ok(ForkResult::Parent { .. }) => {
exit(0);
}
Ok(ForkResult::Child) => match setsid() {
_ => (),
},
Err(_) => exit(255),
}
}
pub fn setsig(sig: Signal, hnd: SigHandler) {
unsafe {
signal(sig, hnd).unwrap();
}
}
pub fn init_env() {
let envs = [".env"];
for envfile in &envs {
dotenv::from_filename(envfile).ok();
}
}

@ -0,0 +1,11 @@
pub mod combined_result;
pub mod conv;
pub mod misc;
pub mod tempfile;
pub mod vec_display;
pub use combined_result::*;
pub use conv::*;
pub use misc::*;
pub use tempfile::*;
pub use vec_display::*;

@ -0,0 +1,38 @@
use std::{env::temp_dir, fs, ops::Drop, os::unix::fs::PermissionsExt, path::PathBuf};
use uuid::Uuid;
pub struct TempFile {
path: PathBuf,
}
impl TempFile {
pub fn get_path(&self) -> String {
self.path.to_string_lossy().to_string()
}
pub fn new() -> Self {
let name = Uuid::simple(&Uuid::new_v4()).to_string();
let mut path = temp_dir();
path.push(name);
Self { path }
}
pub fn write_all(&self, data: &[u8]) -> Result<(), String> {
fs::write(&self.path, data).map_err(|e| e.to_string())
}
pub fn write_exec(data: &[u8]) -> Result<Self, (String, String)> {
let this = Self::new();
let path = this.get_path();
this.write_all(data).map_err(|e| (path.clone(), e))?;
let perms = fs::Permissions::from_mode(0o555);
fs::set_permissions(&path, perms).map_err(|e| (path, e.to_string()))?;
Ok(this)
}
}
impl Drop for TempFile {
fn drop(&mut self) {
fs::remove_file(&self.path).ok();
}
}

@ -0,0 +1,40 @@
use crate::{messaging::AsMsg, utils::OneOrVec};
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
use std::ops::{Deref, DerefMut};
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct VecDisplay<T: AsMsg + Display>(pub Vec<T>);
impl<T: AsMsg + Display> VecDisplay<T> {
pub fn new<I: OneOrVec<T>>(inner: I) -> Self {
VecDisplay(inner.into_vec())
}
pub fn into_builtin_vec(self) -> Vec<T> {
self.0
}
}
impl<T: AsMsg + Display> Deref for VecDisplay<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: AsMsg + Display> DerefMut for VecDisplay<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: AsMsg + Display> Display for VecDisplay<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (i, itm) in self.0.iter().enumerate() {
writeln!(f, "### {}:\n{}\n", i, itm)?;
}
Ok(())
}
}

@ -1,6 +1,7 @@
#!/bin/bash
set -e
source $(dirname $0)/rootdir.sh #set ROOTDIR
umask 002
docker run \
-v $ROOTDIR:/volume \
-v cargo-cache:/root/.cargo/registry \
@ -8,4 +9,3 @@ docker run \
-it \
unki/musllibs \
cargo $@

Loading…
Cancel
Save