Files
start-os/backend/src/manager/health.rs
Aiden McClelland 8ee64d22b3 add start/stop/restart to effects
fix: add a default always to the get status

chore: Only do updates when the thing is installed.

chore: Make the thing buildable for testing

chore: Add in the debugging

chore: Remove the bluj tracing

chore: Fix the build

Chore: Make these fn's instead of allways ran.

chore: Fix the testing

fix: The stopping/ restarting service

fix: Fix the restarting.
2023-07-06 15:08:30 -06:00

182 lines
5.7 KiB
Rust

use std::collections::BTreeMap;
use itertools::Itertools;
use patch_db::{DbHandle, LockReceipt, LockType};
use tracing::instrument;
use crate::context::RpcContext;
use crate::db::model::CurrentDependents;
use crate::dependencies::{break_transitive, heal_transitive, DependencyError};
use crate::s9pk::manifest::{Manifest, PackageId};
use crate::status::health_check::{HealthCheckId, HealthCheckResult};
use crate::status::MainStatus;
use crate::Error;
struct HealthCheckPreInformationReceipt {
status_model: LockReceipt<MainStatus, ()>,
manifest: LockReceipt<Manifest, ()>,
}
impl HealthCheckPreInformationReceipt {
pub async fn new(db: &'_ mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
let mut locks = Vec::new();
let setup = Self::setup(&mut locks, id);
setup(&db.lock_all(locks).await?)
}
pub fn setup(
locks: &mut Vec<patch_db::LockTargetId>,
id: &PackageId,
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
let status_model = crate::db::DatabaseModel::new()
.package_data()
.idx_model(id)
.and_then(|x| x.installed())
.map(|x| x.status().main())
.make_locker(LockType::Read)
.add_to_keys(locks);
let manifest = crate::db::DatabaseModel::new()
.package_data()
.idx_model(id)
.and_then(|x| x.installed())
.map(|x| x.manifest())
.make_locker(LockType::Read)
.add_to_keys(locks);
move |skeleton_key| {
Ok(Self {
status_model: status_model.verify(skeleton_key)?,
manifest: manifest.verify(skeleton_key)?,
})
}
}
}
struct HealthCheckStatusReceipt {
status: LockReceipt<MainStatus, ()>,
current_dependents: LockReceipt<CurrentDependents, ()>,
}
impl HealthCheckStatusReceipt {
pub async fn new(db: &'_ mut impl DbHandle, id: &PackageId) -> Result<Self, Error> {
let mut locks = Vec::new();
let setup = Self::setup(&mut locks, id);
setup(&db.lock_all(locks).await?)
}
pub fn setup(
locks: &mut Vec<patch_db::LockTargetId>,
id: &PackageId,
) -> impl FnOnce(&patch_db::Verifier) -> Result<Self, Error> {
let status = crate::db::DatabaseModel::new()
.package_data()
.idx_model(id)
.and_then(|x| x.installed())
.map(|x| x.status().main())
.make_locker(LockType::Write)
.add_to_keys(locks);
let current_dependents = crate::db::DatabaseModel::new()
.package_data()
.idx_model(id)
.and_then(|x| x.installed())
.map(|x| x.current_dependents())
.make_locker(LockType::Read)
.add_to_keys(locks);
move |skeleton_key| {
Ok(Self {
status: status.verify(skeleton_key)?,
current_dependents: current_dependents.verify(skeleton_key)?,
})
}
}
}
#[instrument(skip_all)]
pub async fn check<Db: DbHandle>(
ctx: &RpcContext,
db: &mut Db,
id: &PackageId,
) -> Result<(), Error> {
let mut tx = db.begin().await?;
let (manifest, started) = {
let mut checkpoint = tx.begin().await?;
let receipts = HealthCheckPreInformationReceipt::new(&mut checkpoint, id).await?;
let manifest = receipts.manifest.get(&mut checkpoint).await?;
let started = receipts.status_model.get(&mut checkpoint).await?.started();
checkpoint.save().await?;
(manifest, started)
};
let health_results = if let Some(started) = started {
tracing::debug!("Checking health of {}", id);
manifest
.health_checks
.check_all(
ctx,
&manifest.containers,
started,
id,
&manifest.version,
&manifest.volumes,
)
.await?
} else {
return Ok(());
};
let current_dependents = {
let mut checkpoint = tx.begin().await?;
let receipts = HealthCheckStatusReceipt::new(&mut checkpoint, id).await?;
let status = receipts.status.get(&mut checkpoint).await?;
if let MainStatus::Running { health: _, started } = status {
receipts
.status
.set(
&mut checkpoint,
MainStatus::Running {
health: health_results.clone(),
started,
},
)
.await?;
}
let current_dependents = receipts.current_dependents.get(&mut checkpoint).await?;
checkpoint.save().await?;
current_dependents
};
let receipts = crate::dependencies::BreakTransitiveReceipts::new(&mut tx).await?;
for (dependent, info) in (current_dependents).0.iter() {
let failures: BTreeMap<HealthCheckId, HealthCheckResult> = health_results
.iter()
.filter(|(_, hc_res)| !matches!(hc_res, HealthCheckResult::Success { .. }))
.filter(|(hc_id, _)| info.health_checks.contains(hc_id))
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
if !failures.is_empty() {
break_transitive(
&mut tx,
&dependent,
id,
DependencyError::HealthChecksFailed { failures },
&mut BTreeMap::new(),
&receipts,
)
.await?;
} else {
heal_transitive(ctx, &mut tx, &dependent, id, &receipts.dependency_receipt).await?;
}
}
tx.save().await?;
Ok(())
}