diff --git a/lib/sync/src/resource.rs b/lib/sync/src/resource.rs index 93ffe895c..de7e461d0 100644 --- a/lib/sync/src/resource.rs +++ b/lib/sync/src/resource.rs @@ -27,6 +27,7 @@ pub trait ResourceSync: Sized { + Default + Send + From + + PartialDiff + 'static; type Info: Default + 'static; type PartialConfig: std::fmt::Debug @@ -35,7 +36,9 @@ pub trait ResourceSync: Sized { + From + Serialize + MaybeNone + + From + 'static; + type ConfigDiff: Diff + MaybeNone; fn name_to_resource( ) -> &'static HashMap>; @@ -68,29 +71,24 @@ pub trait ResourceSync: Sized { id: String, description: String, ) -> anyhow::Result<()>; -} - -pub trait ResourceSyncOuter< - Implementer: ResourceSync, - Logger: SyncLogger, -> where - Self: Sized, - Implementer::Config: - PartialDiff, - Implementer::PartialConfig: From, -{ - type ConfigDiff: Diff + MaybeNone; - - fn display() -> &'static str; - - fn resource_target(id: String) -> ResourceTarget; /// Diffs the declared toml (partial) against the full existing config. /// Removes all fields from toml (partial) that haven't changed. fn get_diff( - original: Implementer::Config, - update: Implementer::PartialConfig, + original: Self::Config, + update: Self::PartialConfig, ) -> anyhow::Result; +} + +pub trait ResourceSyncOuter< + Implementer: ResourceSync, + Logger: SyncLogger, +> where + Self: Sized, +{ + fn display() -> &'static str; + + fn resource_target(id: String) -> ResourceTarget; #[allow(async_fn_in_trait)] async fn run_updates( @@ -203,14 +201,9 @@ pub trait ResourceSyncOuter< } } -pub trait SyncLogger +pub trait SyncLogger where Self: Sized, - Implementer: ResourceSync, - Resource: ResourceSyncOuter, - Implementer::Config: - PartialDiff, - Implementer::PartialConfig: From, { fn log_to_create( resource: &ResourceToml, @@ -219,7 +212,7 @@ where name: &str, description: &str, tags: &[String], - diff: &Resource::ConfigDiff, + diff: &Implementer::ConfigDiff, ); fn log_to_delete(name: &str); @@ -237,6 +230,8 @@ where fn log_description_updated(name: &str); fn log_failed_description_update(name: &str, e: anyhow::Error); + + fn log_procedure_sync_failed_max_iter(); } pub trait IdToTag { @@ -251,11 +246,8 @@ pub fn get_updates( where Implementer: ResourceSync, Resource: ResourceSyncOuter, - Implementer::Config: - PartialDiff, - Implementer::PartialConfig: From, Tags: IdToTag, - Logger: SyncLogger, + Logger: SyncLogger, { let map = Implementer::name_to_resource(); @@ -279,7 +271,7 @@ where let config: Implementer::Config = resource.config.into(); resource.config = config.into(); - let diff = Resource::get_diff( + let diff = Implementer::get_diff( original.config.clone(), resource.config, )?; @@ -400,10 +392,7 @@ pub async fn run_update_tags( ) where Implementer: ResourceSync, Resource: ResourceSyncOuter, - Implementer::Config: - PartialDiff, - Implementer::PartialConfig: From, - Logger: SyncLogger, + Logger: SyncLogger, { // Update tags if let Err(e) = Implementer::update_tags(id, tags).await { @@ -430,10 +419,7 @@ pub async fn run_update_description( ) where Implementer: ResourceSync, Resource: ResourceSyncOuter, - Implementer::Config: - PartialDiff, - Implementer::PartialConfig: From, - Logger: SyncLogger, + Logger: SyncLogger, { if let Err(e) = Implementer::update_description(id, description).await diff --git a/lib/sync/src/resources.rs b/lib/sync/src/resources.rs new file mode 100644 index 000000000..000c7523e --- /dev/null +++ b/lib/sync/src/resources.rs @@ -0,0 +1,243 @@ +use monitor_client::entities::{ + alerter::Alerter, build::Build, builder::Builder, + deployment::Deployment, procedure::Procedure, repo::Repo, + server::Server, server_template::ServerTemplate, sync, + update::ResourceTarget, +}; +use partial_derive2::MaybeNone; + +use crate::resource::{ + ResourceSync, ResourceSyncOuter, SyncLogger, ToUpdateItem, +}; + +impl> + ResourceSyncOuter for Server +{ + fn display() -> &'static str { + "server" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Server(id) + } +} + +impl> + ResourceSyncOuter for Deployment +{ + fn display() -> &'static str { + "deployment" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Deployment(id) + } +} + +impl> + ResourceSyncOuter for Build +{ + fn display() -> &'static str { + "build" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Build(id) + } +} + +impl> + ResourceSyncOuter for Repo +{ + fn display() -> &'static str { + "repo" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Repo(id) + } +} + +impl> + ResourceSyncOuter for Alerter +{ + fn display() -> &'static str { + "alerter" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Alerter(id) + } +} + +impl> + ResourceSyncOuter for Builder +{ + fn display() -> &'static str { + "builder" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Builder(id) + } +} + +impl> + ResourceSyncOuter for ServerTemplate +{ + fn display() -> &'static str { + "server_template" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::ServerTemplate(id) + } +} + +impl> + ResourceSyncOuter for sync::ResourceSync +{ + fn display() -> &'static str { + "resource_sync" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::ResourceSync(id) + } +} + +impl> + ResourceSyncOuter for Procedure +{ + fn display() -> &'static str { + "procedure" + } + + fn resource_target(id: String) -> ResourceTarget { + ResourceTarget::Procedure(id) + } + + async fn run_updates( + mut to_create: crate::resource::ToCreate< + ::PartialConfig, + >, + mut to_update: crate::resource::ToUpdate< + ::PartialConfig, + >, + to_delete: crate::resource::ToDelete, + ) { + for name in to_delete { + if let Err(e) = Implementer::delete(name.clone()).await { + Logger::log_failed_delete(&name, e); + // warn!("failed to delete procedure {name} | {e:#}",); + } else { + Logger::log_deleted(&name); + // info!( + // "{} procedure '{}'", + // "deleted".red().bold(), + // name.bold(), + // ); + } + } + + if to_update.is_empty() && to_create.is_empty() { + return; + } + + for i in 0..10 { + let mut to_pull = Vec::new(); + for ToUpdateItem { + id, + resource, + update_description, + update_tags, + } in &to_update + { + // Update resource + let name = resource.name.clone(); + let tags = resource.tags.clone(); + let description = resource.description.clone(); + if *update_description { + crate::resource::run_update_description::< + Implementer, + Self, + Logger, + >(id.clone(), &name, description) + .await; + } + if *update_tags { + crate::resource::run_update_tags::( + id.clone(), + &name, + tags, + ) + .await; + } + if !resource.config.is_none() { + if let Err(e) = + Implementer::update(id.clone(), resource.clone()).await + { + if i == 9 { + Logger::log_failed_update(&name, e); + // warn!( + // "failed to update {} {name} | {e:#}", + // Self::display() + // ); + } + } + } + + // info!("{} {name} updated", Self::display()); + Logger::log_updated(&name); + + // have to clone out so to_update is mutable + to_pull.push(id.clone()); + } + // + to_update.retain(|resource| !to_pull.contains(&resource.id)); + + let mut to_pull = Vec::new(); + for resource in &to_create { + let name = resource.name.clone(); + let tags = resource.tags.clone(); + let description = resource.description.clone(); + let id = match Implementer::create(resource.clone()).await { + Ok(id) => id, + Err(e) => { + if i == 9 { + // warn!( + // "failed to create {} {name} | {e:#}", + // Self::display(), + // ); + Logger::log_failed_create(&name, e); + } + continue; + } + }; + crate::resource::run_update_tags::( + id.clone(), + &name, + tags, + ) + .await; + crate::resource::run_update_description::< + Implementer, + Self, + Logger, + >(id, &name, description) + .await; + Logger::log_created(&name); + // info!("{} {name} created", Self::display()); + to_pull.push(name); + } + to_create.retain(|resource| !to_pull.contains(&resource.name)); + + if to_update.is_empty() && to_create.is_empty() { + // info!("all procedures synced"); + return; + } + } + Logger::log_procedure_sync_failed_max_iter(); + // warn!("procedure sync loop exited after max iterations"); + } +} diff --git a/lib/sync/src/resources/mod.rs b/lib/sync/src/resources/mod.rs deleted file mode 100644 index e69de29bb..000000000