mirror of
https://github.com/moghtech/komodo.git
synced 2026-07-27 10:09:07 -05:00
306 lines
7.6 KiB
Rust
306 lines
7.6 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use anyhow::Context;
|
|
use database::mungos::find::find_collect;
|
|
use formatting::{Color, bold, colored, muted};
|
|
use komodo_client::entities::{
|
|
ResourceTargetVariant, tag::Tag, toml::ResourceToml, update::Log,
|
|
user::sync_user,
|
|
};
|
|
use partial_derive2::MaybeNone;
|
|
|
|
use crate::{api::write::WriteArgs, resource::ResourceMetaUpdate};
|
|
|
|
use super::{ResourceSyncTrait, SyncDeltas, ToUpdateItem};
|
|
|
|
/// Gets all the resources to update. For use in sync execution.
|
|
pub async fn get_updates_for_execution<
|
|
Resource: ResourceSyncTrait,
|
|
>(
|
|
resources: Vec<ResourceToml<Resource::PartialConfig>>,
|
|
delete: bool,
|
|
match_resource_type: Option<ResourceTargetVariant>,
|
|
match_resources: Option<&[String]>,
|
|
id_to_tags: &HashMap<String, Tag>,
|
|
match_tags: &[String],
|
|
) -> anyhow::Result<SyncDeltas<Resource::PartialConfig>> {
|
|
let map = find_collect(Resource::coll(), None, None)
|
|
.await
|
|
.context("failed to get resources from db")?
|
|
.into_iter()
|
|
.filter(|r| {
|
|
Resource::include_resource(
|
|
&r.name,
|
|
&r.config,
|
|
match_resource_type,
|
|
match_resources,
|
|
&r.tags,
|
|
id_to_tags,
|
|
match_tags,
|
|
)
|
|
})
|
|
.map(|r| (r.name.clone(), r))
|
|
.collect::<HashMap<_, _>>();
|
|
let resources = resources
|
|
.into_iter()
|
|
.filter(|r| {
|
|
Resource::include_resource_partial(
|
|
&r.name,
|
|
&r.config,
|
|
match_resource_type,
|
|
match_resources,
|
|
&r.tags,
|
|
id_to_tags,
|
|
match_tags,
|
|
)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut deltas = SyncDeltas::<Resource::PartialConfig>::default();
|
|
|
|
if delete {
|
|
for resource in map.values() {
|
|
if !resources.iter().any(|r| r.name == resource.name) {
|
|
deltas.to_delete.push(resource.name.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
for mut resource in resources {
|
|
match map.get(&resource.name) {
|
|
Some(original) => {
|
|
// First merge toml resource config (partial) onto default resource config.
|
|
// Makes sure things that aren't defined in toml (come through as None) actually get removed.
|
|
let config: Resource::Config = resource.config.into();
|
|
resource.config = config.into();
|
|
|
|
Resource::validate_partial_config(&mut resource.config);
|
|
|
|
let mut diff = Resource::get_diff(
|
|
original.config.clone(),
|
|
resource.config,
|
|
)?;
|
|
|
|
Resource::validate_diff(&mut diff);
|
|
|
|
let original_tags = original
|
|
.tags
|
|
.iter()
|
|
.filter_map(|id| id_to_tags.get(id).map(|t| t.name.clone()))
|
|
.collect::<Vec<_>>();
|
|
|
|
// Only proceed if there are any fields to update,
|
|
// or a change to tags / description
|
|
if diff.is_none()
|
|
&& resource.description == original.description
|
|
&& resource.template == original.template
|
|
&& resource.tags == original_tags
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Minimizes updates through diffing.
|
|
resource.config = diff.into();
|
|
|
|
let update = ToUpdateItem {
|
|
id: original.id.clone(),
|
|
update_description: resource.description
|
|
!= original.description,
|
|
update_template: resource.template != original.template,
|
|
update_tags: resource.tags != original_tags,
|
|
resource,
|
|
};
|
|
|
|
deltas.to_update.push(update);
|
|
}
|
|
None => deltas.to_create.push(resource),
|
|
}
|
|
}
|
|
|
|
Ok(deltas)
|
|
}
|
|
|
|
pub trait ExecuteResourceSync: ResourceSyncTrait {
|
|
async fn execute_sync_updates(
|
|
SyncDeltas {
|
|
to_create,
|
|
to_update,
|
|
to_delete,
|
|
}: SyncDeltas<Self::PartialConfig>,
|
|
) -> Option<Log> {
|
|
if to_create.is_empty()
|
|
&& to_update.is_empty()
|
|
&& to_delete.is_empty()
|
|
{
|
|
return None;
|
|
}
|
|
|
|
let mut has_error = false;
|
|
let mut log =
|
|
format!("running updates on {}s", Self::resource_type());
|
|
|
|
for resource in to_create {
|
|
let name = resource.name.clone();
|
|
let id = match crate::resource::create::<Self>(
|
|
&resource.name,
|
|
resource.config,
|
|
None,
|
|
sync_user(),
|
|
)
|
|
.await
|
|
.map_err(|e| e.error)
|
|
{
|
|
Ok(resource) => resource.id,
|
|
Err(e) => {
|
|
has_error = true;
|
|
log.push_str(&format!(
|
|
"\n{}: failed to create {} '{}' | {e:#}",
|
|
colored("ERROR", Color::Red),
|
|
Self::resource_type(),
|
|
bold(&name)
|
|
));
|
|
continue;
|
|
}
|
|
};
|
|
run_update_meta::<Self>(
|
|
id.clone(),
|
|
&name,
|
|
ResourceMetaUpdate {
|
|
description: Some(resource.description),
|
|
template: Some(resource.template),
|
|
tags: Some(resource.tags),
|
|
},
|
|
&mut log,
|
|
&mut has_error,
|
|
)
|
|
.await;
|
|
log.push_str(&format!(
|
|
"\n{}: {} {} '{}'",
|
|
muted("INFO"),
|
|
colored("created", Color::Green),
|
|
Self::resource_type(),
|
|
bold(&name)
|
|
));
|
|
}
|
|
|
|
for ToUpdateItem {
|
|
id,
|
|
resource,
|
|
update_description,
|
|
update_template,
|
|
update_tags,
|
|
} in to_update
|
|
{
|
|
let name = resource.name.clone();
|
|
|
|
let meta = ResourceMetaUpdate {
|
|
description: update_description
|
|
.then(|| resource.description.clone()),
|
|
template: update_template.then_some(resource.template),
|
|
tags: update_tags.then(|| resource.tags.clone()),
|
|
};
|
|
|
|
if !meta.is_none() {
|
|
run_update_meta::<Self>(
|
|
id.clone(),
|
|
&name,
|
|
meta,
|
|
&mut log,
|
|
&mut has_error,
|
|
)
|
|
.await;
|
|
}
|
|
|
|
if !resource.config.is_none() {
|
|
if let Err(e) = crate::resource::update::<Self>(
|
|
&id,
|
|
resource.config,
|
|
sync_user(),
|
|
)
|
|
.await
|
|
{
|
|
has_error = true;
|
|
log.push_str(&format!(
|
|
"\n{}: failed to update config on {} '{}' | {e:#}",
|
|
colored("ERROR", Color::Red),
|
|
Self::resource_type(),
|
|
bold(&name),
|
|
))
|
|
} else {
|
|
log.push_str(&format!(
|
|
"\n{}: {} {} '{}' configuration",
|
|
muted("INFO"),
|
|
colored("updated", Color::Blue),
|
|
Self::resource_type(),
|
|
bold(&name)
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
for resource in to_delete {
|
|
if let Err(e) =
|
|
crate::resource::delete::<Self>(&resource, sync_user()).await
|
|
{
|
|
has_error = true;
|
|
log.push_str(&format!(
|
|
"\n{}: failed to delete {} '{}' | {e:#}",
|
|
colored("ERROR", Color::Red),
|
|
Self::resource_type(),
|
|
bold(&resource),
|
|
))
|
|
} else {
|
|
log.push_str(&format!(
|
|
"\n{}: {} {} '{}'",
|
|
muted("INFO"),
|
|
colored("deleted", Color::Red),
|
|
Self::resource_type(),
|
|
bold(&resource)
|
|
));
|
|
}
|
|
}
|
|
|
|
let stage = format!("Update {}s", Self::resource_type());
|
|
Some(if has_error {
|
|
Log::error(&stage, log)
|
|
} else {
|
|
Log::simple(&stage, log)
|
|
})
|
|
}
|
|
}
|
|
|
|
pub async fn run_update_meta<Resource: ResourceSyncTrait>(
|
|
id: String,
|
|
name: &str,
|
|
meta: ResourceMetaUpdate,
|
|
log: &mut String,
|
|
has_error: &mut bool,
|
|
) {
|
|
if let Err(e) = crate::resource::update_meta::<Resource>(
|
|
&id,
|
|
meta,
|
|
&WriteArgs {
|
|
user: sync_user().to_owned(),
|
|
},
|
|
)
|
|
.await
|
|
{
|
|
*has_error = true;
|
|
log.push_str(&format!(
|
|
"\n{}: failed to update tags on {} '{}' | {:#}",
|
|
colored("ERROR", Color::Red),
|
|
Resource::resource_type(),
|
|
bold(name),
|
|
e
|
|
))
|
|
} else {
|
|
log.push_str(&format!(
|
|
"\n{}: {} {} '{}' meta",
|
|
muted("INFO"),
|
|
colored("updated", Color::Blue),
|
|
Resource::resource_type(),
|
|
bold(name)
|
|
));
|
|
}
|
|
}
|