forked from github-starred/komodo
implement a not good busy guard
This commit is contained in:
@@ -3,8 +3,9 @@ use diff::Diff;
|
||||
use helpers::to_monitor_name;
|
||||
use mungos::{doc, to_bson};
|
||||
use types::{
|
||||
monitor_timestamp, traits::Permissioned, Build, Log, Operation, PermissionLevel, Update,
|
||||
UpdateStatus, UpdateTarget,
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Build, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -31,19 +32,26 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_busy(&self, id: &str) -> bool {
|
||||
match self.build_action_states.lock().unwrap().get(id) {
|
||||
Some(a) => a.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_build(
|
||||
&self,
|
||||
name: &str,
|
||||
server_id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Write)
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let build = Build {
|
||||
name: to_monitor_name(name),
|
||||
server_id,
|
||||
permissions: [(user.id.clone(), PermissionLevel::Write)]
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
created_at: start_ts.clone(),
|
||||
@@ -84,8 +92,11 @@ impl State {
|
||||
}
|
||||
|
||||
pub async fn delete_build(&self, build_id: &str, user: &RequestUser) -> anyhow::Result<Build> {
|
||||
if self.build_busy(build_id) {
|
||||
return Err(anyhow!("build busy"))
|
||||
}
|
||||
let build = self
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Write)
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
@@ -120,8 +131,17 @@ impl State {
|
||||
mut new_build: Build,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
if self.build_busy(&new_build.id) {
|
||||
return Err(anyhow!("build busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().unwrap();
|
||||
let entry = lock.entry(new_build.id.clone()).or_default();
|
||||
entry.updating = true;
|
||||
}
|
||||
|
||||
let current_build = self
|
||||
.get_build_check_permissions(&new_build.id, user, PermissionLevel::Write)
|
||||
.get_build_check_permissions(&new_build.id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
@@ -175,12 +195,28 @@ impl State {
|
||||
update.status = UpdateStatus::Complete;
|
||||
|
||||
self.update_update(update).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().unwrap();
|
||||
let entry = lock.entry(new_build.id.clone()).or_default();
|
||||
entry.updating = false;
|
||||
}
|
||||
|
||||
Ok(new_build)
|
||||
}
|
||||
|
||||
pub async fn build(&self, build_id: &str, user: &RequestUser) -> anyhow::Result<Update> {
|
||||
if self.build_busy(build_id) {
|
||||
return Err(anyhow!("build busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().unwrap();
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.building = true;
|
||||
}
|
||||
|
||||
let mut build = self
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Write)
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
|
||||
@@ -231,6 +267,13 @@ impl State {
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().unwrap();
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.building = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
@@ -239,8 +282,16 @@ impl State {
|
||||
build_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.build_busy(build_id) {
|
||||
return Err(anyhow!("build busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().unwrap();
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.recloning = true;
|
||||
}
|
||||
let build = self
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Write)
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
let mut update = Update {
|
||||
@@ -272,6 +323,12 @@ impl State {
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().unwrap();
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.recloning = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use helpers::to_monitor_name;
|
||||
use types::{
|
||||
monitor_timestamp, traits::Permissioned, Deployment, Log, Operation, PermissionLevel, Update,
|
||||
UpdateStatus, UpdateTarget,
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Deployment, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -30,19 +31,26 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deployment_busy(&self, id: &str) -> bool {
|
||||
match self.deployment_action_states.lock().unwrap().get(id) {
|
||||
Some(a) => a.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_deployment(
|
||||
&self,
|
||||
name: &str,
|
||||
server_id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Write)
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = Deployment {
|
||||
name: to_monitor_name(name),
|
||||
server_id,
|
||||
permissions: [(user.id.clone(), PermissionLevel::Write)]
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
created_at: start_ts.clone(),
|
||||
@@ -87,8 +95,11 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
if self.deployment_busy(deployment_id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
@@ -131,8 +142,16 @@ impl State {
|
||||
mut new_deployment: Deployment,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
if self.deployment_busy(&new_deployment.id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(new_deployment.id.clone()).or_default();
|
||||
entry.updating = true;
|
||||
}
|
||||
let current_deployment = self
|
||||
.get_deployment_check_permissions(&new_deployment.id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(&new_deployment.id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
@@ -190,6 +209,12 @@ impl State {
|
||||
|
||||
self.update_update(update).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(new_deployment.id.clone()).or_default();
|
||||
entry.updating = false;
|
||||
}
|
||||
|
||||
Ok(new_deployment)
|
||||
}
|
||||
|
||||
@@ -198,8 +223,16 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.recloning = true;
|
||||
}
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
let mut update = Update {
|
||||
@@ -231,6 +264,12 @@ impl State {
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.recloning = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
@@ -239,8 +278,16 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.deploying = true;
|
||||
}
|
||||
let mut deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
if let Some(build_id) = &deployment.build_id {
|
||||
let build = self.db.get_build(build_id).await?;
|
||||
@@ -281,6 +328,12 @@ impl State {
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.deploying = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
@@ -289,9 +342,17 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.starting = true;
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
let mut update = Update {
|
||||
@@ -329,6 +390,12 @@ impl State {
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.starting = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
@@ -337,9 +404,17 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.stopping = true;
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
let mut update = Update {
|
||||
@@ -377,6 +452,12 @@ impl State {
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.stopping = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
@@ -385,9 +466,17 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id) {
|
||||
return Err(anyhow!("deployment busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.removing = true;
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Write)
|
||||
.get_deployment_check_permissions(deployment_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
let mut update = Update {
|
||||
@@ -425,6 +514,12 @@ impl State {
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().unwrap();
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.removing = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ impl State {
|
||||
let start_ts = monitor_timestamp();
|
||||
let procedure = Procedure {
|
||||
name: to_monitor_name(name),
|
||||
permissions: [(user.id.clone(), PermissionLevel::Write)]
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
created_at: start_ts.clone(),
|
||||
@@ -78,7 +78,7 @@ impl State {
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Procedure> {
|
||||
let procedure = self
|
||||
.get_procedure_check_permissions(id, user, PermissionLevel::Write)
|
||||
.get_procedure_check_permissions(id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
self.db
|
||||
@@ -109,7 +109,7 @@ impl State {
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Procedure> {
|
||||
let current_procedure = self
|
||||
.get_procedure_check_permissions(&new_procedure.id, user, PermissionLevel::Write)
|
||||
.get_procedure_check_permissions(&new_procedure.id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
@@ -154,7 +154,7 @@ impl State {
|
||||
|
||||
pub async fn run_procedure(&self, id: &str, user: &RequestUser) -> anyhow::Result<Vec<Update>> {
|
||||
let procedure = self
|
||||
.get_procedure_check_permissions(id, user, PermissionLevel::Write)
|
||||
.get_procedure_check_permissions(id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let mut updates = Vec::new();
|
||||
for ProcedureStage {
|
||||
@@ -163,7 +163,7 @@ impl State {
|
||||
} in procedure.stages
|
||||
{
|
||||
match operation {
|
||||
None => {},
|
||||
None => {}
|
||||
// deployment
|
||||
StartContainer => {
|
||||
let update = self
|
||||
|
||||
@@ -2,8 +2,9 @@ use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use helpers::to_monitor_name;
|
||||
use types::{
|
||||
monitor_timestamp, traits::Permissioned, Log, Operation, PermissionLevel, Server, Update,
|
||||
UpdateStatus, UpdateTarget,
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Log, Operation, PermissionLevel, Server, Update, UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{auth::RequestUser, state::State};
|
||||
@@ -26,6 +27,13 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_busy(&self, id: &str) -> bool {
|
||||
match self.server_action_states.lock().unwrap().get(id) {
|
||||
Some(a) => a.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_server(
|
||||
&self,
|
||||
name: &str,
|
||||
@@ -41,7 +49,7 @@ impl State {
|
||||
let server = Server {
|
||||
name: to_monitor_name(name),
|
||||
address,
|
||||
permissions: [(user.id.clone(), PermissionLevel::Write)]
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
created_at: start_ts.clone(),
|
||||
@@ -87,8 +95,11 @@ impl State {
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Server> {
|
||||
if self.server_busy(server_id) {
|
||||
return Err(anyhow!("server busy"))
|
||||
}
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Write)
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
self.db.servers.delete_one(&server_id).await?;
|
||||
@@ -114,8 +125,11 @@ impl State {
|
||||
mut new_server: Server,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Server> {
|
||||
if self.server_busy(&new_server.id) {
|
||||
return Err(anyhow!("server busy"))
|
||||
}
|
||||
let current_server = self
|
||||
.get_server_check_permissions(&new_server.id, user, PermissionLevel::Write)
|
||||
.get_server_check_permissions(&new_server.id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
@@ -149,4 +163,168 @@ impl State {
|
||||
self.add_update(update).await?;
|
||||
Ok(new_server)
|
||||
}
|
||||
|
||||
pub async fn prune_networks(
|
||||
&self,
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.server_busy(server_id) {
|
||||
return Err(anyhow!("server busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().unwrap();
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_networks = true;
|
||||
}
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
|
||||
let start_ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_owned()),
|
||||
operation: Operation::PruneNetworksServer,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let log = match self.periphery.network_prune(&server).await.context(format!(
|
||||
"failed to prune networks on server {}",
|
||||
server.name
|
||||
)) {
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("prune networks", format!("{e:#?}")),
|
||||
};
|
||||
|
||||
update.success = log.success;
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().unwrap();
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_networks = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn prune_images(
|
||||
&self,
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.server_busy(server_id) {
|
||||
return Err(anyhow!("server busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().unwrap();
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_images = true;
|
||||
}
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_owned()),
|
||||
operation: Operation::PruneImagesServer,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let log = match self
|
||||
.periphery
|
||||
.image_prune(&server)
|
||||
.await
|
||||
.context(format!("failed to prune images on server {}", server.name))
|
||||
{
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("prune images", format!("{e:#?}")),
|
||||
};
|
||||
|
||||
update.success = log.success;
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().unwrap();
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_images = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn prune_containers(
|
||||
&self,
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.server_busy(server_id) {
|
||||
return Err(anyhow!("server busy"))
|
||||
}
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().unwrap();
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_containers = true;
|
||||
}
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
|
||||
let start_ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_owned()),
|
||||
operation: Operation::PruneContainersServer,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let log = match self
|
||||
.periphery
|
||||
.container_prune(&server)
|
||||
.await
|
||||
.context(format!(
|
||||
"failed to prune containers on server {}",
|
||||
server.name
|
||||
)) {
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("prune containers", format!("{e:#?}")),
|
||||
};
|
||||
|
||||
update.success = log.success;
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().unwrap();
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_containers = false;
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use axum::{
|
||||
};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use types::{traits::Permissioned, Build, PermissionLevel};
|
||||
use types::{traits::Permissioned, Build, BuildActionState, PermissionLevel};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::{
|
||||
@@ -141,6 +141,20 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/action_state",
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(BuildId { id }): Path<BuildId>| async move {
|
||||
let action_state = state
|
||||
.get_build_action_states(id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(action_state))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -168,4 +182,21 @@ impl State {
|
||||
builds.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
Ok(builds)
|
||||
}
|
||||
|
||||
async fn get_build_action_states(
|
||||
&self,
|
||||
id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<BuildActionState> {
|
||||
self.get_server_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let action_state = self
|
||||
.build_action_states
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.clone();
|
||||
Ok(action_state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ use axum::{
|
||||
use futures_util::future::join_all;
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use types::{traits::Permissioned, Deployment, DeploymentWithContainer, PermissionLevel, Server};
|
||||
use types::{
|
||||
traits::Permissioned, Deployment, DeploymentActionState, DeploymentWithContainer,
|
||||
PermissionLevel, Server,
|
||||
};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::{
|
||||
@@ -186,6 +189,20 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/action_state",
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(DeploymentId { id }): Path<DeploymentId>| async move {
|
||||
let action_state = state
|
||||
.get_deployment_action_states(id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(action_state))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -263,4 +280,21 @@ impl State {
|
||||
.collect();
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
async fn get_deployment_action_states(
|
||||
&self,
|
||||
id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<DeploymentActionState> {
|
||||
self.get_server_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let action_state = self
|
||||
.deployment_action_states
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.clone();
|
||||
Ok(action_state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use axum::{middleware, routing::get, Extension, Json, Router};
|
||||
use helpers::handle_anyhow_error;
|
||||
use periphery::PeripheryClient;
|
||||
use types::User;
|
||||
|
||||
use crate::{
|
||||
@@ -32,7 +29,6 @@ pub fn router() -> Router {
|
||||
.nest("/update", update::router())
|
||||
.nest("/permissions", permissions::router())
|
||||
.nest("/secret", secret::router())
|
||||
.layer(Extension(Arc::new(PeripheryClient::new())))
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ use axum::{
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use types::{
|
||||
monitor_timestamp, traits::Permissioned, BasicContainerInfo, ImageSummary, Log, Network,
|
||||
Operation, PermissionLevel, Server, SystemStats, Update, UpdateStatus, UpdateTarget,
|
||||
traits::Permissioned, BasicContainerInfo, ImageSummary, Network, PermissionLevel, Server,
|
||||
ServerActionState, SystemStats,
|
||||
};
|
||||
use typeshare::typeshare;
|
||||
|
||||
@@ -214,6 +214,20 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/action_state",
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(ServerId { id }): Path<ServerId>| async move {
|
||||
let action_state = state
|
||||
.get_server_action_states(id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(action_state))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -273,45 +287,6 @@ impl State {
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
pub async fn prune_networks(
|
||||
&self,
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Write)
|
||||
.await?;
|
||||
|
||||
let start_ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_owned()),
|
||||
operation: Operation::PruneNetworksServer,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let log = match self.periphery.network_prune(&server).await.context(format!(
|
||||
"failed to prune networks on server {}",
|
||||
server.name
|
||||
)) {
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("prune networks", format!("{e:#?}")),
|
||||
};
|
||||
|
||||
update.success = log.success;
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
async fn get_images(
|
||||
&self,
|
||||
server_id: &str,
|
||||
@@ -328,46 +303,6 @@ impl State {
|
||||
Ok(images)
|
||||
}
|
||||
|
||||
pub async fn prune_images(
|
||||
&self,
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Write)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_owned()),
|
||||
operation: Operation::PruneImagesServer,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let log = match self
|
||||
.periphery
|
||||
.image_prune(&server)
|
||||
.await
|
||||
.context(format!("failed to prune images on server {}", server.name))
|
||||
{
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("prune images", format!("{e:#?}")),
|
||||
};
|
||||
|
||||
update.success = log.success;
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
async fn get_containers(
|
||||
&self,
|
||||
server_id: &str,
|
||||
@@ -387,46 +322,20 @@ impl State {
|
||||
Ok(containers)
|
||||
}
|
||||
|
||||
pub async fn prune_containers(
|
||||
async fn get_server_action_states(
|
||||
&self,
|
||||
server_id: &str,
|
||||
id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = self
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Write)
|
||||
) -> anyhow::Result<ServerActionState> {
|
||||
self.get_server_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
|
||||
let start_ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_owned()),
|
||||
operation: Operation::PruneContainersServer,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let log = match self
|
||||
.periphery
|
||||
.container_prune(&server)
|
||||
.await
|
||||
.context(format!(
|
||||
"failed to prune containers on server {}",
|
||||
server.name
|
||||
)) {
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("prune containers", format!("{e:#?}")),
|
||||
};
|
||||
|
||||
update.success = log.success;
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
let action_state = self
|
||||
.server_action_states
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.clone();
|
||||
Ok(action_state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use axum::Extension;
|
||||
use db::DbClient;
|
||||
use periphery::PeripheryClient;
|
||||
use types::CoreConfig;
|
||||
use types::{BuildActionState, CoreConfig, DeploymentActionState, ServerActionState};
|
||||
|
||||
use crate::ws::update::UpdateWsChannel;
|
||||
|
||||
pub type StateExtension = Extension<Arc<State>>;
|
||||
|
||||
pub type ActionStateMap<T> = Mutex<HashMap<String, T>>;
|
||||
|
||||
pub struct State {
|
||||
pub config: CoreConfig,
|
||||
pub db: DbClient,
|
||||
pub update: UpdateWsChannel,
|
||||
pub periphery: PeripheryClient,
|
||||
pub build_action_states: ActionStateMap<BuildActionState>,
|
||||
pub deployment_action_states: ActionStateMap<DeploymentActionState>,
|
||||
pub server_action_states: ActionStateMap<ServerActionState>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn new(config: CoreConfig) -> State {
|
||||
State {
|
||||
db: DbClient::new(config.mongo.clone()).await,
|
||||
periphery: PeripheryClient::new(),
|
||||
update: UpdateWsChannel::new(),
|
||||
config,
|
||||
update: UpdateWsChannel::new(),
|
||||
periphery: PeripheryClient::default(),
|
||||
build_action_states: Default::default(),
|
||||
deployment_action_states: Default::default(),
|
||||
server_action_states: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ use crate::{
|
||||
pub type UpdateWsSender = Mutex<Sender<Update>>;
|
||||
|
||||
pub type UpdateWsReciever = Receiver<Update>;
|
||||
|
||||
pub struct UpdateWsChannel {
|
||||
pub sender: UpdateWsSender,
|
||||
pub reciever: UpdateWsReciever,
|
||||
|
||||
23
frontend/src/types.d.ts
vendored
23
frontend/src/types.d.ts
vendored
@@ -42,6 +42,12 @@ export interface Server {
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface ServerActionState {
|
||||
pruning_networks: boolean;
|
||||
pruning_containers: boolean;
|
||||
pruning_images: boolean;
|
||||
}
|
||||
|
||||
export interface Deployment {
|
||||
_id?: string;
|
||||
name: string;
|
||||
@@ -63,6 +69,15 @@ export interface DeploymentWithContainer {
|
||||
container?: BasicContainerInfo;
|
||||
}
|
||||
|
||||
export interface DeploymentActionState {
|
||||
deploying: boolean;
|
||||
stopping: boolean;
|
||||
starting: boolean;
|
||||
removing: boolean;
|
||||
pulling: boolean;
|
||||
recloning: boolean;
|
||||
}
|
||||
|
||||
export interface Build {
|
||||
_id?: string;
|
||||
name: string;
|
||||
@@ -80,6 +95,11 @@ export interface Build {
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface BuildActionState {
|
||||
building: boolean;
|
||||
recloning: boolean;
|
||||
}
|
||||
|
||||
export interface Update {
|
||||
_id?: string;
|
||||
target: UpdateTarget;
|
||||
@@ -270,7 +290,8 @@ export enum ProcedureOperation {
|
||||
export enum PermissionLevel {
|
||||
None = "none",
|
||||
Read = "read",
|
||||
Write = "write",
|
||||
Execute = "execute",
|
||||
Update = "update",
|
||||
}
|
||||
|
||||
export enum PermissionsTarget {
|
||||
|
||||
@@ -2,10 +2,13 @@ import axios from "axios";
|
||||
import {
|
||||
BasicContainerInfo,
|
||||
Build,
|
||||
BuildActionState,
|
||||
Deployment,
|
||||
DeploymentActionState,
|
||||
DeploymentWithContainer,
|
||||
Log,
|
||||
Server,
|
||||
ServerActionState,
|
||||
SystemStats,
|
||||
Update,
|
||||
User,
|
||||
@@ -102,6 +105,10 @@ export class Client {
|
||||
return this.post(`/api/deployment/${deployment_id}/remove_container`);
|
||||
}
|
||||
|
||||
get_deployment_action_state(id: string): Promise<DeploymentActionState> {
|
||||
return this.get(`/api/deployment/${id}/action_state`);
|
||||
}
|
||||
|
||||
// server
|
||||
|
||||
list_servers(query?: QueryObject): Promise<Server[]> {
|
||||
@@ -132,10 +139,18 @@ export class Client {
|
||||
return this.get(`/api/server/${server_id}/stats`);
|
||||
}
|
||||
|
||||
get_docker_networks(server_id: string): Promise<any[]> {
|
||||
return this.get(`/api/server/${server_id}/networks`);
|
||||
}
|
||||
|
||||
prune_docker_networks(server_id: string): Promise<Log> {
|
||||
return this.post(`/api/server/${server_id}/networks/prune`);
|
||||
}
|
||||
|
||||
get_docker_images(server_id: string): Promise<any[]> {
|
||||
return this.get(`/api/server/${server_id}/images`);
|
||||
}
|
||||
|
||||
prune_docker_images(server_id: string): Promise<Log> {
|
||||
return this.post(`/api/server/${server_id}/images/prune`);
|
||||
}
|
||||
@@ -148,6 +163,10 @@ export class Client {
|
||||
return this.post(`/api/server/${server_id}/containers/prune`);
|
||||
}
|
||||
|
||||
get_server_action_state(id: string): Promise<ServerActionState> {
|
||||
return this.get(`/api/server/${id}/action_state`);
|
||||
}
|
||||
|
||||
// build
|
||||
|
||||
list_builds(query?: QueryObject): Promise<Build[]> {
|
||||
@@ -182,6 +201,10 @@ export class Client {
|
||||
return this.post(`/api/build/${id}/reclone`);
|
||||
}
|
||||
|
||||
get_build_action_state(id: string): Promise<BuildActionState> {
|
||||
return this.get(`/api/build/${id}/action_state`);
|
||||
}
|
||||
|
||||
// api secrets
|
||||
|
||||
create_api_secret(body: CreateSecretBody): Promise<string> {
|
||||
|
||||
@@ -9,17 +9,12 @@ mod git;
|
||||
mod image;
|
||||
mod network;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PeripheryClient {
|
||||
http_client: reqwest::Client,
|
||||
}
|
||||
|
||||
impl PeripheryClient {
|
||||
pub fn new() -> PeripheryClient {
|
||||
PeripheryClient {
|
||||
http_client: reqwest::Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn health_check(&self, server: &Server) -> anyhow::Result<String> {
|
||||
self.get_text(server, "health")
|
||||
.await
|
||||
|
||||
@@ -183,6 +183,14 @@ fn default_disk_alert() -> f64 {
|
||||
75.0
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct ServerActionState {
|
||||
pub pruning_networks: bool,
|
||||
pub pruning_containers: bool,
|
||||
pub pruning_images: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, Diff, Builder)]
|
||||
#[diff(attr(#[derive(Debug, Serialize)]))]
|
||||
@@ -253,6 +261,18 @@ pub struct DeploymentWithContainer {
|
||||
pub container: Option<BasicContainerInfo>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct DeploymentActionState {
|
||||
pub deploying: bool,
|
||||
pub stopping: bool,
|
||||
pub starting: bool,
|
||||
pub removing: bool,
|
||||
pub pulling: bool,
|
||||
pub recloning: bool,
|
||||
pub updating: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, Diff, Builder)]
|
||||
#[diff(attr(#[derive(Debug, Serialize)]))]
|
||||
@@ -315,6 +335,14 @@ pub struct Build {
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct BuildActionState {
|
||||
pub building: bool,
|
||||
pub recloning: bool,
|
||||
pub updating: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct Update {
|
||||
@@ -793,7 +821,8 @@ impl Default for ProcedureOperation {
|
||||
pub enum PermissionLevel {
|
||||
None,
|
||||
Read,
|
||||
Write,
|
||||
Execute,
|
||||
Update,
|
||||
}
|
||||
|
||||
impl Default for PermissionLevel {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{Build, Deployment, PermissionLevel, PermissionsMap, Procedure, Server};
|
||||
use crate::{
|
||||
Build, BuildActionState, Deployment, DeploymentActionState, PermissionLevel, PermissionsMap,
|
||||
Procedure, Server, ServerActionState,
|
||||
};
|
||||
|
||||
pub trait Permissioned {
|
||||
fn permissions_map(&self) -> &PermissionsMap;
|
||||
@@ -31,3 +34,31 @@ impl Permissioned for Procedure {
|
||||
&self.permissions
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Busy {
|
||||
fn busy(&self) -> bool;
|
||||
}
|
||||
|
||||
impl Busy for ServerActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.pruning_containers || self.pruning_images || self.pruning_networks
|
||||
}
|
||||
}
|
||||
|
||||
impl Busy for DeploymentActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.deploying
|
||||
|| self.pulling
|
||||
|| self.recloning
|
||||
|| self.removing
|
||||
|| self.starting
|
||||
|| self.stopping
|
||||
|| self.updating
|
||||
}
|
||||
}
|
||||
|
||||
impl Busy for BuildActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.building || self.recloning || self.updating
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user