mirror of
https://github.com/moghtech/komodo.git
synced 2026-05-04 23:09:34 -05:00
* get stack state from project * move custom image name / tag below image setting for build config * services also trigger stack action state * add status to stack page * 1.13.1 patch
319 lines
8.3 KiB
Rust
319 lines
8.3 KiB
Rust
use anyhow::Context;
|
|
use formatting::format_serror;
|
|
use monitor_client::{
|
|
api::execute::*,
|
|
entities::{
|
|
permission::PermissionLevel, stack::StackInfo, update::Update,
|
|
user::User,
|
|
},
|
|
};
|
|
use mungos::mongodb::bson::{doc, to_document};
|
|
use periphery_client::api::compose::*;
|
|
use resolver_api::Resolve;
|
|
|
|
use crate::{
|
|
helpers::{
|
|
interpolate_variables_secrets_into_environment, periphery_client,
|
|
stack::{
|
|
execute::execute_compose, get_stack_and_server,
|
|
services::extract_services_into_res,
|
|
},
|
|
update::update_update,
|
|
},
|
|
monitor::update_cache_for_server,
|
|
state::{action_states, db_client, State},
|
|
};
|
|
|
|
impl Resolve<DeployStack, (User, Update)> for State {
|
|
#[instrument(name = "DeployStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
DeployStack { stack, stop_time }: DeployStack,
|
|
(user, mut update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
let (mut stack, server) = get_stack_and_server(
|
|
&stack,
|
|
&user,
|
|
PermissionLevel::Execute,
|
|
true,
|
|
)
|
|
.await?;
|
|
|
|
// get the action state for the stack (or insert default).
|
|
let action_state =
|
|
action_states().stack.get_or_insert_default(&stack.id).await;
|
|
|
|
// Will check to ensure stack not already busy before updating, and return Err if so.
|
|
// The returned guard will set the action state back to default when dropped.
|
|
let _action_guard =
|
|
action_state.update(|state| state.deploying = true)?;
|
|
|
|
update_update(update.clone()).await?;
|
|
|
|
let git_token = crate::helpers::git_token(
|
|
&stack.config.git_provider,
|
|
&stack.config.git_account,
|
|
|https| stack.config.git_https = https,
|
|
).await.with_context(
|
|
|| format!("Failed to get git token in call to db. Stopping run. | {} | {}", stack.config.git_provider, stack.config.git_account),
|
|
)?;
|
|
|
|
let registry_token = crate::helpers::registry_token(
|
|
&stack.config.registry_provider,
|
|
&stack.config.registry_account,
|
|
).await.with_context(
|
|
|| format!("Failed to get registry token in call to db. Stopping run. | {} | {}", stack.config.registry_provider, stack.config.registry_account),
|
|
)?;
|
|
|
|
if !stack.config.skip_secret_interp {
|
|
interpolate_variables_secrets_into_environment(
|
|
&mut stack.config.environment,
|
|
&mut update,
|
|
)
|
|
.await?;
|
|
}
|
|
|
|
let ComposeUpResponse {
|
|
logs,
|
|
deployed,
|
|
file_contents,
|
|
missing_files,
|
|
remote_errors,
|
|
commit_hash,
|
|
commit_message,
|
|
} = periphery_client(&server)?
|
|
.request(ComposeUp {
|
|
stack: stack.clone(),
|
|
service: None,
|
|
git_token,
|
|
registry_token,
|
|
})
|
|
.await?;
|
|
|
|
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 {
|
|
// maybe better to do something else here for services.
|
|
stack.info.latest_services.clone()
|
|
};
|
|
|
|
let project_name = stack.project_name(true);
|
|
|
|
let (
|
|
deployed_services,
|
|
deployed_contents,
|
|
deployed_hash,
|
|
deployed_message,
|
|
) = if deployed {
|
|
(
|
|
Some(latest_services.clone()),
|
|
Some(file_contents.clone()),
|
|
commit_hash.clone(),
|
|
commit_message.clone(),
|
|
)
|
|
} else {
|
|
(
|
|
stack.info.deployed_services,
|
|
stack.info.deployed_contents,
|
|
stack.info.deployed_hash,
|
|
stack.info.deployed_message,
|
|
)
|
|
};
|
|
|
|
let info = StackInfo {
|
|
missing_files,
|
|
deployed_project_name: project_name.into(),
|
|
deployed_services,
|
|
deployed_contents,
|
|
deployed_hash,
|
|
deployed_message,
|
|
latest_services,
|
|
remote_contents: stack
|
|
.config
|
|
.file_contents
|
|
.is_empty()
|
|
.then_some(file_contents),
|
|
remote_errors: stack
|
|
.config
|
|
.file_contents
|
|
.is_empty()
|
|
.then_some(remote_errors),
|
|
latest_hash: commit_hash,
|
|
latest_message: commit_message,
|
|
};
|
|
|
|
let info = to_document(&info)
|
|
.context("failed to serialize stack info to bson")?;
|
|
|
|
db_client()
|
|
.await
|
|
.stacks
|
|
.update_one(
|
|
doc! { "name": &stack.name },
|
|
doc! { "$set": { "info": info } },
|
|
)
|
|
.await
|
|
.context("failed to update stack info on db")?;
|
|
anyhow::Ok(())
|
|
};
|
|
|
|
// This will be weird with single service deploys. Come back to it.
|
|
if let Err(e) = update_info.await {
|
|
update.push_error_log(
|
|
"refresh stack info",
|
|
format_serror(
|
|
&e.context("failed to refresh stack info on db").into(),
|
|
),
|
|
)
|
|
}
|
|
|
|
// Ensure cached stack state up to date by updating server cache
|
|
update_cache_for_server(&server).await;
|
|
|
|
update.finalize();
|
|
update_update(update.clone()).await?;
|
|
|
|
Ok(update)
|
|
}
|
|
}
|
|
|
|
impl Resolve<StartStack, (User, Update)> for State {
|
|
#[instrument(name = "StartStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
StartStack { stack, service }: StartStack,
|
|
(user, update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
execute_compose::<StartStack>(
|
|
&stack,
|
|
service,
|
|
&user,
|
|
|state| state.starting = true,
|
|
update,
|
|
(),
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl Resolve<RestartStack, (User, Update)> for State {
|
|
#[instrument(name = "RestartStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
RestartStack { stack, service }: RestartStack,
|
|
(user, update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
execute_compose::<RestartStack>(
|
|
&stack,
|
|
service,
|
|
&user,
|
|
|state| {
|
|
state.restarting = true;
|
|
},
|
|
update,
|
|
(),
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl Resolve<PauseStack, (User, Update)> for State {
|
|
#[instrument(name = "PauseStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
PauseStack { stack, service }: PauseStack,
|
|
(user, update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
execute_compose::<PauseStack>(
|
|
&stack,
|
|
service,
|
|
&user,
|
|
|state| state.pausing = true,
|
|
update,
|
|
(),
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl Resolve<UnpauseStack, (User, Update)> for State {
|
|
#[instrument(name = "UnpauseStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
UnpauseStack { stack, service }: UnpauseStack,
|
|
(user, update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
execute_compose::<UnpauseStack>(
|
|
&stack,
|
|
service,
|
|
&user,
|
|
|state| state.unpausing = true,
|
|
update,
|
|
(),
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl Resolve<StopStack, (User, Update)> for State {
|
|
#[instrument(name = "StopStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
StopStack {
|
|
stack,
|
|
stop_time,
|
|
service,
|
|
}: StopStack,
|
|
(user, update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
execute_compose::<StopStack>(
|
|
&stack,
|
|
service,
|
|
&user,
|
|
|state| state.stopping = true,
|
|
update,
|
|
stop_time,
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl Resolve<DestroyStack, (User, Update)> for State {
|
|
#[instrument(name = "DestroyStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))]
|
|
async fn resolve(
|
|
&self,
|
|
DestroyStack {
|
|
stack,
|
|
remove_orphans,
|
|
stop_time,
|
|
}: DestroyStack,
|
|
(user, update): (User, Update),
|
|
) -> anyhow::Result<Update> {
|
|
execute_compose::<DestroyStack>(
|
|
&stack,
|
|
None,
|
|
&user,
|
|
|state| state.destroying = true,
|
|
update,
|
|
(stop_time, remove_orphans),
|
|
)
|
|
.await
|
|
}
|
|
}
|