From be58cec1c99e8aedbb85d41f802bf48afa61bfa5 Mon Sep 17 00:00:00 2001 From: beckerinj Date: Sat, 17 Dec 2022 01:27:06 -0500 Subject: [PATCH] add start stop remove container --- core/src/actions/deployment.rs | 241 +++++++++++++++++++++----- core/src/actions/procedure.rs | 57 +++++- core/src/api/deployment.rs | 70 ++++++-- lib/monitor_client/src/deployment.rs | 46 ++++- lib/periphery_client/src/build.rs | 6 +- lib/periphery_client/src/container.rs | 18 +- lib/periphery_client/src/git.rs | 6 +- lib/periphery_client/src/image.rs | 9 +- lib/periphery_client/src/lib.rs | 16 +- lib/periphery_client/src/network.rs | 11 +- lib/types/src/lib.rs | 14 +- 11 files changed, 404 insertions(+), 90 deletions(-) diff --git a/core/src/actions/deployment.rs b/core/src/actions/deployment.rs index da4abb192..96ec6278c 100644 --- a/core/src/actions/deployment.rs +++ b/core/src/actions/deployment.rs @@ -194,53 +194,6 @@ impl State { Ok(new_deployment) } - pub async fn deploy(&self, deployment_id: &str, user: &RequestUser) -> anyhow::Result { - let mut deployment = self - .get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write) - .await?; - if let Some(build_id) = &deployment.build_id { - let build = self.db.get_build(build_id).await?; - let image = if let Some(docker_account) = &build.docker_account { - if deployment.docker_run_args.docker_account.is_none() { - deployment.docker_run_args.docker_account = Some(docker_account.to_string()) - } - format!("{docker_account}/{}", to_monitor_name(&build.name)) - } else { - to_monitor_name(&build.name) - }; - let version = if let Some(version) = &deployment.build_version { - version.to_string() - } else { - "latest".to_string() - }; - deployment.docker_run_args.image = format!("{image}:{version}"); - }; - let server = self.db.get_server(&deployment.server_id).await?; - let mut update = Update { - target: UpdateTarget::Deployment(deployment_id.to_string()), - operation: Operation::DeployDeployment, - start_ts: unix_timestamp_ms() as i64, - status: UpdateStatus::InProgress, - operator: user.id.clone(), - success: true, - // version: deployment.docker_run_args., - ..Default::default() - }; - - update.id = self.add_update(update.clone()).await?; - - let deploy_log = self.periphery.deploy(&server, &deployment).await?; - - update.success = deploy_log.success; - update.logs.push(deploy_log); - update.status = UpdateStatus::Complete; - update.end_ts = Some(unix_timestamp_ms() as i64); - - self.update_update(update.clone()).await?; - - Ok(update) - } - pub async fn reclone_deployment( &self, deployment_id: &str, @@ -281,4 +234,198 @@ impl State { Ok(update) } + + pub async fn deploy_container( + &self, + deployment_id: &str, + user: &RequestUser, + ) -> anyhow::Result { + let mut deployment = self + .get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write) + .await?; + if let Some(build_id) = &deployment.build_id { + let build = self.db.get_build(build_id).await?; + let image = if let Some(docker_account) = &build.docker_account { + if deployment.docker_run_args.docker_account.is_none() { + deployment.docker_run_args.docker_account = Some(docker_account.to_string()) + } + format!("{docker_account}/{}", to_monitor_name(&build.name)) + } else { + to_monitor_name(&build.name) + }; + let version = if let Some(version) = &deployment.build_version { + version.to_string() + } else { + "latest".to_string() + }; + deployment.docker_run_args.image = format!("{image}:{version}"); + }; + let server = self.db.get_server(&deployment.server_id).await?; + let mut update = Update { + target: UpdateTarget::Deployment(deployment_id.to_string()), + operation: Operation::DeployContainer, + start_ts: unix_timestamp_ms() as i64, + status: UpdateStatus::InProgress, + operator: user.id.clone(), + success: true, + ..Default::default() + }; + + update.id = self.add_update(update.clone()).await?; + + let deploy_log = self.periphery.deploy(&server, &deployment).await?; + + update.success = deploy_log.success; + update.logs.push(deploy_log); + update.status = UpdateStatus::Complete; + update.end_ts = Some(unix_timestamp_ms() as i64); + + self.update_update(update.clone()).await?; + + Ok(update) + } + + pub async fn start_container( + &self, + deployment_id: &str, + user: &RequestUser, + ) -> anyhow::Result { + let start_ts = unix_timestamp_ms() as i64; + let deployment = self + .get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write) + .await?; + let server = self.db.get_server(&deployment.server_id).await?; + let mut update = Update { + target: UpdateTarget::Deployment(deployment_id.to_string()), + operation: Operation::StartContainer, + start_ts, + status: UpdateStatus::InProgress, + success: true, + operator: user.id.clone(), + ..Default::default() + }; + update.id = self.add_update(update.clone()).await?; + + let log = self + .periphery + .container_start(&server, &deployment.name) + .await; + + update.success = match log { + Ok(log) => { + let success = log.success; + update.logs.push(log); + success + } + Err(e) => { + update + .logs + .push(Log::error("start container", format!("{e:#?}"))); + false + } + }; + + update.end_ts = Some(unix_timestamp_ms() as i64); + update.status = UpdateStatus::Complete; + + self.update_update(update.clone()).await?; + + Ok(update) + } + + pub async fn stop_container( + &self, + deployment_id: &str, + user: &RequestUser, + ) -> anyhow::Result { + let start_ts = unix_timestamp_ms() as i64; + let deployment = self + .get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write) + .await?; + let server = self.db.get_server(&deployment.server_id).await?; + let mut update = Update { + target: UpdateTarget::Deployment(deployment_id.to_string()), + operation: Operation::StopContainer, + start_ts, + status: UpdateStatus::InProgress, + success: true, + operator: user.id.clone(), + ..Default::default() + }; + update.id = self.add_update(update.clone()).await?; + + let log = self + .periphery + .container_stop(&server, &deployment.name) + .await; + + update.success = match log { + Ok(log) => { + let success = log.success; + update.logs.push(log); + success + } + Err(e) => { + update + .logs + .push(Log::error("stop container", format!("{e:#?}"))); + false + } + }; + + update.end_ts = Some(unix_timestamp_ms() as i64); + update.status = UpdateStatus::Complete; + + self.update_update(update.clone()).await?; + + Ok(update) + } + + pub async fn remove_container( + &self, + deployment_id: &str, + user: &RequestUser, + ) -> anyhow::Result { + let start_ts = unix_timestamp_ms() as i64; + let deployment = self + .get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write) + .await?; + let server = self.db.get_server(&deployment.server_id).await?; + let mut update = Update { + target: UpdateTarget::Deployment(deployment_id.to_string()), + operation: Operation::RemoveContainer, + start_ts, + status: UpdateStatus::InProgress, + success: true, + operator: user.id.clone(), + ..Default::default() + }; + update.id = self.add_update(update.clone()).await?; + + let log = self + .periphery + .container_remove(&server, &deployment.name) + .await; + + update.success = match log { + Ok(log) => { + let success = log.success; + update.logs.push(log); + success + } + Err(e) => { + update + .logs + .push(Log::error("remove container", format!("{e:#?}"))); + false + } + }; + + update.end_ts = Some(unix_timestamp_ms() as i64); + update.status = UpdateStatus::Complete; + + self.update_update(update.clone()).await?; + + Ok(update) + } } diff --git a/core/src/actions/procedure.rs b/core/src/actions/procedure.rs index d89be1b93..70f060885 100644 --- a/core/src/actions/procedure.rs +++ b/core/src/actions/procedure.rs @@ -1,9 +1,10 @@ use anyhow::{anyhow, Context}; use async_timing_util::unix_timestamp_ms; +use diff::Diff; use helpers::to_monitor_name; use types::{ - traits::Permissioned, Log, Operation, PermissionLevel, Procedure, ProcedureStage, Update, - UpdateTarget, + traits::Permissioned, Log, Operation, PermissionLevel, Procedure, ProcedureOperation::*, + ProcedureStage, Update, UpdateStatus, UpdateTarget, }; use crate::{auth::RequestUser, state::State}; @@ -105,13 +106,51 @@ impl State { pub async fn update_procedure( &self, - new_procedure: Procedure, + mut new_procedure: Procedure, user: &RequestUser, ) -> anyhow::Result { let current_procedure = self .get_procedure_check_permissions(&new_procedure.id, user, PermissionLevel::Write) .await?; - todo!() + let start_ts = unix_timestamp_ms() as i64; + + // none of these should be changed through this method + new_procedure.name = current_procedure.name.clone(); + new_procedure.permissions = current_procedure.permissions.clone(); + new_procedure.created_at = current_procedure.created_at; + new_procedure.updated_at = start_ts; + + // check to make sure no stages have been added that user does not have access to + + self.db + .procedures + .update_one( + &new_procedure.id, + mungos::Update::Regular(new_procedure.clone()), + ) + .await + .context("failed at update one deployment")?; + + let diff = current_procedure.diff(&new_procedure); + + let update = Update { + operation: Operation::UpdateProcedure, + target: UpdateTarget::Procedure(new_procedure.id.clone()), + end_ts: Some(start_ts), + start_ts, + status: UpdateStatus::Complete, + logs: vec![Log::simple( + "procedure update", + serde_json::to_string_pretty(&diff).unwrap(), + )], + operator: user.id.clone(), + success: true, + ..Default::default() + }; + + self.add_update(update).await?; + + Ok(new_procedure) } pub async fn run_procedure(&self, id: &str, user: &RequestUser) -> anyhow::Result> { @@ -125,6 +164,16 @@ impl State { } in procedure.stages { match operation { + StartContainer => {} + StopContainer => {} + RemoveContainer => {} + DeployContainer => {} + RecloneDeployment => {} + + BuildBuild => {} + RecloneBuild => {} + + PruneImagesServer => {} _ => {} } } diff --git a/core/src/api/deployment.rs b/core/src/api/deployment.rs index a56c97879..1b6d790aa 100644 --- a/core/src/api/deployment.rs +++ b/core/src/api/deployment.rs @@ -114,20 +114,6 @@ pub fn router() -> Router { }, ), ) - .route( - "/:id/deploy", - post( - |Extension(state): StateExtension, - Extension(user): RequestUserExtension, - Path(deployment_id): Path| async move { - let update = state - .deploy(&deployment_id.id, &user) - .await - .map_err(handle_anyhow_error)?; - response!(Json(update)) - }, - ), - ) .route( "/:id/reclone", post( @@ -142,6 +128,62 @@ pub fn router() -> Router { }, ), ) + .route( + "/:id/deploy", + post( + |Extension(state): StateExtension, + Extension(user): RequestUserExtension, + Path(deployment_id): Path| async move { + let update = state + .deploy_container(&deployment_id.id, &user) + .await + .map_err(handle_anyhow_error)?; + response!(Json(update)) + }, + ), + ) + .route( + "/:id/start_container", + post( + |Extension(state): StateExtension, + Extension(user): RequestUserExtension, + Path(deployment_id): Path| async move { + let update = state + .start_container(&deployment_id.id, &user) + .await + .map_err(handle_anyhow_error)?; + response!(Json(update)) + }, + ), + ) + .route( + "/:id/stop_container", + post( + |Extension(state): StateExtension, + Extension(user): RequestUserExtension, + Path(deployment_id): Path| async move { + let update = state + .stop_container(&deployment_id.id, &user) + .await + .map_err(handle_anyhow_error)?; + response!(Json(update)) + }, + ), + ) + .route( + "/:id/remove_container", + post( + |Extension(state): StateExtension, + Extension(user): RequestUserExtension, + Path(deployment_id): Path| async move { + let update = state + .remove_container(&deployment_id.id, &user) + .await + .map_err(handle_anyhow_error)?; + response!(Json(update)) + }, + ), + ) } impl State { diff --git a/lib/monitor_client/src/deployment.rs b/lib/monitor_client/src/deployment.rs index 73a6bbe9f..e1e3514ac 100644 --- a/lib/monitor_client/src/deployment.rs +++ b/lib/monitor_client/src/deployment.rs @@ -23,6 +23,7 @@ impl MonitorClient { Option::<()>::None, ) .await + .context(format!("failed at get deployment {deployment_id}")) } pub async fn create_deployment( @@ -61,15 +62,48 @@ impl MonitorClient { .context("failed at updating deployment") } - pub async fn deploy(&self, deployment_id: &str) -> anyhow::Result { - self.post::<(), _>(&format!("/api/deployment/{deployment_id}/deploy"), None) - .await - .context(format!("failed at deploy deployment {deployment_id}")) - } - pub async fn reclone_deployment(&self, id: &str) -> anyhow::Result { self.post::<(), _>(&format!("/api/deployment/{id}/reclone"), None) .await .context(format!("failed at reclone deployment {id}")) } + + pub async fn deploy_container(&self, deployment_id: &str) -> anyhow::Result { + self.post::<(), _>(&format!("/api/deployment/{deployment_id}/deploy"), None) + .await + .context(format!("failed at deploy deployment {deployment_id}")) + } + + pub async fn start_container(&self, deployment_id: &str) -> anyhow::Result { + self.post::<(), _>( + &format!("/api/deployment/{deployment_id}/start_container"), + None, + ) + .await + .context(format!( + "failed at start container for deployment {deployment_id}" + )) + } + + pub async fn stop_container(&self, deployment_id: &str) -> anyhow::Result { + self.post::<(), _>( + &format!("/api/deployment/{deployment_id}/stop_container"), + None, + ) + .await + .context(format!( + "failed at stop container for deployment {deployment_id}" + )) + } + + pub async fn remove_container(&self, deployment_id: &str) -> anyhow::Result { + self.post::<(), _>( + &format!("/api/deployment/{deployment_id}/remove_container"), + None, + ) + .await + .context(format!( + "failed at remove container for deployment {deployment_id}" + )) + } } diff --git a/lib/periphery_client/src/build.rs b/lib/periphery_client/src/build.rs index d296ad51e..beea97ea3 100644 --- a/lib/periphery_client/src/build.rs +++ b/lib/periphery_client/src/build.rs @@ -1,10 +1,14 @@ +use anyhow::Context; use types::{Build, Log, Server, PERIPHERY_BUILDER_BUSY}; use crate::PeripheryClient; impl PeripheryClient { pub async fn build(&self, server: &Server, build: &Build) -> anyhow::Result>> { - let res = self.post_json::<_, Vec>(server, "/build", build).await; + let res = self + .post_json::<_, Vec>(server, "/build", build) + .await + .context("failed to build image on periphery"); match res { Ok(logs) => Ok(Some(logs)), Err(e) => { diff --git a/lib/periphery_client/src/container.rs b/lib/periphery_client/src/container.rs index 75580a215..e9946e31a 100644 --- a/lib/periphery_client/src/container.rs +++ b/lib/periphery_client/src/container.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use serde_json::json; use types::{BasicContainerInfo, Deployment, DockerContainerStats, Log, Server}; @@ -5,7 +6,9 @@ use crate::PeripheryClient; impl PeripheryClient { pub async fn container_list(&self, server: &Server) -> anyhow::Result> { - self.get_json(server, "/container/list").await + self.get_json(server, "/container/list") + .await + .context("failed to get container list on periphery") } pub async fn container_start( @@ -19,6 +22,7 @@ impl PeripheryClient { &json!({ "name": container_name }), ) .await + .context("failed to start container on periphery") } pub async fn container_stop( @@ -32,6 +36,7 @@ impl PeripheryClient { &json!({ "name": container_name }), ) .await + .context("failed to stop container on periphery") } pub async fn container_remove( @@ -45,15 +50,19 @@ impl PeripheryClient { &json!({ "name": container_name }), ) .await + .context("failed to remove container on periphery") } pub async fn deploy(&self, server: &Server, deployment: &Deployment) -> anyhow::Result { self.post_json(server, "/container/deploy", deployment) .await + .context("failed to deploy container on periphery") } pub async fn container_prune(&self, server: &Server) -> anyhow::Result { - self.post_json(server, "/container/prune", &json!({})).await + self.post_json(server, "/container/prune", &json!({})) + .await + .context("failed to prune containers on periphery") } pub async fn container_stats( @@ -63,12 +72,15 @@ impl PeripheryClient { ) -> anyhow::Result> { self.get_json(server, &format!("/container/stats/{container_name}")) .await + .context("failed to get container stats from periphery") } pub async fn container_stats_list( &self, server: &Server, ) -> anyhow::Result> { - self.get_json(server, "/container/stats/list").await + self.get_json(server, "/container/stats/list") + .await + .context("failed to get stats list from periphery") } } diff --git a/lib/periphery_client/src/git.rs b/lib/periphery_client/src/git.rs index cbcf4f465..9a6b7c7b3 100644 --- a/lib/periphery_client/src/git.rs +++ b/lib/periphery_client/src/git.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use helpers::git::CloneArgs; use serde_json::json; use types::{Log, Server}; @@ -11,11 +12,14 @@ impl PeripheryClient { clone_args: impl Into, ) -> anyhow::Result> { let clone_args: CloneArgs = clone_args.into(); - self.post_json(server, "/git/clone", &clone_args).await + self.post_json(server, "/git/clone", &clone_args) + .await + .context("failed to clone repo on periphery") } pub async fn delete_repo(&self, server: &Server, build_name: &str) -> anyhow::Result { self.post_json(server, "/git/delete", &json!({ "name": build_name })) .await + .context("failed to delete repo on periphery") } } diff --git a/lib/periphery_client/src/image.rs b/lib/periphery_client/src/image.rs index 4c4d6c9c4..a00a307fc 100644 --- a/lib/periphery_client/src/image.rs +++ b/lib/periphery_client/src/image.rs @@ -1,13 +1,18 @@ +use anyhow::Context; use types::{ImageSummary, Log, Server}; use crate::PeripheryClient; impl PeripheryClient { pub async fn image_list(&self, server: &Server) -> anyhow::Result> { - self.get_json(server, "/image/list").await + self.get_json(server, "/image/list") + .await + .context("failed to get image list from periphery") } pub async fn image_prune(&self, server: &Server) -> anyhow::Result { - self.post_json(server, &format!("/image/prune"), &()).await + self.post_json(server, &format!("/image/prune"), &()) + .await + .context("failed to prune images on periphery") } } diff --git a/lib/periphery_client/src/lib.rs b/lib/periphery_client/src/lib.rs index 3fef2e736..b951989b0 100644 --- a/lib/periphery_client/src/lib.rs +++ b/lib/periphery_client/src/lib.rs @@ -21,19 +21,27 @@ impl PeripheryClient { } pub async fn health_check(&self, server: &Server) -> anyhow::Result { - self.get_text(server, "health").await + self.get_text(server, "health") + .await + .context("failed at health check on periphery") } pub async fn get_github_accounts(&self, server: &Server) -> anyhow::Result> { - self.get_json(server, "/accounts/github").await + self.get_json(server, "/accounts/github") + .await + .context("failed to get github accounts from periphery") } pub async fn get_docker_accounts(&self, server: &Server) -> anyhow::Result> { - self.get_json(server, "/accounts/docker").await + self.get_json(server, "/accounts/docker") + .await + .context("failed to get docker accounts from periphery") } pub async fn get_system_stats(&self, server: &Server) -> anyhow::Result { - self.get_json(server, "/stats/system").await + self.get_json(server, "/stats/system") + .await + .context("failed to get system stats from periphery") } async fn get_text(&self, server: &Server, endpoint: &str) -> anyhow::Result { diff --git a/lib/periphery_client/src/network.rs b/lib/periphery_client/src/network.rs index 49fe1a491..76a6450c7 100644 --- a/lib/periphery_client/src/network.rs +++ b/lib/periphery_client/src/network.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use serde_json::json; use types::{Log, Network, Server}; @@ -5,7 +6,9 @@ use crate::PeripheryClient; impl PeripheryClient { pub async fn network_list(&self, server: &Server) -> anyhow::Result> { - self.get_json(server, "/network/list").await + self.get_json(server, "/network/list") + .await + .context("failed to get network list from periphery") } pub async fn network_create( @@ -23,14 +26,18 @@ impl PeripheryClient { }), ) .await + .context("failed to create network on periphery") } pub async fn network_delete(&self, server: &Server, name: &str) -> anyhow::Result { self.post_json(server, "/network/delete", &json!({ "name": name })) .await + .context("failed to delete network on periphery") } pub async fn network_prune(&self, server: &Server) -> anyhow::Result { - self.post_json(server, "/network/prune", &json!({})).await + self.post_json(server, "/network/prune", &json!({})) + .await + .context("failed to prune networks on periphery") } } diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 1e1ffc99c..a9663e086 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -689,9 +689,10 @@ pub enum Operation { CreateDeployment, UpdateDeployment, DeleteDeployment, - DeployDeployment, - StopDeployment, - StartDeployment, + DeployContainer, + StopContainer, + StartContainer, + RemoveContainer, PullDeployment, RecloneDeployment, @@ -727,9 +728,10 @@ pub enum ProcedureOperation { RecloneBuild, // deployment - DeployDeployment, - StopDeployment, - StartDeployment, + DeployContainer, + StopContainer, + StartContainer, + RemoveContainer, PullDeployment, RecloneDeployment,