delete deployment

This commit is contained in:
beckerinj
2022-12-01 01:32:54 -05:00
parent bf80b34e8e
commit 15faefff93
8 changed files with 233 additions and 70 deletions

8
.vscode/tasks.json vendored
View File

@@ -68,6 +68,14 @@
"cwd": "${workspaceFolder}/cli"
},
},
{
"type": "cargo",
"command": "publish",
"label": "publish types",
"options": {
"cwd": "${workspaceFolder}/lib/types"
},
},
{
"type": "shell",
"command": "docker build -t monitor_periphery -f Dockerfile.periphery .",

View File

@@ -1,23 +1,40 @@
use anyhow::Context;
use anyhow::{anyhow, Context};
use async_timing_util::unix_timestamp_ms;
use axum::{routing::post, Extension, Json, Router};
use db::DbExtension;
use helpers::handle_anyhow_error;
use types::{Deployment, Operation, PermissionLevel, Update, UpdateTarget};
use mungos::Deserialize;
use types::{
traits::Permissioned, Deployment, Log, Operation, PermissionLevel, Update, UpdateTarget,
};
use crate::{auth::RequestUserExtension, ws::update};
use super::add_update;
use super::{add_update, PeripheryExtension};
#[derive(Deserialize)]
pub struct DeploymentIdBody {
id: String,
}
pub fn router() -> Router {
Router::new().route(
"/create",
post(|db, user, update_ws, deployment| async {
create(db, user, update_ws, deployment)
.await
.map_err(handle_anyhow_error)
}),
)
Router::new()
.route(
"/create",
post(|db, user, update_ws, deployment| async {
create(db, user, update_ws, deployment)
.await
.map_err(handle_anyhow_error)
}),
)
.route(
"/delete",
post(|db, user, update_ws, periphery, deployment_id| async {
delete(db, user, update_ws, periphery, deployment_id)
.await
.map_err(handle_anyhow_error)
}),
)
}
async fn create(
@@ -45,3 +62,41 @@ async fn create(
};
add_update(update, &db, &update_ws).await
}
async fn delete(
Extension(db): DbExtension,
Extension(user): RequestUserExtension,
Extension(update_ws): update::UpdateWsSenderExtension,
Extension(periphery): PeripheryExtension,
Json(DeploymentIdBody { id }): Json<DeploymentIdBody>,
) -> anyhow::Result<()> {
let deployment = db.get_deployment(&id).await?;
let permissions = deployment.get_user_permissions(&user.id);
if permissions != PermissionLevel::Write {
return Err(anyhow!(
"user does not have permissions to delete deployment {id}"
));
}
let server = db.get_server(&deployment.server_id).await?;
let start_ts = unix_timestamp_ms() as i64;
let log = periphery
.container_remove(&server, &deployment.name)
.await?;
db.deployments.delete_one(&id).await?;
let update = Update {
target: UpdateTarget::System,
operation: Operation::DeleteDeployment,
start_ts,
end_ts: Some(unix_timestamp_ms() as i64),
operator: user.id.clone(),
log: vec![
log,
Log::simple(format!(
"deleted deployment {} on server {}",
deployment.name, server.name
)),
],
..Default::default()
};
add_update(update, &db, &update_ws).await
}

View File

@@ -8,9 +8,11 @@ use axum::{
use db::DbExtension;
use helpers::handle_anyhow_error;
use mungos::Deserialize;
use types::{Operation, PermissionLevel, Server, SystemStats, Update, UpdateTarget};
use types::{
traits::Permissioned, Operation, PermissionLevel, Server, SystemStats, Update, UpdateTarget,
};
use crate::{auth::RequestUserExtension, helpers::get_user_permissions, ws::update};
use crate::{auth::RequestUserExtension, ws::update};
use super::{add_update, PeripheryExtension};
@@ -107,7 +109,7 @@ async fn stats(
.await
.context("failed at query to get server")?
.ok_or(anyhow!("failed to find server with id {server_id}"))?;
let permissions = get_user_permissions(&user.id, &server.permissions);
let permissions = server.get_user_permissions(&user.id);
if permissions == PermissionLevel::None {
return Err(anyhow!("user does not have permissions on this server"));
}

View File

@@ -1,12 +1,6 @@
use types::{PermissionLevel, PermissionsMap};
#[macro_export]
macro_rules! response {
($x:expr) => {
Ok::<_, (axum::http::StatusCode, String)>($x)
};
}
pub fn get_user_permissions(user_id: &str, permissions: &PermissionsMap) -> PermissionLevel {
*permissions.get(user_id).unwrap_or(&PermissionLevel::None)
}

View File

@@ -1,6 +1,6 @@
use std::sync::Arc;
use anyhow::{anyhow, Context};
use anyhow::anyhow;
use axum::{
extract::{
ws::{Message, WebSocket},
@@ -23,10 +23,7 @@ use tokio::{
use tokio_util::sync::CancellationToken;
use types::{PermissionLevel, Update, UpdateTarget, User};
use crate::{
auth::{JwtClient, JwtExtension},
helpers::get_user_permissions,
};
use crate::auth::{JwtClient, JwtExtension};
pub type UpdateWsSender = Arc<Mutex<Sender<Update>>>;
pub type UpdateWsSenderExtension = Extension<UpdateWsSender>;
@@ -192,54 +189,32 @@ async fn user_can_see_update(
if user.admin {
return Ok(());
}
match update_target {
UpdateTarget::System => {
if user.admin {
Ok(())
} else {
Err(anyhow!("user not admin, can't recieve system updates"))
}
}
let (permissions, target) = match update_target {
UpdateTarget::Server(server_id) => {
let server = db_client
.servers
.find_one_by_id(server_id)
.await
.context(format!("failed at query to get server at {server_id}"))?
.ok_or(anyhow!("did not server with id {server_id}"))?;
if get_user_permissions(user_id, &server.permissions) != PermissionLevel::None {
Ok(())
} else {
Err(anyhow!("user does not have permissions on server"))
}
let permissions = db_client
.get_user_permission_on_server(user_id, server_id)
.await?;
(permissions, "server")
}
UpdateTarget::Deployment(deployment_id) => {
let deployment = db_client
.deployments
.find_one_by_id(deployment_id)
.await
.context(format!(
"failed at query to get deployment at {deployment_id}"
))?
.ok_or(anyhow!("did not deployment with id {deployment_id}"))?;
if get_user_permissions(user_id, &deployment.permissions) != PermissionLevel::None {
Ok(())
} else {
Err(anyhow!("user does not have permissions on deployment"))
}
let permissions = db_client
.get_user_permission_on_deployment(user_id, deployment_id)
.await?;
(permissions, "deployment")
}
UpdateTarget::Build(build_id) => {
let build = db_client
.builds
.find_one_by_id(build_id)
.await
.context(format!("failed at query to get build at {build_id}"))?
.ok_or(anyhow!("did not build with id {build_id}"))?;
if get_user_permissions(user_id, &build.permissions) != PermissionLevel::None {
Ok(())
} else {
Err(anyhow!("user does not have permissions on build"))
}
let permissions = db_client
.get_user_permission_on_build(user_id, build_id)
.await?;
(permissions, "build")
}
UpdateTarget::System => {
return Err(anyhow!("user not admin, can't recieve system updates"))
}
};
if permissions != PermissionLevel::None {
Ok(())
} else {
Err(anyhow!("user does not have permissions on {target}"))
}
}

View File

@@ -1,12 +1,13 @@
use std::{sync::Arc, time::Duration};
use anyhow::{anyhow, Context};
use axum::Extension;
use collections::{
builds_collection, deployments_collection, procedures_collection, servers_collection,
updates_collection, users_collection,
};
use mungos::{Collection, Mungos};
use types::{Build, Deployment, MongoConfig, Procedure, Server, Update, User};
use types::{Build, Deployment, MongoConfig, PermissionLevel, Procedure, Server, Update, User};
mod collections;
@@ -49,4 +50,78 @@ impl DbClient {
};
Extension(Arc::new(client))
}
pub async fn get_deployment(&self, deployment_id: &str) -> anyhow::Result<Deployment> {
let deployment = self
.deployments
.find_one_by_id(deployment_id)
.await
.context(format!(
"failed at mongo query for deployment {deployment_id}"
))?
.ok_or(anyhow!("deployment at {deployment_id} doesn't exist"))?;
Ok(deployment)
}
pub async fn get_user_permission_on_deployment(
&self,
user_id: &str,
deployment_id: &str,
) -> anyhow::Result<PermissionLevel> {
let permissions = *self
.get_deployment(deployment_id)
.await?
.permissions
.get(user_id)
.unwrap_or_default();
Ok(permissions)
}
pub async fn get_build(&self, build_id: &str) -> anyhow::Result<Build> {
let build = self
.builds
.find_one_by_id(build_id)
.await
.context(format!("failed at mongo query for build {build_id}"))?
.ok_or(anyhow!("build at {build_id} doesn't exist"))?;
Ok(build)
}
pub async fn get_user_permission_on_build(
&self,
user_id: &str,
build_id: &str,
) -> anyhow::Result<PermissionLevel> {
let permissions = *self
.get_build(build_id)
.await?
.permissions
.get(user_id)
.unwrap_or_default();
Ok(permissions)
}
pub async fn get_server(&self, server_id: &str) -> anyhow::Result<Server> {
let server = self
.servers
.find_one_by_id(server_id)
.await
.context(format!("failed at mongo query for server {server_id}"))?
.ok_or(anyhow!("server at {server_id} doesn't exist"))?;
Ok(server)
}
pub async fn get_user_permission_on_server(
&self,
user_id: &str,
server_id: &str,
) -> anyhow::Result<PermissionLevel> {
let permissions = *self
.get_server(server_id)
.await?
.permissions
.get(user_id)
.unwrap_or_default();
Ok(permissions)
}
}

View File

@@ -1,10 +1,12 @@
use std::{collections::HashMap, path::PathBuf};
use async_timing_util::Timelength;
use async_timing_util::{unix_timestamp_ms, Timelength};
use mungos::ObjectId;
use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumString};
pub mod traits;
pub const PERIPHERY_BUILDER_BUSY: &str = "builder is busy";
pub type UserId = String;
@@ -227,6 +229,19 @@ pub struct Log {
pub end_ts: i64,
}
impl Log {
pub fn simple(msg: String) -> Log {
let ts = unix_timestamp_ms() as i64;
Log {
stdout: msg,
success: true,
start_ts: ts,
end_ts: ts,
..Default::default()
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Command {
pub path: String,
@@ -458,6 +473,18 @@ pub enum PermissionLevel {
Write,
}
impl Default for PermissionLevel {
fn default() -> Self {
PermissionLevel::None
}
}
impl Default for &PermissionLevel {
fn default() -> Self {
&PermissionLevel::None
}
}
#[derive(Serialize, Deserialize, Debug, Display, EnumString, PartialEq, Hash, Eq, Clone, Copy)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]

27
lib/types/src/traits.rs Normal file
View File

@@ -0,0 +1,27 @@
use crate::{Build, Deployment, PermissionLevel, PermissionsMap, Server};
pub trait Permissioned {
fn permissions_map(&self) -> &PermissionsMap;
fn get_user_permissions(&self, user_id: &str) -> PermissionLevel {
*self.permissions_map().get(user_id).unwrap_or_default()
}
}
impl Permissioned for Deployment {
fn permissions_map(&self) -> &PermissionsMap {
&self.permissions
}
}
impl Permissioned for Build {
fn permissions_map(&self) -> &PermissionsMap {
&self.permissions
}
}
impl Permissioned for Server {
fn permissions_map(&self) -> &PermissionsMap {
&self.permissions
}
}