From f0ba2ff17bcf17647222f5b43f361d84939b94ae Mon Sep 17 00:00:00 2001 From: leon3s Date: Mon, 8 Jan 2024 15:16:29 +0100 Subject: [PATCH] refactor/nanocld: mutualised kill --- bin/nanocld/specs/swagger.yaml | 72 ++++++++++++---------- bin/nanocld/src/objects/generic/process.rs | 26 ++++++++ bin/nanocld/src/services/cargo.rs | 33 +--------- bin/nanocld/src/services/openapi.rs | 2 +- bin/nanocld/src/services/process.rs | 42 +++++++++++++ bin/nanocld/src/utils/cargo.rs | 21 +------ crates/nanocld_client/src/cargo.rs | 30 +-------- crates/nanocld_client/src/process.rs | 30 ++++++++- 8 files changed, 143 insertions(+), 113 deletions(-) diff --git a/bin/nanocld/specs/swagger.yaml b/bin/nanocld/specs/swagger.yaml index ed224930a..fe3434a52 100644 --- a/bin/nanocld/specs/swagger.yaml +++ b/bin/nanocld/specs/swagger.yaml @@ -441,38 +441,6 @@ paths: application/json: schema: $ref: '#/components/schemas/CargoInspect' - /cargoes/{name}/kill: - post: - tags: - - Cargoes - summary: Send a signal to a cargo this will kill the cargo if the signal is SIGKILL - description: Send a signal to a cargo this will kill the cargo if the signal is SIGKILL - operationId: kill_cargo - parameters: - - name: name - in: path - description: Name of the cargo - required: true - schema: - type: string - - name: namespace - in: query - description: Namespace where the cargo belongs - required: false - schema: - type: string - nullable: true - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CargoKillOptions' - required: true - responses: - '200': - description: Cargo killed - '404': - description: Cargo does not exist /cargoes/{name}/stats: get: tags: @@ -1078,6 +1046,46 @@ paths: description: Process stopped '404': description: Process does not exist + /processes/{name}/kill: + post: + tags: + - Processes + summary: Send a signal to processes of given kind and name + description: Send a signal to processes of given kind and name + operationId: kill_process + parameters: + - name: kind + in: path + description: Kind of the process + required: true + schema: + type: string + example: cargo + - name: name + in: path + description: Name of the process + required: true + schema: + type: string + example: deploy-example + - name: namespace + in: query + description: Namespace where the process belongs is needed + required: false + schema: + type: string + nullable: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CargoKillOptions' + required: true + responses: + '200': + description: Cargo killed + '404': + description: Cargo does not exist /resource/kinds: get: tags: diff --git a/bin/nanocld/src/objects/generic/process.rs b/bin/nanocld/src/objects/generic/process.rs index bed69567c..c5a5fd622 100644 --- a/bin/nanocld/src/objects/generic/process.rs +++ b/bin/nanocld/src/objects/generic/process.rs @@ -10,6 +10,7 @@ use nanocl_error::{ use nanocl_stubs::{ system::NativeEventAction, process::{ProcessKind, ProcessPartial, Process}, + cargo::CargoKillOptions, }; use crate::{ @@ -175,6 +176,31 @@ pub trait ObjProcess { Ok(()) } + async fn kill_process_by_kind_pk( + pk: &str, + opts: &CargoKillOptions, + state: &SystemState, + ) -> HttpResult<()> { + let processes = ProcessDb::read_by_kind_key(pk, &state.pool).await?; + processes + .into_iter() + .map(|process| async move { + let id = process.data.id.clone().unwrap_or_default(); + let options = opts.clone().into(); + state + .docker_api + .kill_container(&id, Some(options)) + .await + .map_err(HttpError::from) + }) + .collect::>() + .collect::>>() + .await + .into_iter() + .collect::>>()?; + Ok(()) + } + /// Delete a process by pk async fn del_process_by_pk( pk: &str, diff --git a/bin/nanocld/src/services/cargo.rs b/bin/nanocld/src/services/cargo.rs index d5cbf1491..b66bb1802 100644 --- a/bin/nanocld/src/services/cargo.rs +++ b/bin/nanocld/src/services/cargo.rs @@ -4,7 +4,7 @@ use nanocl_error::{http::HttpResult, io::IoResult}; use nanocl_stubs::{ generic::{GenericNspQuery, GenericListNspQuery}, - cargo::{CargoDeleteQuery, CargoKillOptions, CargoStatsQuery}, + cargo::{CargoDeleteQuery, CargoStatsQuery}, cargo_spec::{CargoSpecPartial, CargoSpecUpdate}, }; @@ -187,34 +187,6 @@ pub async fn patch_cargo( Ok(web::HttpResponse::Ok().json(&cargo)) } -/// Send a signal to a cargo this will kill the cargo if the signal is SIGKILL -#[cfg_attr(feature = "dev", utoipa::path( - post, - tag = "Cargoes", - request_body = CargoKillOptions, - path = "/cargoes/{name}/kill", - params( - ("name" = String, Path, description = "Name of the cargo"), - ("namespace" = Option, Query, description = "Namespace where the cargo belongs"), - ), - responses( - (status = 200, description = "Cargo killed"), - (status = 404, description = "Cargo does not exist"), - ), -))] -#[web::post("/cargoes/{name}/kill")] -pub async fn kill_cargo( - state: web::types::State, - path: web::types::Path<(String, String)>, - payload: web::types::Json, - qs: web::types::Query, -) -> HttpResult { - let namespace = utils::key::resolve_nsp(&qs.namespace); - let key = utils::key::gen_key(&namespace, &path.1); - utils::cargo::kill_by_key(&key, &payload, &state).await?; - Ok(web::HttpResponse::Ok().into()) -} - /// List cargo histories #[cfg_attr(feature = "dev", utoipa::path( get, @@ -314,7 +286,6 @@ pub async fn stats_cargo( pub fn ntex_config(config: &mut web::ServiceConfig) { config.service(create_cargo); config.service(delete_cargo); - config.service(kill_cargo); config.service(patch_cargo); config.service(put_cargo); config.service(list_cargo); @@ -415,7 +386,7 @@ mod tests { ); let res = client .send_post( - &format!("{ENDPOINT}/{main_test_cargo}/kill"), + &format!("/processes/cargo/{main_test_cargo}/kill"), Some(&CargoKillOptions { signal: "SIGINT".to_owned(), }), diff --git a/bin/nanocld/src/services/openapi.rs b/bin/nanocld/src/services/openapi.rs index 58d90983e..893582853 100644 --- a/bin/nanocld/src/services/openapi.rs +++ b/bin/nanocld/src/services/openapi.rs @@ -252,7 +252,6 @@ impl Modify for VersionModifier { cargo::delete_cargo, cargo::put_cargo, cargo::patch_cargo, - cargo::kill_cargo, cargo::list_cargo_history, cargo::revert_cargo, cargo::stats_cargo, @@ -304,6 +303,7 @@ impl Modify for VersionModifier { process::stop_process, process::list_process, process::restart_process, + process::kill_process, // Event event::list_event, ), diff --git a/bin/nanocld/src/services/process.rs b/bin/nanocld/src/services/process.rs index 1450f44e8..aa445d2a8 100644 --- a/bin/nanocld/src/services/process.rs +++ b/bin/nanocld/src/services/process.rs @@ -7,6 +7,7 @@ use bollard_next::container::LogsOptions; use nanocl_stubs::{ generic::{GenericNspQuery, GenericFilter, GenericListQuery}, process::{ProcessLogQuery, ProcessOutputLog, ProcessKind}, + cargo::CargoKillOptions, }; use crate::{ @@ -220,12 +221,53 @@ pub async fn stop_process( Ok(web::HttpResponse::Accepted().finish()) } +/// Send a signal to processes of given kind and name +#[cfg_attr(feature = "dev", utoipa::path( + post, + tag = "Processes", + request_body = CargoKillOptions, + path = "/processes/{name}/kill", + params( + ("kind" = String, Path, description = "Kind of the process", example = "cargo"), + ("name" = String, Path, description = "Name of the process", example = "deploy-example"), + ("namespace" = Option, Query, description = "Namespace where the process belongs is needed"), + ), + responses( + (status = 200, description = "Cargo killed"), + (status = 404, description = "Cargo does not exist"), + ), +))] +#[web::post("/processes/kind/{name}/kill")] +pub async fn kill_process( + state: web::types::State, + path: web::types::Path<(String, String, String)>, + payload: web::types::Json, + qs: web::types::Query, +) -> HttpResult { + let (_, kind, name) = path.into_inner(); + let kind = kind.parse().map_err(HttpError::bad_request)?; + let kind_pk = utils::key::gen_kind_key(&kind, &name, &qs.namespace); + match &kind { + ProcessKind::Vm => { + VmDb::kill_process_by_kind_pk(&kind_pk, &payload, &state).await?; + } + ProcessKind::Job => { + JobDb::kill_process_by_kind_pk(&kind_pk, &payload, &state).await?; + } + ProcessKind::Cargo => { + CargoDb::kill_process_by_kind_pk(&kind_pk, &payload, &state).await?; + } + } + Ok(web::HttpResponse::Ok().into()) +} + pub fn ntex_config(config: &mut web::ServiceConfig) { config.service(list_process); config.service(logs_process); config.service(restart_process); config.service(start_process); config.service(stop_process); + config.service(kill_process); } #[cfg(test)] diff --git a/bin/nanocld/src/utils/cargo.rs b/bin/nanocld/src/utils/cargo.rs index d69d3a6ca..275070223 100644 --- a/bin/nanocld/src/utils/cargo.rs +++ b/bin/nanocld/src/utils/cargo.rs @@ -13,7 +13,7 @@ use bollard_next::{ use nanocl_stubs::{ process::Process, generic::{GenericListNspQuery, GenericClause, GenericFilter}, - cargo::{Cargo, CargoSummary, CargoKillOptions, CargoStats, CargoStatsQuery}, + cargo::{Cargo, CargoSummary, CargoStats, CargoStatsQuery}, }; use crate::{ @@ -282,25 +282,6 @@ pub async fn list( Ok(cargo_summaries) } -/// Send a signal to a cargo instance the cargo name can be used if the cargo has only one instance -/// The signal is send to one instance only -pub async fn kill_by_key( - key: &str, - options: &CargoKillOptions, - state: &SystemState, -) -> HttpResult<()> { - let instances = ProcessDb::read_by_kind_key(key, &state.pool).await?; - if instances.is_empty() { - return Err(HttpError::not_found(format!( - "Cargo instance not found: {key}" - ))); - } - let id = instances[0].data.id.clone().unwrap_or_default(); - let options = options.clone().into(); - state.docker_api.kill_container(&id, Some(options)).await?; - Ok(()) -} - /// Get the stats of a cargo instance /// The cargo name can be used if the cargo has only one instance pub fn get_stats( diff --git a/crates/nanocld_client/src/cargo.rs b/crates/nanocld_client/src/cargo.rs index 5c798f7f1..ad87f27ae 100644 --- a/crates/nanocld_client/src/cargo.rs +++ b/crates/nanocld_client/src/cargo.rs @@ -6,8 +6,8 @@ use nanocl_error::http_client::HttpClientResult; use bollard_next::service::ContainerSummary; use nanocl_stubs::generic::GenericNspQuery; use nanocl_stubs::cargo::{ - Cargo, CargoSummary, CargoInspect, CargoKillOptions, CargoDeleteQuery, - CargoStatsQuery, CargoStats, + Cargo, CargoSummary, CargoInspect, CargoDeleteQuery, CargoStatsQuery, + CargoStats, }; use nanocl_stubs::cargo_spec::{CargoSpecUpdate, CargoSpecPartial, CargoSpec}; @@ -237,32 +237,6 @@ impl NanocldClient { Ok(Self::res_stream(res).await) } - /// Kill a cargo by it's name - /// - /// ## Example - /// - /// ```no_run,ignore - /// use nanocld_client::NanocldClient; - /// - /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let res = client.kill_cargo("my-cargo", None, None).await; - /// ``` - pub async fn kill_cargo( - &self, - name: &str, - query: Option<&CargoKillOptions>, - namespace: Option<&str>, - ) -> HttpClientResult<()> { - self - .send_post( - &format!("{}/{name}/kill", Self::CARGO_PATH), - query, - Some(GenericNspQuery::new(namespace)), - ) - .await?; - Ok(()) - } - /// List all the instances of a cargo by it's name and namespace /// /// ## Example diff --git a/crates/nanocld_client/src/process.rs b/crates/nanocld_client/src/process.rs index 47cc35438..f38fdb753 100644 --- a/crates/nanocld_client/src/process.rs +++ b/crates/nanocld_client/src/process.rs @@ -1,4 +1,5 @@ use nanocl_error::io::IoError; +use nanocl_stubs::cargo::CargoKillOptions; use ntex::channel::mpsc::Receiver; use nanocl_error::http::HttpResult; @@ -84,7 +85,7 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let res = client.start_process("cargo", "my-cargo", None).await; + /// let res = client.restart_process("cargo", "my-cargo", None).await; /// ``` pub async fn restart_process( &self, @@ -127,6 +128,33 @@ impl NanocldClient { .await?; Ok(()) } + + /// Kill processes by it's kind and name and namespace + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.kill_process("cargo", "my-cargo", None, None).await; + /// ``` + pub async fn kill_cargo( + &self, + kind: &str, + name: &str, + query: Option<&CargoKillOptions>, + namespace: Option<&str>, + ) -> HttpClientResult<()> { + self + .send_post( + &format!("{}/{kind}/{name}/kill", Self::PROCESS_PATH), + query, + Some(GenericNspQuery::new(namespace)), + ) + .await?; + Ok(()) + } } #[cfg(test)]