From 6dcc4b30de0ab44ab0f89bc3a2e6624e65bfa64a Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 27 Mar 2024 05:33:34 -0700 Subject: [PATCH] validate attached resources by name / id --- Cargo.lock | 8 +- Cargo.toml | 2 +- bin/core/src/api/execute/build.rs | 2 +- bin/core/src/api/write/build.rs | 14 +- bin/core/src/api/write/deployment.rs | 13 +- bin/core/src/api/write/procedure.rs | 144 +++++++++++++++++- bin/core/src/api/write/repo.rs | 5 +- bin/core/src/auth/mod.rs | 2 +- bin/core/src/helpers/channel.rs | 8 +- bin/monrun/resources/procedures.toml | 57 +++---- bin/monrun/src/sync/mod.rs | 8 +- bin/monrun/src/sync/resource_file.rs | 1 + client/core/rs/src/entities/build.rs | 1 + client/core/rs/src/entities/deployment.rs | 1 + client/core/rs/src/entities/repo.rs | 1 + client/core/rs/src/entities/user.rs | 2 +- .../resources/deployment/config/index.tsx | 2 +- .../procedure/{index2.tsx => index2.md} | 0 frontend/src/pages/dashboard.tsx | 2 +- frontend/src/pages/keys.tsx | 2 +- 20 files changed, 205 insertions(+), 70 deletions(-) rename frontend/src/components/resources/procedure/{index2.tsx => index2.md} (100%) diff --git a/Cargo.lock b/Cargo.lock index 0313195a5..15b88ac7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2399,18 +2399,18 @@ checksum = "ffa94c2e5674923c67d7f3dfce1279507b191e10eb064881b46ed3e1256e5ca6" [[package]] name = "partial_derive2" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eac44820a8c7e50b7ba6926c388adc05b9a1f41b22a9b18e273fb39d4f892f2" +checksum = "2eecafa276875fa10fbbcbcc2c93d08e9e302627600afe99a2b000dfa68e478d" dependencies = [ "partial_derive2_derive", ] [[package]] name = "partial_derive2_derive" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad412582e4972654640acf910d4c85e2ba14a547fbcec5b8ddb944d82adec1e1" +checksum = "5a543253c8df8d6377ba82d06c5de2d96bf336ade75588ca537d3de083ce2b5c" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 88c5bf2e5..878edfb19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ derive_empty_traits = "0.1.0" merge_config_files = "0.1.3" termination_signal = "0.1.3" async_timing_util = "0.1.14" -partial_derive2 = "0.2.1" +partial_derive2 = "0.2.2" derive_variants = "0.1.3" resolver_api = "0.1.6" parse_csl = "0.1.0" diff --git a/bin/core/src/api/execute/build.rs b/bin/core/src/api/execute/build.rs index 96e702d48..f05f1b367 100644 --- a/bin/core/src/api/execute/build.rs +++ b/bin/core/src/api/execute/build.rs @@ -107,7 +107,7 @@ impl Resolve for State { }); let build_id = build.id.clone(); - + let inner = || async move { update.id = add_update(update.clone()).await?; diff --git a/bin/core/src/api/write/build.rs b/bin/core/src/api/write/build.rs index 21334730d..bc05fb5ee 100644 --- a/bin/core/src/api/write/build.rs +++ b/bin/core/src/api/write/build.rs @@ -36,7 +36,7 @@ use crate::{ impl Resolve for State { async fn resolve( &self, - CreateBuild { name, config }: CreateBuild, + CreateBuild { name, mut config }: CreateBuild, user: User, ) -> anyhow::Result { let name = to_monitor_name(&name); @@ -44,9 +44,10 @@ impl Resolve for State { return Err(anyhow!("valid ObjectIds cannot be used as names")); } if let Some(builder_id) = &config.builder_id { - Builder::get_resource_check_permissions(builder_id, &user, PermissionLevel::Read) + let builder = Builder::get_resource_check_permissions(builder_id, &user, PermissionLevel::Read) .await .context("cannot create build using this builder. user must have at least read permissions on the builder.")?; + config.builder_id = Some(builder.id) } let start_ts = monitor_timestamp(); let build = Build { @@ -112,7 +113,9 @@ impl Resolve for State { PermissionLevel::Write, ) .await?; - Builder::get_resource_check_permissions(&config.builder_id, &user, PermissionLevel::Read).await.context("cannot create build using this builder. user must have at least read permissions on the builder.")?; + Builder::get_resource_check_permissions(&config.builder_id, &user, PermissionLevel::Read) + .await + .context("cannot create build using this builder. user must have at least read permissions on the builder.")?; let start_ts = monitor_timestamp(); let build = Build { id: Default::default(), @@ -233,7 +236,10 @@ impl Resolve for State { let inner = || async move { if let Some(builder_id) = &config.builder_id { - Builder::get_resource_check_permissions(builder_id, &user, PermissionLevel::Read).await.context("cannot create build using this builder. user must have at least read permissions on the builder.")?; + let builder = Builder::get_resource_check_permissions(builder_id, &user, PermissionLevel::Read) + .await + .context("cannot create build using this builder. user must have at least read permissions on the builder.")?; + config.builder_id = Some(builder.id) } if let Some(build_args) = &mut config.build_args { diff --git a/bin/core/src/api/write/deployment.rs b/bin/core/src/api/write/deployment.rs index db344b76d..67d5ce5cc 100644 --- a/bin/core/src/api/write/deployment.rs +++ b/bin/core/src/api/write/deployment.rs @@ -40,7 +40,7 @@ use crate::{ impl Resolve for State { async fn resolve( &self, - CreateDeployment { name, config }: CreateDeployment, + CreateDeployment { name, mut config }: CreateDeployment, user: User, ) -> anyhow::Result { let name = to_monitor_name(&name); @@ -49,18 +49,23 @@ impl Resolve for State { } if let Some(server_id) = &config.server_id { if !server_id.is_empty() { - Server::get_resource_check_permissions(server_id, &user, PermissionLevel::Write) + let server = Server::get_resource_check_permissions(server_id, &user, PermissionLevel::Write) .await .context("cannot create deployment on this server. user must have update permissions on the server to perform this action.")?; + config.server_id = Some(server.id); } } - if let Some(DeploymentImage::Build { build_id, .. }) = + if let Some(DeploymentImage::Build { build_id, version }) = &config.image { if !build_id.is_empty() { - Build::get_resource_check_permissions(build_id, &user, PermissionLevel::Read) + let build = Build::get_resource_check_permissions(build_id, &user, PermissionLevel::Read) .await .context("cannot create deployment with this build attached. user must have at least read permissions on the build to perform this action.")?; + config.image = Some(DeploymentImage::Build { + build_id: build.id, + version: version.clone(), + }); } } let start_ts = monitor_timestamp(); diff --git a/bin/core/src/api/write/procedure.rs b/bin/core/src/api/write/procedure.rs index ef3aa678a..2793323db 100644 --- a/bin/core/src/api/write/procedure.rs +++ b/bin/core/src/api/write/procedure.rs @@ -3,10 +3,17 @@ use std::str::FromStr; use anyhow::{anyhow, Context}; use async_trait::async_trait; use monitor_client::{ - api::write::*, + api::{execute::Execution, write::*}, entities::{ - monitor_timestamp, permission::PermissionLevel, - procedure::Procedure, to_monitor_name, update::Log, user::User, + build::Build, + deployment::Deployment, + monitor_timestamp, + permission::PermissionLevel, + procedure::{Procedure, ProcedureConfig}, + repo::Repo, + server::Server, + update::Log, + user::User, Operation, }, }; @@ -31,13 +38,17 @@ use crate::{ impl Resolve for State { async fn resolve( &self, - CreateProcedure { name, config }: CreateProcedure, + CreateProcedure { name, mut config }: CreateProcedure, user: User, ) -> anyhow::Result { if ObjectId::from_str(&name).is_ok() { return Err(anyhow!("valid ObjectIds cannot be used as names")); } + let start_ts = monitor_timestamp(); + + validate_procedure_config(&mut config, &user).await?; + let procedure = Procedure { id: Default::default(), name, @@ -84,6 +95,130 @@ impl Resolve for State { } } +async fn validate_procedure_config( + config: &mut ProcedureConfig, + user: &User, +) -> anyhow::Result<()> { + let execs = match config { + ProcedureConfig::Sequence(execs) => execs, + ProcedureConfig::Parallel(execs) => execs, + }; + for exec in execs { + match &mut exec.execution { + Execution::None(_) => {} + Execution::RunProcedure(data) => { + let procedure = Procedure::get_resource_check_permissions( + &data.procedure, + user, + PermissionLevel::Execute, + ) + .await?; + data.procedure = procedure.id; + } + Execution::RunBuild(data) => { + let build = Build::get_resource_check_permissions( + &data.build, + user, + PermissionLevel::Execute, + ) + .await?; + data.build = build.id; + } + Execution::Deploy(data) => { + let deployment = Deployment::get_resource_check_permissions( + &data.deployment, + user, + PermissionLevel::Execute, + ) + .await?; + data.deployment = deployment.id; + } + Execution::StartContainer(data) => { + let deployment = Deployment::get_resource_check_permissions( + &data.deployment, + user, + PermissionLevel::Execute, + ) + .await?; + data.deployment = deployment.id; + } + Execution::StopContainer(data) => { + let deployment = Deployment::get_resource_check_permissions( + &data.deployment, + user, + PermissionLevel::Execute, + ) + .await?; + data.deployment = deployment.id; + } + Execution::StopAllContainers(data) => { + let server = Server::get_resource_check_permissions( + &data.server, + user, + PermissionLevel::Execute, + ) + .await?; + data.server = server.id; + } + Execution::RemoveContainer(data) => { + let deployment = Deployment::get_resource_check_permissions( + &data.deployment, + user, + PermissionLevel::Execute, + ) + .await?; + data.deployment = deployment.id; + } + Execution::CloneRepo(data) => { + let repo = Repo::get_resource_check_permissions( + &data.repo, + user, + PermissionLevel::Execute, + ) + .await?; + data.repo = repo.id; + } + Execution::PullRepo(data) => { + let repo = Repo::get_resource_check_permissions( + &data.repo, + user, + PermissionLevel::Execute, + ) + .await?; + data.repo = repo.id; + } + Execution::PruneDockerNetworks(data) => { + let server = Server::get_resource_check_permissions( + &data.server, + user, + PermissionLevel::Execute, + ) + .await?; + data.server = server.id; + } + Execution::PruneDockerImages(data) => { + let server = Server::get_resource_check_permissions( + &data.server, + user, + PermissionLevel::Execute, + ) + .await?; + data.server = server.id; + } + Execution::PruneDockerContainers(data) => { + let server = Server::get_resource_check_permissions( + &data.server, + user, + PermissionLevel::Execute, + ) + .await?; + data.server = server.id; + } + } + } + Ok(()) +} + #[async_trait] impl Resolve for State { async fn resolve( @@ -91,7 +226,6 @@ impl Resolve for State { CopyProcedure { name, id }: CopyProcedure, user: User, ) -> anyhow::Result { - let name = to_monitor_name(&name); let Procedure { config, description, diff --git a/bin/core/src/api/write/repo.rs b/bin/core/src/api/write/repo.rs index 4ad2e18c3..24c3eca0d 100644 --- a/bin/core/src/api/write/repo.rs +++ b/bin/core/src/api/write/repo.rs @@ -37,7 +37,7 @@ use crate::{ impl Resolve for State { async fn resolve( &self, - CreateRepo { name, config }: CreateRepo, + CreateRepo { name, mut config }: CreateRepo, user: User, ) -> anyhow::Result { let name = to_monitor_name(&name); @@ -46,13 +46,14 @@ impl Resolve for State { } if let Some(server_id) = &config.server_id { if !server_id.is_empty() { - Server::get_resource_check_permissions( + let server = Server::get_resource_check_permissions( server_id, &user, PermissionLevel::Write, ) .await .context("cannot create repo on this server. user must have update permissions on the server.")?; + config.server_id = Some(server.id); } } let start_ts = monitor_timestamp(); diff --git a/bin/core/src/auth/mod.rs b/bin/core/src/auth/mod.rs index 40b26f21a..ed1bfe6a6 100644 --- a/bin/core/src/auth/mod.rs +++ b/bin/core/src/auth/mod.rs @@ -37,7 +37,7 @@ const STATE_PREFIX_LENGTH: usize = 20; #[derive(Deserialize)] pub struct RedirectQuery { - pub redirect: Option, + pub redirect: Option, } pub async fn auth_request( diff --git a/bin/core/src/helpers/channel.rs b/bin/core/src/helpers/channel.rs index a51f8aaa0..467ee5d2a 100644 --- a/bin/core/src/helpers/channel.rs +++ b/bin/core/src/helpers/channel.rs @@ -4,9 +4,11 @@ use monitor_client::entities::update::{Update, UpdateListItem}; use tokio::sync::{broadcast, Mutex}; /// A channel sending (build_id, update_id) -pub fn build_cancel_channel() -> &'static BroadcastChannel<(String, Update)> { - static BUILD_CANCEL_CHANNEL: OnceLock> = - OnceLock::new(); +pub fn build_cancel_channel( +) -> &'static BroadcastChannel<(String, Update)> { + static BUILD_CANCEL_CHANNEL: OnceLock< + BroadcastChannel<(String, Update)>, + > = OnceLock::new(); BUILD_CANCEL_CHANNEL.get_or_init(|| BroadcastChannel::new(100)) } diff --git a/bin/monrun/resources/procedures.toml b/bin/monrun/resources/procedures.toml index d4835e144..73e653902 100644 --- a/bin/monrun/resources/procedures.toml +++ b/bin/monrun/resources/procedures.toml @@ -1,59 +1,38 @@ [[procedure]] -name = "redeploy-v1" -description = "builds v1 core / frontend, and redeploys them" +name = "build-deploy-monitor-v1" +description = "builds v1 core / frontend, then redeploys them" config.type = "Sequence" - [[procedure.config.data]] -id = "" enabled = true - - -# Deploys +execution.type = "RunProcedure" +execution.params.procedure = "build-monitor-v1" +[[procedure.config.data]] +enabled = true +execution.type = "RunProcedure" +execution.params.procedure = "deploy-monitor-v1" [[procedure]] -name = "deploy-v1" +name = "deploy-monitor-v1" description = "deploys v1 core / frontend" config.type = "Parallel" [[procedure.config.data]] -id = "deploy-v1-core" enabled = true +execution.type = "Deploy" +execution.params.deployment = "monitor-core-v1" [[procedure.config.data]] -id = "deploy-v1-frontend" enabled = true +execution.type = "Deploy" +execution.params.deployment = "monitor-frontend-v1" [[procedure]] -name = "deploy-v1-core" -config.type = "Execution" -config.data.type = "Deploy" -config.data.params.deployment = "monitor-core-v1" - -[[procedure]] -name = "deploy-v1-frontend" -config.type = "Execution" -config.data.type = "Deploy" -config.data.params.deployment = "monitor-frontend-v1" - -# Builds - -[[procedure]] -name = "build-v1" +name = "build-monitor-v1" description = "builds v1 core / frontend" config.type = "Parallel" [[procedure.config.data]] -id = "build-v1-core" enabled = true +execution.type = "RunBuild" +execution.params.build = "monitor-core-v1" [[procedure.config.data]] -id = "build-v1-frontend" enabled = true - -[[procedure]] -name = "build-v1-core" -config.type = "Execution" -config.data.type = "RunBuild" -config.data.params.build = "monitor-core-v1" - -[[procedure]] -name = "build-v1-frontend" -config.type = "Execution" -config.data.type = "RunBuild" -config.data.params.build = "monitor-frontend-v1" +execution.type = "RunBuild" +execution.params.build = "monitor-frontend-v1" diff --git a/bin/monrun/src/sync/mod.rs b/bin/monrun/src/sync/mod.rs index 677604b68..cf0234a11 100644 --- a/bin/monrun/src/sync/mod.rs +++ b/bin/monrun/src/sync/mod.rs @@ -28,6 +28,8 @@ pub async fn run_sync(path: &Path) -> anyhow::Result<()> { let resources = resource_file::read_resources(path)?; + println!("{resources:#?}"); + let (server_updates, server_creates) = Server::get_updates(resources.servers)?; let (deployment_updates, deployment_creates) = @@ -112,7 +114,8 @@ pub trait ResourceSync { if !to_create.is_empty() { println!( - "\nTO CREATE: {}", + "\n{} TO CREATE: {}", + Self::display(), to_create .iter() .map(|item| item.name.as_str()) @@ -123,7 +126,8 @@ pub trait ResourceSync { if !to_update.is_empty() { println!( - "\nTO UPDATE: {}", + "\n{} TO UPDATE: {}", + Self::display(), to_update .iter() .map(|(_, item)| item.name.as_str()) diff --git a/bin/monrun/src/sync/resource_file.rs b/bin/monrun/src/sync/resource_file.rs index 2b2a413f6..cb5d197b6 100644 --- a/bin/monrun/src/sync/resource_file.rs +++ b/bin/monrun/src/sync/resource_file.rs @@ -62,6 +62,7 @@ fn read_resources_recursive( resources.builders.extend(more.builders); resources.repos.extend(more.repos); resources.alerters.extend(more.alerters); + resources.procedures.extend(more.procedures); Ok(()) } else if res.is_dir() { let directory = fs::read_dir(path) diff --git a/client/core/rs/src/entities/build.rs b/client/core/rs/src/entities/build.rs index 585a927f9..f6bf56896 100644 --- a/client/core/rs/src/entities/build.rs +++ b/client/core/rs/src/entities/build.rs @@ -41,6 +41,7 @@ pub type _PartialBuildConfig = PartialBuildConfig; #[partial_from] pub struct BuildConfig { #[serde(default, alias = "builder")] + #[partial_attr(serde(alias = "builder"))] #[builder(default)] pub builder_id: String, diff --git a/client/core/rs/src/entities/deployment.rs b/client/core/rs/src/entities/deployment.rs index 23474ebcf..844d755b2 100644 --- a/client/core/rs/src/entities/deployment.rs +++ b/client/core/rs/src/entities/deployment.rs @@ -39,6 +39,7 @@ pub type _PartialDeploymentConfig = PartialDeploymentConfig; #[partial_from] pub struct DeploymentConfig { #[serde(default, alias = "server")] + #[partial_attr(serde(alias = "server"))] #[builder(default)] pub server_id: String, diff --git a/client/core/rs/src/entities/repo.rs b/client/core/rs/src/entities/repo.rs index 47adcf002..210e692fb 100644 --- a/client/core/rs/src/entities/repo.rs +++ b/client/core/rs/src/entities/repo.rs @@ -34,6 +34,7 @@ pub type _PartialRepoConfig = PartialRepoConfig; #[partial_from] pub struct RepoConfig { #[serde(default, alias = "server")] + #[partial_attr(serde(alias = "server"))] #[builder(default)] pub server_id: String, diff --git a/client/core/rs/src/entities/user.rs b/client/core/rs/src/entities/user.rs index 2d4c6b18c..ff7309212 100644 --- a/client/core/rs/src/entities/user.rs +++ b/client/core/rs/src/entities/user.rs @@ -80,7 +80,7 @@ impl User { pub enum UserConfig { /// User that logs in with username / password Local { password: String }, - + /// User that logs in via Google Oauth Google { google_id: String, avatar: String }, diff --git a/frontend/src/components/resources/deployment/config/index.tsx b/frontend/src/components/resources/deployment/config/index.tsx index cc7a385a8..e66a7f57b 100644 --- a/frontend/src/components/resources/deployment/config/index.tsx +++ b/frontend/src/components/resources/deployment/config/index.tsx @@ -33,7 +33,7 @@ export const ServerSelector = ({ ); export const DeploymentConfig = ({ id }: { id: string }) => { - const config = useRead("GetDeployment", { id }).data?.config; + const config = useRead("GetDeployment", { deployment: id }).data?.config; const [update, set] = useState>({}); const { mutate } = useWrite("UpdateDeployment"); diff --git a/frontend/src/components/resources/procedure/index2.tsx b/frontend/src/components/resources/procedure/index2.md similarity index 100% rename from frontend/src/components/resources/procedure/index2.tsx rename to frontend/src/components/resources/procedure/index2.md diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index f1af2886d..68ccd241e 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -14,7 +14,7 @@ import { BuildComponents } from "@components/resources/build"; import { RepoComponents } from "@components/resources/repo"; import { BuilderComponents } from "@components/resources/builder"; import { AlerterComponents } from "@components/resources/alerter"; -import { ProcedureComponents } from "@components/resources/procedure/index2"; +import { ProcedureComponents } from "@components/resources/procedure/index"; import { TagsSummary } from "@components/dashboard/tags"; import { ApiKeysSummary } from "@components/dashboard/api-keys"; diff --git a/frontend/src/pages/keys.tsx b/frontend/src/pages/keys.tsx index f69b4b0f8..9a3cc3c87 100644 --- a/frontend/src/pages/keys.tsx +++ b/frontend/src/pages/keys.tsx @@ -1,7 +1,7 @@ import { Page } from "@components/layouts"; import { ConfirmButton, CopyButton } from "@components/util"; import { useInvalidate, useRead, useWrite } from "@lib/hooks"; -import { fmt_date, fmt_date_with_minutes } from "@lib/utils"; +import { fmt_date } from "@lib/utils"; import { Dialog, DialogContent,