small but useful fixes

* optimized error handling
* more usage of tracing-subscriber
* fixed some integration test bugs
* fixed db constraints
pull/1/head
plazmoid 2 years ago
parent c70cdbd262
commit a50e6d242f
  1. 39
      bin/u_agent/src/lib.rs
  2. 1
      bin/u_panel/Cargo.toml
  3. 5
      bin/u_panel/src/argparse.rs
  4. 5
      bin/u_panel/src/main.rs
  5. 2
      bin/u_panel/src/server/mod.rs
  6. 2
      bin/u_server/Cargo.toml
  7. 2
      bin/u_server/src/handlers.rs
  8. 9
      bin/u_server/src/main.rs
  9. 6
      bin/u_server/src/u_server.rs
  10. 3
      integration/Cargo.toml
  11. 15
      integration/docker-compose.yml
  12. 2
      integration/tests/fixtures/agent.rs
  13. 6
      integration/tests/helpers/panel.rs
  14. 20
      integration/tests/integration/behaviour.rs
  15. 3
      lib/u_lib/Cargo.toml
  16. 59
      lib/u_lib/src/api.rs
  17. 2
      lib/u_lib/src/builder.rs
  18. 34
      lib/u_lib/src/errors/chan.rs
  19. 23
      lib/u_lib/src/logging.rs
  20. 2
      lib/u_lib/src/messaging/mod.rs
  21. 7
      lib/u_lib/src/models/jobs/meta.rs
  22. 13
      lib/u_lib/src/utils/combined_result.rs
  23. 2
      lib/u_lib/src/utils/fmt/stripped.rs
  24. 2
      lib/u_lib/src/utils/proc_output.rs
  25. 8
      migrations/2020-10-24-111622_create_all/up.sql

