Compare commits

..
9 changed files with 158 additions and 97 deletions
Generated
+21 -21
View File
@@ -1131,7 +1131,7 @@ dependencies = [
[[package]]
name = "command"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"komodo_client",
"shlex",
@@ -1462,7 +1462,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "database"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"async-compression",
@@ -1732,7 +1732,7 @@ dependencies = [
[[package]]
name = "encoding"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"bytes",
@@ -1774,7 +1774,7 @@ dependencies = [
[[package]]
name = "environment"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"formatting",
@@ -1903,7 +1903,7 @@ dependencies = [
[[package]]
name = "formatting"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"mogh_error",
]
@@ -2069,7 +2069,7 @@ dependencies = [
[[package]]
name = "git"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"command",
@@ -2663,7 +2663,7 @@ dependencies = [
[[package]]
name = "interpolate"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"komodo_client",
@@ -2772,9 +2772,9 @@ dependencies = [
[[package]]
name = "jsonwebtoken"
version = "10.2.0"
version = "10.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c76e1c7d7df3e34443b3621b459b066a7b79644f059fc8b2db7070c825fd417e"
checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1"
dependencies = [
"aws-lc-rs",
"base64 0.22.1",
@@ -2789,7 +2789,7 @@ dependencies = [
[[package]]
name = "komodo_cli"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"chrono",
@@ -2817,7 +2817,7 @@ dependencies = [
[[package]]
name = "komodo_client"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"async_timing_util",
@@ -2856,7 +2856,7 @@ dependencies = [
[[package]]
name = "komodo_core"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"arc-swap",
@@ -2929,7 +2929,7 @@ dependencies = [
[[package]]
name = "komodo_periphery"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"arc-swap",
@@ -3173,9 +3173,9 @@ dependencies = [
[[package]]
name = "mogh_auth_server"
version = "1.2.4"
version = "1.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eeb8c898da571c59ec9f4997b6869c5a3b31303d95c765ee50713cb4c045be9"
checksum = "fa05efbe7caf986807ba12d9dcafb722a7c0f2fb51f6dafd87fa2529122a3b87"
dependencies = [
"anyhow",
"arc-swap",
@@ -3341,9 +3341,9 @@ dependencies = [
[[package]]
name = "mogh_server"
version = "1.3.0"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffd2b7646dbaa1a09b3df6782f1692e459775a6fb46fb2d613d43ae5ca1a9dcb"
checksum = "9601ac6b48b8a932d5944aa68944b03147b79187dd046d78dcc077d5c64089c2"
dependencies = [
"anyhow",
"axum",
@@ -3979,7 +3979,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "periphery_client"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"encoding",
@@ -5883,7 +5883,7 @@ dependencies = [
[[package]]
name = "transport"
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
dependencies = [
"anyhow",
"axum",
@@ -6135,9 +6135,9 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.19.0"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
dependencies = [
"getrandom 0.3.4",
"js-sys",
+3 -3
View File
@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "2.0.0-dev-114"
version = "2.0.0-dev-116"
edition = "2024"
authors = ["mbecker20 <becker.maxh@gmail.com>"]
license = "GPL-3.0-or-later"
@@ -37,7 +37,7 @@ mogh_error = { version = "1.0.3", default-features = false }
derive_default_builder = "0.1.8"
async_timing_util = "1.1.0"
mogh_auth_client = "1.2.1"
mogh_auth_server = "1.2.4"
mogh_auth_server = "1.2.5"
mogh_secret_file = "1.0.0"
mogh_validations = "1.0.0"
mogh_rate_limit = "1.0.0"
@@ -46,7 +46,7 @@ mongo_indexed = "2.0.2"
mogh_resolver = "1.0.0"
mogh_config = "1.0.1"
mogh_logger = "1.3.0"
mogh_server = "1.3.0"
mogh_server = "1.4.3"
toml_pretty = "2.0.0"
mogh_cache = "1.1.1"
mogh_pki = "1.1.0"
+36 -26
View File
@@ -27,12 +27,12 @@ use periphery_client::api::{self, container::InspectContainer};
use crate::{
alert::send_alerts,
api::execute::{self, ExecuteRequest},
api::execute::{self, ExecuteRequest, ExecutionResult},
helpers::{
periphery_client,
query::{get_deployment_state, get_swarm_or_server},
registry_token,
update::{add_update, make_update},
update::{add_update, make_update, poll_update_until_complete},
},
permission::get_check_permissions,
resource::{
@@ -520,32 +520,42 @@ pub async fn check_deployment_for_update_inner(
)
.await
{
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,
swarm_id,
swarm_name,
server_id,
server_name,
image,
},
Ok(res) => {
let ExecutionResult::Single(update) = res else {
unreachable!()
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentAutoUpdated to db | {e:#}"
);
let Ok(update) =
poll_update_until_complete(&update.id).await
else {
return;
};
if update.success {
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,
swarm_id,
swarm_name,
server_id,
server_name,
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;
}
send_alerts(&[alert]).await;
}
Err(e) => {
warn!("Failed to auto update Deployment {name} | {e:#}",)
+53 -33
View File
@@ -33,12 +33,12 @@ use periphery_client::api::compose::{
use crate::{
alert::send_alerts,
api::execute::{self, ExecuteRequest},
api::execute::{self, ExecuteRequest, ExecutionResult},
config::core_config,
helpers::{
query::get_swarm_or_server,
stack_git_token, swarm_or_server_request,
update::{add_update, make_update},
update::{add_update, make_update, poll_update_until_complete},
},
permission::get_check_permissions,
resource::{self, list_full_for_user_using_pattern},
@@ -750,17 +750,28 @@ pub async fn check_stack_for_update_inner(
let cache = image_digest_cache();
for service in &mut stack.info.latest_services {
if service.image.is_empty() ||
// Prefer the image coming from deployed services
// so it will be after any interpolation.
let image = stack
.info
.deployed_services
.as_ref()
.and_then(|services| {
services.iter().find_map(|deployed| {
(deployed.service_name == service.service_name)
.then_some(&deployed.image)
})
})
.unwrap_or(&service.image);
if image.is_empty() ||
// Images with a hardcoded digest can't have update.
service.image.contains('@')
image.contains('@')
{
service.image_digest = None;
continue;
}
match cache
.get(&swarm_or_server, &service.image, None, None)
.await
{
match cache.get(&swarm_or_server, image, None, None).await {
Ok(digest) => service.image_digest = Some(digest),
Err(e) => {
warn!(
@@ -946,33 +957,42 @@ pub async fn check_stack_for_update_inner(
)
.await
{
Ok(_) => {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Stack(stack.id.clone()),
data: AlertData::StackAutoUpdated {
id: stack.id.clone(),
name: stack.name.clone(),
swarm_id,
swarm_name,
server_id,
server_name,
images: services_with_update
.iter()
.map(|service| service.image.clone())
.collect(),
},
Ok(res) => {
let ExecutionResult::Single(update) = res else {
unreachable!()
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!("Failed to record StackAutoUpdated to db | {e:#}");
let Ok(update) = poll_update_until_complete(&update.id).await
else {
return;
};
if update.success {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Stack(stack.id.clone()),
data: AlertData::StackAutoUpdated {
id: stack.id.clone(),
name: stack.name.clone(),
swarm_id,
swarm_name,
server_id,
server_name,
images: services_with_update
.iter()
.map(|service| service.image.clone())
.collect(),
},
};
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;
}
send_alerts(&[alert]).await;
}
Err(e) => {
warn!("Failed to auto update Stack {} | {e:#}", stack.name)
+1 -3
View File
@@ -143,9 +143,7 @@ impl AuthImpl for KomodoAuthImpl {
}
fn host(&self) -> &str {
static AUTH_HOST: LazyLock<String> =
LazyLock::new(|| format!("{}/auth", core_config().host));
&AUTH_HOST
&core_config().host
}
fn post_link_redirect(&self) -> &str {
+3
View File
@@ -309,6 +309,9 @@ pub fn core_config() -> &'static CoreConfig {
cors_allow_credentials: env
.komodo_cors_allow_credentials
.unwrap_or(config.cors_allow_credentials),
session_allow_cross_site: env
.komodo_session_allow_cross_site
.unwrap_or(config.session_allow_cross_site),
resource_poll_interval: env
.komodo_resource_poll_interval
.unwrap_or(config.resource_poll_interval),
+17 -1
View File
@@ -1,3 +1,5 @@
use std::time::Duration;
use anyhow::Context;
use database::mungos::{
by_id::{find_one_by_id, update_one_by_id},
@@ -16,7 +18,7 @@ use komodo_client::entities::{
stack::Stack,
swarm::Swarm,
sync::ResourceSync,
update::{Update, UpdateListItem},
update::{Update, UpdateListItem, UpdateStatus},
user::User,
};
@@ -591,3 +593,17 @@ pub async fn init_execution_update(
Ok(update)
}
pub async fn poll_update_until_complete(
update_id: &str,
) -> anyhow::Result<Update> {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let update = find_one_by_id(&db_client().updates, update_id)
.await?
.context("No update found at given ID")?;
if matches!(update.status, UpdateStatus::Complete) {
return Ok(update);
}
}
}
+17 -7
View File
@@ -225,6 +225,8 @@ pub struct Env {
pub komodo_cors_allowed_origins: Option<Vec<String>>,
/// Override `cors_allow_credentials`
pub komodo_cors_allow_credentials: Option<bool>,
/// Override `session_allow_cross_site`
pub komodo_session_allow_cross_site: Option<bool>,
/// Override `database.uri`
#[serde(alias = "komodo_mongo_uri")]
@@ -561,6 +563,12 @@ pub struct CoreConfig {
#[serde(default)]
pub cors_allow_credentials: bool,
/// Use SameSite=None (actually allows samesite) instead of SameSite=Lax.
/// The third option, SameSite=Strict, won't work with external login,
/// as the session cookie will be lost on redirect with auth provider.
#[serde(default)]
pub session_allow_cross_site: bool,
// ============
// = Webhooks =
// ============
@@ -836,6 +844,7 @@ impl Default for CoreConfig {
default_auth_rate_limit_window_seconds(),
cors_allowed_origins: Default::default(),
cors_allow_credentials: Default::default(),
session_allow_cross_site: Default::default(),
webhook_secret: Default::default(),
webhook_base_url: Default::default(),
logging: Default::default(),
@@ -942,6 +951,7 @@ impl CoreConfig {
.auth_rate_limit_window_seconds,
cors_allowed_origins: config.cors_allowed_origins,
cors_allow_credentials: config.cors_allow_credentials,
session_allow_cross_site: config.session_allow_cross_site,
webhook_secret: empty_or_redacted(&config.webhook_secret),
webhook_base_url: config.webhook_base_url,
database: config.database.sanitized(),
@@ -1023,9 +1033,6 @@ impl mogh_server::ServerConfig for &CoreConfig {
}
impl mogh_server::cors::CorsConfig for &CoreConfig {
fn allowed_origins_env_field(&self) -> &'static str {
"KOMODO_CORS_ALLOWED_ORIGINS"
}
fn allowed_origins(&self) -> &[String] {
&self.cors_allowed_origins
}
@@ -1035,13 +1042,16 @@ impl mogh_server::cors::CorsConfig for &CoreConfig {
}
impl mogh_server::session::SessionConfig for &CoreConfig {
fn expiry_seconds(&self) -> i64 {
60
fn host(&self) -> &str {
&self.host
}
fn host_env_field(&self) -> &str {
"KOMODO_HOST"
}
fn host(&self) -> &str {
&self.host
fn expiry_seconds(&self) -> i64 {
60
}
fn allow_cross_site(&self) -> bool {
self.session_allow_cross_site
}
}
+7 -3
View File
@@ -324,9 +324,9 @@ auth_rate_limit_max_attempts = 5
## Default: 15
auth_rate_limit_window_seconds = 15
########
# CORS #
########
##################
# CORS / SESSION #
##################
## Specifically set list of CORS allowed origins.
## If empty, allows all origins (`*`).
@@ -341,6 +341,10 @@ cors_allowed_origins = []
## Default: false
cors_allow_credentials = false
## Enabling this sets 'SameSite=None', which allows externally
## hosted frontends to use the login flows.
session_allow_cross_site = false
##################
# POLL INTERVALS #
##################