From 3bf8e868f4afdb0889ffac2a7901021a3d391bf9 Mon Sep 17 00:00:00 2001 From: ortem Date: Sat, 24 Jul 2021 17:46:25 +0000 Subject: [PATCH] Resolve "Integration tests" --- .cargo/config.toml | 3 - .env | 4 +- .gitignore | 8 +- Cargo.toml | 6 +- Makefile | 18 ++ bin/u_agent/Cargo.toml | 1 + bin/u_agent/src/lib.rs | 88 +++++++++ bin/u_agent/src/main.rs | 88 +-------- bin/u_panel/Cargo.toml | 3 + bin/u_panel/src/main.rs | 110 ++++++----- bin/u_server/Cargo.toml | 16 +- bin/u_server/src/db.rs | 34 ++-- bin/u_server/src/filters.rs | 88 +++++++++ bin/u_server/src/handlers.rs | 220 ++++++++++++--------- bin/u_server/src/lib.rs | 102 ++++++++++ bin/u_server/src/main.rs | 123 +----------- integration/Cargo.toml | 23 +++ integration/docker-compose.yml | 89 +++++++++ integration/docker.py | 81 ++++++++ integration/docker_compose.py | 66 +++++++ integration/images/tests_runner.Dockerfile | 4 + integration/images/u_agent.Dockerfile | 3 + integration/images/u_db.Dockerfile | 3 + integration/images/u_server.Dockerfile | 3 + integration/integration_tests.py | 36 ++++ integration/integration_tests.sh | 3 + integration/src/main.rs | 3 + integration/tests/behaviour.rs | 1 + integration/tests/helpers/client.rs | 48 +++++ integration/tests/helpers/mod.rs | 5 + integration/tests/helpers/panel.rs | 53 +++++ integration/tests/tests.rs | 80 ++++++++ integration/utils.py | 32 +++ lib/u_lib/Cargo.toml | 3 + lib/u_lib/build.rs | 14 ++ lib/u_lib/src/api.rs | 27 ++- lib/u_lib/src/builder.rs | 179 ++++++++++++++++- lib/u_lib/src/config.rs | 5 +- lib/u_lib/src/executor.rs | 6 +- lib/u_lib/src/lib.rs | 3 + lib/u_lib/src/messaging.rs | 6 +- lib/u_lib/src/models/agent.rs | 3 +- lib/u_lib/src/models/jobs/misc.rs | 48 +++++ lib/u_lib/src/models/mod.rs | 13 +- lib/u_lib/src/utils.rs | 161 --------------- lib/u_lib/src/utils/combined_result.rs | 40 ++++ lib/u_lib/src/utils/conv.rs | 19 ++ lib/u_lib/src/utils/misc.rs | 71 +++++++ lib/u_lib/src/utils/mod.rs | 11 ++ lib/u_lib/src/utils/tempfile.rs | 38 ++++ lib/u_lib/src/utils/vec_display.rs | 40 ++++ lib/u_lib/tests/api_macro.rs | 17 -- lib/u_lib/tests/fixtures/echoer | Bin 313672 -> 0 bytes lib/u_lib/tests/jobs/execution.rs | 161 --------------- lib/u_lib/tests/jobs/output.rs | 43 ---- lib/u_lib/tests/tests.rs | 10 - scripts/cargo_musl.sh | 3 +- 57 files changed, 1569 insertions(+), 797 deletions(-) delete mode 100644 .cargo/config.toml create mode 100644 Makefile create mode 100644 bin/u_agent/src/lib.rs create mode 100644 bin/u_server/src/filters.rs create mode 100644 bin/u_server/src/lib.rs create mode 100644 integration/Cargo.toml create mode 100644 integration/docker-compose.yml create mode 100644 integration/docker.py create mode 100644 integration/docker_compose.py create mode 100644 integration/images/tests_runner.Dockerfile create mode 100644 integration/images/u_agent.Dockerfile create mode 100644 integration/images/u_db.Dockerfile create mode 100644 integration/images/u_server.Dockerfile create mode 100644 integration/integration_tests.py create mode 100755 integration/integration_tests.sh create mode 100644 integration/src/main.rs create mode 100644 integration/tests/behaviour.rs create mode 100644 integration/tests/helpers/client.rs create mode 100644 integration/tests/helpers/mod.rs create mode 100644 integration/tests/helpers/panel.rs create mode 100644 integration/tests/tests.rs create mode 100644 integration/utils.py create mode 100644 lib/u_lib/build.rs delete mode 100644 lib/u_lib/src/utils.rs create mode 100644 lib/u_lib/src/utils/combined_result.rs create mode 100644 lib/u_lib/src/utils/conv.rs create mode 100644 lib/u_lib/src/utils/misc.rs create mode 100644 lib/u_lib/src/utils/mod.rs create mode 100644 lib/u_lib/src/utils/tempfile.rs create mode 100644 lib/u_lib/src/utils/vec_display.rs delete mode 100644 lib/u_lib/tests/api_macro.rs delete mode 100755 lib/u_lib/tests/fixtures/echoer delete mode 100644 lib/u_lib/tests/jobs/execution.rs delete mode 100644 lib/u_lib/tests/jobs/output.rs delete mode 100644 lib/u_lib/tests/tests.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 664ed6c..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build] -target = "x86_64-unknown-linux-gnu" # -musl" - diff --git a/.env b/.env index 2ac5089..001cab7 100644 --- a/.env +++ b/.env @@ -1 +1,3 @@ -export DATABASE_URL=postgres://postgres:12348756@172.17.0.2/u_db +ADMIN_AUTH_TOKEN=464af63dbd241969baa1e94b2461d94d +POSTGRES_PASSWORD=12348756 +DATABASE_URL=postgres://postgres:${POSTGRES_PASSWORD}@u_db/u_db diff --git a/.gitignore b/.gitignore index f8abd6f..fa726f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -/target +target/ **/*.rs.bk -/.idea -/data \ No newline at end of file +.idea/ +data/ +static/ +**/*.pyc \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index e1a1cda..ede90c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ members = [ "bin/u_run", "bin/u_server", "lib/u_lib", - "lib/u_api_proc_macro" + "lib/u_api_proc_macro", + "integration" ] [profile.release] @@ -13,5 +14,4 @@ panic = "abort" [profile.dev] debug = true # Добавляет флаг `-g` для компилятора; -opt-level = 0 - +opt-level = 0 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8c51d78 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +.PHONY: _pre_build debug release run clean + +CARGO=./scripts/cargo_musl.sh + +clean: + ${CARGO} clean + +_pre_build: + docker build -t unki/musllibs ./muslrust + +debug: _pre_build + ${CARGO} build + +release: _pre_build + ${CARGO} build --release + +run: build + ${CARGO} run \ No newline at end of file diff --git a/bin/u_agent/Cargo.toml b/bin/u_agent/Cargo.toml index a0fa747..c963c2b 100644 --- a/bin/u_agent/Cargo.toml +++ b/bin/u_agent/Cargo.toml @@ -13,4 +13,5 @@ log = "^0.4" env_logger = "0.8.3" uuid = "0.6.5" reqwest = { version = "0.11", features = ["json"] } +openssl = "*" u_lib = { version = "*", path = "../../lib/u_lib" } \ No newline at end of file diff --git a/bin/u_agent/src/lib.rs b/bin/u_agent/src/lib.rs new file mode 100644 index 0000000..876a1b6 --- /dev/null +++ b/bin/u_agent/src/lib.rs @@ -0,0 +1,88 @@ +// TODO: +// поддержка питона +// резолв адреса управляющего сервера через DoT +// кроссплатформенность (реализовать интерфейс для винды и никсов) +// проверка обнов +// самоуничтожение + +#[macro_use] +extern crate log; +extern crate env_logger; + +use std::env; +use tokio::time::{sleep, Duration}; +use u_lib::{ + api::ClientHandler, + builder::JobBuilder, + cache::JobCache, + executor::pop_completed, + models::{AssignedJob, ExecResult}, + UID, + //daemonize +}; + +#[macro_export] +macro_rules! retry_until_ok { + ( $body:expr ) => { + loop { + match $body { + Ok(r) => break r, + Err(e) => error!("{:?}", e), + }; + sleep(Duration::from_secs(5)).await; + } + }; +} + +pub async fn process_request(job_requests: Vec, client: &ClientHandler) { + if job_requests.len() > 0 { + for jr in &job_requests { + if !JobCache::contains(&jr.job_id) { + info!("Fetching job: {}", &jr.job_id); + let fetched_job = retry_until_ok!(client.get_jobs(Some(jr.job_id)).await) + .pop() + .unwrap(); + JobCache::insert(fetched_job); + } + } + info!( + "Scheduling jobs: \n{}", + job_requests + .iter() + .map(|j| j.job_id.to_string()) + .collect::>() + .join("\n") + ); + let mut builder = JobBuilder::from_request(job_requests); + let errors = builder.pop_errors(); + if errors.len() > 0 { + error!( + "Some errors encountered: \n{}", + errors + .iter() + .map(|j| j.to_string()) + .collect::>() + .join("\n") + ); + } + builder.unwrap_one().spawn().await; + } +} + +pub async fn run_forever() { + //daemonize(); + env_logger::init(); + let arg_ip = env::args().nth(1); + let instance = ClientHandler::new(arg_ip.as_deref()); + info!("Connecting to the server"); + loop { + let job_requests: Vec = + retry_until_ok!(instance.get_agent_jobs(Some(*UID)).await).into_builtin_vec(); + process_request(job_requests, &instance).await; + let result: Vec = pop_completed().await.into_iter().collect(); + if result.len() > 0 { + retry_until_ok!(instance.report(&result).await); + } + sleep(Duration::from_secs(5)).await; + } +} diff --git a/bin/u_agent/src/main.rs b/bin/u_agent/src/main.rs index a854863..43166a8 100644 --- a/bin/u_agent/src/main.rs +++ b/bin/u_agent/src/main.rs @@ -1,89 +1,7 @@ -// TODO: -// поддержка питона -// резолв адреса управляющего сервера через DoT -// кроссплатформенность (реализовать интерфейс для винды и никсов) -// проверка обнов -// самоуничтожение - -#[macro_use] -extern crate log; -extern crate env_logger; - -use std::env; -use tokio::time::{sleep, Duration}; -use u_lib::{ - api::ClientHandler, - builder::JobBuilder, - cache::JobCache, - executor::pop_completed, - models::{AssignedJob, ExecResult}, - UID, - //daemonize -}; - -#[macro_export] -macro_rules! retry_until_ok { - ( $body:expr ) => { - loop { - match $body { - Ok(r) => break r, - Err(e) => error!("{:?}", e), - }; - sleep(Duration::from_secs(5)).await; - } - }; -} - -async fn process_request(job_requests: Vec, client: &ClientHandler) { - if job_requests.len() > 0 { - for jr in &job_requests { - if !JobCache::contains(&jr.job_id) { - info!("Fetching job: {}", &jr.job_id); - let fetched_job = retry_until_ok!(client.get_jobs(Some(jr.job_id)).await) - .pop() - .unwrap(); - JobCache::insert(fetched_job); - } - } - info!( - "Scheduling jobs: \n{}", - job_requests - .iter() - .map(|j| j.job_id.to_string()) - .collect::>() - .join("\n") - ); - let mut builder = JobBuilder::from_request(job_requests); - let errors = builder.pop_errors(); - if errors.len() > 0 { - error!( - "Some errors encountered: \n{}", - errors - .iter() - .map(|j| j.to_string()) - .collect::>() - .join("\n") - ); - } - builder.unwrap_one().spawn().await; - } -} +use tokio; +use u_agent::run_forever; #[tokio::main] async fn main() { - //daemonize(); - env_logger::init(); - let arg_ip = env::args().nth(1); - let instance = ClientHandler::new(arg_ip); - info!("Connecting to the server"); - loop { - let job_requests: Vec = - retry_until_ok!(instance.get_agent_jobs(Some(*UID)).await); - process_request(job_requests, &instance).await; - let result: Vec = pop_completed().await.into_iter().collect(); - if result.len() > 0 { - retry_until_ok!(instance.report(&result).await) - } - sleep(Duration::from_secs(5)).await; - } + run_forever().await; } diff --git a/bin/u_panel/Cargo.toml b/bin/u_panel/Cargo.toml index 734d36c..1f15786 100644 --- a/bin/u_panel/Cargo.toml +++ b/bin/u_panel/Cargo.toml @@ -13,4 +13,7 @@ log = "^0.4" env_logger = "0.7.1" 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"] } diff --git a/bin/u_panel/src/main.rs b/bin/u_panel/src/main.rs index e6b8990..f6335a2 100644 --- a/bin/u_panel/src/main.rs +++ b/bin/u_panel/src/main.rs @@ -1,20 +1,25 @@ +use serde::Serialize; +use std::env; +use std::fmt; use structopt::StructOpt; -use u_lib::{api::ClientHandler, models::JobMeta, 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)] enum Cmd { Agents(LD), Jobs(JobALD), - Jobmap(JmALD), + Jobmap(JobMapALD), } #[derive(StructOpt, Debug)] @@ -23,6 +28,9 @@ enum JobALD { #[structopt(long, parse(try_from_str = parse_uuid))] agent: Option, + #[structopt(long)] + alias: String, + #[structopt(subcommand)] cmd: JobCmd, }, @@ -37,13 +45,12 @@ enum JobCmd { } #[derive(StructOpt, Debug)] -enum JmALD { +enum JobMapALD { Add { #[structopt(parse(try_from_str = parse_uuid))] agent_uid: Uuid, - #[structopt(parse(try_from_str = parse_uuid))] - job_uids: Vec, + job_idents: Vec, }, List { #[structopt(parse(try_from_str = parse_uuid))] @@ -71,58 +78,71 @@ fn parse_uuid(src: &str) -> Result { Uuid::parse_str(src).map_err(|e| e.to_string()) } -async fn process_cmd(cmd: Cmd) -> Result<(), UError> { - let cli_handler = ClientHandler::new(None).password("123qwe".to_string()); - match 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?); +async fn process_cmd(args: Args) { + fn printer(data: UResult, json: bool) { + if json { + #[derive(Serialize)] + #[serde(rename_all = "lowercase")] + #[serde(tag = "status", content = "data")] + enum DataResult { + 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); + let json = args.json; + match args.cmd { + Cmd::Agents(action) => match action { + 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, + alias, + 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::builder() + .with_shell(cmd.join(" ")) + .with_alias(alias) + .build() + .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 { + JobMapALD::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?), + job_idents, + } => printer( + cli_handler.set_jobs(Some(agent_uid), &job_idents).await, + json, + ), + JobMapALD::List { uid } => printer(cli_handler.get_agent_jobs(uid).await, json), + JobMapALD::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; } diff --git a/bin/u_server/Cargo.toml b/bin/u_server/Cargo.toml index 6ef7838..96a3651 100644 --- a/bin/u_server/Cargo.toml +++ b/bin/u_server/Cargo.toml @@ -5,14 +5,16 @@ name = "u_server" version = "0.1.0" [dependencies] -dotenv = "0.15.0" -env_logger = "0.7.1" log = "0.4.11" +simplelog = "0.10" thiserror = "*" warp = "0.2.4" uuid = { version = "0.6.5", features = ["serde", "v4"] } once_cell = "1.7.2" hyper = "0.13.10" +mockall = "0.9.1" +mockall_double = "0.2" +openssl = "*" [dependencies.diesel] features = ["postgres", "uuid"] @@ -30,3 +32,13 @@ version = "0.2.22" path = "../../lib/u_lib" version = "*" +[dev-dependencies] +test-case = "1.1.0" + +[lib] +name = "u_server_lib" +path = "src/lib.rs" + +[[bin]] +name = "u_server" +path = "src/main.rs" \ No newline at end of file diff --git a/bin/u_server/src/db.rs b/bin/u_server/src/db.rs index 1302443..c104968 100644 --- a/bin/u_server/src/db.rs +++ b/bin/u_server/src/db.rs @@ -1,5 +1,4 @@ use diesel::{pg::PgConnection, prelude::*, result::Error as DslError}; -use dotenv::dotenv; use once_cell::sync::OnceCell; use std::{ env, @@ -17,19 +16,19 @@ pub struct UDB { static DB: OnceCell>> = OnceCell::new(); -pub fn lock_db() -> MutexGuard<'static, UDB> { - DB.get_or_init(|| { - dotenv().unwrap(); - let db_path = env::var("DATABASE_URL").unwrap(); - let conn = PgConnection::establish(&db_path).unwrap(); - let instance = UDB { conn }; - Arc::new(Mutex::new(instance)) - }) - .lock() - .unwrap() -} - +#[cfg_attr(test, automock)] impl UDB { + pub fn lock_db() -> MutexGuard<'static, UDB> { + DB.get_or_init(|| { + let db_path = env::var("DATABASE_URL").unwrap(); + let conn = PgConnection::establish(&db_path).unwrap(); + let instance = UDB { conn }; + Arc::new(Mutex::new(instance)) + }) + .lock() + .unwrap() + } + pub fn insert_jobs(&self, job_metas: &[JobMeta]) -> ULocalResult<()> { use schema::jobs; diesel::insert_into(jobs::table) @@ -117,7 +116,11 @@ impl UDB { Ok(result) } - pub fn set_jobs_for_agent(&self, agent_uid: &Uuid, job_uids: &[Uuid]) -> ULocalResult<()> { + pub fn set_jobs_for_agent( + &self, + agent_uid: &Uuid, + job_uids: &[Uuid], + ) -> ULocalResult> { use schema::{agents::dsl::agents, jobs::dsl::jobs, results}; if let Err(DslError::NotFound) = agents.find(agent_uid).first::(&self.conn) { return Err(ULocalError::NotFound(agent_uid.to_string())); @@ -146,7 +149,8 @@ impl UDB { diesel::insert_into(results::table) .values(&job_requests) .execute(&self.conn)?; - Ok(()) + let assigned_uids = job_requests.iter().map(|aj| aj.id).collect(); + Ok(assigned_uids) } pub fn del_jobs(&self, uids: &Vec) -> ULocalResult { diff --git a/bin/u_server/src/filters.rs b/bin/u_server/src/filters.rs new file mode 100644 index 0000000..a9c51f0 --- /dev/null +++ b/bin/u_server/src/filters.rs @@ -0,0 +1,88 @@ +use crate::handlers::Endpoints; +use serde::de::DeserializeOwned; +use std::env; +use u_lib::{ + messaging::{AsMsg, BaseMessage}, + models::*, +}; +use uuid::Uuid; +use warp::{body, Filter, Rejection, Reply}; + +fn get_content() -> impl Filter,), Error = Rejection> + Clone +where + M: AsMsg + Sync + Send + DeserializeOwned + 'static, +{ + body::content_length_limit(1024 * 64).and(body::json::>()) +} + +pub fn make_filters() -> impl Filter + Clone { + let infallible_none = |_| async { Ok::<(Option,), std::convert::Infallible>((None,)) }; + + let get_agents = warp::get() + .and(warp::path("get_agents")) + .and( + warp::path::param::() + .map(Some) + .or_else(infallible_none), + ) + .and_then(Endpoints::get_agents); + + let upload_jobs = warp::post() + .and(warp::path("upload_jobs")) + .and(get_content::>()) + .and_then(Endpoints::upload_jobs); + + let get_jobs = warp::get() + .and(warp::path("get_jobs")) + .and( + warp::path::param::() + .map(Some) + .or_else(infallible_none), + ) + .and_then(Endpoints::get_jobs); + + let get_agent_jobs = warp::get() + .and(warp::path("get_agent_jobs")) + .and( + warp::path::param::() + .map(Some) + .or_else(infallible_none), + ) + .and_then(|uid| Endpoints::get_agent_jobs(uid, false)); + + let get_personal_jobs = warp::get() + .and(warp::path("get_agent_jobs")) + .and(warp::path::param::().map(Some)) + .and_then(|uid| Endpoints::get_agent_jobs(uid, true)); + + let del = warp::get() + .and(warp::path("del")) + .and(warp::path::param::()) + .and_then(Endpoints::del); + + let set_jobs = warp::post() + .and(warp::path("set_jobs")) + .and(warp::path::param::()) + .and(get_content::>()) + .and_then(Endpoints::set_jobs); + + let report = warp::post() + .and(warp::path("report")) + .and(get_content::>().and_then(Endpoints::report)); + + let auth_token = format!("Bearer {}", env::var("ADMIN_AUTH_TOKEN").unwrap()).into_boxed_str(); + 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 = auth_header.and( + get_agents + .or(get_jobs) + .or(upload_jobs) + .or(del) + .or(set_jobs) + .or(get_agent_jobs), + ); + + agent_zone.or(auth_zone) +} diff --git a/bin/u_server/src/handlers.rs b/bin/u_server/src/handlers.rs index df6e58f..f792188 100644 --- a/bin/u_server/src/handlers.rs +++ b/bin/u_server/src/handlers.rs @@ -1,10 +1,10 @@ -use crate::db::{lock_db, UDB}; +use crate::db::UDB; use diesel::SaveChangesDsl; use hyper::Body; use serde::Serialize; use u_lib::{ messaging::{AsMsg, BaseMessage}, - models::{Agent, AssignedJob, ExecResult, JobMeta, JobState}, + models::{Agent, AgentState, AssignedJob, ExecResult, JobMeta, JobState}, ULocalError, }; use uuid::Uuid; @@ -13,130 +13,156 @@ use warp::{ Rejection, Reply, }; -fn build_response>(code: StatusCode, body: S) -> Response { +pub fn build_response>(code: StatusCode, body: S) -> Response { Response::builder().status(code).body(body.into()).unwrap() } -fn build_ok>(body: S) -> Response { +pub fn build_ok>(body: S) -> Response { build_response(StatusCode::OK, body) } -fn build_err(body: S) -> Response { +pub fn build_err(body: S) -> Response { build_response(StatusCode::BAD_REQUEST, body.to_string()) } -fn build_message(m: M) -> Response { +pub fn build_message(m: M) -> Response { warp::reply::json(&m.as_message()).into_response() } -pub async fn add_agent(msg: Agent) -> Result { - debug!("hnd: add_agent"); - lock_db() - .insert_agent(&msg) - .map(|_| build_ok("")) - .or_else(|e| Ok(build_err(e))) -} +pub struct Endpoints; -pub async fn get_agents(uid: Option) -> Result { - debug!("hnd: get_agents"); - lock_db() - .get_agents(uid) - .map(|m| build_message(m)) - .or_else(|e| Ok(build_err(e))) -} +#[cfg_attr(test, automock)] +impl Endpoints { + pub async fn add_agent(msg: Agent) -> Result, Rejection> { + info!("hnd: add_agent"); + UDB::lock_db() + .insert_agent(&msg) + .map(|_| build_ok("")) + .or_else(|e| Ok(build_err(e))) + } -pub async fn get_jobs(uid: Option) -> Result { - debug!("hnd: get_jobs"); - lock_db() - .get_jobs(uid) - .map(|m| build_message(m)) - .or_else(|e| Ok(build_err(e))) -} + pub async fn get_agents(uid: Option) -> Result, Rejection> { + info!("hnd: get_agents"); + UDB::lock_db() + .get_agents(uid) + .map(|m| build_message(m)) + .or_else(|e| Ok(build_err(e))) + } -pub async fn get_agent_jobs(uid: Option, personal: bool) -> Result { - info!("hnd: get_agent_jobs {}", personal); - if personal { - let agents = lock_db().get_agents(uid).unwrap(); - if agents.len() == 0 { - let db = lock_db(); - db.insert_agent(&Agent::with_id(uid.unwrap())).unwrap(); - let job = db.find_job_by_alias("agent_hello").unwrap(); - if let Err(e) = db.set_jobs_for_agent(&uid.unwrap(), &[job.id]) { - return Ok(build_err(e)); + pub async fn get_jobs(uid: Option) -> Result, Rejection> { + info!("hnd: get_jobs"); + UDB::lock_db() + .get_jobs(uid) + .map(|m| build_message(m)) + .or_else(|e| Ok(build_err(e))) + } + + pub async fn get_agent_jobs( + uid: Option, + personal: bool, + ) -> Result, Rejection> { + info!("hnd: get_agent_jobs {}", personal); + if personal { + let agents = UDB::lock_db().get_agents(uid).unwrap(); + if agents.len() == 0 { + let db = UDB::lock_db(); + db.insert_agent(&Agent::with_id(uid.unwrap())).unwrap(); + let job = db.find_job_by_alias("agent_hello").unwrap(); + if let Err(e) = db.set_jobs_for_agent(&uid.unwrap(), &[job.id]) { + return Ok(build_err(e)); + } } } - } - let result = lock_db().get_exact_jobs(uid, personal); - match result { - Ok(r) => { - let db = lock_db(); - for j in r.iter() { - db.update_job_status(j.id, JobState::Running).ok(); + let result = UDB::lock_db().get_exact_jobs(uid, personal); + match result { + Ok(r) => { + if personal { + let db = UDB::lock_db(); + for j in r.iter() { + db.update_job_status(j.id, JobState::Running).ok(); + } + } + Ok(build_message(r)) } - Ok(build_message(r)) + Err(e) => Ok(build_err(e)), } - Err(e) => Ok(build_err(e)), } -} -pub async fn upload_jobs(msg: BaseMessage<'_, Vec>) -> Result { - debug!("hnd: upload_jobs"); - lock_db() - .insert_jobs(&msg.into_inner()) - .map(|_| build_ok("")) - .or_else(|e| Ok(build_err(e))) -} + pub async fn upload_jobs( + msg: BaseMessage<'static, Vec>, + ) -> Result, Rejection> { + info!("hnd: upload_jobs"); + UDB::lock_db() + .insert_jobs(&msg.into_inner()) + .map(|_| build_ok("")) + .or_else(|e| Ok(build_err(e))) + } -pub async fn del(uid: Uuid) -> Result { - debug!("hnd: del"); - let db = lock_db(); - let del_fns = &[UDB::del_agents, UDB::del_jobs, UDB::del_results]; - for del_fn in del_fns { - let affected = del_fn(&db, &vec![uid]).unwrap(); - if affected > 0 { - return Ok(build_ok(affected.to_string())); + pub async fn del(uid: Uuid) -> Result, Rejection> { + info!("hnd: del"); + let db = UDB::lock_db(); + let del_fns = &[UDB::del_agents, UDB::del_jobs, UDB::del_results]; + for del_fn in del_fns { + let affected = del_fn(&db, &vec![uid]).unwrap(); + if affected > 0 { + return Ok(build_ok(affected.to_string())); + } } + Ok(build_err("0")) } - Ok(build_err("0")) -} -pub async fn set_jobs( - agent_uid: Uuid, - msg: BaseMessage<'_, Vec>, -) -> Result { - debug!("hnd: set_jobs"); - lock_db() - .set_jobs_for_agent(&agent_uid, &msg.into_inner()) - .map(|_| build_ok("")) - .or_else(|e| Ok(build_err(e))) -} + pub async fn set_jobs( + agent_uid: Uuid, + msg: BaseMessage<'static, Vec>, + ) -> Result, Rejection> { + info!("hnd: set_jobs_by_alias"); + let jobs: Result, ULocalError> = msg + .into_inner() + .into_iter() + .map(|ident| { + Uuid::parse_str(&ident) + .or_else(|_| UDB::lock_db().find_job_by_alias(&ident).map(|j| j.id)) + }) + .collect(); + match jobs { + Ok(j) => UDB::lock_db() + .set_jobs_for_agent(&agent_uid, &j) + .map(|assigned_uids| build_message(assigned_uids)) + .or_else(|e| Ok(build_err(e))), + Err(e) => Ok(build_err(e)), + } + } -pub async fn report(msg: BaseMessage<'_, Vec>) -> Result { - debug!("hnd: report"); - let id = msg.id; - let mut failed = vec![]; - for entry in msg.into_inner() { - match entry { - ExecResult::Assigned(res) => { - if id != res.agent_id { - continue; + pub async fn report( + msg: BaseMessage<'static, Vec>, + ) -> Result, Rejection> { + info!("hnd: report"); + let id = msg.id; + let mut failed = vec![]; + for entry in msg.into_inner() { + match entry { + ExecResult::Assigned(res) => { + if id != res.agent_id { + continue; + } + let db = UDB::lock_db(); + if let Err(e) = res + .save_changes::(&db.conn) + .map_err(ULocalError::from) + { + failed.push(e.to_string()) + } } - let db = lock_db(); - if let Err(e) = res - .save_changes::(&db.conn) - .map_err(ULocalError::from) - { - failed.push(e.to_string()) + ExecResult::Agent(mut a) => { + a.state = AgentState::Active; + Self::add_agent(a).await?; } } - ExecResult::Agent(a) => { - add_agent(a).await?; - } } + if failed.len() > 0 { + let err_msg = ULocalError::ProcessingError(failed.join(", ")); + return Ok(build_err(err_msg)); + } + Ok(build_ok("")) } - if failed.len() > 0 { - let err_msg = ULocalError::ProcessingError(failed.join(", ")); - return Ok(build_err(err_msg)); - } - Ok(build_ok("")) } diff --git a/bin/u_server/src/lib.rs b/bin/u_server/src/lib.rs new file mode 100644 index 0000000..6b24a9a --- /dev/null +++ b/bin/u_server/src/lib.rs @@ -0,0 +1,102 @@ +#[macro_use] +extern crate log; + +#[macro_use] +extern crate mockall; +#[macro_use] +extern crate mockall_double; + +// because of linking errors +extern crate openssl; +#[macro_use] +extern crate diesel; +// +mod db; +mod filters; +mod handlers; + +use db::UDB; +use filters::make_filters; +use u_lib::{config::MASTER_PORT, models::*, utils::init_env}; +use warp::Filter; + +const LOGFILE: &str = "u_server.log"; + +fn prefill_jobs() { + let agent_hello = JobMeta::builder() + .with_type(misc::JobType::Manage) + .with_alias("agent_hello") + .build() + .unwrap(); + UDB::lock_db().insert_jobs(&[agent_hello]).ok(); +} + +fn init_logger() { + use simplelog::*; + use std::fs::OpenOptions; + let log_cfg = ConfigBuilder::new() + .set_time_format_str("%x %X") + .set_time_to_local(true) + .build(); + let logfile = OpenOptions::new() + .append(true) + .create(true) + .open(LOGFILE) + .unwrap(); + let loggers = vec![ + WriteLogger::new(LevelFilter::Info, log_cfg.clone(), logfile) as Box, + TermLogger::new( + LevelFilter::Info, + log_cfg, + TerminalMode::Stderr, + ColorChoice::Auto, + ), + ]; + CombinedLogger::init(loggers).unwrap(); +} + +fn init_all() { + init_logger(); + init_env(); + prefill_jobs(); +} + +pub async fn serve() { + init_all(); + let routes = make_filters(); + warp::serve(routes.with(warp::log("warp"))) + .run(([0, 0, 0, 0], MASTER_PORT)) + .await; +} + +#[cfg(test)] +mod tests { + use super::*; + #[double] + use crate::handlers::Endpoints; + use handlers::build_ok; + use mockall::predicate::*; + use test_case::test_case; + use uuid::Uuid; + use warp::test::request; + + #[test_case(Some(Uuid::new_v4()))] + #[test_case(None => panics)] + #[tokio::test] + async fn test_get_agent_jobs_unauthorized(uid: Option) { + let mock = Endpoints::get_agent_jobs_context(); + mock.expect() + .with(eq(uid), eq(uid.is_some())) + .returning(|_, _| Ok(build_ok(""))); + request() + .path(&format!( + "/get_agent_jobs/{}", + uid.map(|u| u.simple().to_string()).unwrap_or(String::new()) + )) + .method("GET") + .filter(&make_filters()) + .await + .unwrap(); + mock.checkpoint(); + } +} diff --git a/bin/u_server/src/main.rs b/bin/u_server/src/main.rs index c606e09..05ec4e0 100644 --- a/bin/u_server/src/main.rs +++ b/bin/u_server/src/main.rs @@ -1,125 +1,6 @@ -mod db; -mod handlers; - -use warp::{body, Filter, Rejection}; - -#[macro_use] -extern crate log; -extern crate env_logger; - -use db::lock_db; -use serde::de::DeserializeOwned; -use u_lib::{ - config::MASTER_PORT, - messaging::{AsMsg, BaseMessage}, - models::*, -}; -use uuid::Uuid; - -fn get_content() -> impl Filter,), Error = Rejection> + Clone -where - M: AsMsg + Sync + Send + DeserializeOwned + 'static, -{ - body::content_length_limit(1024 * 64).and(body::json::>()) -} - -fn prefill_jobs() { - let agent_hello = JobMeta::builder() - .with_type(misc::JobType::Manage) - .with_alias("agent_hello") - .build() - .unwrap(); - lock_db().insert_jobs(&[agent_hello]).ok(); -} - -fn init() { - env_logger::init(); - prefill_jobs(); -} +use u_server_lib::serve; #[tokio::main] async fn main() { - init(); - let infallible_none = |_| async { Ok::<(Option,), std::convert::Infallible>((None,)) }; - - let get_agents = warp::get() - .and(warp::path("get_agents")) - .and( - warp::path::param::() - .map(Some) - .or_else(infallible_none), - ) - .and_then(handlers::get_agents); - - let upload_jobs = warp::post() - .and(warp::path("upload_jobs")) - .and(get_content::>()) - .and_then(handlers::upload_jobs); - - let get_jobs = warp::get() - .and(warp::path("get_jobs")) - .and( - warp::path::param::() - .map(Some) - .or_else(infallible_none), - ) - .and_then(handlers::get_jobs); - - let get_agent_jobs = warp::get() - .and(warp::path("get_agent_jobs")) - .and( - warp::path::param::() - .map(Some) - .or_else(infallible_none), - ) - .and_then(|uid| handlers::get_agent_jobs(uid, false)); - - let get_personal_jobs = warp::get() - .and(warp::path("get_agent_jobs")) - .and(warp::path::param::().map(Some)) - .and_then(|uid| handlers::get_agent_jobs(uid, true)); - - let del = warp::get() - .and(warp::path("del")) - .and(warp::path::param::()) - .and_then(handlers::del); - - let set_jobs = warp::post() - .and(warp::path("set_jobs")) - .and(warp::path::param::()) - .and(get_content::>()) - .and_then(handlers::set_jobs); - - let report = warp::post() - .and(warp::path("report")) - .and(get_content::>().and_then(handlers::report)); - - let auth_token = warp::header::exact("authorization", "Bearer 123qwe"); - - let agent_zone = get_jobs.clone().or(get_personal_jobs).or(report); - - let auth_zone = auth_token.and( - get_agents - .or(get_jobs) - .or(upload_jobs) - .or(del) - .or(set_jobs) - .or(get_agent_jobs), - ); - - let routes = agent_zone.or(auth_zone); - warp::serve(routes.with(warp::log("warp"))) - .run(([0, 0, 0, 0], MASTER_PORT)) - .await; -} -/* -#[cfg(test)] -mod tests { - use super::*; - /* - #[tokio::test] - async fn test_gather() { - } - */ + serve().await; } -*/ diff --git a/integration/Cargo.toml b/integration/Cargo.toml new file mode 100644 index 0000000..dfccd4c --- /dev/null +++ b/integration/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "integration" +version = "0.1.0" +authors = ["plazmoid "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread", "process", "time"] } +log = "^0.4" +env_logger = "0.8.3" +uuid = { version = "0.6.5", features = ["serde", "v4"] } +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]] +name = "integration" +path = "tests/tests.rs" \ No newline at end of file diff --git a/integration/docker-compose.yml b/integration/docker-compose.yml new file mode 100644 index 0000000..bd83d3e --- /dev/null +++ b/integration/docker-compose.yml @@ -0,0 +1,89 @@ +version: "2.1" + +networks: + u_net: + +services: + + u_server: + image: unki/u_server + networks: + - u_net + volumes: + - ../target/x86_64-unknown-linux-musl/release/u_server:/u_server + - ../:/unki/ + working_dir: /unki + command: bash -c "diesel setup && diesel migration run && /u_server" + depends_on: + u_db: + condition: service_healthy + expose: + - '63714' + environment: + RUST_LOG: warp=info + env_file: + - ../.env + healthcheck: + test: /bin/ss -tlpn | grep 63714 + interval: 5s + timeout: 2s + retries: 2 + + u_db: + image: unki/u_db + networks: + - u_net + expose: + - '5432' + env_file: + - ../.env + healthcheck: + test: /bin/ss -tlpn | grep 5432 + interval: 5s + timeout: 2s + retries: 2 + + u_agent_1: + image: unki/u_agent + networks: + - u_net + volumes: + - ../target/x86_64-unknown-linux-musl/release/u_agent:/u_agent + command: /u_agent u_server + depends_on: + u_server: + condition: service_healthy + + u_agent_2: + image: unki/u_agent + networks: + - u_net + volumes: + - ../target/x86_64-unknown-linux-musl/release/u_agent:/u_agent + command: /u_agent u_server + depends_on: + u_server: + condition: service_healthy + + tests_runner: + image: unki/tests_runner + networks: + - u_net + volumes: + - ./:/tests/ + - ../target/x86_64-unknown-linux-musl/release/u_panel:/u_panel + - ~/.cargo/registry:/root/.cargo/registry + working_dir: + /tests/ + env_file: + - ../.env + depends_on: + u_agent_1: + condition: service_started + u_agent_2: + condition: service_started + u_server: + condition: service_healthy + environment: + RUST_BACKTRACE: 1 + U_SERVER: u_server \ No newline at end of file diff --git a/integration/docker.py b/integration/docker.py new file mode 100644 index 0000000..68abd69 --- /dev/null +++ b/integration/docker.py @@ -0,0 +1,81 @@ +import subprocess +from utils import * + +BASE_IMAGE_DIR = 'images' + +DOCKERFILES = { + 'u_agent': { + 'ctx': BASE_IMAGE_DIR, + 'dockerfile_prefix': 'u_agent' + }, + 'u_server': { + 'ctx': BASE_IMAGE_DIR, + 'dockerfile_prefix': 'u_server' + }, + 'u_db': { + 'ctx': BASE_IMAGE_DIR, + 'dockerfile_prefix': 'u_db' + }, + 'tests_runner': { + 'ctx': BASE_IMAGE_DIR, + 'dockerfile_prefix': 'tests_runner' + }, +} + + +def docker(args): + cmd = ['docker'] + args + log(f'Running docker command: {cmd}') + return subprocess.run( + cmd, + check=True, + ) + + +def print_errors(errors): + err_msg = '\n'.join( + ' {container}: {error}'.format(container=item['container'], + error=item['error']) + for item in errors) + + err('There are some errors in next containers:\n%s' % err_msg) + + +def check_state(containers): + errors = [] + for container in containers: + ret, out = subprocess.getstatusoutput( + 'docker inspect --format \'{{ .State.Running }}\' %s' + % container) + out = out.strip() + if ret == 0: + if out == 'true': + continue + else: + errors.append({'container': container, + 'error': 'Bad state: Running=%s' % out}) + else: + errors.append({'container': container, + 'error': out}) + + return errors + + +def rebuild_images_if_needed(force_rebuild=False): + for img_name, data in DOCKERFILES.items(): + ctx = data['ctx'] + df_prefix = data.get('dockerfile_prefix') + df_suffix = 'Dockerfile' + img_name = f'unki/{img_name}' + log(f'Building docker image {img_name}') + cmd = [ + 'build', + '-t', + img_name, + ctx, + ] + if df_prefix: + cmd += ['-f', f'{ctx}/{df_prefix}.{df_suffix}'] + if force_rebuild: + cmd += ['--no-cache'] + docker(cmd) diff --git a/integration/docker_compose.py b/integration/docker_compose.py new file mode 100644 index 0000000..47e3916 --- /dev/null +++ b/integration/docker_compose.py @@ -0,0 +1,66 @@ +import subprocess +import shlex +from utils import * +from docker import docker, check_state, print_errors + + +class Compose: + ALL_CONTAINERS = [ + 'u_agent_1', + 'u_agent_2', + 'u_server', + 'u_db', + 'tests_runner', + ] + + def __init__(self): + self.container_tpl = 'integration_%s_1' + self.cmd_container = self.container_tpl % 'tests_runner' + self.ALL_CONTAINERS = [self.container_tpl % c for c in self.ALL_CONTAINERS] + + def _call(self, *args): + subprocess.check_call([ + 'docker-compose', + '--no-ansi', + ] + list(args) + ) + + def up(self): + log('Instanciating cluster') + self._call('up', '-d') + log('Ok') + + def down(self): + log('Shutting down cluster') + self._call('down') + log('Ok') + + def stop(self): + log('Stopping cluster') + self._call('stop') + log('Ok') + + def run(self, cmd): + container = self.cmd_container + if isinstance(cmd, str): + cmd = shlex.split(cmd) + log(f'Running command "{cmd}" in container {container}') + result = docker([ + 'exec', + '-ti', + container + ] + cmd) + log('Ok') + return result + + def is_alive(self): + log('Check if all containers are alive') + + errors = check_state(self.ALL_CONTAINERS) + log('Check done') + + if errors: + print_errors(errors) + raise TestsError('Error during `is_alive` check') + else: + log('All containers are alive') diff --git a/integration/images/tests_runner.Dockerfile b/integration/images/tests_runner.Dockerfile new file mode 100644 index 0000000..2411bd6 --- /dev/null +++ b/integration/images/tests_runner.Dockerfile @@ -0,0 +1,4 @@ +FROM rust:1.53 + +RUN rustup target add x86_64-unknown-linux-musl +CMD ["sleep", "3600"] \ No newline at end of file diff --git a/integration/images/u_agent.Dockerfile b/integration/images/u_agent.Dockerfile new file mode 100644 index 0000000..3058997 --- /dev/null +++ b/integration/images/u_agent.Dockerfile @@ -0,0 +1,3 @@ +FROM centos:7 + +RUN yum update -y \ No newline at end of file diff --git a/integration/images/u_db.Dockerfile b/integration/images/u_db.Dockerfile new file mode 100644 index 0000000..8577c8d --- /dev/null +++ b/integration/images/u_db.Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:13.3 + +RUN apt update && apt -y upgrade && apt install -y iproute2 \ No newline at end of file diff --git a/integration/images/u_server.Dockerfile b/integration/images/u_server.Dockerfile new file mode 100644 index 0000000..ea82ccd --- /dev/null +++ b/integration/images/u_server.Dockerfile @@ -0,0 +1,3 @@ +FROM rust:1.53 + +RUN cargo install diesel_cli --no-default-features --features postgres \ No newline at end of file diff --git a/integration/integration_tests.py b/integration/integration_tests.py new file mode 100644 index 0000000..e4fe460 --- /dev/null +++ b/integration/integration_tests.py @@ -0,0 +1,36 @@ +import signal +import sys +from utils import * +from docker import rebuild_images_if_needed +from docker_compose import Compose + + +cluster = Compose() + + +def abort_handler(s, _): + warn(f'Received signal: {s}') + warn(f'Gracefully stopping...') + cluster.down() + + +def run_tests(): + force_rebuild = '--rebuild' in sys.argv + preserve_containers = '--preserve' in sys.argv + for s in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP): + signal.signal(s, abort_handler) + rebuild_images_if_needed(force_rebuild) + try: + cluster.up() + cluster.is_alive() + cluster.run('cargo test --test integration') + except Exception as e: + err(e) + sys.exit(1) + finally: + if not preserve_containers: + cluster.down() + + +if __name__ == '__main__': + run_tests() diff --git a/integration/integration_tests.sh b/integration/integration_tests.sh new file mode 100755 index 0000000..3f3eca1 --- /dev/null +++ b/integration/integration_tests.sh @@ -0,0 +1,3 @@ +#!/bin/bash +set -e +python integration_tests.py $@ diff --git a/integration/src/main.rs b/integration/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/integration/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/integration/tests/behaviour.rs b/integration/tests/behaviour.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/integration/tests/behaviour.rs @@ -0,0 +1 @@ + diff --git a/integration/tests/helpers/client.rs b/integration/tests/helpers/client.rs new file mode 100644 index 0000000..01c5c89 --- /dev/null +++ b/integration/tests/helpers/client.rs @@ -0,0 +1,48 @@ +use reqwest::{Client, RequestBuilder, Url}; +use serde::Serialize; +use serde_json::{from_str, json, Value}; + +const SERVER: &str = "u_server"; +const PORT: &str = "63714"; + +pub struct AgentClient { + client: Client, + base_url: Url, +} + +impl AgentClient { + pub fn new() -> Self { + Self { + client: Client::new(), + base_url: Url::parse(&format!("http://{}:{}", SERVER, PORT)).unwrap(), + } + } + + async fn process_request(&self, req: RequestBuilder, resp_needed: bool) -> Value { + let resp = req.send().await.unwrap(); + if let Err(e) = resp.error_for_status_ref() { + panic!( + "Server responded with code {}\nError: {}", + e.status() + .map(|s| s.to_string()) + .unwrap_or(String::from("")), + e.to_string() + ); + } + if !resp_needed { + return json!([]); + } + let resp: Value = from_str(&resp.text().await.unwrap()).unwrap(); + resp.get("inner").unwrap().get(0).unwrap().clone() + } + + pub async fn get>(&self, url: S) -> Value { + let req = self.client.get(self.base_url.join(url.as_ref()).unwrap()); + self.process_request(req, true).await + } + + pub async fn post, B: Serialize>(&self, url: S, body: &B) -> Value { + let req = self.client.post(self.base_url.join(url.as_ref()).unwrap()); + self.process_request(req.json(body), false).await + } +} diff --git a/integration/tests/helpers/mod.rs b/integration/tests/helpers/mod.rs new file mode 100644 index 0000000..0957ad9 --- /dev/null +++ b/integration/tests/helpers/mod.rs @@ -0,0 +1,5 @@ +pub mod client; +pub mod panel; + +pub use client::AgentClient; +pub use panel::Panel; diff --git a/integration/tests/helpers/panel.rs b/integration/tests/helpers/panel.rs new file mode 100644 index 0000000..f250332 --- /dev/null +++ b/integration/tests/helpers/panel.rs @@ -0,0 +1,53 @@ +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: S) -> Value { + let splitted = split(args.into().as_ref()).unwrap(); + Self::output_argv( + splitted + .iter() + .map(|s| s.as_ref()) + .collect::>() + .as_ref(), + ) + } + + fn status_is_ok(data: &Value) { + assert_eq!( + data["status"], "ok", + "Panel failed with erroneous status: {}", + data["data"] + ); + } + + pub fn check_status>(args: S) { + let result = Self::output(args); + Self::status_is_ok(&result); + } + + pub fn check_output>(args: S) -> Vec { + let result = Self::output(args); + Self::status_is_ok(&result); + result["data"].as_array().unwrap().clone() + } +} diff --git a/integration/tests/tests.rs b/integration/tests/tests.rs new file mode 100644 index 0000000..2cc9bce --- /dev/null +++ b/integration/tests/tests.rs @@ -0,0 +1,80 @@ +mod helpers; + +use helpers::{AgentClient, Panel}; + +use serde_json::json; +use std::thread::sleep; +use std::time::Duration; +use uuid::Uuid; + +type TestResult = Result>; + +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_registration() -> TestResult { + let agent_uid = register_agent().await; + let agents = Panel::check_output("agents list"); + let found = agents + .iter() + .find(|v| v["id"].as_str().unwrap() == agent_uid.to_string()); + assert!(found.is_some()); + Ok(()) +} + +#[tokio::test] +async fn test_setup_tasks() -> TestResult { + let agent_uid = Panel::check_output("agents list")[0]["id"].clone(); + dbg!(&agent_uid); + let job_alias = "passwd_contents"; + let cmd = format!("jobs add --alias {} 'cat /etc/passwd'", job_alias); + Panel::check_status(cmd); + let cmd = format!("jobmap add {} {}", agent_uid, job_alias); + let assigned_uids = Panel::check_output(cmd); + dbg!(&assigned_uids); + loop { + let result = Panel::check_output(format!("jobmap list {}", assigned_uids[0])); + dbg!(&result); + match result.get(0) { + Some(entry) if entry["state"] == "Finished" => { + println!("{}", result[0]); + break; + } + None => { + eprintln!("jobmap list is empty (bad bad bad)"); + continue; + } + _ => { + sleep(Duration::from_secs(1)); + eprintln!("waiting for task"); + } + } + } + Ok(()) +} diff --git a/integration/utils.py b/integration/utils.py new file mode 100644 index 0000000..0015df2 --- /dev/null +++ b/integration/utils.py @@ -0,0 +1,32 @@ +from termcolor import colored + +__all__ = ['log', 'warn', 'err', 'TestsError'] + + +class TestsError(Exception): + pass + + +COLORS = { + 'question': colored('[?]', 'magenta'), + 'info': colored('[~]', 'green'), + 'warning': colored('[!]', 'yellow'), + 'error': colored('[X]', 'red'), +} + + +def warn(msg): + log(msg, log_lvl='w') + + +def err(msg): + log(msg, log_lvl='e') + + +def log(msg, log_lvl='i'): + for lvl, text in COLORS.items(): + if lvl.startswith(log_lvl): + print(f'{text} {msg}') + break + else: + ValueError('Unknown log level') diff --git a/lib/u_lib/Cargo.toml b/lib/u_lib/Cargo.toml index 295b216..dad63a9 100644 --- a/lib/u_lib/Cargo.toml +++ b/lib/u_lib/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dotenv = "0.15.0" serde = { version = "1.0.114", features = ["derive"] } uuid = { version = "0.6.5", features = ["serde", "v4"] } nix = "0.17" @@ -14,10 +15,12 @@ libc = "^0.2" lazy_static = "1.4.0" tokio = { version = "1.2.0", features = ["rt-multi-thread", "sync", "macros", "process", "time"] } reqwest = { version = "0.11", features = ["json"] } +openssl = "*" futures = "0.3.5" guess_host_triple = "0.1.2" thiserror = "*" log = "*" +mockall = "0.9.1" env_logger = "0.8.3" diesel-derive-enum = { version = "1", features = ["postgres"] } chrono = "0.4.19" diff --git a/lib/u_lib/build.rs b/lib/u_lib/build.rs new file mode 100644 index 0000000..cf1757d --- /dev/null +++ b/lib/u_lib/build.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let echoer = PathBuf::from("./tests/fixtures/echoer"); + let mut echoer_src = echoer.clone(); + echoer_src.set_extension("rs"); + Command::new("rustc") + .args(&[echoer_src.to_str().unwrap(), "-o", echoer.to_str().unwrap()]) + .status() + .unwrap(); + println!("cargo:rerun-if-changed={}", echoer_src.display()); + println!("cargo:rerun-if-changed={}", echoer.display()); +} diff --git a/lib/u_lib/src/api.rs b/lib/u_lib/src/api.rs index 5e0cd3c..636a0dc 100644 --- a/lib/u_lib/src/api.rs +++ b/lib/u_lib/src/api.rs @@ -2,12 +2,12 @@ use crate::{ config::{MASTER_PORT, MASTER_SERVER}, messaging::{AsMsg, BaseMessage}, - models::*, - utils::opt_to_string, + models, + utils::{opt_to_string, VecDisplay}, UError, UResult, }; use reqwest::{Client, RequestBuilder, Url}; -use std::{net::Ipv4Addr, str::FromStr}; +use std::env; use u_api_proc_macro::api_route; use uuid::Uuid; @@ -18,10 +18,9 @@ pub struct ClientHandler { } impl ClientHandler { - pub fn new(server: Option) -> Self { - let master_server = server - .map(|s| Ipv4Addr::from_str(&s).unwrap()) - .unwrap_or(MASTER_SERVER); + pub fn new(server: Option<&str>) -> Self { + 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(), @@ -53,24 +52,24 @@ impl ClientHandler { // // get jobs for client #[api_route("GET")] - fn get_agent_jobs(&self, url_param: Option) -> Vec {} + fn get_agent_jobs(&self, url_param: Option) -> VecDisplay {} // // send something to server #[api_route("POST")] - fn report(&self, payload: &M) {} + fn report(&self, payload: &M) -> models::Empty {} - //#/////////#// Admin area //#////////#// + //##########// Admin area //##########// /// client listing #[api_route("GET")] - fn get_agents(&self, url_param: Option) -> Vec {} + fn get_agents(&self, url_param: Option) -> VecDisplay {} // // get all available jobs #[api_route("GET")] - fn get_jobs(&self, url_param: Option) -> Vec {} + fn get_jobs(&self, url_param: Option) -> VecDisplay {} // // create and upload job #[api_route("POST")] - fn upload_jobs(&self, payload: &Vec) {} + fn upload_jobs(&self, payload: &[models::JobMeta]) -> models::Empty {} // // delete something #[api_route("GET")] @@ -78,5 +77,5 @@ impl ClientHandler { // // set jobs for client #[api_route("POST")] - fn set_jobs(&self, url_param: Option, payload: &Vec) {} + fn set_jobs(&self, url_param: Option, payload: &[String]) -> VecDisplay {} } diff --git a/lib/u_lib/src/builder.rs b/lib/u_lib/src/builder.rs index 7948b33..14b172d 100644 --- a/lib/u_lib/src/builder.rs +++ b/lib/u_lib/src/builder.rs @@ -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>(job_requests: J) -> CombinedResult { + pub fn from_request>(job_requests: J) -> CombinedResult { let job_requests = job_requests.into_vec(); let mut prepared: Vec = vec![]; let mut result = CombinedResult::new(); @@ -52,7 +52,7 @@ impl JobBuilder { result } - pub fn from_meta>(job_metas: J) -> CombinedResult { + pub fn from_meta>(job_metas: J) -> CombinedResult { let job_requests = job_metas .into_vec() .into_iter() @@ -90,7 +90,7 @@ pub struct NamedJobBuilder { } impl NamedJobBuilder { - pub fn from_shell>( + pub fn from_shell>( named_jobs: J, ) -> CombinedResult { let mut result = CombinedResult::new(); @@ -111,7 +111,7 @@ impl NamedJobBuilder { result } - pub fn from_meta>(named_jobs: J) -> Self { + pub fn from_meta>(named_jobs: J) -> Self { let mut job_names = vec![]; let job_metas: Vec = named_jobs .into_vec() @@ -144,3 +144,172 @@ impl NamedJobBuilder { self.pop_opt(name).unwrap() } } + +#[cfg(test)] +mod tests { + + use test_case::test_case; + use std::{time::SystemTime}; + use crate::{ + errors::UError, + models::{ + jobs::{JobMeta}, + ExecResult, + misc::JobType + }, + builder::{JobBuilder, NamedJobBuilder}, + unwrap_enum, + }; + + type TestResult = Result>; + + #[tokio::test] + async fn test_is_really_async() { + const SLEEP_SECS: u64 = 1; + let job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)).unwrap(); + let sleep_jobs: Vec = (0..50).map(|_| job.clone()).collect(); + let now = SystemTime::now(); + JobBuilder::from_meta(sleep_jobs).unwrap_one().wait().await; + assert!(now.elapsed().unwrap().as_secs() < SLEEP_SECS + 2) + } + + #[test_case( + "/bin/sh {}", + Some(b"echo test01 > /tmp/asd; cat /tmp/asd"), + "test01" + ;"sh payload" + )] + #[test_case( + r#"/usr/bin/python -c 'print("test02")'"#, + None, + "test02" + ;"python cmd" + )] + #[test_case( + "/{}", + Some( + br#"#!/bin/sh + TMPPATH=/tmp/lol + mkdir -p $TMPPATH + echo test03 > $TMPPATH/t + cat $TMPPATH/t"# + ), + "test03" + ;"sh multiline payload" + )] + #[test_case( + "/{} 'some msg as arg'", + Some(include_bytes!("../tests/fixtures/echoer")), + "some msg as arg" + ;"standalone binary with args" + )] + #[tokio::test] + async fn test_shell_job(cmd: &str, payload: Option<&[u8]>, expected_result: &str) -> TestResult { + let mut job = JobMeta::builder().with_shell(cmd); + if let Some(p) = payload { + job = job.with_payload(p); + } + let job = job.build().unwrap(); + let job_result = JobBuilder::from_meta(job).unwrap_one().wait_one().await; + let result = unwrap_enum!(job_result, ExecResult::Assigned); + let result = result.to_string_result().unwrap(); + assert_eq!(result.trim(), expected_result); + Ok(()) + } + + #[tokio::test] + async fn test_complex_load() -> TestResult { + const SLEEP_SECS: u64 = 1; + let now = SystemTime::now(); + let longest_job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)).unwrap(); + let longest_job = JobBuilder::from_meta(longest_job).unwrap_one().spawn().await; + let ls = JobBuilder::from_meta(JobMeta::from_shell("ls")?).unwrap_one() + .wait_one() + .await; + let ls = unwrap_enum!(ls, ExecResult::Assigned); + assert_eq!(ls.retcode.unwrap(), 0); + let folders = ls.to_string_result().unwrap(); + let subfolders_jobs: Vec = folders + .lines() + .map(|f| JobMeta::from_shell(format!("ls {}", f)).unwrap()) + .collect(); + let ls_subfolders = JobBuilder::from_meta(subfolders_jobs) + .unwrap_one() + .wait() + .await; + for result in ls_subfolders { + let result = unwrap_enum!(result, ExecResult::Assigned); + assert_eq!(result.retcode.unwrap(), 0); + } + longest_job.wait().await; + assert_eq!(now.elapsed().unwrap().as_secs(), SLEEP_SECS); + Ok(()) + } + /* + #[tokio::test] + async fn test_exec_multiple_jobs_nowait() -> UResult<()> { + const REPEATS: usize = 10; + let job = JobMeta::from_shell("whoami"); + let sleep_jobs: Vec = (0..=REPEATS).map(|_| job.clone()).collect(); + build_jobs(sleep_jobs).spawn().await; + let mut completed = 0; + while completed < REPEATS { + let c = pop_completed().await.len(); + if c > 0 { + completed += c; + println!("{}", c); + } + } + Ok(()) + } + */ + #[tokio::test] + async fn test_failing_shell_job() -> TestResult { + let job = JobMeta::from_shell("lol_kek_puk")?; + let job_result = JobBuilder::from_meta(job) + .unwrap_one() + .wait_one() + .await; + let job_result = unwrap_enum!(job_result, ExecResult::Assigned); + let output = job_result.to_string_result().unwrap(); + assert!(output.contains("No such file")); + assert!(job_result.retcode.is_none()); + Ok(()) + } + + #[test_case( + "/bin/bash {}", + None, + "contains executable" + ; "no binary" + )] + #[test_case( + "/bin/bash", + Some(b"whoami"), + "contains no executable" + ; "no path to binary" + )] + #[tokio::test] + async fn test_job_building_failed(cmd: &str, payload: Option<&[u8]>, err_str: &str) -> TestResult { + let mut job = JobMeta::builder().with_shell(cmd); + if let Some(p) = payload { + job = job.with_payload(p); + } + let err = job.build().unwrap_err(); + let err_msg = unwrap_enum!(err, UError::JobArgsError); + assert!(err_msg.contains(err_str)); + Ok(()) + } + + #[tokio::test] + async fn test_different_job_types() -> TestResult { + let mut jobs = NamedJobBuilder::from_meta(vec![ + ("sleeper", JobMeta::from_shell("sleep 3")?), + ("gatherer", JobMeta::builder().with_type(JobType::Manage).build()?) + ]).wait().await; + let gathered = jobs.pop("gatherer"); + assert_eq!(unwrap_enum!(gathered, ExecResult::Agent).alias, None); + Ok(()) + } + +} \ No newline at end of file diff --git a/lib/u_lib/src/config.rs b/lib/u_lib/src/config.rs index 7db464c..88fc795 100644 --- a/lib/u_lib/src/config.rs +++ b/lib/u_lib/src/config.rs @@ -1,9 +1,8 @@ -use std::net::Ipv4Addr; use uuid::Uuid; -pub const MASTER_SERVER: Ipv4Addr = Ipv4Addr::LOCALHOST; //Ipv4Addr::new(3,9,16,40) +pub const MASTER_SERVER: &str = "127.0.0.1"; //Ipv4Addr::new(3,9,16,40) pub const MASTER_PORT: u16 = 63714; lazy_static! { pub static ref UID: Uuid = Uuid::new_v4(); -} \ No newline at end of file +} diff --git a/lib/u_lib/src/executor.rs b/lib/u_lib/src/executor.rs index c4d6492..95ab8aa 100644 --- a/lib/u_lib/src/executor.rs +++ b/lib/u_lib/src/executor.rs @@ -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>(tasks: S) -> Self { + pub fn new>(tasks: S) -> Self { Self { tasks: tasks.into_vec(), fids: vec![], diff --git a/lib/u_lib/src/lib.rs b/lib/u_lib/src/lib.rs index d615391..00d1c18 100644 --- a/lib/u_lib/src/lib.rs +++ b/lib/u_lib/src/lib.rs @@ -26,3 +26,6 @@ extern crate diesel; #[macro_use] extern crate log; extern crate env_logger; + +#[macro_use] +extern crate mockall; diff --git a/lib/u_lib/src/messaging.rs b/lib/u_lib/src/messaging.rs index 7d7a34c..3af1933 100644 --- a/lib/u_lib/src/messaging.rs +++ b/lib/u_lib/src/messaging.rs @@ -1,9 +1,11 @@ +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: Clone>(pub Cow<'cow, T>); +pub struct Moo<'cow, T: AsMsg + Clone>(pub Cow<'cow, T>); pub trait AsMsg: Clone + Serialize { fn as_message<'m>(&'m self) -> BaseMessage<'m, Self> @@ -29,6 +31,8 @@ impl<'cow, M: AsMsg> From<&'cow M> for Moo<'cow, M> { } impl AsMsg for Vec {} +impl AsMsg for VecDisplay {} +impl<'msg, M: AsMsg> AsMsg for &'msg [M] {} #[derive(Serialize, Deserialize, Debug)] pub struct BaseMessage<'cow, I: AsMsg> { diff --git a/lib/u_lib/src/models/agent.rs b/lib/u_lib/src/models/agent.rs index fe213b9..2c66f1e 100644 --- a/lib/u_lib/src/models/agent.rs +++ b/lib/u_lib/src/models/agent.rs @@ -79,10 +79,9 @@ impl Agent { .await; let decoder = |job_result: ExecResult| { let assoc_job = unwrap_enum!(job_result, ExecResult::Assigned); - assoc_job.to_string_result().unwrap() + assoc_job.to_string_result().unwrap().trim().to_string() }; - #[cfg(unix)] Self { hostname: decoder(builder.pop("hostname")), is_root: &decoder(builder.pop("is_root")) == "0", diff --git a/lib/u_lib/src/models/jobs/misc.rs b/lib/u_lib/src/models/jobs/misc.rs index 9c45195..7451548 100644 --- a/lib/u_lib/src/models/jobs/misc.rs +++ b/lib/u_lib/src/models/jobs/misc.rs @@ -136,3 +136,51 @@ impl JobOutput { 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); + } +} diff --git a/lib/u_lib/src/models/mod.rs b/lib/u_lib/src/models/mod.rs index 9406e9a..d30a33a 100644 --- a/lib/u_lib/src/models/mod.rs +++ b/lib/u_lib/src/models/mod.rs @@ -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, "") + } +} diff --git a/lib/u_lib/src/utils.rs b/lib/u_lib/src/utils.rs deleted file mode 100644 index ebf3ebe..0000000 --- a/lib/u_lib/src/utils.rs +++ /dev/null @@ -1,161 +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 { - fn into_vec(self) -> Vec; -} - -impl OneOrMany for T { - fn into_vec(self) -> Vec { - vec![self] - } -} - -impl OneOrMany for Vec { - fn into_vec(self) -> Vec { - self - } -} - -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(item: Option) -> String { - match item { - Some(s) => s.to_string(), - None => String::new(), - } -} - -pub fn systime_to_string(time: &SystemTime) -> String { - DateTime::::from(*time) - .format("%d/%m/%Y %T") - .to_string() -} - -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 { - 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 { - ok: Vec, - err: Vec, -} - -impl CombinedResult { - pub fn new() -> Self { - Self { - ok: vec![], - err: vec![], - } - } - - pub fn ok>(&mut self, result: I) { - self.ok.extend(result.into_vec()); - } - - pub fn err>(&mut self, err: I) { - self.err.extend(err.into_vec()); - } - - pub fn unwrap(self) -> Vec { - 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 { - self.err.drain(..).collect() - } -} - -#[macro_export] -macro_rules! unwrap_enum { - ($src:ident, $t:path) => { - if let $t(result) = $src { - result - } else { - panic!("wrong type") - } - }; -} diff --git a/lib/u_lib/src/utils/combined_result.rs b/lib/u_lib/src/utils/combined_result.rs new file mode 100644 index 0000000..687f541 --- /dev/null +++ b/lib/u_lib/src/utils/combined_result.rs @@ -0,0 +1,40 @@ +use crate::utils::OneOrVec; +use crate::UError; + +pub struct CombinedResult { + ok: Vec, + err: Vec, +} + +impl CombinedResult { + pub fn new() -> Self { + Self { + ok: vec![], + err: vec![], + } + } + + pub fn ok>(&mut self, result: I) { + self.ok.extend(result.into_vec()); + } + + pub fn err>(&mut self, err: I) { + self.err.extend(err.into_vec()); + } + + pub fn unwrap(self) -> Vec { + 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 { + self.err.drain(..).collect() + } +} diff --git a/lib/u_lib/src/utils/conv.rs b/lib/u_lib/src/utils/conv.rs new file mode 100644 index 0000000..9cb2166 --- /dev/null +++ b/lib/u_lib/src/utils/conv.rs @@ -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(item: Option) -> String { + match item { + Some(s) => s.to_string(), + None => String::new(), + } +} + +pub fn systime_to_string(time: &SystemTime) -> String { + DateTime::::from(*time) + .format("%d/%m/%Y %T") + .to_string() +} diff --git a/lib/u_lib/src/utils/misc.rs b/lib/u_lib/src/utils/misc.rs new file mode 100644 index 0000000..4f93a85 --- /dev/null +++ b/lib/u_lib/src/utils/misc.rs @@ -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 { + fn into_vec(self) -> Vec; +} + +impl OneOrVec for T { + fn into_vec(self) -> Vec { + vec![self] + } +} + +impl OneOrVec for Vec { + fn into_vec(self) -> Vec { + 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(); + } +} diff --git a/lib/u_lib/src/utils/mod.rs b/lib/u_lib/src/utils/mod.rs new file mode 100644 index 0000000..13f94d3 --- /dev/null +++ b/lib/u_lib/src/utils/mod.rs @@ -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::*; diff --git a/lib/u_lib/src/utils/tempfile.rs b/lib/u_lib/src/utils/tempfile.rs new file mode 100644 index 0000000..e76baa8 --- /dev/null +++ b/lib/u_lib/src/utils/tempfile.rs @@ -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 { + 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(); + } +} diff --git a/lib/u_lib/src/utils/vec_display.rs b/lib/u_lib/src/utils/vec_display.rs new file mode 100644 index 0000000..117ee44 --- /dev/null +++ b/lib/u_lib/src/utils/vec_display.rs @@ -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(pub Vec); + +impl VecDisplay { + pub fn new>(inner: I) -> Self { + VecDisplay(inner.into_vec()) + } + + pub fn into_builtin_vec(self) -> Vec { + self.0 + } +} + +impl Deref for VecDisplay { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for VecDisplay { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Display for VecDisplay { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + for (i, itm) in self.0.iter().enumerate() { + writeln!(f, "### {}:\n{}\n", i, itm)?; + } + Ok(()) + } +} diff --git a/lib/u_lib/tests/api_macro.rs b/lib/u_lib/tests/api_macro.rs deleted file mode 100644 index e5fd094..0000000 --- a/lib/u_lib/tests/api_macro.rs +++ /dev/null @@ -1,17 +0,0 @@ -/* -use std::fmt::Display; -use u_api_proc_macro::api_route; -use uuid::Uuid; - -struct Paths; -struct ClientHandler; - -#[test] -fn test_api_proc_macro() { - #[api_route("GET", Uuid)] - fn list(&self, msg: T) -> String {} - - #[api_route("POST", Uuid)] - fn report(&self, msg: T) -> String {} -} -*/ diff --git a/lib/u_lib/tests/fixtures/echoer b/lib/u_lib/tests/fixtures/echoer deleted file mode 100755 index 4c13f8d7e39718179e68c0fb8af1e04b0af515fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 313672 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

