From 51ae0ed12015733a1704e2be7ea61871edd00d42 Mon Sep 17 00:00:00 2001 From: Kek5chen Date: Fri, 26 Apr 2024 12:20:23 +0200 Subject: [PATCH] vickylib: split mod into entities --- vicky/src/lib/database/entities/lock.rs | 19 ++ vicky/src/lib/database/entities/mod.rs | 5 + vicky/src/lib/database/entities/task.rs | 278 ++++++++++++++++++++++++ vicky/src/lib/database/mod.rs | 229 +------------------ vicky/src/lib/vicky/scheduler.rs | 4 +- 5 files changed, 305 insertions(+), 230 deletions(-) create mode 100644 vicky/src/lib/database/entities/lock.rs create mode 100644 vicky/src/lib/database/entities/mod.rs create mode 100644 vicky/src/lib/database/entities/task.rs diff --git a/vicky/src/lib/database/entities/lock.rs b/vicky/src/lib/database/entities/lock.rs new file mode 100644 index 0000000..a3d4ba4 --- /dev/null +++ b/vicky/src/lib/database/entities/lock.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum Lock { + WRITE { name: String }, + READ { name: String }, +} + +impl Lock { + pub fn is_conflicting(&self, other: &Lock) -> bool { + match (self, other) { + (Lock::WRITE { name: name1 }, Lock::WRITE { name: name2 }) + | (Lock::READ { name: name1 }, Lock::WRITE { name: name2 }) + | (Lock::WRITE { name: name1 }, Lock::READ { name: name2 }) => name1 == name2, + _ => false, + } + } +} diff --git a/vicky/src/lib/database/entities/mod.rs b/vicky/src/lib/database/entities/mod.rs new file mode 100644 index 0000000..fb793e2 --- /dev/null +++ b/vicky/src/lib/database/entities/mod.rs @@ -0,0 +1,5 @@ +mod task; +mod lock; + +pub use task::*; +pub use lock::*; diff --git a/vicky/src/lib/database/entities/task.rs b/vicky/src/lib/database/entities/task.rs new file mode 100644 index 0000000..27137a6 --- /dev/null +++ b/vicky/src/lib/database/entities/task.rs @@ -0,0 +1,278 @@ +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use crate::database::entities::lock::Lock; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "result")] +pub enum TaskResult { + SUCCESS, + ERROR, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "state")] +pub enum TaskStatus { + NEW, + RUNNING, + FINISHED(TaskResult), +} + +type FlakeURI = String; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct FlakeRef { + pub flake: FlakeURI, + pub args: Vec, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Task { + pub id: Uuid, + pub display_name: String, + pub status: TaskStatus, + pub locks: Vec, + pub flake_ref: FlakeRef, + pub features: Vec, +} + +impl Task { + pub fn builder() -> TaskBuilder { + TaskBuilder::default() + } +} + +impl From for Task { + fn from(builder: TaskBuilder) -> Self { + builder.build() + } +} + +pub struct TaskBuilder { + id: Option, + display_name: Option, + status: TaskStatus, + locks: Vec, + flake_ref: FlakeRef, + features: Vec, +} + +impl Default for TaskBuilder { + fn default() -> Self { + TaskBuilder { + id: None, + display_name: None, + status: TaskStatus::NEW, + locks: Vec::new(), + flake_ref: FlakeRef { + flake: "".to_string(), + args: Vec::new(), + }, + features: Vec::new(), + } + } +} + +impl TaskBuilder { + pub fn with_id(mut self, id: Uuid) -> Self { + self.id = Some(id); + self + } + + pub fn with_display_name>(mut self, display_name: S) -> Self { + self.display_name = Some(display_name.into()); + self + } + + pub fn with_status(mut self, status: TaskStatus) -> Self { + self.status = status; + self + } + + pub fn with_read_lock>(mut self, name: S) -> Self { + self.locks.push(Lock::READ { name: name.into() }); + self + } + + pub fn with_write_lock>(mut self, name: S) -> Self { + self.locks.push(Lock::WRITE { name: name.into() }); + self + } + + pub fn with_locks(mut self, locks: Vec) -> Self { + self.locks = locks; + self + } + + pub fn with_flake>(mut self, flake_uri: S) -> Self { + self.flake_ref.flake = flake_uri.into(); + self + } + + pub fn with_flake_arg>(mut self, flake_arg: S) -> Self { + self.flake_ref.args.push(flake_arg.into()); + self + } + + pub fn with_flake_args(mut self, args: Vec) -> Self { + self.flake_ref.args = args; + self + } + + pub fn requires_feature>(mut self, feature: S) -> Self { + self.features.push(feature.into()); + self + } + + pub fn requires_features(mut self, features: Vec) -> Self { + self.features = features; + self + } + + pub fn id(&self) -> Option { + self.id + } + + pub fn display_name(&self) -> &Option { + &self.display_name + } + + pub fn status(&self) -> &TaskStatus { + &self.status + } + + pub fn locks(&self) -> &Vec { + &self.locks + } + + pub fn flake_ref(&self) -> &FlakeRef { + &self.flake_ref + } + + pub fn features(&self) -> &Vec { + &self.features + } + + pub fn build(self) -> Task { + Task { + id: self.id.unwrap_or_else(Uuid::new_v4), + display_name: self.display_name.unwrap_or_else(|| "Task".to_string()), + features: self.features, + status: self.status, + locks: self.locks, + flake_ref: self.flake_ref, + } + } +} + +// this was on purpose because these macro-generated entity types +// mess up the whole namespace and HAVE to be scoped +pub mod db_impl { + use crate::database::entities::task::{Task, TaskResult, TaskStatus}; + use crate::errors::VickyError; + use async_trait::async_trait; + use diesel::{Insertable, Queryable, Selectable}; + use uuid::Uuid; + use crate::database::entities::lock::Lock; + // these here are evil >:( + use crate::database::schema::locks; + use crate::database::schema::tasks; + + #[derive(Insertable, Queryable)] + #[diesel(table_name = tasks)] + struct DbTask { + pub id: Uuid, + pub display_name: Option, + pub status: Option, + pub flake_ref_uri: Option, + pub flake_ref_args: Option, + } + + impl ToString for TaskStatus { + fn to_string(&self) -> String { + match self { + TaskStatus::NEW => "NEW", + TaskStatus::RUNNING => "RUNNING", + TaskStatus::FINISHED(r) => match r { + TaskResult::SUCCESS => "FINISHED::SUCCESS", + TaskResult::ERROR => "FINISHED::ERROR", + }, + }.to_string() + } + } + + impl Into for Task { + fn into(self) -> DbTask { + DbTask { + id: self.id, + display_name: Some(self.display_name), + status: Some(self.status.to_string()), + flake_ref_uri: Some(self.flake_ref.flake), + flake_ref_args: Some(self.flake_ref.args.join("||")), + } + } + } + + #[derive(Insertable, Queryable)] + #[diesel(table_name = locks)] + struct DbLock { + id: Option, + task_id: Uuid, + name: String, + type_: String, + } + + impl DbLock { + fn from_lock(lock: Lock, task_id: Uuid) -> Self { + match lock { + Lock::WRITE { name } => DbLock { id: None, task_id, name, type_: "WRITE".to_string() }, + Lock::READ { name } => DbLock { id: None, task_id, name, type_: "READ".to_string() }, + } + } + } + + impl Into for DbLock { + fn into(self) -> Lock { + match self.type_.as_str() { + "WRITE" => Lock::WRITE { name: self.name }, + "READ" => Lock::READ { name: self.name }, + _ => panic!( + "Can't parse lock from database lock. Database corrupted? \ + Expected READ or WRITE but found {} as type at key {}.", + self.type_, + self.id.unwrap_or(-1) + ), + } + } + } + + #[async_trait] + pub trait TaskDatabase { + async fn get_all_tasks(&mut self) -> Result, VickyError>; + async fn get_task(&self, task_id: Uuid) -> Result, VickyError>; + async fn put_task(&mut self, task: &Task) -> Result<(), VickyError>; + } + + impl TaskDatabase for diesel::pg::PgConnection { + async fn get_all_tasks(mut self) -> Result, VickyError> { + // very evil >>:( + use self::tasks::dsl::*; + + todo!() + } + + async fn get_task(&self, task_id: Uuid) -> Result, VickyError> { + // so evil >:O + use self::tasks::dsl::*; + + todo!(); + } + + async fn put_task(&mut self, task: &Task) -> Result<(), VickyError> { + // even more evil >;( + use self::tasks::dsl::*; + + todo!(); + } + } +} diff --git a/vicky/src/lib/database/mod.rs b/vicky/src/lib/database/mod.rs index 8bd803f..6edc672 100644 --- a/vicky/src/lib/database/mod.rs +++ b/vicky/src/lib/database/mod.rs @@ -1,229 +1,2 @@ mod schema; - -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -use uuid::Uuid; - -use crate::{errors::VickyError, etcd::client::ClientExt}; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(tag = "result")] - -pub enum TaskResult { - SUCCESS, - ERROR, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(tag = "state")] - -pub enum TaskStatus { - NEW, - RUNNING, - FINISHED(TaskResult), -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum Lock { - WRITE { name: String }, - READ { name: String }, -} - -impl Lock { - pub fn is_conflicting(&self, other: &Lock) -> bool { - match (self, other) { - (Lock::WRITE { name: name1 }, Lock::WRITE { name: name2 }) - | (Lock::READ { name: name1 }, Lock::WRITE { name: name2 }) - | (Lock::WRITE { name: name1 }, Lock::READ { name: name2 }) => name1 == name2, - _ => false, - } - } -} - -type FlakeURI = String; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct FlakeRef { - pub flake: FlakeURI, - pub args: Vec, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct Task { - pub id: Uuid, - pub display_name: String, - pub status: TaskStatus, - pub locks: Vec, - pub flake_ref: FlakeRef, - pub features: Vec, -} - -impl Task { - pub fn builder() -> TaskBuilder { - TaskBuilder::default() - } -} - -impl From for Task { - fn from(builder: TaskBuilder) -> Self { - builder.build() - } -} - -pub struct TaskBuilder { - id: Option, - display_name: Option, - status: TaskStatus, - locks: Vec, - flake_ref: FlakeRef, - features: Vec, -} - -impl Default for TaskBuilder { - fn default() -> Self { - TaskBuilder { - id: None, - display_name: None, - status: TaskStatus::NEW, - locks: Vec::new(), - flake_ref: FlakeRef { - flake: "".to_string(), - args: Vec::new(), - }, - features: Vec::new(), - } - } -} - -impl TaskBuilder { - pub fn with_id(mut self, id: Uuid) -> Self { - self.id = Some(id); - self - } - - pub fn with_display_name>(mut self, display_name: S) -> Self { - self.display_name = Some(display_name.into()); - self - } - - pub fn with_status(mut self, status: TaskStatus) -> Self { - self.status = status; - self - } - - pub fn with_read_lock>(mut self, name: S) -> Self { - self.locks.push(Lock::READ { name: name.into() }); - self - } - - pub fn with_write_lock>(mut self, name: S) -> Self { - self.locks.push(Lock::WRITE { name: name.into() }); - self - } - - pub fn with_locks(mut self, locks: Vec) -> Self { - self.locks = locks; - self - } - - pub fn with_flake>(mut self, flake_uri: S) -> Self { - self.flake_ref.flake = flake_uri.into(); - self - } - - pub fn with_flake_arg>(mut self, flake_arg: S) -> Self { - self.flake_ref.args.push(flake_arg.into()); - self - } - - pub fn with_flake_args(mut self, args: Vec) -> Self { - self.flake_ref.args = args; - self - } - - pub fn requires_feature>(mut self, feature: S) -> Self { - self.features.push(feature.into()); - self - } - - pub fn requires_features(mut self, features: Vec) -> Self { - self.features = features; - self - } - - pub fn id(&self) -> Option { - self.id - } - - pub fn display_name(&self) -> &Option { - &self.display_name - } - - pub fn status(&self) -> &TaskStatus { - &self.status - } - - pub fn locks(&self) -> &Vec { - &self.locks - } - - pub fn flake_ref(&self) -> &FlakeRef { - &self.flake_ref - } - - pub fn features(&self) -> &Vec { - &self.features - } - - pub fn build(self) -> Task { - Task { - id: self.id.unwrap_or_else(Uuid::new_v4), - display_name: self.display_name.unwrap_or_else(|| "Task".to_string()), - features: self.features, - status: self.status, - locks: self.locks, - flake_ref: self.flake_ref, - } - } -} - -#[async_trait] -pub trait TaskDatabase { - async fn get_all_tasks(&self) -> Result, VickyError>; - async fn get_task(&self, task_id: Uuid) -> Result, VickyError>; - async fn put_task(&self, task: &Task) -> Result<(), VickyError>; -} - -#[async_trait] -impl TaskDatabase for diesel::pg::PgConnection { - async fn get_all_tasks(&self) -> Result, VickyError> { - let mut kv = self.kv_client(); - let get_options: GetOptions = GetOptions::new().with_prefix().with_sort( - etcd_client::SortTarget::Create, - etcd_client::SortOrder::Descend, - ); - let tasks: Vec = kv - .get_yaml_list( - "vicky.wobcom.de/task/manifest".to_string(), - Some(get_options), - ) - .await?; - Ok(tasks) - } - - async fn get_task(&self, task_id: Uuid) -> Result, VickyError> { - let mut kv = self.kv_client(); - let key = format!("vicky.wobcom.de/task/manifest/{}", task_id); - let task: Option = kv.get_yaml(key.clone(), None).await?; - Ok(task) - } - - async fn put_task(&self, task: &Task) -> Result<(), VickyError> { - let mut kv = self.kv_client(); - let key = format!("vicky.wobcom.de/task/manifest/{}", task.id); - kv.put_yaml(key, &task, None).await?; - Ok(()) - } -} +pub mod entities; diff --git a/vicky/src/lib/vicky/scheduler.rs b/vicky/src/lib/vicky/scheduler.rs index b51b2a9..4a6c2d9 100644 --- a/vicky/src/lib/vicky/scheduler.rs +++ b/vicky/src/lib/vicky/scheduler.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use log::debug; use crate::{ - database::{Lock, Task, TaskStatus}, + database::entities::{Lock, Task, TaskStatus}, errors::SchedulerError, }; @@ -148,7 +148,7 @@ impl Scheduler { #[cfg(test)] mod tests { - use crate::database::{Task, TaskStatus}; + use crate::database::entities::{Task, TaskStatus}; use super::Scheduler;