From 5683929bbe9ca8477cbbe5c8e1007b0214d0fec6 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Sun, 28 Apr 2024 04:33:27 -0700 Subject: [PATCH] monrun diffing --- bin/monrun/src/maps.rs | 30 +++++- bin/monrun/src/sync/mod.rs | 2 +- bin/monrun/src/sync/user_group.rs | 130 ++++++++++++++++++++++++-- client/core/rs/src/api/read/user.rs | 2 +- client/core/rs/src/entities/toml.rs | 2 +- client/core/rs/src/entities/update.rs | 6 +- 6 files changed, 158 insertions(+), 14 deletions(-) diff --git a/bin/monrun/src/maps.rs b/bin/monrun/src/maps.rs index ad11f1520..51f399883 100644 --- a/bin/monrun/src/maps.rs +++ b/bin/monrun/src/maps.rs @@ -6,7 +6,7 @@ use monitor_client::{ alerter::AlerterListItem, build::BuildListItem, builder::BuilderListItem, deployment::DeploymentListItem, procedure::ProcedureListItem, repo::RepoListItem, - server::ServerListItem, user_group::UserGroup, + server::ServerListItem, user::User, user_group::UserGroup, }, }; @@ -144,6 +144,20 @@ pub fn name_to_alerter() -> &'static HashMap }) } +pub fn id_to_alerter() -> &'static HashMap { + static ID_TO_ALERTER: OnceLock> = + OnceLock::new(); + ID_TO_ALERTER.get_or_init(|| { + futures::executor::block_on( + monitor_client().read(read::ListAlerters::default()), + ) + .expect("failed to get alerters from monitor") + .into_iter() + .map(|alerter| (alerter.id.clone(), alerter)) + .collect() + }) +} + pub fn name_to_repo() -> &'static HashMap { static NAME_TO_ALERTER: OnceLock> = OnceLock::new(); @@ -217,3 +231,17 @@ pub fn name_to_user_group() -> &'static HashMap { .collect() }) } + +pub fn id_to_user() -> &'static HashMap { + static ID_TO_USER: OnceLock> = + OnceLock::new(); + ID_TO_USER.get_or_init(|| { + futures::executor::block_on( + monitor_client().read(read::ListUsers::default()), + ) + .expect("failed to get procedures from monitor") + .into_iter() + .map(|user| (user.id.clone(), user)) + .collect() + }) +} diff --git a/bin/monrun/src/sync/mod.rs b/bin/monrun/src/sync/mod.rs index f9e689471..bee3ec805 100644 --- a/bin/monrun/src/sync/mod.rs +++ b/bin/monrun/src/sync/mod.rs @@ -34,7 +34,7 @@ pub async fn run_sync(path: &Path) -> anyhow::Result<()> { let (procedure_creates, procedure_updates) = Procedure::get_updates(resources.procedures).await?; let (user_group_creates, user_group_updates) = - user_group::get_updates(resources.user_groups); + user_group::get_updates(resources.user_groups).await?; wait_for_enter("CONTINUE")?; diff --git a/bin/monrun/src/sync/user_group.rs b/bin/monrun/src/sync/user_group.rs index b384638ed..43001a621 100644 --- a/bin/monrun/src/sync/user_group.rs +++ b/bin/monrun/src/sync/user_group.rs @@ -1,26 +1,124 @@ +use std::cmp::Ordering; + +use anyhow::Context; use monitor_client::{ - api::write::{ - CreateUserGroup, SetUsersInUserGroup, UpdatePermissionOnTarget, + api::{ + read::ListUserTargetPermissions, + write::{ + CreateUserGroup, SetUsersInUserGroup, UpdatePermissionOnTarget, + }, }, entities::{ permission::UserTarget, toml::{PermissionToml, UserGroupToml}, + update::ResourceTarget, }, }; -use crate::{maps::name_to_user_group, monitor_client}; +use crate::{ + maps::{ + id_to_alerter, id_to_build, id_to_builder, id_to_deployment, + id_to_procedure, id_to_repo, id_to_server, id_to_user, + name_to_user_group, + }, + monitor_client, +}; -pub fn get_updates( +pub async fn get_updates( user_groups: Vec, -) -> (Vec, Vec) { +) -> anyhow::Result<(Vec, Vec)> { let map = name_to_user_group(); let mut to_create = Vec::::new(); let mut to_update = Vec::::new(); - for user_group in user_groups { + for mut user_group in user_groups { match map.get(&user_group.name).cloned() { - Some(_) => to_update.push(user_group), + Some(original) => { + // replace the user ids with usernames + let mut users = original + .users + .into_iter() + .filter_map(|user_id| { + id_to_user().get(&user_id).map(|u| u.username.clone()) + }) + .collect::>(); + + let mut permissions = monitor_client() + .read(ListUserTargetPermissions { + user_target: UserTarget::UserGroup(original.id), + }) + .await + .context("failed to query for UserGroup permissions")? + .into_iter() + .map(|mut p| { + // replace the ids with names + match &mut p.resource_target { + ResourceTarget::System(_) => {} + ResourceTarget::Build(id) => { + *id = id_to_build() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Builder(id) => { + *id = id_to_builder() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Deployment(id) => { + *id = id_to_deployment() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Server(id) => { + *id = id_to_server() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Repo(id) => { + *id = id_to_repo() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Alerter(id) => { + *id = id_to_alerter() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Procedure(id) => { + *id = id_to_procedure() + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + } + PermissionToml { + target: p.resource_target, + level: p.level, + } + }) + .collect::>(); + + users.sort(); + user_group.users.sort(); + + user_group.permissions.sort_by(sort_permissions); + permissions.sort_by(sort_permissions); + + // only push update after failed diff + if user_group.users != users + || user_group.permissions != permissions + { + // no update from users + to_update.push(user_group); + } + } None => to_create.push(user_group), } } @@ -47,7 +145,23 @@ pub fn get_updates( ); } - (to_create, to_update) + Ok((to_create, to_update)) +} + +/// order permissions in deterministic way +fn sort_permissions( + a: &PermissionToml, + b: &PermissionToml, +) -> Ordering { + let (a_t, a_id) = a.target.extract_variant_id(); + let (b_t, b_id) = b.target.extract_variant_id(); + match (a_t.cmp(&b_t), a_id.cmp(b_id)) { + (Ordering::Greater, _) => Ordering::Greater, + (Ordering::Less, _) => Ordering::Less, + (_, Ordering::Greater) => Ordering::Greater, + (_, Ordering::Less) => Ordering::Less, + _ => Ordering::Equal, + } } pub async fn run_updates( diff --git a/client/core/rs/src/api/read/user.rs b/client/core/rs/src/api/read/user.rs index dab934765..39bd85385 100644 --- a/client/core/rs/src/api/read/user.rs +++ b/client/core/rs/src/api/read/user.rs @@ -47,7 +47,7 @@ pub type ListApiKeysForServiceUserResponse = Vec; /// Response: [ListUsersResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, )] #[empty_traits(MonitorReadRequest)] #[response(ListUsersResponse)] diff --git a/client/core/rs/src/entities/toml.rs b/client/core/rs/src/entities/toml.rs index 098a3f486..785c6cf88 100644 --- a/client/core/rs/src/entities/toml.rs +++ b/client/core/rs/src/entities/toml.rs @@ -97,7 +97,7 @@ pub struct UserGroupToml { pub permissions: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PermissionToml { pub target: ResourceTarget, pub level: PermissionLevel, diff --git a/client/core/rs/src/entities/update.rs b/client/core/rs/src/entities/update.rs index 610952b65..9736b3cb3 100644 --- a/client/core/rs/src/entities/update.rs +++ b/client/core/rs/src/entities/update.rs @@ -152,11 +152,13 @@ impl Log { Debug, Clone, Copy, + PartialEq, + Eq, + PartialOrd, + Ord, Display, EnumString, AsRefStr, - PartialEq, - Eq )] #[serde(tag = "type", content = "id")] pub enum ResourceTarget {