4#1N^vFQ?InNHGf+ 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 = Result>; - -#[tokio::test] -async fn test_is_really_async() { - const SLEEP_SECS: u64 = 1; - let job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)).unwrap(); - let sleep_jobs: Vec = (0..50).map(|_| job.clone()).collect(); - let now = SystemTime::now(); - JobBuilder::from_meta(sleep_jobs).unwrap_one().wait().await; - assert!(now.elapsed().unwrap().as_secs() < SLEEP_SECS + 2) -} - -#[test_case( - "/bin/sh {}", - Some(b"echo test01 > /tmp/asd; cat /tmp/asd"), - "test01" - ;"sh payload" -)] -#[test_case( - r#"/usr/bin/python -c 'print("test02")'"#, - None, - "test02" - ;"python cmd" -)] -#[test_case( - "/{}", - Some( -br#"#!/bin/sh -TMPPATH=/tmp/lol -mkdir -p $TMPPATH -echo test03 > $TMPPATH/t -cat $TMPPATH/t"# - ), - "test03" - ;"sh multiline payload" -)] -#[test_case( - "/{} 'some msg as arg'", - Some(include_bytes!("../fixtures/echoer")), - "some msg as arg" - ;"standalone binary with args" -)] -#[tokio::test] -async fn test_shell_job(cmd: &str, payload: Option<&[u8]>, expected_result: &str) -> TestResult { - let mut job = JobMeta::builder().with_shell(cmd); - if let Some(p) = payload { - job = job.with_payload(p); - } - let job = job.build().unwrap(); - let job_result = JobBuilder::from_meta(job).unwrap_one().wait_one().await; - let result = unwrap_enum!(job_result, ExecResult::Assigned); - let result = result.to_string_result().unwrap(); - assert_eq!(result.trim(), expected_result); - Ok(()) -} - -#[tokio::test] -async fn test_complex_load() -> TestResult { - const SLEEP_SECS: u64 = 1; - let now = SystemTime::now(); - let longest_job = JobMeta::from_shell(format!("sleep {}", SLEEP_SECS)).unwrap(); - let longest_job = JobBuilder::from_meta(longest_job).unwrap_one().spawn().await; - let ls = JobBuilder::from_meta(JobMeta::from_shell("ls")?).unwrap_one() - .wait_one() - .await; - let ls = unwrap_enum!(ls, ExecResult::Assigned); - assert_eq!(ls.retcode.unwrap(), 0); - let folders = ls.to_string_result().unwrap(); - let subfolders_jobs: Vec = folders - .lines() - .map(|f| JobMeta::from_shell(format!("ls {}", f)).unwrap()) - .collect(); - let ls_subfolders = JobBuilder::from_meta(subfolders_jobs) - .unwrap_one() - .wait() - .await; - for result in ls_subfolders { - let result = unwrap_enum!(result, ExecResult::Assigned); - assert_eq!(result.retcode.unwrap(), 0); - } - longest_job.wait().await; - assert_eq!(now.elapsed().unwrap().as_secs(), SLEEP_SECS); - Ok(()) -} -/* -#[tokio::test] -async fn test_exec_multiple_jobs_nowait() -> UResult<()> { - const REPEATS: usize = 10; - let job = JobMeta::from_shell("whoami"); - let sleep_jobs: Vec = (0..=REPEATS).map(|_| job.clone()).collect(); - build_jobs(sleep_jobs).spawn().await; - let mut completed = 0; - while completed < REPEATS { - let c = pop_completed().await.len(); - if c > 0 { - completed += c; - println!("{}", c); - } - } - Ok(()) -} -*/ -#[tokio::test] -async fn test_failing_shell_job() -> TestResult { - let job = JobMeta::from_shell("lol_kek_puk")?; - let job_result = JobBuilder::from_meta(job) - .unwrap_one() - .wait_one() - .await; - let job_result = unwrap_enum!(job_result, ExecResult::Assigned); - let output = job_result.to_string_result().unwrap(); - assert!(output.contains("No such file")); - assert!(job_result.retcode.is_none()); - Ok(()) -} - -#[test_case( - "/bin/bash {}", - None, - "contains executable" - ; "no binary" -)] -#[test_case( - "/bin/bash", - Some(b"whoami"), - "contains no executable" - ; "no path to binary" -)] -#[tokio::test] -async fn test_job_building_failed(cmd: &str, payload: Option<&[u8]>, err_str: &str) -> TestResult { - let mut job = JobMeta::builder().with_shell(cmd); - if let Some(p) = payload { - job = job.with_payload(p); - } - let err = job.build().unwrap_err(); - let err_msg = unwrap_enum!(err, UError::JobArgsError); - assert!(err_msg.contains(err_str)); - Ok(()) -} - -#[tokio::test] -async fn test_different_job_types() -> TestResult { - let mut jobs = NamedJobBuilder::from_meta(vec![ - ("sleeper", JobMeta::from_shell("sleep 3")?), - ("gatherer", JobMeta::builder().with_type(JobType::Manage).build()?) - ]).wait().await; - let gathered = jobs.pop("gatherer"); - assert_eq!(unwrap_enum!(gathered, ExecResult::Agent).alias, None); - Ok(()) -} diff --git a/lib/u_lib/tests/jobs/output.rs b/lib/u_lib/tests/jobs/output.rs deleted file mode 100644 index 0fc1522..0000000 --- a/lib/u_lib/tests/jobs/output.rs +++ /dev/null @@ -1,43 +0,0 @@ -use u_lib::{models::JobOutput, utils::vec_to_string}; - -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!(&vec_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!(vec_to_string(&output.to_appropriate()).trim(), result); -} diff --git a/lib/u_lib/tests/tests.rs b/lib/u_lib/tests/tests.rs deleted file mode 100644 index b0e8959..0000000 --- a/lib/u_lib/tests/tests.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[macro_use] -extern crate test_case; - -#[macro_use] -extern crate u_lib; - -mod jobs { - mod execution; - mod output; -} diff --git a/scripts/cargo_musl.sh b/scripts/cargo_musl.sh index 49cf0cd..c33121b 100755 --- a/scripts/cargo_musl.sh +++ b/scripts/cargo_musl.sh @@ -1,10 +1,11 @@ #!/bin/bash set -e source $(dirname $0)/rootdir.sh #set ROOTDIR +umask 002 docker run \ -v $ROOTDIR:/volume \ -v cargo-cache:/root/.cargo/registry \ -w /volume \ -it \ - clux/muslrust \ + unki/musllibs \ cargo $@