diff --git a/bin/core/src/helpers/query.rs b/bin/core/src/helpers/query.rs index ded3f7117..ef7d3178b 100644 --- a/bin/core/src/helpers/query.rs +++ b/bin/core/src/helpers/query.rs @@ -61,14 +61,6 @@ pub async fn get_user(user: &str) -> anyhow::Result { .with_context(|| format!("No user found matching '{user}'")) } -pub async fn get_swarm_with_reachability( - swarm_id_or_name: &str, -) -> anyhow::Result<(Swarm, bool)> { - let swarm = resource::get::(swarm_id_or_name).await?; - let reachable = get_swarm_reachability(&swarm).await.is_ok(); - Ok((swarm, reachable)) -} - pub async fn get_swarm_reachability( swarm: &Swarm, ) -> anyhow::Result<()> { @@ -202,7 +194,9 @@ pub fn get_stack_state_from_containers( pub async fn get_stack_state( stack: &Stack, ) -> anyhow::Result { - if stack.config.server_id.is_empty() { + if stack.config.swarm_id.is_empty() + && stack.config.server_id.is_empty() + { return Ok(StackState::Down); } let state = stack_status_cache() diff --git a/bin/core/src/monitor/helpers.rs b/bin/core/src/monitor/helpers.rs index 47acffcc2..587a0018a 100644 --- a/bin/core/src/monitor/helpers.rs +++ b/bin/core/src/monitor/helpers.rs @@ -60,6 +60,7 @@ pub async fn insert_stacks_status_unknown(stacks: Vec) { id: stack.id, state: StackState::Unknown, services: Vec::new(), + swarm_stack: None, }, prev, } @@ -84,6 +85,7 @@ pub async fn insert_deployments_status_unknown( id: deployment.id, state: DeploymentState::Unknown, container: None, + service: None, update_available: false, }, prev, diff --git a/bin/core/src/monitor/mod.rs b/bin/core/src/monitor/mod.rs index 367921f37..052448701 100644 --- a/bin/core/src/monitor/mod.rs +++ b/bin/core/src/monitor/mod.rs @@ -197,19 +197,19 @@ pub async fn update_cache_for_server(server: &Server, force: bool) { .unwrap_or(&[]); tokio::join!( - resources::update_deployment_cache( + resources::update_server_stack_cache( + server.name.clone(), + resources.stacks, + containers, + images + ), + resources::update_server_deployment_cache( server.name.clone(), resources.deployments, containers, images, &resources.builds, ), - resources::update_stack_cache( - server.name.clone(), - resources.stacks, - containers, - images - ), ); insert_server_status( diff --git a/bin/core/src/monitor/resources.rs b/bin/core/src/monitor/resources.rs index 740df06ef..c89a94ecc 100644 --- a/bin/core/src/monitor/resources.rs +++ b/bin/core/src/monitor/resources.rs @@ -15,9 +15,13 @@ use komodo_client::{ docker::{ container::{ContainerListItem, ContainerStateStatusEnum}, image::ImageListItem, + service::SwarmServiceListItem, + stack::SwarmStackListItem, + task::SwarmTaskListItem, }, komodo_timestamp, stack::{Stack, StackService, StackServiceNames, StackState}, + swarm::SwarmState, user::auto_redeploy_user, }, }; @@ -44,7 +48,87 @@ fn stack_alert_sent_cache() -> &'static AlertCache<(String, String)> { CACHE.get_or_init(Default::default) } -pub async fn update_stack_cache( +pub async fn update_swarm_stack_cache( + stacks: Vec, + swarm_stacks: &[SwarmStackListItem], + swarm_services: &[SwarmServiceListItem], +) { + let stack_status_cache = stack_status_cache(); + for stack in stacks { + let project_name = stack.project_name(false); + let swarm_stack = swarm_stacks + .iter() + .find(|stack| { + stack + .name + .as_ref() + .map(|name| name == &project_name) + .unwrap_or_default() + }) + .cloned(); + let services = extract_services_from_stack(&stack); + let mut services_with_swarm_services = services + .iter() + .map( + |StackServiceNames { + service_name, + image, + .. + }| { + let swarm_service = swarm_services + .iter() + .find(|service| { + service + .name + .as_ref() + .map(|name| name == service_name) + .unwrap_or_default() + }) + .cloned(); + StackService { + service: service_name.clone(), + image: image.clone(), + container: None, + swarm_service, + update_available: false, + } + }, + ) + .collect::>(); + services_with_swarm_services + .sort_by(|a, b| a.service.cmp(&b.service)); + let current_state = swarm_stack + .as_ref() + .map(|stack| match stack.state { + Some(SwarmState::Healthy) => StackState::Running, + Some(SwarmState::Unhealthy) => StackState::Unhealthy, + Some(SwarmState::Unknown) | None => StackState::Unknown, + }) + .unwrap_or(StackState::Down); + let prev_state = stack_status_cache + .get(&stack.id) + .await + .map(|s| s.curr.state); + let status = CachedStackStatus { + id: stack.id.clone(), + state: current_state, + services: services_with_swarm_services, + swarm_stack, + }; + stack_status_cache + .insert( + stack.id, + History { + curr: status, + prev: prev_state, + } + .into(), + ) + .await; + } +} + +pub async fn update_server_stack_cache( server_name: String, stacks: Vec, containers: &[ContainerListItem], @@ -132,6 +216,7 @@ pub async fn update_stack_cache( service: service_name.clone(), image: image.clone(), container, + swarm_service: None, update_available, } }).collect::>(); @@ -155,14 +240,14 @@ pub async fn update_stack_cache( } } - let state = get_stack_state_from_containers( + let current_state = get_stack_state_from_containers( &stack.config.ignore_services, &services, containers, ); if !services_to_update.is_empty() && stack.config.auto_update - && state == StackState::Running + && current_state == StackState::Running && !action_states() .stack .get_or_insert_default(&stack.id) @@ -221,17 +306,25 @@ pub async fn update_stack_cache( } services_with_containers .sort_by(|a, b| a.service.cmp(&b.service)); - let prev = stack_status_cache + let prev_state = stack_status_cache .get(&stack.id) .await .map(|s| s.curr.state); let status = CachedStackStatus { id: stack.id.clone(), - state, + state: current_state, services: services_with_containers, + swarm_stack: None, }; stack_status_cache - .insert(stack.id, History { curr: status, prev }.into()) + .insert( + stack.id, + History { + curr: status, + prev: prev_state, + } + .into(), + ) .await; } } @@ -241,7 +334,75 @@ fn deployment_alert_sent_cache() -> &'static AlertCache { CACHE.get_or_init(Default::default) } -pub async fn update_deployment_cache( +pub async fn update_swarm_deployment_cache( + deployments: Vec, + swarm_services: &[SwarmServiceListItem], + swarm_tasks: &[SwarmTaskListItem], +) { + let deployment_status_cache = deployment_status_cache(); + for deployment in deployments { + let service = swarm_services + .iter() + .find(|service| { + service + .name + .as_ref() + .map(|name| name == &deployment.name) + .unwrap_or_default() + }) + .cloned(); + let prev_state = deployment_status_cache + .get(&deployment.id) + .await + .map(|s| s.curr.state); + let current_state = service + .as_ref() + .map(|service| { + let Some(service_id) = &service.id else { + return DeploymentState::Unknown; + }; + let tasks = swarm_tasks + .iter() + .filter(|task| { + task + .service_id + .as_ref() + .map(|id| id == service_id) + .unwrap_or_default() + }) + .collect::>(); + // If service exists but no tasks, it is unhealthy + if tasks.is_empty() { + return DeploymentState::Unhealthy; + } + for task in tasks { + if task.desired_state != task.state { + return DeploymentState::Unhealthy; + } + } + DeploymentState::Running + }) + .unwrap_or(DeploymentState::NotDeployed); + deployment_status_cache + .insert( + deployment.id.clone(), + History { + curr: CachedDeploymentStatus { + id: deployment.id, + state: current_state, + service, + container: None, + update_available: false, + }, + prev: prev_state, + } + .into(), + ) + .await; + } +} + +pub async fn update_server_deployment_cache( server_name: String, deployments: Vec, containers: &[ContainerListItem], @@ -256,11 +417,11 @@ pub async fn update_deployment_cache( .iter() .find(|container| container.name == deployment.name) .cloned(); - let prev = deployment_status_cache + let prev_state = deployment_status_cache .get(&deployment.id) .await .map(|s| s.curr.state); - let state = container + let current_state = container .as_ref() .map(|c| c.state.into()) .unwrap_or(DeploymentState::NotDeployed); @@ -307,7 +468,7 @@ pub async fn update_deployment_cache( if update_available { if deployment.config.auto_update { - if state == DeploymentState::Running + if current_state == DeploymentState::Running && !action_states() .deployment .get_or_insert_default(&deployment.id) @@ -362,7 +523,7 @@ pub async fn update_deployment_cache( } }); } - } else if state == DeploymentState::Running + } else if current_state == DeploymentState::Running && deployment.config.send_alerts && deployment_alert_sent_cache.contains(&deployment.id) { @@ -404,11 +565,12 @@ pub async fn update_deployment_cache( History { curr: CachedDeploymentStatus { id: deployment.id, - state, + state: current_state, container, + service: None, update_available, }, - prev, + prev: prev_state, } .into(), ) diff --git a/bin/core/src/monitor/swarm.rs b/bin/core/src/monitor/swarm.rs index f7ae804cf..32d834656 100644 --- a/bin/core/src/monitor/swarm.rs +++ b/bin/core/src/monitor/swarm.rs @@ -22,7 +22,12 @@ use tokio::sync::Mutex; use crate::{ config::monitoring_interval, helpers::swarm::swarm_request_custom_timeout, - monitor::UpdateCacheResources, + monitor::{ + UpdateCacheResources, + resources::{ + update_swarm_deployment_cache, update_swarm_stack_cache, + }, + }, state::{CachedSwarmStatus, db_client, swarm_status_cache}, }; @@ -30,17 +35,16 @@ const ADDITIONAL_MS: u128 = 1000; pub fn spawn_swarm_monitoring_loop() { tokio::spawn(async move { - refresh_swarm_cache(komodo_timestamp()).await; + refresh_swarm_cache().await; let interval = monitoring_interval(); loop { - let ts = (wait_until_timelength(interval, ADDITIONAL_MS).await - - ADDITIONAL_MS) as i64; - refresh_swarm_cache(ts).await; + wait_until_timelength(interval, ADDITIONAL_MS).await; + refresh_swarm_cache().await; } }); } -async fn refresh_swarm_cache(_ts: i64) { +async fn refresh_swarm_cache() { let swarms = match find_collect(&db_client().swarms, None, None).await { Ok(swarms) => swarms, @@ -148,7 +152,18 @@ pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) { } } - // TODO: UPDATE STACKS / DEPLOYMENT CACHES + tokio::join!( + update_swarm_stack_cache( + resources.stacks, + &lists.stacks, + &lists.services, + ), + update_swarm_deployment_cache( + resources.deployments, + &lists.services, + &lists.tasks, + ) + ); swarm_status_cache() .insert( diff --git a/bin/core/src/resource/deployment.rs b/bin/core/src/resource/deployment.rs index fb55b74a0..942eef8fb 100644 --- a/bin/core/src/resource/deployment.rs +++ b/bin/core/src/resource/deployment.rs @@ -3,7 +3,7 @@ use database::mungos::mongodb::Collection; use formatting::format_serror; use indexmap::IndexSet; use komodo_client::entities::{ - Operation, ResourceTarget, ResourceTargetVariant, + Operation, ResourceTarget, ResourceTargetVariant, SwarmOrServer, build::Build, deployment::{ Deployment, DeploymentConfig, DeploymentConfigDiff, @@ -19,13 +19,16 @@ use komodo_client::entities::{ update::Update, user::User, }; -use periphery_client::api::container::RemoveContainer; +use periphery_client::api::{ + container::RemoveContainer, swarm::RemoveSwarmServices, +}; use crate::{ config::core_config, helpers::{ empty_or_only_spaces, periphery_client, - query::get_deployment_state, + query::{get_deployment_state, get_swarm_or_server}, + swarm::swarm_request, }, monitor::update_cache_for_server, state::{action_states, db_client, deployment_status_cache}, @@ -115,14 +118,28 @@ impl super::KomodoResource for Deployment { let (image, update_available) = status .as_ref() .and_then(|s| { - s.curr.container.as_ref().map(|c| { - ( - c.image - .clone() - .unwrap_or_else(|| String::from("Unknown")), - s.curr.update_available, - ) - }) + s.curr + .service + .as_ref() + .map(|service| { + ( + service + .image + .clone() + .unwrap_or_else(|| String::from("Unknown")), + s.curr.update_available, + ) + }) + .or_else(|| { + s.curr.container.as_ref().map(|c| { + ( + c.image + .clone() + .unwrap_or_else(|| String::from("Unknown")), + s.curr.update_available, + ) + }) + }) }) .unwrap_or((build_image, false)); DeploymentListItem { @@ -229,6 +246,11 @@ impl super::KomodoResource for Deployment { deployment: &Resource, update: &mut Update, ) -> anyhow::Result<()> { + if deployment.config.swarm_id.is_empty() + && deployment.config.server_id.is_empty() + { + return Ok(()); + } let state = get_deployment_state(&deployment.id) .await .context("Failed to get deployment state")?; @@ -238,65 +260,86 @@ impl super::KomodoResource for Deployment { ) { return Ok(()); } - // container needs to be destroyed - let server = match super::get::( + // container / service needs to be destroyed + let swarm_or_server = match get_swarm_or_server( + &deployment.config.swarm_id, &deployment.config.server_id, ) .await { - Ok(server) => server, + Ok(res) => res, Err(e) => { update.push_error_log( - "Remove Container", + "Remove Container / Service", format_serror( - &e.context(format!( - "failed to retrieve server at {} from db.", - deployment.config.server_id - )) + &e.context( + "Failed to retrieve Swarm / Server from database", + ) .into(), ), ); return Ok(()); } }; - if !server.config.enabled { - // Don't need to - update.push_simple_log( - "Remove Container", - "Skipping container removal, server is disabled.", - ); - return Ok(()); - } - let periphery = match periphery_client(&server).await { - Ok(periphery) => periphery, - Err(e) => { - // This case won't ever happen, as periphery_client only fallible if the server is disabled. - // Leaving it for completeness sake - update.push_error_log( - "Remove Container", - format_serror( - &e.context("Failed to get periphery client").into(), - ), - ); - return Ok(()); - } - }; - match periphery - .request(RemoveContainer { - name: deployment.name.clone(), - signal: deployment.config.termination_signal.into(), - time: deployment.config.termination_timeout.into(), - }) + match swarm_or_server { + SwarmOrServer::Swarm(swarm) => match swarm_request( + &swarm.config.server_ids, + RemoveSwarmServices { + services: vec![deployment.name.clone()], + }, + ) .await - { - Ok(log) => update.logs.push(log), - Err(e) => update.push_error_log( - "Remove Container", - format_serror( - &e.context("Failed to remove container").into(), + { + Ok(log) => update.logs.push(log), + Err(e) => update.push_error_log( + "Remove Service", + format_serror( + &e.context("Failed to remove service").into(), + ), ), - ), - }; + }, + SwarmOrServer::Server(server) => { + if !server.config.enabled { + // Don't need to + update.push_simple_log( + "Remove Container", + "Skipping container removal, server is disabled.", + ); + return Ok(()); + } + let periphery = match periphery_client(&server).await { + Ok(periphery) => periphery, + Err(e) => { + // This case won't ever happen, as periphery_client only fallible if the server is disabled. + // Leaving it for completeness sake + update.push_error_log( + "Remove Container", + format_serror( + &e.context("Failed to get periphery client").into(), + ), + ); + return Ok(()); + } + }; + match periphery + .request(RemoveContainer { + name: deployment.name.clone(), + signal: deployment.config.termination_signal.into(), + time: deployment.config.termination_timeout.into(), + }) + .await + { + Ok(log) => update.logs.push(log), + Err(e) => update.push_error_log( + "Remove Container", + format_serror( + &e.context("Failed to remove container").into(), + ), + ), + }; + } + } + Ok(()) } diff --git a/bin/core/src/resource/stack.rs b/bin/core/src/resource/stack.rs index 4a8366f5d..3b3ba8814 100644 --- a/bin/core/src/resource/stack.rs +++ b/bin/core/src/resource/stack.rs @@ -5,7 +5,7 @@ use indexmap::IndexSet; use komodo_client::{ api::write::RefreshStackCache, entities::{ - Operation, ResourceTarget, ResourceTargetVariant, + Operation, ResourceTarget, ResourceTargetVariant, SwarmOrServer, permission::{PermissionLevel, SpecificPermission}, repo::Repo, resource::Resource, @@ -20,14 +20,21 @@ use komodo_client::{ user::{User, stack_user}, }, }; -use periphery_client::api::compose::ComposeExecution; +use periphery_client::api::{ + compose::ComposeExecution, swarm::RemoveSwarmStacks, +}; use resolver_api::Resolve; use crate::{ api::write::WriteArgs, config::core_config, - helpers::{periphery_client, query::get_stack_state, repo_link}, - monitor::update_cache_for_server, + helpers::{ + periphery_client, + query::{get_stack_state, get_swarm_or_server}, + repo_link, + swarm::swarm_request, + }, + monitor::{update_cache_for_server, update_cache_for_swarm}, state::{ action_states, all_resources_cache, db_client, server_status_cache, stack_status_cache, @@ -138,14 +145,14 @@ impl super::KomodoResource for Stack { // This is only true if it is KNOWN to be true. so other cases are false. let (project_missing, status) = - if stack.config.server_id.is_empty() - || matches!(state, StackState::Down | StackState::Unknown) - { + if matches!(state, StackState::Down | StackState::Unknown) { (false, None) - } else if let Some(status) = server_status_cache() - .get(&stack.config.server_id) - .await - .as_ref() + } else if stack.config.swarm_id.is_empty() + && !stack.config.server_id.is_empty() + && let Some(status) = server_status_cache() + .get(&stack.config.server_id) + .await + .as_ref() { if let Some(docker) = &status.docker { if let Some(project) = docker @@ -177,6 +184,7 @@ impl super::KomodoResource for Stack { services, project_missing, file_contents: !stack.config.file_contents.is_empty(), + swarm_id: stack.config.swarm_id, server_id: stack.config.server_id, linked_repo: stack.config.linked_repo, missing_files: stack.info.missing_files, @@ -239,21 +247,32 @@ impl super::KomodoResource for Stack { format_serror(&e.error.context("The stack cache has failed to refresh. This may be due to a misconfiguration of the Stack").into()) ); }; - if created.config.server_id.is_empty() { + if created.config.swarm_id.is_empty() + && created.config.server_id.is_empty() + { return Ok(()); } - let Ok(server) = super::get::(&created.config.server_id) - .await - .inspect_err(|e| { - warn!( - "Failed to get Server for Stack {} | {e:#}", - created.name - ) - }) - else { + let Ok(swarm_or_server) = get_swarm_or_server( + &created.config.swarm_id, + &created.config.server_id, + ) + .await + .inspect_err(|e| { + warn!( + "Failed to get Swarm or Server for Stack {} | {e:#}", + created.name + ) + }) else { return Ok(()); }; - update_cache_for_server(&server, true).await; + match swarm_or_server { + SwarmOrServer::Swarm(swarm) => { + update_cache_for_swarm(&swarm, true).await; + } + SwarmOrServer::Server(server) => { + update_cache_for_server(&server, true).await; + } + } Ok(()) } @@ -302,65 +321,94 @@ impl super::KomodoResource for Stack { return Ok(()); } // stack needs to be destroyed - let server = - match super::get::(&stack.config.server_id).await { - Ok(server) => server, - Err(e) => { - update.push_error_log( - "destroy stack", - format_serror( - &e.context(format!( - "failed to retrieve server at {} from db.", - stack.config.server_id - )) - .into(), - ), - ); - return Ok(()); - } - }; - - if !server.config.enabled { - update.push_simple_log( - "destroy stack", - "skipping stack destroy, server is disabled.", - ); - return Ok(()); - } - - let periphery = match periphery_client(&server).await { - Ok(periphery) => periphery, + let swarm_or_server = match get_swarm_or_server( + &stack.config.swarm_id, + &stack.config.server_id, + ) + .await + { + Ok(res) => res, Err(e) => { - // This case won't ever happen, as periphery_client only fallible if the server is disabled. - // Leaving it for completeness sake update.push_error_log( - "destroy stack", + "Destroy Stack", format_serror( - &e.context("failed to get periphery client").into(), + &e.context( + "Failed to retrieve Swarm or Server from database.", + ) + .into(), ), ); return Ok(()); } }; - match periphery - .request(ComposeExecution { - project: stack.project_name(false), - command: String::from("down --remove-orphans"), - }) - .await - { - Ok(log) => update.logs.push(log), - Err(e) => update.push_simple_log( - "Failed to destroy stack", - format_serror( - &e.context( - "failed to destroy stack on periphery server before delete", - ) - .into(), - ), - ), - }; + match swarm_or_server { + SwarmOrServer::Swarm(swarm) => { + match swarm_request( + &swarm.config.server_ids, + RemoveSwarmStacks { + stacks: vec![stack.project_name(false)], + detach: true, + }, + ) + .await + { + Ok(log) => update.logs.push(log), + Err(e) => update.push_simple_log( + "Failed to destroy stack", + format_serror( + &e.context( + "Failed to destroy Stack on Swarm before delete", + ) + .into(), + ), + ), + } + } + SwarmOrServer::Server(server) => { + if !server.config.enabled { + update.push_simple_log( + "Destroy Stack", + "Skipping stack destroy, Server is disabled.", + ); + return Ok(()); + } + + let periphery = match periphery_client(&server).await { + Ok(periphery) => periphery, + Err(e) => { + // This case won't ever happen, as periphery_client only fallible if the server is disabled. + // Leaving it for completeness sake + update.push_error_log( + "Destroy Stack", + format_serror( + &e.context("Failed to get periphery client").into(), + ), + ); + return Ok(()); + } + }; + + match periphery + .request(ComposeExecution { + project: stack.project_name(false), + command: String::from("down --remove-orphans"), + }) + .await + { + Ok(log) => update.logs.push(log), + Err(e) => update.push_simple_log( + "Failed to destroy stack", + format_serror( + &e.context( + "failed to destroy stack on periphery server before delete", + ) + .into(), + ), + ), + }; + } + } Ok(()) } diff --git a/bin/core/src/state.rs b/bin/core/src/state.rs index b89f18e0d..3fb3580fa 100644 --- a/bin/core/src/state.rs +++ b/bin/core/src/state.rs @@ -9,6 +9,7 @@ use komodo_client::entities::{ deployment::DeploymentState, docker::{ DockerLists, SwarmLists, container::ContainerListItem, + service::SwarmServiceListItem, stack::SwarmStackListItem, swarm::SwarmInspectInfo, }, procedure::ProcedureState, @@ -141,6 +142,8 @@ pub struct CachedStackStatus { pub state: StackState, /// The services connected to the stack pub services: Vec, + /// Swarm mode only. Associated swarm stack. + pub swarm_stack: Option, } pub type StackStatusCache = @@ -158,6 +161,7 @@ pub struct CachedDeploymentStatus { pub id: String, pub state: DeploymentState, pub container: Option, + pub service: Option, pub update_available: bool, } diff --git a/client/core/rs/src/entities/deployment.rs b/client/core/rs/src/entities/deployment.rs index f60b7889b..f9f59ae37 100644 --- a/client/core/rs/src/entities/deployment.rs +++ b/client/core/rs/src/entities/deployment.rs @@ -168,7 +168,7 @@ pub struct DeploymentConfig { /// Extra args which are interpolated into the /// `docker run` / `docker service create` command, /// and affect the container configuration. - /// + /// /// - Container ref: https://docs.docker.com/reference/cli/docker/container/run/#options /// - Swarm Service ref: https://docs.docker.com/reference/cli/docker/service/create/#options #[serde(default, deserialize_with = "string_list_deserializer")] @@ -379,23 +379,25 @@ pub fn conversions_from_str( pub enum DeploymentState { /// The deployment is currently re/deploying Deploying, - /// Container is running + /// Container / Service is running Running, - /// Container is created but not running + /// Server mode only. Container is created but not running. Created, - /// Container is in restart loop + /// Server mode only. Container is in restart loop Restarting, - /// Container is being removed + /// Server mode only. Container is being removed Removing, - /// Container is paused + /// Server mode only. Container is paused Paused, - /// Container is exited + /// Server mode only. Container is exited Exited, - /// Container is dead + /// Server mode only. Container is dead Dead, - /// The deployment is not deployed (no matching container) + /// Swarm mode only. Some tasks don't match their desired state. + Unhealthy, + /// The deployment is not deployed (no matching Container / Service) NotDeployed, - /// Server not reachable for status + /// Server / Swarm not reachable for status #[default] Unknown, } diff --git a/client/core/rs/src/entities/docker/mod.rs b/client/core/rs/src/entities/docker/mod.rs index 5d3b4d47b..11896fd37 100644 --- a/client/core/rs/src/entities/docker/mod.rs +++ b/client/core/rs/src/entities/docker/mod.rs @@ -33,11 +33,11 @@ pub mod volume; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SwarmLists { pub nodes: Vec, + pub stacks: Vec, pub services: Vec, pub tasks: Vec, - pub secrets: Vec, pub configs: Vec, - pub stacks: Vec, + pub secrets: Vec, } /// Standard docker lists available from a Server. diff --git a/client/core/rs/src/entities/stack.rs b/client/core/rs/src/entities/stack.rs index 14db90339..c6ba3472f 100644 --- a/client/core/rs/src/entities/stack.rs +++ b/client/core/rs/src/entities/stack.rs @@ -20,7 +20,10 @@ use crate::{ option_maybe_string_i64_deserializer, option_string_list_deserializer, string_list_deserializer, }, - entities::{EnvironmentVar, environment_vars_from_str}, + entities::{ + EnvironmentVar, docker::service::SwarmServiceListItem, + environment_vars_from_str, + }, }; use super::{ @@ -133,7 +136,9 @@ pub type StackListItem = ResourceListItem; #[typeshare] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StackListItemInfo { - /// The server that stack is deployed on. + /// The swarm that stack is deployed on, when in Swarm mode. + pub swarm_id: String, + /// The server that stack is deployed on, when in Server mode. pub server_id: String, /// Whether stack is using files on host mode pub files_on_host: bool, @@ -731,8 +736,10 @@ pub struct StackService { pub service: String, /// The service image pub image: String, - /// The container + /// The container (Server mode) pub container: Option, + /// The service (Swarm mode) + pub swarm_service: Option, /// Whether there is an update available for this services image. pub update_available: bool, }