mirror of
https://github.com/moghtech/komodo.git
synced 2026-03-16 13:40:55 -05:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64d13666a9 | ||
|
|
2b2f354a3c |
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -41,7 +41,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "alerter"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -847,7 +847,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cache"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"tokio",
|
||||
@@ -951,7 +951,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "command"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"komodo_client",
|
||||
"run_command",
|
||||
@@ -1363,7 +1363,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "environment_file"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@@ -1447,7 +1447,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "formatting"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"serror",
|
||||
]
|
||||
@@ -1579,7 +1579,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "git"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cache",
|
||||
@@ -2200,7 +2200,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_cli"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2216,7 +2216,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_client"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2247,7 +2247,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_core"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2307,7 +2307,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_periphery"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2395,7 +2395,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "logger"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -3101,7 +3101,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -4876,7 +4876,7 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "update_logger"
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
|
||||
@@ -9,7 +9,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.16.9"
|
||||
version = "1.16.10"
|
||||
edition = "2021"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
@@ -100,6 +100,16 @@ pub async fn send_alert(
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
format!("⬆ Deployment **{name}** has an update available\nserver: **{server_name}**\nimage: **{image}**\n{link}")
|
||||
}
|
||||
AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
format!("⬆ Deployment **{name}** was updated automatically ⏫\nserver: **{server_name}**\nimage: **{image}**\n{link}")
|
||||
}
|
||||
AlertData::StackStateChange {
|
||||
id,
|
||||
name,
|
||||
@@ -123,6 +133,19 @@ pub async fn send_alert(
|
||||
let link = resource_link(ResourceTargetVariant::Stack, id);
|
||||
format!("⬆ Stack **{name}** has an update available\nserver: **{server_name}**\nservice: **{service}**\nimage: **{image}**\n{link}")
|
||||
}
|
||||
AlertData::StackAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
images,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let images_label =
|
||||
if images.len() > 1 { "images" } else { "image" };
|
||||
let images = images.join(", ");
|
||||
format!("⬆ Stack **{name}** was updated automatically ⏫\nserver: **{server_name}**\n{images_label}: **{images}**\n{link}")
|
||||
}
|
||||
AlertData::AwsBuilderTerminationFailed {
|
||||
instance_id,
|
||||
message,
|
||||
|
||||
@@ -216,6 +216,27 @@ pub async fn send_alert(
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
server_name,
|
||||
server_id: _server_id,
|
||||
image,
|
||||
} => {
|
||||
let text =
|
||||
format!("⬆ Deployment *{name}* was updated automatically ⏫");
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!(
|
||||
"server: *{server_name}*\nimage: *{image}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Deployment,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::StackStateChange {
|
||||
name,
|
||||
server_name,
|
||||
@@ -259,6 +280,30 @@ pub async fn send_alert(
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::StackAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
server_name,
|
||||
server_id: _server_id,
|
||||
images,
|
||||
} => {
|
||||
let text =
|
||||
format!("⬆ Stack *{name}* was updated automatically ⏫");
|
||||
let images_label =
|
||||
if images.len() > 1 { "images" } else { "image" };
|
||||
let images = images.join(", ");
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!(
|
||||
"server: *{server_name}*\n{images_label}: *{images}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Stack,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::AwsBuilderTerminationFailed {
|
||||
instance_id,
|
||||
message,
|
||||
|
||||
@@ -30,10 +30,7 @@ use crate::{
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
resource,
|
||||
stack::{
|
||||
execute::execute_compose, get_stack_and_server,
|
||||
services::extract_services_into_res,
|
||||
},
|
||||
stack::{execute::execute_compose, get_stack_and_server},
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -163,6 +160,7 @@ impl Resolve<DeployStack, (User, Update)> for State {
|
||||
let ComposeUpResponse {
|
||||
logs,
|
||||
deployed,
|
||||
services,
|
||||
file_contents,
|
||||
missing_files,
|
||||
remote_errors,
|
||||
@@ -181,24 +179,11 @@ impl Resolve<DeployStack, (User, Update)> for State {
|
||||
update.logs.extend(logs);
|
||||
|
||||
let update_info = async {
|
||||
let latest_services = if !file_contents.is_empty() {
|
||||
let mut services = Vec::new();
|
||||
for contents in &file_contents {
|
||||
if let Err(e) = extract_services_into_res(
|
||||
&stack.project_name(true),
|
||||
&contents.contents,
|
||||
&mut services,
|
||||
) {
|
||||
update.push_error_log(
|
||||
"extract services",
|
||||
format_serror(&e.context(format!("Failed to extract stack services for compose file path {}. Things probably won't work correctly", contents.path)).into())
|
||||
);
|
||||
}
|
||||
}
|
||||
services
|
||||
} else {
|
||||
let latest_services = if services.is_empty() {
|
||||
// maybe better to do something else here for services.
|
||||
stack.info.latest_services.clone()
|
||||
} else {
|
||||
services
|
||||
};
|
||||
|
||||
// This ensures to get the latest project name,
|
||||
|
||||
@@ -103,11 +103,12 @@ pub async fn update_deployment_cache(
|
||||
.busy()
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let deployment = deployment.name.clone();
|
||||
let id = deployment.id.clone();
|
||||
let server_name = server_name.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = execute::inner_handler(
|
||||
match execute::inner_handler(
|
||||
ExecuteRequest::Deploy(Deploy {
|
||||
deployment: deployment.clone(),
|
||||
deployment: deployment.name.clone(),
|
||||
stop_time: None,
|
||||
stop_signal: None,
|
||||
}),
|
||||
@@ -115,9 +116,37 @@ pub async fn update_deployment_cache(
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Failed to auto update Deployment {deployment} | {e:#}"
|
||||
)
|
||||
Ok(_) => {
|
||||
let ts = komodo_timestamp();
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: true,
|
||||
resolved_ts: ts.into(),
|
||||
level: SeverityLevel::Ok,
|
||||
target: ResourceTarget::Deployment(id.clone()),
|
||||
data: AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name: deployment.name,
|
||||
server_name,
|
||||
server_id: deployment.config.server_id,
|
||||
image,
|
||||
},
|
||||
};
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record DeploymentAutoUpdated to db | {e:#}"
|
||||
);
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Failed to auto update Deployment {} | {e:#}",
|
||||
deployment.name
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -152,7 +181,7 @@ pub async fn update_deployment_cache(
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record Deployment update avaialable to db | {e:#}"
|
||||
"Failed to record DeploymentImageUpdateAvailable to db | {e:#}"
|
||||
);
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
@@ -257,7 +286,7 @@ pub async fn update_stack_cache(
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record Stack update avaialable to db | {e:#}"
|
||||
"Failed to record StackImageUpdateAvailable to db | {e:#}"
|
||||
);
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
@@ -271,21 +300,31 @@ pub async fn update_stack_cache(
|
||||
}
|
||||
StackService {
|
||||
service: service_name.clone(),
|
||||
image: image.to_string(),
|
||||
image: image.clone(),
|
||||
container,
|
||||
update_available,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
let update_available =
|
||||
services_with_containers.iter().any(|service| {
|
||||
service.update_available
|
||||
// Only consider running services with available updates
|
||||
&& service
|
||||
.container
|
||||
.as_ref()
|
||||
.map(|c| c.state == ContainerStateStatusEnum::Running)
|
||||
.unwrap_or_default()
|
||||
});
|
||||
|
||||
let mut update_available = false;
|
||||
let mut images_with_update = Vec::new();
|
||||
|
||||
for service in services_with_containers.iter() {
|
||||
if service.update_available {
|
||||
images_with_update.push(service.image.clone());
|
||||
// Only allow it to actually trigger an auto update deploy
|
||||
// if the service is running.
|
||||
if service
|
||||
.container
|
||||
.as_ref()
|
||||
.map(|c| c.state == ContainerStateStatusEnum::Running)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
update_available = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state = get_stack_state_from_containers(
|
||||
&stack.config.ignore_services,
|
||||
&services,
|
||||
@@ -301,11 +340,12 @@ pub async fn update_stack_cache(
|
||||
.busy()
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let stack = stack.name.clone();
|
||||
let id = stack.id.clone();
|
||||
let server_name = server_name.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = execute::inner_handler(
|
||||
match execute::inner_handler(
|
||||
ExecuteRequest::DeployStack(DeployStack {
|
||||
stack: stack.clone(),
|
||||
stack: stack.name.clone(),
|
||||
service: None,
|
||||
stop_time: None,
|
||||
}),
|
||||
@@ -313,7 +353,34 @@ pub async fn update_stack_cache(
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("Failed auto update Stack {stack} | {e:#}")
|
||||
Ok(_) => {
|
||||
let ts = komodo_timestamp();
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: true,
|
||||
resolved_ts: ts.into(),
|
||||
level: SeverityLevel::Ok,
|
||||
target: ResourceTarget::Stack(id.clone()),
|
||||
data: AlertData::StackAutoUpdated {
|
||||
id,
|
||||
name: stack.name.clone(),
|
||||
server_name,
|
||||
server_id: stack.config.server_id,
|
||||
images: images_with_update,
|
||||
},
|
||||
};
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record StackAutoUpdated to db | {e:#}"
|
||||
);
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed auto update Stack {} | {e:#}", stack.name)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,8 +5,14 @@ use command::run_komodo_command;
|
||||
use formatting::format_serror;
|
||||
use git::environment;
|
||||
use komodo_client::entities::{
|
||||
all_logs_success, environment_vars_from_str, stack::Stack,
|
||||
to_komodo_name, update::Log, CloneArgs, FileContents,
|
||||
all_logs_success, environment_vars_from_str,
|
||||
stack::{
|
||||
ComposeFile, ComposeService, ComposeServiceDeploy, Stack,
|
||||
StackServiceNames,
|
||||
},
|
||||
to_komodo_name,
|
||||
update::Log,
|
||||
CloneArgs, FileContents,
|
||||
};
|
||||
use periphery_client::api::{
|
||||
compose::ComposeUpResponse,
|
||||
@@ -150,7 +156,66 @@ pub async fn compose_up(
|
||||
output
|
||||
});
|
||||
|
||||
// Build images before destroying to minimize downtime.
|
||||
// Uses 'docker compose config' command to extract services (including image)
|
||||
// after performing interpolation
|
||||
{
|
||||
let command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file}{additional_env_files} config --format json",
|
||||
);
|
||||
let config_log = run_komodo_command(
|
||||
"compose build",
|
||||
run_directory.as_ref(),
|
||||
command,
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
if !config_log.success {
|
||||
res.logs.push(config_log);
|
||||
return Err(anyhow!(
|
||||
"Failed to validate compose files, stopping the run."
|
||||
));
|
||||
}
|
||||
let compose =
|
||||
serde_json::from_str::<ComposeFile>(&config_log.stdout)
|
||||
.context("Failed to parse compose contents")?;
|
||||
for (
|
||||
service_name,
|
||||
ComposeService {
|
||||
container_name,
|
||||
deploy,
|
||||
image,
|
||||
},
|
||||
) in compose.services
|
||||
{
|
||||
let image = image.unwrap_or_default();
|
||||
match deploy {
|
||||
Some(ComposeServiceDeploy {
|
||||
replicas: Some(replicas),
|
||||
}) if replicas > 1 => {
|
||||
for i in 1..1 + replicas {
|
||||
res.services.push(StackServiceNames {
|
||||
container_name: format!(
|
||||
"{project_name}-{service_name}-{i}"
|
||||
),
|
||||
service_name: format!("{service_name}-{i}"),
|
||||
image: image.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
res.services.push(StackServiceNames {
|
||||
container_name: container_name.unwrap_or_else(|| {
|
||||
format!("{project_name}-{service_name}")
|
||||
}),
|
||||
service_name,
|
||||
image,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build images before deploying.
|
||||
// If this fails, do not continue.
|
||||
if stack.config.run_build {
|
||||
let build_extra_args =
|
||||
@@ -198,7 +263,7 @@ pub async fn compose_up(
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Pull images before deploying
|
||||
if stack.config.auto_pull {
|
||||
// Pull images before destroying to minimize downtime.
|
||||
// If this fails, do not continue.
|
||||
|
||||
@@ -158,6 +158,20 @@ pub enum AlertData {
|
||||
image: String,
|
||||
},
|
||||
|
||||
/// A Deployment has an image update available
|
||||
DeploymentAutoUpdated {
|
||||
/// The id of the deployment
|
||||
id: String,
|
||||
/// The name of the deployment
|
||||
name: String,
|
||||
/// The server id of server that the deployment is on
|
||||
server_id: String,
|
||||
/// The server name
|
||||
server_name: String,
|
||||
/// The updated image
|
||||
image: String,
|
||||
},
|
||||
|
||||
/// A stack's state has changed unexpectedly.
|
||||
StackStateChange {
|
||||
/// The id of the stack
|
||||
@@ -190,6 +204,20 @@ pub enum AlertData {
|
||||
image: String,
|
||||
},
|
||||
|
||||
/// A Stack was auto updated
|
||||
StackAutoUpdated {
|
||||
/// The id of the stack
|
||||
id: String,
|
||||
/// The name of the stack
|
||||
name: String,
|
||||
/// The server id of server that the stack is on
|
||||
server_id: String,
|
||||
/// The server name
|
||||
server_name: String,
|
||||
/// One or more images that were updated
|
||||
images: Vec<String>,
|
||||
},
|
||||
|
||||
/// An AWS builder failed to terminate.
|
||||
AwsBuilderTerminationFailed {
|
||||
/// The id of the aws instance which failed to terminate
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "komodo_client",
|
||||
"version": "1.16.9",
|
||||
"version": "1.16.10",
|
||||
"description": "Komodo client package",
|
||||
"homepage": "https://komo.do",
|
||||
"main": "dist/lib.js",
|
||||
|
||||
@@ -786,8 +786,10 @@ export interface DeploymentConfig {
|
||||
/** Whether to poll for any updates to the image. */
|
||||
poll_for_updates?: boolean;
|
||||
/**
|
||||
* Whether to automatically redeploy when a
|
||||
* newer image is found.
|
||||
* Whether to automatically redeploy when
|
||||
* newer a image is found. Will implicitly
|
||||
* enable `poll_for_updates`, you don't need to
|
||||
* enable both.
|
||||
*/
|
||||
auto_update?: boolean;
|
||||
/** Whether to send ContainerStateChange alerts for this deployment. */
|
||||
@@ -1000,6 +1002,19 @@ export type AlertData =
|
||||
server_name: string;
|
||||
/** The image with update */
|
||||
image: string;
|
||||
}}
|
||||
/** A Deployment has an image update available */
|
||||
| { type: "DeploymentAutoUpdated", data: {
|
||||
/** The id of the deployment */
|
||||
id: string;
|
||||
/** The name of the deployment */
|
||||
name: string;
|
||||
/** The server id of server that the deployment is on */
|
||||
server_id: string;
|
||||
/** The server name */
|
||||
server_name: string;
|
||||
/** The updated image */
|
||||
image: string;
|
||||
}}
|
||||
/** A stack's state has changed unexpectedly. */
|
||||
| { type: "StackStateChange", data: {
|
||||
@@ -1030,6 +1045,19 @@ export type AlertData =
|
||||
service: string;
|
||||
/** The image with update */
|
||||
image: string;
|
||||
}}
|
||||
/** A Stack was auto updated */
|
||||
| { type: "StackAutoUpdated", data: {
|
||||
/** The id of the stack */
|
||||
id: string;
|
||||
/** The name of the stack */
|
||||
name: string;
|
||||
/** The server id of server that the stack is on */
|
||||
server_id: string;
|
||||
/** The server name */
|
||||
server_name: string;
|
||||
/** One or more images that were updated */
|
||||
images: string[];
|
||||
}}
|
||||
/** An AWS builder failed to terminate. */
|
||||
| { type: "AwsBuilderTerminationFailed", data: {
|
||||
@@ -1549,8 +1577,10 @@ export interface StackConfig {
|
||||
/** Whether to poll for any updates to the images. */
|
||||
poll_for_updates?: boolean;
|
||||
/**
|
||||
* Whether to automatically redeploy when a
|
||||
* newer images are found.
|
||||
* Whether to automatically redeploy when
|
||||
* newer images are found. Will implicitly
|
||||
* enable `poll_for_updates`, you don't need to
|
||||
* enable both.
|
||||
*/
|
||||
auto_update?: boolean;
|
||||
/** Whether to run `docker compose down` before `compose up`. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use komodo_client::entities::{
|
||||
stack::{ComposeProject, Stack},
|
||||
stack::{ComposeProject, Stack, StackServiceNames},
|
||||
update::Log,
|
||||
FileContents, SearchCombinator,
|
||||
};
|
||||
@@ -167,8 +167,13 @@ pub struct ComposeUpResponse {
|
||||
pub missing_files: Vec<String>,
|
||||
/// The logs produced by the deploy
|
||||
pub logs: Vec<Log>,
|
||||
/// whether stack was successfully deployed
|
||||
/// Whether stack was successfully deployed
|
||||
pub deployed: bool,
|
||||
/// The stack services.
|
||||
///
|
||||
/// Note. The "image" is after interpolation.
|
||||
#[serde(default)]
|
||||
pub services: Vec<StackServiceNames>,
|
||||
/// The deploy compose file contents if they could be acquired, or empty vec.
|
||||
pub file_contents: Vec<FileContents>,
|
||||
/// The error in getting remote file contents at the path, or null
|
||||
|
||||
44
frontend/public/client/types.d.ts
vendored
44
frontend/public/client/types.d.ts
vendored
@@ -888,8 +888,10 @@ export interface DeploymentConfig {
|
||||
/** Whether to poll for any updates to the image. */
|
||||
poll_for_updates?: boolean;
|
||||
/**
|
||||
* Whether to automatically redeploy when a
|
||||
* newer image is found.
|
||||
* Whether to automatically redeploy when
|
||||
* newer a image is found. Will implicitly
|
||||
* enable `poll_for_updates`, you don't need to
|
||||
* enable both.
|
||||
*/
|
||||
auto_update?: boolean;
|
||||
/** Whether to send ContainerStateChange alerts for this deployment. */
|
||||
@@ -1107,6 +1109,22 @@ export type AlertData =
|
||||
image: string;
|
||||
};
|
||||
}
|
||||
/** A Deployment has an image update available */
|
||||
| {
|
||||
type: "DeploymentAutoUpdated";
|
||||
data: {
|
||||
/** The id of the deployment */
|
||||
id: string;
|
||||
/** The name of the deployment */
|
||||
name: string;
|
||||
/** The server id of server that the deployment is on */
|
||||
server_id: string;
|
||||
/** The server name */
|
||||
server_name: string;
|
||||
/** The updated image */
|
||||
image: string;
|
||||
};
|
||||
}
|
||||
/** A stack's state has changed unexpectedly. */
|
||||
| {
|
||||
type: "StackStateChange";
|
||||
@@ -1143,6 +1161,22 @@ export type AlertData =
|
||||
image: string;
|
||||
};
|
||||
}
|
||||
/** A Stack was auto updated */
|
||||
| {
|
||||
type: "StackAutoUpdated";
|
||||
data: {
|
||||
/** The id of the stack */
|
||||
id: string;
|
||||
/** The name of the stack */
|
||||
name: string;
|
||||
/** The server id of server that the stack is on */
|
||||
server_id: string;
|
||||
/** The server name */
|
||||
server_name: string;
|
||||
/** One or more images that were updated */
|
||||
images: string[];
|
||||
};
|
||||
}
|
||||
/** An AWS builder failed to terminate. */
|
||||
| {
|
||||
type: "AwsBuilderTerminationFailed";
|
||||
@@ -1638,8 +1672,10 @@ export interface StackConfig {
|
||||
/** Whether to poll for any updates to the images. */
|
||||
poll_for_updates?: boolean;
|
||||
/**
|
||||
* Whether to automatically redeploy when a
|
||||
* newer images are found.
|
||||
* Whether to automatically redeploy when
|
||||
* newer images are found. Will implicitly
|
||||
* enable `poll_for_updates`, you don't need to
|
||||
* enable both.
|
||||
*/
|
||||
auto_update?: boolean;
|
||||
/** Whether to run `docker compose down` before `compose up`. */
|
||||
|
||||
@@ -1164,30 +1164,20 @@ export const RenameResource = ({
|
||||
const [name, set] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="w-full">
|
||||
Rename{" "}
|
||||
{type === "ServerTemplate"
|
||||
? "Template"
|
||||
: type === "ResourceSync"
|
||||
? "Sync"
|
||||
: type}
|
||||
</div>
|
||||
<div className="flex gap-4 w-full justify-end">
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => set(e.target.value)}
|
||||
className="w-96"
|
||||
placeholder="Enter new name"
|
||||
/>
|
||||
<ConfirmButton
|
||||
title="Rename"
|
||||
icon={<Pen className="w-4 h-4" />}
|
||||
disabled={!name || isPending}
|
||||
loading={isPending}
|
||||
onClick={() => mutate({ id, name })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 w-full justify-end flex-wrap">
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => set(e.target.value)}
|
||||
className="w-96"
|
||||
placeholder="Enter new name"
|
||||
/>
|
||||
<ConfirmButton
|
||||
title="Rename"
|
||||
icon={<Pen className="w-4 h-4" />}
|
||||
disabled={!name || isPending}
|
||||
loading={isPending}
|
||||
onClick={() => mutate({ id, name })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as prettier from "prettier/standalone";
|
||||
import * as pluginTypescript from "prettier/plugins/typescript";
|
||||
import * as pluginEstree from "prettier/plugins/estree";
|
||||
import * as pluginYaml from "prettier/plugins/yaml";
|
||||
import { useWindowDimensions } from "@lib/hooks";
|
||||
|
||||
const MIN_EDITOR_HEIGHT = 56;
|
||||
// const MAX_EDITOR_HEIGHT = 500;
|
||||
@@ -39,6 +40,7 @@ export const MonacoEditor = ({
|
||||
minHeight?: number;
|
||||
className?: string;
|
||||
}) => {
|
||||
const dimensions = useWindowDimensions();
|
||||
const [editor, setEditor] =
|
||||
useState<monaco.editor.IStandaloneCodeEditor | null>(null);
|
||||
|
||||
@@ -100,9 +102,13 @@ export const MonacoEditor = ({
|
||||
const contentHeight = line_count * 18 + 30;
|
||||
const containerNode = editor.getContainerDomNode();
|
||||
|
||||
containerNode.style.height = `${Math.max(
|
||||
Math.ceil(contentHeight),
|
||||
minHeight ?? MIN_EDITOR_HEIGHT
|
||||
// containerNode.style.height = `${Math.max(
|
||||
// Math.ceil(contentHeight),
|
||||
// minHeight ?? MIN_EDITOR_HEIGHT
|
||||
// )}px`;
|
||||
containerNode.style.height = `${Math.min(
|
||||
Math.max(Math.ceil(contentHeight), minHeight ?? MIN_EDITOR_HEIGHT),
|
||||
Math.floor(dimensions.height * (3 / 5))
|
||||
)}px`;
|
||||
}, [editor, line_count]);
|
||||
|
||||
@@ -116,7 +122,7 @@ export const MonacoEditor = ({
|
||||
|
||||
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
minimap: { enabled: false },
|
||||
scrollbar: { alwaysConsumeMouseWheel: false },
|
||||
// scrollbar: { alwaysConsumeMouseWheel: false },
|
||||
scrollBeyondLastLine: false,
|
||||
folding: false,
|
||||
automaticLayout: true,
|
||||
|
||||
@@ -10,12 +10,14 @@ const ALERT_TYPES: Types.AlertData["type"][] = [
|
||||
"ServerCpu",
|
||||
"ServerMem",
|
||||
"ServerDisk",
|
||||
// State change
|
||||
"ContainerStateChange",
|
||||
// Stack
|
||||
"StackStateChange",
|
||||
// Updates
|
||||
"DeploymentImageUpdateAvailable",
|
||||
"StackImageUpdateAvailable",
|
||||
"StackAutoUpdated",
|
||||
// Deployment
|
||||
"ContainerStateChange",
|
||||
"DeploymentImageUpdateAvailable",
|
||||
"DeploymentAutoUpdated",
|
||||
// Misc
|
||||
"AwsBuilderTerminationFailed",
|
||||
"ResourceSyncPendingUpdates",
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
AddExtraArgMenu,
|
||||
ConfigItem,
|
||||
ConfigList,
|
||||
ConfigSwitch,
|
||||
InputList,
|
||||
} from "@components/config/util";
|
||||
import { ImageConfig } from "./components/image";
|
||||
@@ -46,6 +47,7 @@ export const DeploymentConfig = ({
|
||||
|
||||
const network = update.network ?? config.network;
|
||||
const hide_ports = network === "host" || network === "none";
|
||||
const auto_update = update.auto_update ?? config.auto_update ?? false;
|
||||
|
||||
const disabled = global_disabled || perms !== Types.PermissionLevel.Write;
|
||||
|
||||
@@ -229,9 +231,18 @@ export const DeploymentConfig = ({
|
||||
},
|
||||
{
|
||||
label: "Auto Update",
|
||||
hidden: (update.image ?? config.image)?.type === "Build",
|
||||
components: {
|
||||
poll_for_updates: !(update.auto_update ?? config.auto_update) && {
|
||||
description: "Check for updates to the image on an interval.",
|
||||
poll_for_updates: (poll, set) => {
|
||||
return (
|
||||
<ConfigSwitch
|
||||
label="Poll for Updates"
|
||||
description="Check for updates to the image on an interval."
|
||||
value={auto_update || poll}
|
||||
onChange={(poll_for_updates) => set({ poll_for_updates })}
|
||||
disabled={disabled || auto_update}
|
||||
/>
|
||||
);
|
||||
},
|
||||
auto_update: {
|
||||
description: "Trigger a redeploy if a newer image is found.",
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
AccountSelectorConfig,
|
||||
ConfigItem,
|
||||
ConfigList,
|
||||
ConfigSwitch,
|
||||
ProviderSelectorConfig,
|
||||
WebhookBuilder,
|
||||
} from "@components/config/util";
|
||||
@@ -92,7 +93,7 @@ export const ResourceSyncConfig = ({
|
||||
const integration = getWebhookIntegration(integrations, git_provider);
|
||||
|
||||
const mode = getSyncMode(update, config);
|
||||
const managed = update.managed ?? config.managed;
|
||||
const managed = update.managed ?? config.managed ?? false;
|
||||
|
||||
const setMode = (mode: SyncMode) => {
|
||||
if (mode === "Files On Server") {
|
||||
@@ -170,10 +171,16 @@ export const ResourceSyncConfig = ({
|
||||
const general_common: ConfigComponent<Types.ResourceSyncConfig> = {
|
||||
label: "General",
|
||||
components: {
|
||||
delete: !managed && {
|
||||
label: "Delete Unmatched Resources",
|
||||
description:
|
||||
"Executions will delete any resources not found in the resource files. Only use this when using one sync for everything.",
|
||||
delete: (delete_mode, set) => {
|
||||
return (
|
||||
<ConfigSwitch
|
||||
label="Delete Unmatched Resources"
|
||||
description="Executions will delete any resources not found in the resource files. Only use this when using one sync for everything."
|
||||
value={managed || delete_mode}
|
||||
onChange={(delete_mode) => set({ delete: delete_mode })}
|
||||
disabled={disabled || managed}
|
||||
/>
|
||||
);
|
||||
},
|
||||
managed: {
|
||||
label: "Managed",
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
AddExtraArgMenu,
|
||||
ConfigItem,
|
||||
ConfigList,
|
||||
ConfigSwitch,
|
||||
InputList,
|
||||
ProviderSelectorConfig,
|
||||
SystemCommand,
|
||||
@@ -193,6 +194,8 @@ export const StackConfig = ({
|
||||
},
|
||||
};
|
||||
|
||||
const auto_update = update.auto_update ?? config.auto_update ?? false;
|
||||
|
||||
const general_common: ConfigComponent<Types.StackConfig>[] = [
|
||||
{
|
||||
label: "Environment",
|
||||
@@ -239,8 +242,16 @@ export const StackConfig = ({
|
||||
{
|
||||
label: "Auto Update",
|
||||
components: {
|
||||
poll_for_updates: !(update.auto_update ?? config.auto_update) && {
|
||||
description: "Check for updates to the image on an interval.",
|
||||
poll_for_updates: (poll, set) => {
|
||||
return (
|
||||
<ConfigSwitch
|
||||
label="Poll for Updates"
|
||||
description="Check for updates to the image on an interval."
|
||||
value={auto_update || poll}
|
||||
onChange={(poll_for_updates) => set({ poll_for_updates })}
|
||||
disabled={disabled || auto_update}
|
||||
/>
|
||||
);
|
||||
},
|
||||
auto_update: {
|
||||
description: "Trigger a redeploy if a newer image is found.",
|
||||
|
||||
@@ -463,3 +463,22 @@ const WEBHOOK_ID_OR_NAME_ATOM = atomWithStorage<WebhookIdOrName>(
|
||||
export const useWebhookIdOrName = () => {
|
||||
return useAtom<WebhookIdOrName>(WEBHOOK_ID_OR_NAME_ATOM);
|
||||
};
|
||||
|
||||
export type Dimensions = { width: number; height: number };
|
||||
export const useWindowDimensions = () => {
|
||||
const [dimensions, setDimensions] = useState<Dimensions>({ width: 0, height: 0 });
|
||||
useEffect(() => {
|
||||
const callback = () => {
|
||||
setDimensions({
|
||||
width: window.screen.availWidth,
|
||||
height: window.screen.availHeight,
|
||||
});
|
||||
}
|
||||
callback();
|
||||
window.addEventListener("resize", callback);
|
||||
return () => {
|
||||
window.removeEventListener("resize", callback);
|
||||
}
|
||||
}, []);
|
||||
return dimensions;
|
||||
}
|
||||
@@ -27,8 +27,12 @@ import { ResourceSelector } from "@components/resources/common";
|
||||
|
||||
const ALERT_TYPES_BY_RESOURCE: { [key: string]: Types.AlertData["type"][] } = {
|
||||
Server: ["ServerUnreachable", "ServerCpu", "ServerMem", "ServerDisk"],
|
||||
Stack: ["StackStateChange"],
|
||||
Deployment: ["ContainerStateChange"],
|
||||
Stack: ["StackStateChange", "StackImageUpdateAvailable", "StackAutoUpdated"],
|
||||
Deployment: [
|
||||
"ContainerStateChange",
|
||||
"DeploymentImageUpdateAvailable",
|
||||
"DeploymentAutoUpdated",
|
||||
],
|
||||
Build: ["BuildFailed"],
|
||||
Repo: ["RepoBuildFailed"],
|
||||
ResourceSync: ["ResourceSyncPendingUpdates"],
|
||||
@@ -64,7 +68,7 @@ export const AlertsPage = () => {
|
||||
});
|
||||
|
||||
const alert_types: string[] = type
|
||||
? ALERT_TYPES_BY_RESOURCE[type] ?? FALLBACK_ALERT_TYPES
|
||||
? (ALERT_TYPES_BY_RESOURCE[type] ?? FALLBACK_ALERT_TYPES)
|
||||
: FALLBACK_ALERT_TYPES;
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user