improv the sync

This commit is contained in:
mbecker20
2024-06-08 00:50:30 -07:00
parent 6aa801b705
commit cad1ee123e
15 changed files with 232 additions and 136 deletions

8
Cargo.lock generated
View File

@@ -2552,18 +2552,18 @@ checksum = "ffa94c2e5674923c67d7f3dfce1279507b191e10eb064881b46ed3e1256e5ca6"
[[package]] [[package]]
name = "partial_derive2" name = "partial_derive2"
version = "0.4.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7b915bd76bc306b7ae6f1b5d99b0434e498ab979ecbd5df119db8a00dab972" checksum = "6b2bd06fda40521c285c8dfc5f6b90208d586328a295e83332b6166c2b2a4241"
dependencies = [ dependencies = [
"partial_derive2_derive", "partial_derive2_derive",
] ]
[[package]] [[package]]
name = "partial_derive2_derive" name = "partial_derive2_derive"
version = "0.4.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddaac49c0e65bcb207999d2514b10b43d5f2ec2d0fb47b9d875c9a10536a294e" checksum = "3a506f66d52e40b2385d7b9f776fd5243d6cff16ba79147f859aa4e27d2d27cc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -30,7 +30,7 @@ derive_empty_traits = "0.1.0"
merge_config_files = "0.1.5" merge_config_files = "0.1.5"
termination_signal = "0.1.3" termination_signal = "0.1.3"
async_timing_util = "0.1.14" async_timing_util = "0.1.14"
partial_derive2 = "0.4.2" partial_derive2 = "0.4.3"
derive_variants = "1.0.0" derive_variants = "1.0.0"
mongo_indexed = "0.3.0" mongo_indexed = "0.3.0"
resolver_api = "1.1.0" resolver_api = "1.1.0"

View File