@ -7,7 +7,6 @@
extern crate log;
use daemonize::Daemonize;
use std::panic;
use std::sync::Arc;
use tokio::time::{sleep, Duration};
use u_lib::{
@ -22,7 +21,7 @@ pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHand
if !job_requests.is_empty() {
for jr in &job_requests {
if !JobCache::contains(jr.job_id) {
debug!("Fetching job: {}", &jr.job_id);
info!("Fetching job: {}", &jr.job_id);
let fetched_job = loop {
match client.get_jobs(Some(jr.job_id)).await {
Ok(mut result) => break result.pop().unwrap(),
@ -35,7 +34,7 @@ pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHand
JobCache::insert(fetched_job);
}
}
debug!(
info!(
"Scheduling jobs: {}",
job_requests
.iter()
@ -46,7 +45,9 @@ pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHand
let mut builder = JobBuilder::from_request(job_requests);
let errors = builder.pop_errors();
if !errors.is_empty() {
errors.into_iter().for_each(ErrChan::send)
for e in errors {
ErrChan::send(e, "ebld").await;
}
}
builder.unwrap_one().spawn().await;
}
@ -54,8 +55,8 @@ pub async fn process_request(job_requests: Vec<AssignedJob>, client: &ClientHand
async fn error_reporting(client: Arc<ClientHandler>) -> ! {
loop {
let err = ErrChan::recv();
debug!("Error encountered: {:?}", err);
match ErrChan::recv().await {
Some(err) => {
'retry: for _ in 0..3 {
match client.report(&[Reportable::Error(err.to_string())]).await {
Ok(_) => break 'retry,
@ -66,20 +67,23 @@ async fn error_reporting(client: Arc<ClientHandler>) -> ! {
}
}
}
None => sleep(Duration::from_secs(3)).await,
}
}
}
async fn do_stuff(client: Arc<ClientHandler>) -> ! {
loop {
match client.get_personal_jobs(Some(get_self_uid())).await {
match client.get_personal_jobs(get_self_uid()).await {
Ok(resp) => {
process_request(resp, &client).await;
}
Err(err) => ErrChan::send(err),
Err(err) => ErrChan::send(err, "personal").await,
}
let result: Vec<Reportable> = pop_completed().await.into_iter().collect();
if !result.is_empty() {
if let Err(err) = client.report(&result).await {
ErrChan::send(err)
ErrChan::send(err, "report").await;
}
}
sleep(Duration::from_secs(ITERATION_LATENCY)).await;
@ -87,11 +91,12 @@ async fn do_stuff(client: Arc<ClientHandler>) -> ! {
}
pub async fn run_forever() -> ! {
panic::set_hook(Box::new(|panic_info| {
ErrChan::send(UError::Panic(panic_info.to_string()))
}));
let env = load_env_default().unwrap();
let client = Arc::new(ClientHandler::new(&env.u_server, None));
tokio::spawn(error_reporting(client.clone()));
if cfg!(debug_assertions) {
init_logger(format!(
init_logger(Some(format!(
"u_agent-{}",
get_self_uid()
.hyphenated()
@ -99,14 +104,12 @@ pub async fn run_forever() -> ! {
.split("-")
.next()
.unwrap()
));
)));
} else {
if let Err(e) = Daemonize::new().start() {
ErrChan::send(UError::Runtime(e.to_string()))
ErrChan::send(UError::Runtime(e.to_string()), "deeeemon").await
}
}
let env = load_env_default().unwrap();
let client = Arc::new(ClientHandler::new(&env.u_server, None));
tokio::spawn(error_reporting(client.clone()));
info!("Startup");
do_stuff(client).await
}

@ -10,7 +10,6 @@ edition = "2021"
actix-web = "3.3.2"
backtrace = "0.3.61"
structopt = "0.3.21"
log = "^0.4"
uuid = "0.6.5"
serde_json = "1.0.4"
serde = { version = "1.0.114", features = ["derive"] }

@ -1,3 +1,4 @@
use anyhow::Result as AnyResult;
use structopt::StructOpt;
use u_lib::{
api::ClientHandler,
@ -76,10 +77,10 @@ fn parse_uuid(src: &str) -> Result<Uuid, String> {
}
pub async fn process_cmd(client: ClientHandler, args: Args) -> UResult<String> {
fn to_json<Msg: AsMsg>(data: UResult<Msg>) -> String {
fn to_json<Msg: AsMsg>(data: AnyResult<Msg>) -> String {
let data = match data {
Ok(r) => PanelResult::Ok(r),
Err(e) => PanelResult::Err(e),
Err(e) => PanelResult::Err(e.downcast().expect("unknown error type")),
};
serde_json::to_string(&data).unwrap()
}

@ -10,6 +10,7 @@ use argparse::{process_cmd, Args};
use serde::Deserialize;
use structopt::StructOpt;
use u_lib::api::ClientHandler;
use u_lib::logging::init_logger;
use u_lib::utils::{env::default_host, load_env};
#[derive(Deserialize)]
@ -25,6 +26,8 @@ async fn main() -> AnyResult<()> {
let client = ClientHandler::new(&env.u_server, Some(env.admin_auth_token));
let args = Args::from_args();
process_cmd(client, args).await?;
init_logger(None::<&str>);
let result = process_cmd(client, args).await?;
println!("{result}");
Ok(())
}

@ -68,7 +68,7 @@ async fn send_cmd(
#[actix_web::main]
pub async fn serve(client: ClientHandler) -> std::io::Result<()> {
init_logger("u_panel");
init_logger(Some("u_panel"));
let addr = "127.0.0.1:8080";
info!("Serving at http://{}", addr);

@ -5,7 +5,7 @@ name = "u_server"
version = "0.1.0"
[dependencies]
log = "0.4.11"
tracing = "0.1.35"
thiserror = "*"
warp = { version = "0.3.1", features = ["tls"] }
uuid = { version = "0.6.5", features = ["serde", "v4"] }

@ -112,7 +112,7 @@ impl Endpoints {
err.agent_id,
Stripped(&err.msg.as_str())
);
UDB::lock_db().report_error(&err)?;
//UDB::lock_db().report_error(&err)?;
}
Reportable::Dummy => (),
}

@ -1,6 +1,11 @@
use u_server_lib::serve;
#[macro_use]
extern crate tracing;
#[tokio::main]
async fn main() -> Result<(), String> {
serve().await.map_err(|e| e.to_string())
async fn main() {
if let Err(e) = serve().await {
error!("U_SERVER error: {}", e);
}
}

@ -1,5 +1,5 @@
#[macro_use]
extern crate log;
extern crate tracing;
#[cfg(test)]
#[macro_use]
@ -131,7 +131,7 @@ pub fn init_endpoints(
.map(ok);
let auth_token = format!("Bearer {auth_token}",).into_boxed_str();
let auth_header = warp::header::exact("authorization", Box::leak(auth_token));
let auth_header = warp::header::exact("Authorization", Box::leak(auth_token));
let auth_zone = (get_agents
.or(get_jobs)
@ -160,7 +160,7 @@ pub fn prefill_jobs() -> SResult<()> {
}
pub async fn serve() -> SResult<()> {
init_logger("u_server");
init_logger(Some("u_server"));
prefill_jobs()?;
let env = load_env::<ServEnv>().map_err(|e| Error::Other(e.to_string()))?;

@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread", "process", "time"] }
log = "^0.4"
tracing = "0.1.35"
uuid = { version = "0.6.5", features = ["serde", "v4"] }
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1.0"
@ -20,6 +20,7 @@ once_cell = "1.10.0"
[dependencies.u_lib]
path = "../lib/u_lib"
version = "*"
features = ["panel"]
[[test]]

@ -25,7 +25,7 @@ services:
- ../.env
- ../.env.private
environment:
RUST_LOG: trace
RUST_LOG: info
healthcheck:
test: ss -tlpn | grep 63714
interval: 5s
@ -36,8 +36,8 @@ services:
image: unki/u_db
networks:
- u_net
expose:
- '5432'
ports:
- 54321:5432
env_file:
- ../.env
- ../.env.private
@ -58,12 +58,14 @@ services:
networks:
- u_net
volumes:
- ../target/x86_64-unknown-linux-musl/${PROFILE:-debug}/u_agent:/u_agent
command: /u_agent u_server
- ../target/x86_64-unknown-linux-musl/${PROFILE:-debug}/u_agent:/unki/u_agent
- ../logs:/unki/logs:rw
working_dir: /unki
command: /unki/u_agent u_server
env_file:
- ../.env
environment:
RUST_LOG: u_agent=debug
RUST_LOG: u_agent=debug,u_lib=debug
depends_on:
u_server:
condition: service_healthy
@ -78,6 +80,7 @@ services:
- ../certs:/certs
- ../target/x86_64-unknown-linux-musl/${PROFILE:-debug}/u_panel:/u_panel
- ../lib/u_lib:/lib/u_lib
- ../logs:/tests/logs:rw
working_dir:
/tests/
depends_on:

@ -18,7 +18,7 @@ pub async fn register_agent() -> RegisteredAgent {
let cli = ClientHandler::new(&ENV.u_server, None);
let agent_uid = Uuid::new_v4();
let resp = cli
.get_personal_jobs(Some(agent_uid))
.get_personal_jobs(agent_uid)
.await
.unwrap()
.pop()

@ -13,11 +13,7 @@ pub struct Panel;
impl Panel {
fn run(args: &[&str]) -> Output {
Command::new(PANEL_BINARY)
.arg("--json")
.args(args)
.output()
.unwrap()
Command::new(PANEL_BINARY).args(args).output().unwrap()
}
pub fn output_argv<T: DeserializeOwned>(argv: &[&str]) -> PanelResult<T> {

@ -2,6 +2,7 @@ use crate::fixtures::agent::*;
use crate::helpers::Panel;
use rstest::rstest;
use serde_json::{json, to_string};
use std::error::Error;
use std::time::Duration;
use tokio::time::sleep;
@ -14,7 +15,7 @@ type TestResult<R = ()> = Result<R, Box<dyn Error>>;
#[tokio::test]
async fn test_registration(#[future] register_agent: RegisteredAgent) -> TestResult {
let agent = register_agent.await;
let agents: Vec<Agent> = Panel::check_output("agents list");
let agents: Vec<Agent> = Panel::check_output("agents read");
let found = agents.iter().find(|v| v.id == agent.uid);
assert!(found.is_some());
Panel::check_status(format!("agents delete {}", agent.uid));
@ -23,17 +24,22 @@ async fn test_registration(#[future] register_agent: RegisteredAgent) -> TestRes
#[tokio::test]
async fn test_setup_tasks() -> TestResult {
//some independent agents should present
let agents: Vec<Agent> = Panel::check_output("agents list");
let agent_uid = agents[0].id;
let agents: Vec<Agent> = Panel::check_output("agents read");
let agent_uid = match agents.get(0) {
Some(a) => a.id,
None => panic!("Some independent agents should present"),
};
let job_alias = "passwd_contents";
let cmd = format!("jobs add --alias {job_alias} 'cat /etc/passwd'");
let job = json!(
{"alias": job_alias, "payload": b"cat /etc/passwd" }
);
let cmd = format!("jobs create '{}'", to_string(&job).unwrap());
Panel::check_status(cmd);
let cmd = format!("map add {} {}", agent_uid, job_alias);
let cmd = format!("map create {} {}", agent_uid, job_alias);
let assigned_uids: Vec<Uuid> = Panel::check_output(cmd);
for _ in 0..3 {
let result: Vec<AssignedJob> =
Panel::check_output(format!("map list {}", assigned_uids[0]));
Panel::check_output(format!("map read {}", assigned_uids[0]));
if result[0].state == JobState::Finished {
return Ok(());
} else {

@ -15,7 +15,6 @@ libc = "^0.2"
lazy_static = "1.4.0"
futures = "0.3.5"
thiserror = "*"
log = "*"
diesel-derive-enum = { version = "1", features = ["postgres"] }
chrono = "0.4.19"
strum = { version = "0.20", features = ["derive"] }
@ -27,6 +26,8 @@ envy = "0.4.2"
serde_json = "1.0.81"
tracing-subscriber = { version = "0.3.14", features = ["env-filter"] }
tracing-appender = "0.2.2"
log = "*"
anyhow = "1.0.58"
[features]
panel = []

@ -1,20 +1,23 @@
use std::collections::HashMap;
use std::fmt::Debug;
use crate::{
config::MASTER_PORT,
messaging::{self, AsMsg, BaseMessage, Empty},
models::{self, Agent},
models::{self},
utils::opt_to_string,
UError, UResult,
UError,
};
use anyhow::{Context, Result};
use reqwest::{header::HeaderMap, Certificate, Client, Identity, Url};
use serde::de::DeserializeOwned;
use serde_json::from_str;
use uuid::Uuid;
const AGENT_IDENTITY: &[u8] = include_bytes!("../../../certs/alice.p12");
const ROOT_CA_CERT: &[u8] = include_bytes!("../../../certs/ca.crt");
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ClientHandler {
base_url: Url,
client: Client,
@ -43,50 +46,50 @@ impl ClientHandler {
}
}
async fn _req<P: AsMsg, M: AsMsg + DeserializeOwned>(
async fn _req<P: AsMsg + Debug, M: AsMsg + DeserializeOwned + Debug + Default>(
&self,
url: impl AsRef<str>,
url: impl AsRef<str> + Debug,
payload: P,
) -> UResult<M> {
) -> Result<M> {
let request = self
.client
.post(self.base_url.join(url.as_ref()).unwrap())
.json(&payload.as_message());
let response = request.send().await?;
let response = request.send().await.context("send")?;
let content_len = response.content_length();
let is_success = match response.error_for_status_ref() {
Ok(_) => Ok(()),
Err(e) => Err(UError::from(e)),
};
let resp = response.text().await?;
let resp = response.text().await.context("resp")?;
debug!("url = {}, resp = {}", url.as_ref(), resp);
match is_success {
Ok(_) => serde_json::from_str::<BaseMessage<M>>(&resp)
Ok(_) => from_str::<BaseMessage<M>>(&resp)
.map(|msg| msg.into_inner())
.or_else(|e| Err(UError::NetError(e.to_string(), resp.clone()))),
.or_else(|e| match content_len {
Some(0) => Ok(Default::default()),
_ => Err(UError::NetError(e.to_string(), resp)),
}),
Err(UError::NetError(err, _)) => Err(UError::NetError(err, resp)),
_ => unreachable!(),
}
.map_err(From::from)
}
// get jobs for client
pub async fn get_personal_jobs(
&self,
url_param: Option<Uuid>,
) -> UResult<Vec<models::AssignedJob>> {
self._req(
format!("get_personal_jobs/{}", opt_to_string(url_param)),
Empty,
)
pub async fn get_personal_jobs(&self, url_param: Uuid) -> Result<Vec<models::AssignedJob>> {
self._req(format!("get_personal_jobs/{}", url_param), Empty)
.await
}
// send something to server
pub async fn report(&self, payload: &[messaging::Reportable]) -> UResult<Empty> {
pub async fn report(&self, payload: &[messaging::Reportable]) -> Result<Empty> {
self._req("report", payload).await
}
// download file
pub async fn dl(&self, file: String) -> UResult<Vec<u8>> {
pub async fn dl(&self, file: String) -> Result<Vec<u8>> {
self._req(format!("dl/{file}"), Empty).await
}
}
@ -95,40 +98,40 @@ impl ClientHandler {
#[cfg(feature = "panel")]
impl ClientHandler {
/// agent listing
pub async fn get_agents(&self, agent: Option<Uuid>) -> UResult<Vec<models::Agent>> {
pub async fn get_agents(&self, agent: Option<Uuid>) -> Result<Vec<models::Agent>> {
self._req(format!("get_agents/{}", opt_to_string(agent)), Empty)
.await
}
/// update something
pub async fn update_item(&self, item: impl AsMsg) -> UResult<Empty> {
pub async fn update_item(&self, item: impl AsMsg + Debug) -> Result<Empty> {
self._req("update_item", item).await
}
/// get all available jobs
pub async fn get_jobs(&self, job: Option<Uuid>) -> UResult<Vec<models::JobMeta>> {
pub async fn get_jobs(&self, job: Option<Uuid>) -> Result<Vec<models::JobMeta>> {
self._req(format!("get_jobs/{}", opt_to_string(job)), Empty)
.await
}
/// create and upload job
pub async fn upload_jobs(&self, payload: &[models::JobMeta]) -> UResult<Empty> {
pub async fn upload_jobs(&self, payload: &[models::JobMeta]) -> Result<Empty> {
self._req("upload_jobs", payload).await
}
/// delete something
pub async fn del(&self, item: Uuid) -> UResult<i32> {
pub async fn del(&self, item: Uuid) -> Result<i32> {
self._req(format!("del/{item}"), Empty).await
}
/// set jobs for any agent
pub async fn set_jobs(&self, agent: Uuid, job_idents: &[String]) -> UResult<Vec<Uuid>> {
pub async fn set_jobs(&self, agent: Uuid, job_idents: &[String]) -> Result<Vec<Uuid>> {
self._req(format!("set_jobs/{agent}"), job_idents).await
}
/// get jobs for any agent
pub async fn get_agent_jobs(&self, agent: Option<Uuid>) -> UResult<Vec<models::AssignedJob>> {
self._req(format!("set_jobs/{}", opt_to_string(agent)), Empty)
pub async fn get_agent_jobs(&self, agent: Option<Uuid>) -> Result<Vec<models::AssignedJob>> {
self._req(format!("get_personal_jobs/{}", opt_to_string(agent)), Empty)
.await
}
}

@ -17,7 +17,7 @@ impl JobBuilder {
pub fn from_request(job_requests: impl OneOrVec<AssignedJob>) -> CombinedResult<Self> {
let job_requests = job_requests.into_vec();
let mut prepared: Vec<DynFut> = vec![];
let mut result = CombinedResult::<JobBuilder, UError>::new();
let mut result = CombinedResult::<JobBuilder, _>::new();
for req in job_requests {
let job_meta = JobCache::get(req.job_id);
if job_meta.is_none() {

@ -1,9 +1,10 @@
use crate::UError;
use crossbeam::channel::{self, Receiver, Sender};
use anyhow::Error;
use once_cell::sync::OnceCell;
use tokio::sync::mpsc::{channel, error::TryRecvError, Receiver, Sender};
use tokio::sync::{Mutex, MutexGuard};
type ChanError = UError;
static ERR_CHAN: OnceCell<ErrChan> = OnceCell::new();
type ChanError = Error;
static ERR_CHAN: OnceCell<Mutex<ErrChan>> = OnceCell::new();
pub struct ErrChan {
tx: Sender<ChanError>,
@ -11,18 +12,27 @@ pub struct ErrChan {
}
impl ErrChan {
fn get() -> &'static Self {
ERR_CHAN.get_or_init(|| {
let (tx, rx) = channel::bounded(20);
Self { tx, rx }
async fn get() -> MutexGuard<'static, Self> {
ERR_CHAN
.get_or_init(|| {
let (tx, rx) = channel(20);
Mutex::new(Self { tx, rx })
})
.lock()
.await
}
pub fn send(msg: ChanError) {
Self::get().tx.send(msg).unwrap()
pub async fn send(err: impl Into<ChanError>, ctx: impl AsRef<str>) {
let err = err.into();
error!("Encountered an error at '{}': {:?}", ctx.as_ref(), err);
Self::get().await.tx.try_send(err).unwrap();
}
pub fn recv() -> ChanError {
Self::get().rx.recv().unwrap()
pub async fn recv() -> Option<ChanError> {
match Self::get().await.rx.try_recv() {
Ok(r) => Some(r),
Err(TryRecvError::Disconnected) => panic!("err chan disconnected"),
Err(TryRecvError::Empty) => None,
}
}
}

@ -2,15 +2,26 @@ use std::env;
use std::path::Path;
use tracing_appender::rolling;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::{fmt, prelude::*, registry, EnvFilter};
pub fn init_logger(logfile: impl AsRef<Path> + Send + Sync + 'static) {
pub fn init_logger(logfile: Option<impl AsRef<Path> + Send + Sync + 'static>) {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "info")
}
tracing_subscriber::fmt::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(move || rolling::never(".", logfile.as_ref().with_extension("log")))
.init();
let reg = registry()
.with(EnvFilter::from_default_env())
.with(fmt::layer());
match logfile {
Some(file) => reg
.with(
fmt::layer()
.with_writer(move || {
rolling::never("logs", file.as_ref().with_extension("log"))
})
.with_ansi(false),
)
.init(),
None => reg.init(),
};
}

@ -28,7 +28,7 @@ impl fmt::Display for Empty {
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Reportable {
Assigned(AssignedJob),
Agent(Agent),

@ -25,13 +25,20 @@ pub struct JobMeta {
#[derive(Deserialize)]
pub struct RawJobMeta {
#[serde(default)]
pub alias: Option<String>,
#[serde(default)]
pub argv: String,
#[serde(default)]
pub id: Uuid,
#[serde(default)]
pub exec_type: JobType,
//pub schedule: JobSchedule,
#[serde(default)]
pub platform: String,
#[serde(default)]
pub payload: Option<Vec<u8>>,
#[serde(default)]
pub payload_path: Option<PathBuf>,
}

@ -1,9 +1,9 @@
use std::fmt::Debug;
use crate::utils::OneOrVec;
use crate::UError;
use anyhow::Error;
pub struct CombinedResult<T, E: Debug = UError> {
pub struct CombinedResult<T, E: Debug = Error> {
ok: Vec<T>,
err: Vec<E>,
}
@ -20,8 +20,13 @@ impl<T, E: Debug> CombinedResult<T, E> {
self.ok.extend(result.into_vec());
}
pub fn err(&mut self, err: impl OneOrVec<E>) {
self.err.extend(err.into_vec());
pub fn err<I: Into<E>>(&mut self, err: impl OneOrVec<I>) {
self.err.extend(
err.into_vec()
.into_iter()
.map(Into::into)
.collect::<Vec<_>>(),
);
}
pub fn unwrap(self) -> Vec<T> {

@ -3,7 +3,7 @@ use std::iter::Iterator;
use std::slice::Iter as SliceIter;
use std::str::Chars;
const MAX_DATA_LEN: usize = 200;
const MAX_DATA_LEN: usize = 2000;
pub trait Strippable {
type Item: fmt::Display;

@ -108,7 +108,7 @@ impl ProcOutput {
altered = true;
}
if !altered {
result.extend(b"No data");
result.extend(b"<empty output>");
}
result
}

@ -4,7 +4,7 @@ CREATE TYPE JobState AS ENUM ('queued', 'running', 'finished');
CREATE TYPE AgentState AS ENUM ('new', 'active', 'banned');
CREATE TABLE IF NOT EXISTS agents (
alias TEXT UNIQUE
alias TEXT
, hostname TEXT NOT NULL
, id UUID NOT NULL DEFAULT uuid_generate_v4()
, is_root BOOLEAN NOT NULL DEFAULT false
@ -34,11 +34,9 @@ CREATE TABLE IF NOT EXISTS ip_addrs (
);
CREATE TABLE IF NOT EXISTS jobs (
alias TEXT UNIQUE
alias TEXT
, argv TEXT NOT NULL
, id UUID NOT NULL DEFAULT uuid_generate_v4()
-- Shell, Binary (with program download),
-- Python (with program and python download if not exist), Management
, exec_type JobType NOT NULL DEFAULT 'shell'
, platform TEXT NOT NULL
, payload BYTEA
@ -47,7 +45,7 @@ CREATE TABLE IF NOT EXISTS jobs (
CREATE TABLE IF NOT EXISTS results (
agent_id UUID NOT NULL
, alias TEXT UNIQUE
, alias TEXT
, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
, id UUID NOT NULL DEFAULT uuid_generate_v4()
, job_id UUID NOT NULL

Loading…
Cancel
Save