You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
9.5 KiB
319 lines
9.5 KiB
use crate::error::Error; |
|
use diesel::{pg::PgConnection, prelude::*, result::Error as DslError, Connection}; |
|
use std::mem::drop; |
|
use u_lib::{ |
|
db::PgAsyncPool, |
|
models::{schema, Agent, AssignedJob, BriefJob, JobModel, JobState, PayloadMeta}, |
|
platform::Platform, |
|
types::Id, |
|
}; |
|
|
|
type Result<T> = std::result::Result<T, Error>; |
|
|
|
pub struct PgRepo { |
|
pool: PgAsyncPool, |
|
} |
|
|
|
impl PgRepo { |
|
pub fn new(pool: PgAsyncPool) -> PgRepo { |
|
PgRepo { pool } |
|
} |
|
|
|
pub async fn interact<F, R>(&self, f: F) -> Result<R> |
|
where |
|
F: for<'c> FnOnce(UDB<'c>) -> Result<R>, |
|
F: Send + 'static, |
|
R: Send + 'static, |
|
{ |
|
let connection = self.pool.get().await?; |
|
connection |
|
.interact(|conn| f(UDB { conn })) |
|
.await |
|
.expect("deadpool interaction failed") |
|
} |
|
|
|
pub async fn transaction<F, R>(&self, f: F) -> Result<R> |
|
where |
|
F: for<'c> FnOnce(UDB<'c>) -> Result<R>, |
|
F: Send + 'static, |
|
R: Send + 'static, |
|
{ |
|
let conn = self.pool.get().await?; |
|
conn.interact(|c| c.transaction(|conn| f(UDB { conn }))) |
|
.await |
|
.expect("deadpool interaction failed") |
|
} |
|
} |
|
|
|
pub struct UDB<'c> { |
|
conn: &'c mut PgConnection, |
|
} |
|
|
|
impl UDB<'_> { |
|
pub fn insert_jobs(&mut self, jobs: &[BriefJob]) -> Result<()> { |
|
use schema::{jobs, payloads}; |
|
|
|
let (jobs, payloads_opt): (Vec<_>, Vec<_>) = jobs |
|
.iter() |
|
.map(|j| (&j.job, j.payload_meta.as_ref())) |
|
.unzip(); |
|
|
|
let payloads = payloads_opt |
|
.into_iter() |
|
.filter_map(|p| p) |
|
.collect::<Vec<_>>(); |
|
|
|
diesel::insert_into(payloads::table) |
|
.values(payloads) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx("Can't insert payloads"))?; |
|
|
|
diesel::insert_into(jobs::table) |
|
.values(jobs) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx("Can't insert jobs")) |
|
} |
|
|
|
pub fn get_job(&mut self, id: Id) -> Result<Option<BriefJob>> { |
|
use schema::{jobs, payloads}; |
|
|
|
let maybe_job_with_payload = jobs::table |
|
.left_join(payloads::table) |
|
.filter(jobs::id.eq(id)) |
|
.first::<(JobModel, Option<PayloadMeta>)>(self.conn) |
|
.optional() |
|
.map_err(with_err_ctx(format!("Can't get job {id}")))?; |
|
|
|
Ok(maybe_job_with_payload.map(|(job, payload_meta)| BriefJob { job, payload_meta })) |
|
} |
|
|
|
pub fn get_jobs(&mut self) -> Result<Vec<JobModel>> { |
|
use schema::jobs; |
|
|
|
jobs::table |
|
.load(self.conn) |
|
.map_err(with_err_ctx("Can't get jobs")) |
|
} |
|
|
|
pub fn get_payload_meta(&mut self, id: Id) -> Result<Option<PayloadMeta>> { |
|
use schema::payloads; |
|
|
|
payloads::table |
|
.filter(payloads::id.eq(id)) |
|
.first(self.conn) |
|
.optional() |
|
.map_err(with_err_ctx(format!("Can't get payload {id}"))) |
|
} |
|
|
|
pub fn get_payload_metas(&mut self) -> Result<Vec<PayloadMeta>> { |
|
use schema::payloads; |
|
|
|
payloads::table |
|
.load(self.conn) |
|
.map_err(with_err_ctx("Can't get payloads")) |
|
} |
|
|
|
pub fn find_job_by_alias(&mut self, alias: &str) -> Result<Option<BriefJob>> { |
|
use schema::{jobs, payloads}; |
|
|
|
let maybe_job_with_payload = jobs::table |
|
.left_join(payloads::table) |
|
.filter(jobs::alias.eq(alias)) |
|
.first::<(JobModel, Option<PayloadMeta>)>(self.conn) |
|
.optional() |
|
.map_err(with_err_ctx(format!("Can't get job by alias {alias}")))?; |
|
|
|
Ok(maybe_job_with_payload.map(|(job, payload_meta)| BriefJob { job, payload_meta })) |
|
} |
|
|
|
pub fn insert_result(&mut self, result: &AssignedJob) -> Result<()> { |
|
use schema::results; |
|
|
|
diesel::insert_into(results::table) |
|
.values(result) |
|
.execute(self.conn) |
|
.map_err(with_err_ctx(format!("Can't insert result {result:?}")))?; |
|
Ok(()) |
|
} |
|
|
|
pub fn get_agent(&mut self, id: Id) -> Result<Option<Agent>> { |
|
use schema::agents; |
|
|
|
agents::table |
|
.filter(agents::id.eq(id)) |
|
.first(self.conn) |
|
.optional() |
|
.map_err(with_err_ctx(format!("Can't get agent {id:?}"))) |
|
} |
|
|
|
pub fn get_agents(&mut self) -> Result<Vec<Agent>> { |
|
use schema::agents; |
|
|
|
agents::table |
|
.load::<Agent>(self.conn) |
|
.map_err(with_err_ctx(format!("Can't get agents"))) |
|
} |
|
|
|
pub fn update_job_status(&mut self, id: Id, status: JobState) -> Result<()> { |
|
use schema::results; |
|
|
|
diesel::update(results::table) |
|
.filter(results::id.eq(id)) |
|
.set(results::state.eq(status)) |
|
.execute(self.conn) |
|
.map_err(with_err_ctx(format!("Can't update status of job {id}")))?; |
|
Ok(()) |
|
} |
|
|
|
//TODO: filters possibly could work in a wrong way, check |
|
pub fn get_assigned_jobs( |
|
&mut self, |
|
id: Option<Id>, |
|
personal: bool, |
|
) -> Result<Vec<AssignedJob>> { |
|
use schema::results; |
|
|
|
let mut q = results::table.into_boxed(); |
|
/*if id.is_some() { |
|
q = q.filter(results::agent_id.eq(id.unwrap())) |
|
}*/ |
|
if personal { |
|
q = q.filter( |
|
results::state |
|
.eq(JobState::Queued) |
|
.and(results::agent_id.eq(id.unwrap())), |
|
) |
|
} else if id.is_some() { |
|
q = q |
|
.filter(results::agent_id.eq(id.unwrap())) |
|
.or_filter(results::job_id.eq(id.unwrap())) |
|
.or_filter(results::id.eq(id.unwrap())) |
|
} |
|
let result = q |
|
.load::<AssignedJob>(self.conn) |
|
.map_err(with_err_ctx("Can't get exact jobs"))?; |
|
Ok(result) |
|
} |
|
|
|
pub fn set_jobs_for_agent(&mut self, agent_id: Id, job_ids: &[Id]) -> Result<()> { |
|
use schema::{jobs, results}; |
|
|
|
let agent_platform = match self.get_agent(agent_id)? { |
|
Some(agent) => Platform::new(&agent.platform), |
|
None => { |
|
return Err(Error::ProcessingError(format!( |
|
"Agent {agent_id} not found" |
|
))) |
|
} |
|
}; |
|
|
|
let jobs_meta = jobs::table |
|
.select((jobs::id, jobs::alias, jobs::target_platforms)) |
|
.filter(jobs::id.eq_any(job_ids)) |
|
.load::<(Id, Option<String>, String)>(self.conn) |
|
.map_err(with_err_ctx(format!("Can't find jobs {job_ids:?}")))?; |
|
|
|
for meta in &jobs_meta { |
|
if !agent_platform.matches(&meta.2) { |
|
return Err(Error::InsuitablePlatform( |
|
agent_platform.into_string(), |
|
meta.2.clone(), |
|
)); |
|
} |
|
} |
|
|
|
let job_requests = jobs_meta |
|
.into_iter() |
|
.map(|(job_id, alias, _)| AssignedJob { |
|
job_id, |
|
agent_id, |
|
alias, |
|
..Default::default() |
|
}) |
|
.collect::<Vec<AssignedJob>>(); |
|
|
|
diesel::insert_into(results::table) |
|
.values(&job_requests) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx(format!( |
|
"Can't setup jobs {job_ids:?} for agent {agent_id:?}" |
|
))) |
|
} |
|
|
|
pub fn del_jobs(&mut self, ids: &[Id]) -> Result<()> { |
|
use schema::jobs; |
|
|
|
diesel::delete(jobs::table) |
|
.filter(jobs::id.eq_any(ids)) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx("Can't delete jobs")) |
|
} |
|
|
|
pub fn del_results(&mut self, ids: &[Id]) -> Result<()> { |
|
use schema::results; |
|
|
|
diesel::delete(results::table) |
|
.filter(results::id.eq_any(ids)) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx("Can't delete results")) |
|
} |
|
|
|
pub fn del_agents(&mut self, ids: &[Id]) -> Result<()> { |
|
use schema::agents; |
|
|
|
diesel::delete(agents::table) |
|
.filter(agents::id.eq_any(ids)) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx("Can't delete agents")) |
|
} |
|
|
|
pub fn del_payloads(&mut self, ids: &[Id]) -> Result<()> { |
|
use schema::payloads; |
|
|
|
diesel::delete(payloads::table) |
|
.filter(payloads::id.eq_any(ids)) |
|
.execute(self.conn) |
|
.map(drop) |
|
.map_err(with_err_ctx("Can't delete payloads")) |
|
} |
|
|
|
pub fn upsert_agent(&mut self, agent: &Agent) -> Result<()> { |
|
use schema::agents; |
|
|
|
diesel::insert_into(agents::table) |
|
.values(agent) |
|
.on_conflict(agents::id) |
|
.do_update() |
|
.set(agent) |
|
.execute(self.conn) |
|
.map_err(with_err_ctx(format!("Can't insert agent {agent:?}")))?; |
|
Ok(()) |
|
} |
|
|
|
pub fn update_job(&mut self, job: &JobModel) -> Result<()> { |
|
job.save_changes::<JobModel>(self.conn) |
|
.map_err(with_err_ctx(format!("Can't update job {job:?}")))?; |
|
Ok(()) |
|
} |
|
|
|
pub fn update_result(&mut self, result: &AssignedJob) -> Result<()> { |
|
debug!( |
|
"updating result: id = {}, job_id = {}, agent_id = {}", |
|
result.id, result.job_id, result.agent_id |
|
); |
|
result |
|
.save_changes::<AssignedJob>(self.conn) |
|
.map_err(with_err_ctx(format!("Can't update result {result:?}")))?; |
|
Ok(()) |
|
} |
|
} |
|
|
|
fn with_err_ctx(msg: impl AsRef<str>) -> impl Fn(DslError) -> Error { |
|
move |err| Error::DBErrorCtx(format!("{}, reason: {err}", msg.as_ref())) |
|
}
|
|
|