diff --git a/Cargo.lock b/Cargo.lock index 3e294dadd..f715e0e82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "alerter" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "axum 0.7.5", @@ -967,7 +967,7 @@ dependencies = [ [[package]] name = "command" -version = "1.13.0" +version = "1.13.1" dependencies = [ "monitor_client", "run_command", @@ -1351,7 +1351,7 @@ dependencies = [ [[package]] name = "formatting" -version = "1.13.0" +version = "1.13.1" dependencies = [ "serror", ] @@ -1482,7 +1482,7 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "command", @@ -2085,7 +2085,7 @@ dependencies = [ [[package]] name = "logger" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "monitor_client", @@ -2154,7 +2154,7 @@ dependencies = [ [[package]] name = "migrator" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "chrono", @@ -2289,7 +2289,7 @@ dependencies = [ [[package]] name = "monitor_cli" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "clap", @@ -2309,7 +2309,7 @@ dependencies = [ [[package]] name = "monitor_client" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "async_timing_util", @@ -2341,7 +2341,7 @@ dependencies = [ [[package]] name = "monitor_core" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "async_timing_util", @@ -2399,7 +2399,7 @@ dependencies = [ [[package]] name = "monitor_periphery" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "async_timing_util", @@ -2835,7 +2835,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "periphery_client" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "monitor_client", @@ -3987,7 +3987,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "dotenvy", @@ -4594,7 +4594,7 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "update_logger" -version = "1.13.0" +version = "1.13.1" dependencies = [ "anyhow", "logger", diff --git a/Cargo.toml b/Cargo.toml index 7fc7b4e49..8adce717b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["bin/*", "lib/*", "client/core/rs", "client/periphery/rs"] [workspace.package] -version = "1.13.0" +version = "1.13.1" edition = "2021" authors = ["mbecker20 "] license = "GPL-3.0-or-later" @@ -15,7 +15,7 @@ monitor_client = { path = "client/core/rs" } [workspace.dependencies] # LOCAL -monitor_client = "1.13.0" +monitor_client = "1.13.1" periphery_client = { path = "client/periphery/rs" } formatting = { path = "lib/formatting" } command = { path = "lib/command" } diff --git a/bin/core/src/api/execute/stack.rs b/bin/core/src/api/execute/stack.rs index fd0c88fb7..238a5b60f 100644 --- a/bin/core/src/api/execute/stack.rs +++ b/bin/core/src/api/execute/stack.rs @@ -200,16 +200,11 @@ impl Resolve for State { StartStack { stack, service }: StartStack, (user, update): (User, Update), ) -> anyhow::Result { - let no_service = service.is_none(); execute_compose::( &stack, service, &user, - |state| { - if no_service { - state.starting = true - } - }, + |state| state.starting = true, update, (), ) @@ -224,15 +219,12 @@ impl Resolve for State { RestartStack { stack, service }: RestartStack, (user, update): (User, Update), ) -> anyhow::Result { - let no_service = service.is_none(); execute_compose::( &stack, service, &user, |state| { - if no_service { - state.restarting = true; - } + state.restarting = true; }, update, (), @@ -248,16 +240,11 @@ impl Resolve for State { PauseStack { stack, service }: PauseStack, (user, update): (User, Update), ) -> anyhow::Result { - let no_service = service.is_none(); execute_compose::( &stack, service, &user, - |state| { - if no_service { - state.pausing = true - } - }, + |state| state.pausing = true, update, (), ) @@ -272,16 +259,11 @@ impl Resolve for State { UnpauseStack { stack, service }: UnpauseStack, (user, update): (User, Update), ) -> anyhow::Result { - let no_service = service.is_none(); execute_compose::( &stack, service, &user, - |state| { - if no_service { - state.unpausing = true - } - }, + |state| state.unpausing = true, update, (), ) @@ -300,16 +282,11 @@ impl Resolve for State { }: StopStack, (user, update): (User, Update), ) -> anyhow::Result { - let no_service = service.is_none(); execute_compose::( &stack, service, &user, - |state| { - if no_service { - state.stopping = true - } - }, + |state| state.stopping = true, update, stop_time, ) diff --git a/bin/core/src/api/read/stack.rs b/bin/core/src/api/read/stack.rs index a749e2cf2..be7903156 100644 --- a/bin/core/src/api/read/stack.rs +++ b/bin/core/src/api/read/stack.rs @@ -214,6 +214,8 @@ impl Resolve for State { StackState::Paused => res.paused += 1, StackState::Stopped => res.stopped += 1, StackState::Restarting => res.restarting += 1, + StackState::Created => res.created += 1, + StackState::Removing => res.removing += 1, StackState::Dead => res.dead += 1, StackState::Unhealthy => res.unhealthy += 1, StackState::Down => res.down += 1, diff --git a/bin/core/src/helpers/query.rs b/bin/core/src/helpers/query.rs index 78df498d5..e9c95bf7e 100644 --- a/bin/core/src/helpers/query.rs +++ b/bin/core/src/helpers/query.rs @@ -5,13 +5,13 @@ use monitor_client::entities::{ alerter::Alerter, build::Build, builder::Builder, - deployment::{ContainerSummary, Deployment, DeploymentState}, + deployment::{Deployment, DeploymentState}, permission::PermissionLevel, procedure::Procedure, repo::Repo, server::{Server, ServerState}, server_template::ServerTemplate, - stack::{Stack, StackServiceNames, StackState}, + stack::{ComposeProject, Stack, StackState}, sync::ResourceSync, tag::Tag, update::{ResourceTarget, ResourceTargetVariant, Update}, @@ -33,11 +33,6 @@ use crate::{ state::db_client, }; -use super::stack::{ - compose_container_match_regex, - services::extract_services_from_stack, -}; - #[instrument(level = "debug")] // user: Id or username pub async fn get_user(user: &str) -> anyhow::Result { @@ -98,63 +93,51 @@ pub async fn get_deployment_state( } /// Can pass all the containers from the same server -pub fn get_stack_state_from_containers( - services: &[StackServiceNames], - containers: &[ContainerSummary], +pub fn get_stack_state_from_projects( + stack: &Stack, + projects: &[ComposeProject], ) -> StackState { - // first filter the containers to only ones which match the service - let containers = containers.iter().filter(|container| { - services.iter().any(|StackServiceNames { service_name, container_name }| { - match compose_container_match_regex(container_name) - .with_context(|| format!("failed to construct container name matching regex for service {service_name}")) - { - Ok(regex) => regex, - Err(e) => { - warn!("{e:#}"); - return false - } - }.is_match(&container.name) + let project_name = stack.project_name(false); + let Some(status) = projects + .iter() + .find(|project| project.name == project_name) + .and_then(|project| project.status.as_deref()) + else { + return StackState::Down; + }; + let Ok(states) = status + .split(", ") + .filter_map(|state| state.split('(').next()) + .map(|state| { + state.parse::().with_context(|| { + format!("failed to parse stack state entry: {state}") + }) }) - }).collect::>(); - if containers.is_empty() { + .collect::>>() + .inspect_err(|e| warn!("{e:#}")) + else { + return StackState::Unknown; + }; + if states.is_empty() { return StackState::Down; } - if services.len() != containers.len() { + if states.len() > 1 { return StackState::Unhealthy; } - let running = containers - .iter() - .all(|container| container.state == DeploymentState::Running); - if running { - return StackState::Running; + match states[0] { + DeploymentState::Unknown => StackState::Unknown, + DeploymentState::NotDeployed => StackState::Down, + DeploymentState::Created => StackState::Created, + DeploymentState::Restarting => StackState::Restarting, + DeploymentState::Running => StackState::Running, + DeploymentState::Removing => StackState::Removing, + DeploymentState::Paused => StackState::Paused, + DeploymentState::Exited => StackState::Stopped, + DeploymentState::Dead => StackState::Dead, } - let paused = containers - .iter() - .all(|container| container.state == DeploymentState::Paused); - if paused { - return StackState::Paused; - } - let stopped = containers - .iter() - .all(|container| container.state == DeploymentState::Exited); - if stopped { - return StackState::Stopped; - } - let restarting = containers - .iter() - .all(|container| container.state == DeploymentState::Restarting); - if restarting { - return StackState::Restarting; - } - let dead = containers - .iter() - .all(|container| container.state == DeploymentState::Dead); - if dead { - return StackState::Dead; - } - StackState::Unhealthy } +/// Gets stack state fresh from periphery #[instrument(level = "debug")] pub async fn get_stack_state( stack: &Stack, @@ -167,13 +150,11 @@ pub async fn get_stack_state( if status != ServerState::Ok { return Ok(StackState::Unknown); } - let containers = super::periphery_client(&server)? - .request(periphery_client::api::container::GetContainerList {}) + let projects = super::periphery_client(&server)? + .request(periphery_client::api::compose::ListComposeProjects {}) .await?; - let services = extract_services_from_stack(stack, false).await?; - - Ok(get_stack_state_from_containers(&services, &containers)) + Ok(get_stack_state_from_projects(stack, &projects)) } #[instrument(level = "debug")] diff --git a/bin/core/src/monitor/mod.rs b/bin/core/src/monitor/mod.rs index 6fcc9bb3c..415c8fe22 100644 --- a/bin/core/src/monitor/mod.rs +++ b/bin/core/src/monitor/mod.rs @@ -214,7 +214,7 @@ pub async fn update_cache_for_server(server: &Server) { Ok((containers, networks, images, projects)) => { tokio::join!( resources::update_deployment_cache(deployments, &containers), - resources::update_stack_cache(stacks, &containers), + resources::update_stack_cache(stacks, &containers, &projects), ); insert_server_status( server, diff --git a/bin/core/src/monitor/resources.rs b/bin/core/src/monitor/resources.rs index b8b78a535..13b33245c 100644 --- a/bin/core/src/monitor/resources.rs +++ b/bin/core/src/monitor/resources.rs @@ -1,12 +1,12 @@ use anyhow::Context; use monitor_client::entities::{ deployment::{ContainerSummary, Deployment, DeploymentState}, - stack::{Stack, StackService, StackServiceNames}, + stack::{ComposeProject, Stack, StackService, StackServiceNames}, }; use crate::{ helpers::{ - query::get_stack_state_from_containers, + query::get_stack_state_from_projects, stack::{ compose_container_match_regex, services::extract_services_from_stack, @@ -55,6 +55,7 @@ pub async fn update_deployment_cache( pub async fn update_stack_cache( stacks: Vec, containers: &[ContainerSummary], + projects: &[ComposeProject], ) { let stack_status_cache = stack_status_cache(); for stack in stacks { @@ -92,7 +93,7 @@ pub async fn update_stack_cache( .map(|s| s.curr.state); let status = CachedStackStatus { id: stack.id.clone(), - state: get_stack_state_from_containers(&services, containers), + state: get_stack_state_from_projects(&stack, projects), services: services_with_containers, }; stack_status_cache diff --git a/bin/core/src/resource/stack.rs b/bin/core/src/resource/stack.rs index 1213e9de9..e68de3af4 100644 --- a/bin/core/src/resource/stack.rs +++ b/bin/core/src/resource/stack.rs @@ -67,28 +67,32 @@ impl super::MonitorResource for Stack { .map(|service| service.service_name) .collect(); // 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) - { - (false, None) - } else if let Some(status) = server_status_cache() - .get(&stack.config.server_id) - .await - .as_ref() - { - if let Some(projects) = &status.projects { - if let Some(project) = projects.iter().find(|project| project.name == project_name) { - (false, project.status.clone()) + let (project_missing, status) = + if stack.config.server_id.is_empty() + || matches!(state, StackState::Down | StackState::Unknown) + { + (false, None) + } else if let Some(status) = server_status_cache() + .get(&stack.config.server_id) + .await + .as_ref() + { + if let Some(projects) = &status.projects { + if let Some(project) = projects + .iter() + .find(|project| project.name == project_name) + { + (false, project.status.clone()) + } else { + // The project doesn't exist + (true, None) + } } else { - // The project doesn't exist - (true, None) + (false, None) } } else { (false, None) - } - } else { - (false, None) - }; + }; StackListItem { id: stack.id, name: stack.name, diff --git a/client/core/rs/src/api/read/stack.rs b/client/core/rs/src/api/read/stack.rs index e60d33b9d..6c0a9204a 100644 --- a/client/core/rs/src/api/read/stack.rs +++ b/client/core/rs/src/api/read/stack.rs @@ -213,6 +213,10 @@ pub struct GetStacksSummaryResponse { pub restarting: u32, /// The number of stacks with Dead state. pub dead: u32, + /// The number of stacks with Created state. + pub created: u32, + /// The number of stacks with Removing state. + pub removing: u32, /// The number of stacks with Unhealthy state. pub unhealthy: u32, /// The number of stacks with Down state. diff --git a/client/core/rs/src/entities/stack.rs b/client/core/rs/src/entities/stack.rs index 25545a14a..87958e0b8 100644 --- a/client/core/rs/src/entities/stack.rs +++ b/client/core/rs/src/entities/stack.rs @@ -107,10 +107,14 @@ pub enum StackState { Paused, /// All contianers are stopped Stopped, + /// All containers are created + Created, /// All containers are restarting Restarting, /// All containers are dead Dead, + /// All containers are removing + Removing, /// The containers are in a mix of states Unhealthy, /// The stack is not deployed diff --git a/frontend/src/components/resources/build/config.tsx b/frontend/src/components/resources/build/config.tsx index 667266eaa..9208efc56 100644 --- a/frontend/src/components/resources/build/config.tsx +++ b/frontend/src/components/resources/build/config.tsx @@ -113,20 +113,6 @@ export const BuildConfig = ({ }, }, }, - { - label: "Custom Name / Tag", - components: { - image_name: { - description: "Optional. Push the image under a different name", - placeholder: "Custom image name", - }, - image_tag: { - description: - "Optional. Postfix the image version with a custom tag.", - placeholder: "Custom image tag", - }, - }, - }, { label: "Git", components: { @@ -194,6 +180,20 @@ export const BuildConfig = ({ ), }, }, + { + label: "Custom Name / Tag", + components: { + image_name: { + description: "Optional. Push the image under a different name", + placeholder: "Custom image name", + }, + image_tag: { + description: + "Optional. Postfix the image version with a custom tag.", + placeholder: "Custom image tag", + }, + }, + }, { label: "Extra Args", description: ( diff --git a/frontend/src/components/resources/stack/index.tsx b/frontend/src/components/resources/stack/index.tsx index 4a15a5581..dd12f2d65 100644 --- a/frontend/src/components/resources/stack/index.tsx +++ b/frontend/src/components/resources/stack/index.tsx @@ -1,7 +1,14 @@ import { useInvalidate, useLocalStorage, useRead, useWrite } from "@lib/hooks"; import { RequiredResourceComponents } from "@types"; import { Card } from "@ui/card"; -import { FolderGit, Layers, Loader2, RefreshCcw, Server } from "lucide-react"; +import { + FolderGit, + Layers, + Loader2, + NotepadText, + RefreshCcw, + Server, +} from "lucide-react"; import { StackConfig } from "./config"; import { DeleteResource, NewResource, ResourceLink } from "../common"; import { StackTable } from "./table"; @@ -132,6 +139,15 @@ export const StackComponents: RequiredResourceComponents = { } return ; }, + Status: ({ id }) => { + const info = useStack(id)?.info; + if (info?.state !== Types.StackState.Unhealthy) return null; + return ( + info?.status && ( +

{info.status}

+ ) + ); + }, NoConfig: ({ id }) => { const config = useFullStack(id)?.config; if (config?.file_contents || config?.repo) { @@ -148,9 +164,9 @@ export const StackComponents: RequiredResourceComponents = {
- No configuration provided for stack. Cannot get stack state. Either - paste the compose file contents into the UI, or configure a git repo - containing your files. + No configuration provided for stack. Cannot get stack state. + Either paste the compose file contents into the UI, or configure a + git repo containing your files.
@@ -286,21 +302,32 @@ export const StackComponents: RequiredResourceComponents = { }, Info: { - Repo: ({ id }) => { - const repo = useStack(id)?.info.repo; + Contents: ({ id }) => { + const config = useFullStack(id)?.config; + const file_contents = config?.file_contents; + if (file_contents) { + return ( +
+ + Local +
+ ); + } return (
- {repo} + {config?.repo}
); }, // Branch: ({ id }) => { - // const branch = useStack(id)?.info.branch; + // const config = useFullStack(id)?.config; + // const file_contents = config?.file_contents; + // if (file_contents || !config?.branch) return null // return ( //
- // - // {branch} + // + // {config.branch} //
// ); // },