validate attached resources by name / id

This commit is contained in:
mbecker20
2024-03-27 05:33:34 -07:00
parent a5e15f9c5e
commit 6dcc4b30de
20 changed files with 205 additions and 70 deletions
Generated
+4 -4
View File
@@ -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",
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -107,7 +107,7 @@ impl Resolve<RunBuild, User> for State {
});
let build_id = build.id.clone();
let inner = || async move {
update.id = add_update(update.clone()).await?;
+10 -4
View File
@@ -36,7 +36,7 @@ use crate::{
impl Resolve<CreateBuild, User> for State {
async fn resolve(
&self,
CreateBuild { name, config }: CreateBuild,
CreateBuild { name, mut config }: CreateBuild,
user: User,
) -> anyhow::Result<Build> {
let name = to_monitor_name(&name);
@@ -44,9 +44,10 @@ impl Resolve<CreateBuild, User> 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<CopyBuild, User> 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<UpdateBuild, User> 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 {
+9 -4
View File
@@ -40,7 +40,7 @@ use crate::{
impl Resolve<CreateDeployment, User> for State {
async fn resolve(
&self,
CreateDeployment { name, config }: CreateDeployment,
CreateDeployment { name, mut config }: CreateDeployment,
user: User,
) -> anyhow::Result<Deployment> {
let name = to_monitor_name(&name);
@@ -49,18 +49,23 @@ impl Resolve<CreateDeployment, User> 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();
+139 -5
View File
@@ -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<CreateProcedure, User> for State {
async fn resolve(
&self,
CreateProcedure { name, config }: CreateProcedure,
CreateProcedure { name, mut config }: CreateProcedure,
user: User,
) -> anyhow::Result<CreateProcedureResponse> {
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<CreateProcedure, User> 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<CopyProcedure, User> for State {
async fn resolve(
@@ -91,7 +226,6 @@ impl Resolve<CopyProcedure, User> for State {
CopyProcedure { name, id }: CopyProcedure,
user: User,
) -> anyhow::Result<CopyProcedureResponse> {
let name = to_monitor_name(&name);
let Procedure {
config,
description,
+3 -2
View File
@@ -37,7 +37,7 @@ use crate::{
impl Resolve<CreateRepo, User> for State {
async fn resolve(
&self,
CreateRepo { name, config }: CreateRepo,
CreateRepo { name, mut config }: CreateRepo,
user: User,
) -> anyhow::Result<Repo> {
let name = to_monitor_name(&name);
@@ -46,13 +46,14 @@ impl Resolve<CreateRepo, User> 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();
+1 -1
View File
@@ -37,7 +37,7 @@ const STATE_PREFIX_LENGTH: usize = 20;
#[derive(Deserialize)]
pub struct RedirectQuery {
pub redirect: Option<String>,
pub redirect: Option<String>,
}
pub async fn auth_request(
+5 -3
View File
@@ -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<BroadcastChannel<(String, Update)>> =
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))
}
+18 -39
View File
@@ -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"
+6 -2
View File
@@ -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())
+1
View File
@@ -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)
+1
View File
@@ -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,
@@ -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,
+1
View File
@@ -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,
+1 -1
View File
@@ -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 },
@@ -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<Partial<Types.DeploymentConfig>>({});
const { mutate } = useWrite("UpdateDeployment");
+1 -1
View File
@@ -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";
+1 -1
View File
@@ -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,