@@ -62,6 +62,10 @@ pub trait ResourceSync: Sized {
resource: ResourceToml<Self::PartialConfig>, resource: ResourceToml<Self::PartialConfig>,
) -> anyhow::Result<()>; ) -> anyhow::Result<()>;
/// Apply any changes to incoming toml partial config
/// before it is diffed against existing config
fn validate_partial_config(_config: &mut Self::PartialConfig) {}
/// Diffs the declared toml (partial) against the full existing config. /// Diffs the declared toml (partial) against the full existing config.
/// Removes all fields from toml (partial) that haven't changed. /// Removes all fields from toml (partial) that haven't changed.
fn get_diff( fn get_diff(
@@ -69,6 +73,10 @@ pub trait ResourceSync: Sized {
update: Self::PartialConfig, update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff>; ) -> anyhow::Result<Self::ConfigDiff>;
/// Apply any changes to computed config diff
/// before logging
fn validate_diff(_diff: &mut Self::ConfigDiff) {}
/// Deletes the target resource /// Deletes the target resource
async fn delete(id_or_name: String) -> anyhow::Result<()>; async fn delete(id_or_name: String) -> anyhow::Result<()>;
@@ -188,11 +196,15 @@ pub fn get_updates<Resource: ResourceSync>(
let config: Resource::Config = resource.config.into(); let config: Resource::Config = resource.config.into();
resource.config = config.into(); resource.config = config.into();
let diff = Resource::get_diff( Resource::validate_partial_config(&mut resource.config);
let mut diff = Resource::get_diff(
original.config.clone(), original.config.clone(),
resource.config, resource.config,
)?; )?;
Resource::validate_diff(&mut diff);
let original_tags = original let original_tags = original
.tags .tags
.iter() .iter()

View File

@@ -78,6 +78,14 @@ impl ResourceSync for Build {
Ok(original.partial_diff(update)) Ok(original.partial_diff(update))
} }
fn validate_diff(diff: &mut Self::ConfigDiff) {
if let Some((_, to)) = &diff.version {
if to.is_none() {
diff.version = None;
}
}
}
async fn delete(id: String) -> anyhow::Result<()> { async fn delete(id: String) -> anyhow::Result<()> {
monitor_client().write(DeleteBuild { id }).await?; monitor_client().write(DeleteBuild { id }).await?;
Ok(()) Ok(())

View File

@@ -30,11 +30,12 @@ use monitor_client::{
}, },
}; };
use mungos::find::find_collect; use mungos::find::find_collect;
use partial_derive2::PartialDiff;
use resolver_api::Resolve; use resolver_api::Resolve;
use crate::{ use crate::{
helpers::query::get_user_user_group_ids, helpers::query::get_user_user_group_ids,
resource, resource::{self, MonitorResource},
state::{db_client, State}, state::{db_client, State},
}; };
@@ -166,7 +167,9 @@ impl Resolve<ExportResourcesToToml, User> for State {
PermissionLevel::Read, PermissionLevel::Read,
) )
.await?; .await?;
res.alerters.push(convert_resource(alerter, &names.tags)) res
.alerters
.push(convert_resource::<Alerter>(alerter, &names.tags))
} }
ResourceTarget::ResourceSync(id) => { ResourceTarget::ResourceSync(id) => {
let sync = resource::get_check_permissions::<ResourceSync>( let sync = resource::get_check_permissions::<ResourceSync>(
@@ -175,7 +178,9 @@ impl Resolve<ExportResourcesToToml, User> for State {
PermissionLevel::Read, PermissionLevel::Read,
) )
.await?; .await?;
res.resource_syncs.push(convert_resource(sync, &names.tags)) res
.resource_syncs
.push(convert_resource::<ResourceSync>(sync, &names.tags))
} }
ResourceTarget::ServerTemplate(id) => { ResourceTarget::ServerTemplate(id) => {
let template = resource::get_check_permissions::< let template = resource::get_check_permissions::<
@@ -184,9 +189,9 @@ impl Resolve<ExportResourcesToToml, User> for State {
&id, &user, PermissionLevel::Read &id, &user, PermissionLevel::Read
) )
.await?; .await?;
res res.server_templates.push(
.server_templates convert_resource::<ServerTemplate>(template, &names.tags),
.push(convert_resource(template, &names.tags)) )
} }
ResourceTarget::Server(id) => { ResourceTarget::Server(id) => {
let server = resource::get_check_permissions::<Server>( let server = resource::get_check_permissions::<Server>(
@@ -195,7 +200,9 @@ impl Resolve<ExportResourcesToToml, User> for State {
PermissionLevel::Read, PermissionLevel::Read,
) )
.await?; .await?;
res.servers.push(convert_resource(server, &names.tags)) res
.servers
.push(convert_resource::<Server>(server, &names.tags))
} }
ResourceTarget::Builder(id) => { ResourceTarget::Builder(id) => {
let mut builder = let mut builder =
@@ -211,7 +218,9 @@ impl Resolve<ExportResourcesToToml, User> for State {
names.servers.get(&id).unwrap_or(&String::new()), names.servers.get(&id).unwrap_or(&String::new()),
) )
} }
res.builders.push(convert_resource(builder, &names.tags)) res
.builders
.push(convert_resource::<Builder>(builder, &names.tags))
} }
ResourceTarget::Build(id) => { ResourceTarget::Build(id) => {
let mut build = resource::get_check_permissions::<Build>( let mut build = resource::get_check_permissions::<Build>(
@@ -227,7 +236,9 @@ impl Resolve<ExportResourcesToToml, User> for State {
.get(&build.config.builder_id) .get(&build.config.builder_id)
.unwrap_or(&String::new()), .unwrap_or(&String::new()),
); );
res.builds.push(convert_resource(build, &names.tags)) res
.builds
.push(convert_resource::<Build>(build, &names.tags))
} }
ResourceTarget::Deployment(id) => { ResourceTarget::Deployment(id) => {
let mut deployment = resource::get_check_permissions::< let mut deployment = resource::get_check_permissions::<
@@ -251,9 +262,10 @@ impl Resolve<ExportResourcesToToml, User> for State {
names.builds.get(build_id).unwrap_or(&String::new()), names.builds.get(build_id).unwrap_or(&String::new()),
); );
} }
res res.deployments.push(convert_resource::<Deployment>(
.deployments deployment,
.push(convert_resource(deployment, &names.tags)) &names.tags,
))
} }
ResourceTarget::Repo(id) => { ResourceTarget::Repo(id) => {
let mut repo = resource::get_check_permissions::<Repo>( let mut repo = resource::get_check_permissions::<Repo>(
@@ -269,7 +281,7 @@ impl Resolve<ExportResourcesToToml, User> for State {
.get(&repo.config.server_id) .get(&repo.config.server_id)
.unwrap_or(&String::new()), .unwrap_or(&String::new()),
); );
res.repos.push(convert_resource(repo, &names.tags)) res.repos.push(convert_resource::<Repo>(repo, &names.tags))
} }
ResourceTarget::Procedure(id) => { ResourceTarget::Procedure(id) => {
add_procedure(&id, &mut res, &user, &names) add_procedure(&id, &mut res, &user, &names)
@@ -381,7 +393,7 @@ async fn add_procedure(
res res
.procedures .procedures
.push(convert_resource(procedure, &names.tags)); .push(convert_resource::<Procedure>(procedure, &names.tags));
Ok(()) Ok(())
} }
@@ -500,13 +512,13 @@ async fn add_user_groups(
Ok(()) Ok(())
} }
fn convert_resource<Config, Info: Default, PartialConfig>( fn convert_resource<R: MonitorResource>(
resource: Resource<Config, Info>, resource: Resource<R::Config, R::Info>,
tag_names: &HashMap<String, String>, tag_names: &HashMap<String, String>,
) -> ResourceToml<PartialConfig> ) -> ResourceToml<R::PartialConfig> {
where // This makes sure all non-necessary (defaulted) fields don't make it into final toml
Config: Into<PartialConfig>, let partial: R::PartialConfig = resource.config.into();
{ let config = R::Config::default().minimize_partial(partial);
ResourceToml { ResourceToml {
name: resource.name, name: resource.name,
tags: resource tags: resource
@@ -515,6 +527,6 @@ where
.filter_map(|t| tag_names.get(t).cloned()) .filter_map(|t| tag_names.get(t).cloned())
.collect(), .collect(),
description: resource.description, description: resource.description,
config: resource.config.into(), config,
} }
} }

View File

@@ -12,7 +12,7 @@ use monitor_client::{
repo::Repo, repo::Repo,
server::Server, server::Server,
server_template::ServerTemplate, server_template::ServerTemplate,
sync::{PendingUpdates, ResourceSync}, sync::{PendingSyncUpdates, ResourceSync},
user::User, user::User,
}, },
}; };
@@ -105,7 +105,7 @@ impl Resolve<RefreshResourceSyncPending, User> for State {
let all_resources = AllResourcesById::load().await?; let all_resources = AllResourcesById::load().await?;
let id_to_tags = get_id_to_tags(None).await?; let id_to_tags = get_id_to_tags(None).await?;
let pending = PendingUpdates { let pending = PendingSyncUpdates {
hash, hash,
message, message,
server_updates: get_updates_for_view::<Server>( server_updates: get_updates_for_view::<Server>(

View File

@@ -13,6 +13,7 @@ use monitor_client::{
repo::Repo, repo::Repo,
server::Server, server::Server,
server_template::ServerTemplate, server_template::ServerTemplate,
sync::SyncUpdate,
tag::Tag, tag::Tag,
toml::ResourceToml, toml::ResourceToml,
update::{Log, ResourceTarget}, update::{Log, ResourceTarget},
@@ -44,6 +45,10 @@ pub struct ToUpdateItem<T> {
pub trait ResourceSync: MonitorResource + Sized { pub trait ResourceSync: MonitorResource + Sized {
fn resource_target(id: String) -> ResourceTarget; fn resource_target(id: String) -> ResourceTarget;
/// Apply any changes to incoming toml partial config
/// before it is diffed against existing config
fn validate_partial_config(_config: &mut Self::PartialConfig) {}
/// Diffs the declared toml (partial) against the full existing config. /// Diffs the declared toml (partial) against the full existing config.
/// Removes all fields from toml (partial) that haven't changed. /// Removes all fields from toml (partial) that haven't changed.
fn get_diff( fn get_diff(
@@ -52,6 +57,10 @@ pub trait ResourceSync: MonitorResource + Sized {
resources: &AllResourcesById, resources: &AllResourcesById,
) -> anyhow::Result<Self::ConfigDiff>; ) -> anyhow::Result<Self::ConfigDiff>;
/// Apply any changes to computed config diff
/// before logging
fn validate_diff(_diff: &mut Self::ConfigDiff) {}
async fn run_updates( async fn run_updates(
to_create: ToCreate<Self::PartialConfig>, to_create: ToCreate<Self::PartialConfig>,
to_update: ToUpdate<Self::PartialConfig>, to_update: ToUpdate<Self::PartialConfig>,
@@ -194,7 +203,7 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
delete: bool, delete: bool,
all_resources: &AllResourcesById, all_resources: &AllResourcesById,
id_to_tags: &HashMap<String, Tag>, id_to_tags: &HashMap<String, Tag>,
) -> anyhow::Result<Option<String>> { ) -> anyhow::Result<Option<SyncUpdate>> {
let map = find_collect(Resource::coll().await, None, None) let map = find_collect(Resource::coll().await, None, None)
.await .await
.context("failed to get resources from db")? .context("failed to get resources from db")?
@@ -202,20 +211,21 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
.map(|r| (r.name.clone(), r)) .map(|r| (r.name.clone(), r))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let mut any_change = false; let mut update = SyncUpdate {
log: format!("{} Updates", Resource::resource_type()),
..Default::default()
};
let mut to_delete = Vec::<String>::new(); let mut to_delete = Vec::<String>::new();
if delete { if delete {
for resource in map.values() { for resource in map.values() {
if !resources.iter().any(|r| r.name == resource.name) { if !resources.iter().any(|r| r.name == resource.name) {
any_change = true; update.to_delete += 1;
to_delete.push(resource.name.clone()) to_delete.push(resource.name.clone())
} }
} }
} }
let mut log = format!("{} Updates", Resource::resource_type());
for mut resource in resources { for mut resource in resources {
match map.get(&resource.name) { match map.get(&resource.name) {
Some(original) => { Some(original) => {
@@ -224,12 +234,16 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
let config: Resource::Config = resource.config.into(); let config: Resource::Config = resource.config.into();
resource.config = config.into(); resource.config = config.into();
let diff = Resource::get_diff( Resource::validate_partial_config(&mut resource.config);
let mut diff = Resource::get_diff(
original.config.clone(), original.config.clone(),
resource.config, resource.config,
all_resources, all_resources,
)?; )?;
Resource::validate_diff(&mut diff);
let original_tags = original let original_tags = original
.tags .tags
.iter() .iter()
@@ -245,9 +259,9 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
continue; continue;
} }
any_change = true; update.to_update += 1;
log.push_str(&format!( update.log.push_str(&format!(
"\n\n{}: {}: '{}'\n-------------------", "\n\n{}: {}: '{}'\n-------------------",
colored("UPDATE", "blue"), colored("UPDATE", "blue"),
Resource::resource_type(), Resource::resource_type(),
@@ -287,12 +301,12 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
) )
}, },
)); ));
log.push('\n'); update.log.push('\n');
log.push_str(&lines.join("\n-------------------\n")); update.log.push_str(&lines.join("\n-------------------\n"));
} }
None => { None => {
any_change = true; update.to_create += 1;
log.push_str(&format!( update.log.push_str(&format!(
"\n\n{}: {}: {}\n{}: {}\n{}: {:?}\n{}: {}", "\n\n{}: {}: {}\n{}: {}\n{}: {:?}\n{}: {}",
colored("CREATE", "green"), colored("CREATE", "green"),
Resource::resource_type(), Resource::resource_type(),
@@ -310,7 +324,7 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
} }
for name in to_delete { for name in to_delete {
log.push_str(&format!( update.log.push_str(&format!(
"\n\n{}: {}: '{}'\n-------------------", "\n\n{}: {}: '{}'\n-------------------",
colored("DELETE", "red"), colored("DELETE", "red"),
Resource::resource_type(), Resource::resource_type(),
@@ -318,7 +332,11 @@ pub async fn get_updates_for_view<Resource: ResourceSync>(
)); ));
} }
Ok(any_change.then_some(log)) let any_change = update.to_create > 0
|| update.to_update > 0
|| update.to_delete > 0;
Ok(any_change.then_some(update))
} }
/// Gets all the resources to update. For use in sync execution. /// Gets all the resources to update. For use in sync execution.
@@ -355,12 +373,16 @@ pub async fn get_updates_for_execution<Resource: ResourceSync>(
let config: Resource::Config = resource.config.into(); let config: Resource::Config = resource.config.into();
resource.config = config.into(); resource.config = config.into();
let diff = Resource::get_diff( Resource::validate_partial_config(&mut resource.config);
let mut diff = Resource::get_diff(
original.config.clone(), original.config.clone(),
resource.config, resource.config,
all_resources, all_resources,
)?; )?;
Resource::validate_diff(&mut diff);
let original_tags = original let original_tags = original
.tags .tags
.iter() .iter()

View File

@@ -63,6 +63,14 @@ impl ResourceSync for Build {
Ok(original.partial_diff(update)) Ok(original.partial_diff(update))
} }
fn validate_diff(diff: &mut Self::ConfigDiff) {
if let Some((_, to)) = &diff.version {
if to.is_none() {
diff.version = None;
}
}
}
} }
impl ResourceSync for Deployment { impl ResourceSync for Deployment {

View File

@@ -11,6 +11,7 @@ use monitor_client::{
}, },
entities::{ entities::{
permission::UserTarget, permission::UserTarget,
sync::SyncUpdate,
toml::{PermissionToml, UserGroupToml}, toml::{PermissionToml, UserGroupToml},
update::{Log, ResourceTarget}, update::{Log, ResourceTarget},
user::sync_user, user::sync_user,
@@ -38,7 +39,7 @@ pub async fn get_updates_for_view(
user_groups: Vec<UserGroupToml>, user_groups: Vec<UserGroupToml>,
delete: bool, delete: bool,
all_resources: &AllResourcesById, all_resources: &AllResourcesById,
) -> anyhow::Result<Option<String>> { ) -> anyhow::Result<Option<SyncUpdate>> {
let map = find_collect(&db_client().await.user_groups, None, None) let map = find_collect(&db_client().await.user_groups, None, None)
.await .await
.context("failed to query db for UserGroups")? .context("failed to query db for UserGroups")?
@@ -46,32 +47,22 @@ pub async fn get_updates_for_view(
.map(|ug| (ug.name.clone(), ug)) .map(|ug| (ug.name.clone(), ug))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let mut update = SyncUpdate {
log: String::from("User Group Updates"),
..Default::default()
};
let mut to_delete = Vec::<String>::new(); let mut to_delete = Vec::<String>::new();
if delete { if delete {
for user_group in map.values() { for user_group in map.values() {
if !user_groups.iter().any(|ug| ug.name == user_group.name) { if !user_groups.iter().any(|ug| ug.name == user_group.name) {
update.to_delete += 1;
to_delete.push(user_group.name.clone()); to_delete.push(user_group.name.clone());
} }
} }
} }
let mut log = String::from("User Group Updates");
if user_groups.is_empty() {
if to_delete.is_empty() {
return Ok(None);
}
for name in &to_delete {
log.push_str(&format!(
"\n\n{}: user group: '{}'\n-------------------",
colored("DELETE", "red"),
bold(name),
));
}
return Ok(Some(log));
}
let id_to_user = find_collect(&db_client().await.users, None, None) let id_to_user = find_collect(&db_client().await.users, None, None)
.await .await
.context("failed to query db for Users")? .context("failed to query db for Users")?
@@ -83,7 +74,8 @@ pub async fn get_updates_for_view(
let original = match map.get(&user_group.name).cloned() { let original = match map.get(&user_group.name).cloned() {
Some(original) => original, Some(original) => original,
None => { None => {
log.push_str(&format!( update.to_create += 1;
update.log.push_str(&format!(
"\n\n{}: user group: {}\n{}: {:?}\n{}: {:?}", "\n\n{}: user group: {}\n{}: {:?}\n{}: {:?}",
colored("CREATE", "green"), colored("CREATE", "green"),
colored(&user_group.name, "green"), colored(&user_group.name, "green"),
@@ -199,9 +191,10 @@ pub async fn get_updates_for_view(
let update_permissions = let update_permissions =
user_group.permissions != original_permissions; user_group.permissions != original_permissions;
// only push update after failed diff // only add log after diff detected
if update_users || update_permissions { if update_users || update_permissions {
log.push_str(&format!( update.to_update += 1;
update.log.push_str(&format!(
"\n\n{}: user group: '{}'\n-------------------", "\n\n{}: user group: '{}'\n-------------------",
colored("UPDATE", "blue"), colored("UPDATE", "blue"),
bold(&user_group.name), bold(&user_group.name),
@@ -269,20 +262,20 @@ pub async fn get_updates_for_view(
muted("adding"), muted("adding"),
)) ))
} }
log.push('\n'); update.log.push('\n');
log.push_str(&lines.join("\n-------------------\n")); update.log.push_str(&lines.join("\n-------------------\n"));
} }
} }
for name in &to_delete { for name in &to_delete {
log.push_str(&format!( update.log.push_str(&format!(
"\n\n{}: user group: '{}'\n-------------------", "\n\n{}: user group: '{}'\n-------------------",
colored("DELETE", "red"), colored("DELETE", "red"),
bold(name), bold(name),
)); ));
} }
Ok(Some(log)) Ok(Some(update))
} }
pub async fn get_updates_for_execution( pub async fn get_updates_for_execution(

View File

@@ -6,7 +6,10 @@ use monitor_client::{
CreateVariable, DeleteVariable, UpdateVariableDescription, CreateVariable, DeleteVariable, UpdateVariableDescription,
UpdateVariableValue, UpdateVariableValue,
}, },
entities::{update::Log, user::sync_user, variable::Variable}, entities::{
sync::SyncUpdate, update::Log, user::sync_user,
variable::Variable,
},
}; };
use mungos::find::find_collect; use mungos::find::find_collect;
use resolver_api::Resolve; use resolver_api::Resolve;
@@ -24,7 +27,7 @@ pub struct ToUpdateItem {
pub async fn get_updates_for_view( pub async fn get_updates_for_view(
variables: Vec<Variable>, variables: Vec<Variable>,
delete: bool, delete: bool,
) -> anyhow::Result<Option<String>> { ) -> anyhow::Result<Option<SyncUpdate>> {
let map = find_collect(&db_client().await.variables, None, None) let map = find_collect(&db_client().await.variables, None, None)
.await .await
.context("failed to query db for variables")? .context("failed to query db for variables")?
@@ -32,32 +35,22 @@ pub async fn get_updates_for_view(
.map(|v| (v.name.clone(), v)) .map(|v| (v.name.clone(), v))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let mut update = SyncUpdate {
log: String::from("Variable Updates"),
..Default::default()
};
let mut to_delete = Vec::<String>::new(); let mut to_delete = Vec::<String>::new();
if delete { if delete {
for variable in map.values() { for variable in map.values() {
if !variables.iter().any(|v| v.name == variable.name) { if !variables.iter().any(|v| v.name == variable.name) {
update.to_delete += 1;
to_delete.push(variable.name.clone()); to_delete.push(variable.name.clone());
} }
} }
} }
let mut log = String::from("Variable Updates");
if variables.is_empty() {
if to_delete.is_empty() {
return Ok(None);
}
for name in &to_delete {
log.push_str(&format!(
"\n\n{}: variable: '{}'\n-------------------",
colored("DELETE", "red"),
bold(name),
));
}
return Ok(Some(log));
}
for variable in variables { for variable in variables {
match map.get(&variable.name) { match map.get(&variable.name) {
Some(original) => { Some(original) => {
@@ -70,7 +63,8 @@ pub async fn get_updates_for_view(
if !item.update_value && !item.update_description { if !item.update_value && !item.update_description {
continue; continue;
} }
log.push_str(&format!( update.to_update += 1;
update.log.push_str(&format!(
"\n\n{}: variable: '{}'\n-------------------", "\n\n{}: variable: '{}'\n-------------------",
colored("UPDATE", "blue"), colored("UPDATE", "blue"),
bold(&item.variable.name), bold(&item.variable.name),
@@ -100,11 +94,12 @@ pub async fn get_updates_for_view(
)) ))
} }
log.push('\n'); update.log.push('\n');
log.push_str(&lines.join("\n-------------------\n")); update.log.push_str(&lines.join("\n-------------------\n"));
} }
None => { None => {
log.push_str(&format!( update.to_create += 1;
update.log.push_str(&format!(
"\n\n{}: variable: {}\n{}: {}\n{}: {}", "\n\n{}: variable: {}\n{}: {}\n{}: {}",
colored("CREATE", "green"), colored("CREATE", "green"),
colored(&variable.name, "green"), colored(&variable.name, "green"),
@@ -118,14 +113,14 @@ pub async fn get_updates_for_view(
} }
for name in &to_delete { for name in &to_delete {
log.push_str(&format!( update.log.push_str(&format!(
"\n\n{}: variable: '{}'\n-------------------", "\n\n{}: variable: '{}'\n-------------------",
colored("DELETE", "red"), colored("DELETE", "red"),
bold(name), bold(name),
)); ));
} }
Ok(Some(log)) Ok(Some(update))
} }
pub async fn get_updates_for_execution( pub async fn get_updates_for_execution(

View File

@@ -64,6 +64,7 @@ pub use repo::{
pub trait MonitorResource { pub trait MonitorResource {
type ListItem: Serialize + Send; type ListItem: Serialize + Send;
type Config: Clone type Config: Clone
+ Default
+ Send + Send
+ Sync + Sync
+ Unpin + Unpin

View File

@@ -62,38 +62,51 @@ pub struct ResourceSyncInfo {
/// Commit message of last applied sync /// Commit message of last applied sync
pub last_sync_message: String, pub last_sync_message: String,
/// Readable logs of pending updates /// Readable logs of pending updates
pub pending: PendingUpdates, pub pending: PendingSyncUpdates,
} }
#[typeshare] #[typeshare]
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PendingUpdates { pub struct SyncUpdate {
/// Resources to create
pub to_create: i32,
/// Resources to update
pub to_update: i32,
/// Resources to delete
pub to_delete: i32,
/// A readable log of all the changes to be applied
pub log: String,
}
#[typeshare]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PendingSyncUpdates {
/// The commit hash which produced these pending updates /// The commit hash which produced these pending updates
pub hash: String, pub hash: String,
/// The commit message which produced these pending updates /// The commit message which produced these pending updates
pub message: String, pub message: String,
/// Readable log of any pending server updates /// Readable log of any pending server updates
pub server_updates: Option<String>, pub server_updates: Option<SyncUpdate>,
/// Readable log of any pending deployment updates /// Readable log of any pending deployment updates
pub deployment_updates: Option<String>, pub deployment_updates: Option<SyncUpdate>,
/// Readable log of any pending build updates /// Readable log of any pending build updates
pub build_updates: Option<String>, pub build_updates: Option<SyncUpdate>,
/// Readable log of any pending repo updates /// Readable log of any pending repo updates
pub repo_updates: Option<String>, pub repo_updates: Option<SyncUpdate>,
/// Readable log of any pending procedure updates /// Readable log of any pending procedure updates
pub procedure_updates: Option<String>, pub procedure_updates: Option<SyncUpdate>,
/// Readable log of any pending alerter updates /// Readable log of any pending alerter updates
pub alerter_updates: Option<String>, pub alerter_updates: Option<SyncUpdate>,
/// Readable log of any pending builder updates /// Readable log of any pending builder updates
pub builder_updates: Option<String>, pub builder_updates: Option<SyncUpdate>,
/// Readable log of any pending server template updates /// Readable log of any pending server template updates
pub server_template_updates: Option<String>, pub server_template_updates: Option<SyncUpdate>,
/// Readable log of any pending resource sync updates /// Readable log of any pending resource sync updates
pub resource_sync_updates: Option<String>, pub resource_sync_updates: Option<SyncUpdate>,
/// Readable log of any pending variable updates /// Readable log of any pending variable updates
pub variable_updates: Option<String>, pub variable_updates: Option<SyncUpdate>,
/// Readable log of any pending user group updates /// Readable log of any pending user group updates
pub user_group_updates: Option<String>, pub user_group_updates: Option<SyncUpdate>,
} }
#[typeshare(serialized_as = "Partial<ResourceSyncConfig>")] #[typeshare(serialized_as = "Partial<ResourceSyncConfig>")]

View File

@@ -1174,33 +1174,44 @@ export interface ResourceSyncConfig {
webhook_enabled: boolean; webhook_enabled: boolean;
} }
export interface PendingUpdates { export interface SyncUpdate {
/** Resources to create */
to_create: number;
/** Resources to update */
to_update: number;
/** Resources to delete */
to_delete: number;
/** A readable log of all the changes to be applied */
log: string;
}
export interface PendingSyncUpdates {
/** The commit hash which produced these pending updates */ /** The commit hash which produced these pending updates */
hash: string; hash: string;
/** The commit message which produced these pending updates */ /** The commit message which produced these pending updates */
message: string; message: string;
/** Readable log of any pending server updates */ /** Readable log of any pending server updates */
server_updates?: string; server_updates?: SyncUpdate;
/** Readable log of any pending deployment updates */ /** Readable log of any pending deployment updates */
deployment_updates?: string; deployment_updates?: SyncUpdate;
/** Readable log of any pending build updates */ /** Readable log of any pending build updates */
build_updates?: string; build_updates?: SyncUpdate;
/** Readable log of any pending repo updates */ /** Readable log of any pending repo updates */
repo_updates?: string; repo_updates?: SyncUpdate;
/** Readable log of any pending procedure updates */ /** Readable log of any pending procedure updates */
procedure_updates?: string; procedure_updates?: SyncUpdate;
/** Readable log of any pending alerter updates */ /** Readable log of any pending alerter updates */
alerter_updates?: string; alerter_updates?: SyncUpdate;
/** Readable log of any pending builder updates */ /** Readable log of any pending builder updates */
builder_updates?: string; builder_updates?: SyncUpdate;
/** Readable log of any pending server template updates */ /** Readable log of any pending server template updates */
server_template_updates?: string; server_template_updates?: SyncUpdate;
/** Readable log of any pending resource sync updates */ /** Readable log of any pending resource sync updates */
resource_sync_updates?: string; resource_sync_updates?: SyncUpdate;
/** Readable log of any pending variable updates */ /** Readable log of any pending variable updates */
variable_updates?: string; variable_updates?: SyncUpdate;
/** Readable log of any pending user group updates */ /** Readable log of any pending user group updates */
user_group_updates?: string; user_group_updates?: SyncUpdate;
} }
export interface ResourceSyncInfo { export interface ResourceSyncInfo {
@@ -1211,7 +1222,7 @@ export interface ResourceSyncInfo {
/** Commit message of last applied sync */ /** Commit message of last applied sync */
last_sync_message: string; last_sync_message: string;
/** Readable logs of pending updates */ /** Readable logs of pending updates */
pending: PendingUpdates; pending: PendingSyncUpdates;
} }
export type ResourceSync = Resource<ResourceSyncConfig, ResourceSyncInfo>; export type ResourceSync = Resource<ResourceSyncConfig, ResourceSyncInfo>;

View File

@@ -46,7 +46,11 @@ const PendingOrConfig = ({ id }: { id: string }) => {
const tabsList = ( const tabsList = (
<TabsList className="justify-start w-fit"> <TabsList className="justify-start w-fit">
<TabsTrigger value="Pending" className="w-[110px]" disabled={true}> <TabsTrigger
value="Pending"
className="w-[110px]"
disabled={pendingDisabled}
>
Pending Pending
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="Config" className="w-[110px]"> <TabsTrigger value="Config" className="w-[110px]">
@@ -65,7 +69,7 @@ const PendingOrConfig = ({ id }: { id: string }) => {
<PendingView <PendingView
key={type} key={type}
type={type} type={type}
log={sync?.info?.pending?.[key]} pending={sync?.info?.pending?.[key]}
/> />
))} ))}
</Section> </Section>
@@ -76,22 +80,39 @@ const PendingOrConfig = ({ id }: { id: string }) => {
const PendingView = ({ const PendingView = ({
type, type,
log, pending,
}: { }: {
type: string; type: string;
log: string | undefined; pending: Types.SyncUpdate | undefined;
}) => { }) => {
if (!log) return; if (!pending) return;
return ( return (
<Card> <Card>
<CardHeader className="flex-col"> <CardHeader className="flex items-center justify-between gap-4">
<CardTitle>{type} Updates</CardTitle> <CardTitle>{type} Updates</CardTitle>
<div className="flex gap-4 items-center">
{pending.to_create && (
<div className="flex gap-2 items-center">
To Create: {pending.to_create}
</div>
)}
{pending.to_update && (
<div className="flex gap-2 items-center">
To Update: {pending.to_update}
</div>
)}
{pending.to_delete && (
<div className="flex gap-2 items-center">
To Delete: {pending.to_delete}
</div>
)}
</div>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<pre <pre
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: sanitizeOnlySpan(log), __html: sanitizeOnlySpan(pending.log),
}} }}
/> />
</CardContent> </CardContent>

View File

@@ -180,16 +180,16 @@ export const sync_no_changes = (sync: Types.ResourceSync) => {
const pending = sync.info?.pending; const pending = sync.info?.pending;
if (!pending) return false; if (!pending) return false;
return ( return (
!pending.server_updates?.length && !pending.server_updates &&
!pending.deployment_updates?.length && !pending.deployment_updates &&
!pending.build_updates?.length && !pending.build_updates &&
!pending.repo_updates?.length && !pending.repo_updates &&
!pending.procedure_updates?.length && !pending.procedure_updates &&
!pending.alerter_updates?.length && !pending.alerter_updates &&
!pending.builder_updates?.length && !pending.builder_updates &&
!pending.server_template_updates?.length && !pending.server_template_updates &&
!pending.resource_sync_updates?.length && !pending.resource_sync_updates &&
!pending.variable_updates?.length && !pending.variable_updates &&
!pending.user_group_updates?.length !pending.user_group_updates
); );
}; };